001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * version 2.1 of the License, or (at your option) any later version.
010 *
011 * This library is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * For further information about Alkacon Software GmbH & Co. KG, please see the
017 * company website: http://www.alkacon.com
018 *
019 * For further information about OpenCms, please see the
020 * project website: http://www.opencms.org
021 *
022 * You should have received a copy of the GNU Lesser General Public
023 * License along with this library; if not, write to the Free Software
024 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
025 */
026
027package org.opencms.workplace.help;
028
029import org.opencms.file.CmsResource;
030import org.opencms.jsp.CmsJspActionElement;
031import org.opencms.jsp.CmsJspNavElement;
032import org.opencms.main.CmsIllegalArgumentException;
033
034import java.util.List;
035import java.util.StringTokenizer;
036
037import javax.servlet.http.HttpServletRequest;
038import javax.servlet.http.HttpServletResponse;
039import javax.servlet.jsp.PageContext;
040
041/**
042 * Generates a simple TOC - list by using a navigation model
043 * obtained from a <code>{@link org.opencms.jsp.CmsJspNavBuilder}</code>. <p>
044 *
045 * This is a simpler facade to a fixed html - list based layout.
046 * Only a navigation root path and a desired depth have to be set.
047 * It is not specific to the online help.<p>
048 *
049 * @since 6.0.0
050 */
051public final class CmsHelpNavigationListView {
052
053    /** The depth (in levels) of the navigation. **/
054    private int m_depth;
055
056    /** The CmsJspActionElement to use. **/
057    private CmsJspActionElement m_jsp;
058
059    /** The root path where the navigation will start. **/
060    private String m_navRootPath;
061
062    /**
063     * Creates a <code>CmsNaviationListView</code> which uses the given
064     * <code>CmsJspActionElement</code> for accessing the underlying
065     * navigation API. <p>
066     *
067     * @param jsp the <code>CmsJspActionElement</code> to use
068     */
069    public CmsHelpNavigationListView(CmsJspActionElement jsp) {
070
071        m_jsp = jsp;
072        m_navRootPath = m_jsp.getCmsObject().getRequestContext().getUri();
073
074    }
075
076    /**
077     * Creates a <code>CmsNaviationListView</code> which creates a
078     * <code>CmsJspActionElement</code> for accessing the underlying
079     * navigation API with the given arguments . <p>
080     *
081     * @param context the <code>PageContext</code> to use
082     * @param request the <code>HttpServletRequest</code> to use
083     * @param response the <code>HttpServletResponse</code> to use
084     *
085     */
086    public CmsHelpNavigationListView(PageContext context, HttpServletRequest request, HttpServletResponse response) {
087
088        this(new CmsJspActionElement(context, request, response));
089    }
090
091    /**
092     * Returns a String of spaces.<p>
093     *
094     * @param n the count of spaces
095     *
096     * @return a String of spaces
097     */
098    private static String getSpaces(int n) {
099
100        // avoid negative NegativeArraySizeException in case uri is missing
101        n = Math.max(n, 0);
102        StringBuffer result = new StringBuffer(n);
103        for (; n > 0; n--) {
104            result.append(' ');
105        }
106        return result.toString();
107    }
108
109    /**
110     * Returns a string containing the navigation created by using the internal members.<p>
111     *
112     * The navigation is a nested html list. <p>
113     *
114     * @return a string containing the navigation created by using the internal members
115     */
116    public String createNavigation() {
117
118        StringBuffer buffer = new StringBuffer(2048);
119        int endlevel = calculateEndLevel();
120        String spaces = getSpaces((endlevel - m_depth) * 2);
121        if (m_navRootPath != null) {
122            buffer.append("\n").append(spaces).append("<p>\n");
123            buffer.append(spaces).append("  <ul>\n");
124            List<CmsJspNavElement> navElements = m_jsp.getNavigation().getSiteNavigation(m_navRootPath, endlevel);
125            if (navElements.size() > 0) {
126                createNavigationInternal(buffer, navElements);
127            }
128            buffer.append(spaces).append("  </ul>\n");
129            buffer.append(spaces).append("</p>");
130            return buffer.toString();
131        } else {
132            CmsIllegalArgumentException ex = new CmsIllegalArgumentException(
133                Messages.get().container(Messages.GUI_HELP_ERR_SITEMAP_MISSING_PARAM_1, "navRootPath"));
134            throw ex;
135        }
136    }
137
138    /**
139     * Returns the depth in levels of the navigation. <p>
140     *
141     * @return the depth in levels of the navigation.
142     */
143    public int getDepth() {
144
145        return m_depth;
146    }
147
148    /**
149     * Returns the navigation root path of the navigation to generate a view for. <p>
150     *
151     * @return the navigation root path of the navigation to generate a view for.
152     */
153    public String getSiteRootPath() {
154
155        return m_navRootPath;
156    }
157
158    /**
159     * Set the depth in level of the navigation to generate a view for. <p>
160     *
161     * @param depth  the depth in level of the navigation to generate a view for to set
162     */
163    public void setDepth(int depth) {
164
165        m_depth = depth;
166    }
167
168    /**
169     * Set the navigation root path of the navigation to generate a view for. <p>
170     *
171     * The navigation will start there. <p>
172     *
173     * @param navRootPath the navigation root path of the navigation to generate a view for to set
174     */
175    public void setNavigationRootPath(String navRootPath) {
176
177        m_navRootPath = navRootPath;
178    }
179
180    /**
181     * Calculates and returns the navigation end level.<p>
182     *
183     * @return the navigation end level
184     */
185    private int calculateEndLevel() {
186
187        int result = 0;
188        if (m_navRootPath != null) {
189            // where are we? (start level)
190
191            StringTokenizer counter = new StringTokenizer(m_navRootPath, "/", false);
192            // one less as level 0 nav elements accepted is one level (depth 1).
193            result = counter.countTokens() - 1;
194            if (!CmsResource.isFolder(m_navRootPath)) {
195                // cut stuff like system/workpalce/locale/de/help/index.html
196                result--;
197            }
198            result += m_depth;
199        }
200        if (result < 0) {
201            result = 0;
202        }
203        return result;
204    }
205
206    /**
207     * Creates the HTML for the internal help.<p>
208     *
209     * @param buffer the StringBuffer to which the Navigation will be appended
210     * @param navElements the navigation elements to build the navigation for
211     */
212    private void createNavigationInternal(StringBuffer buffer, List<CmsJspNavElement> navElements) {
213
214        // take the element to render.
215        CmsJspNavElement element = navElements.remove(0);
216        int elementLevel = element.getNavTreeLevel();
217        String spacing = getSpaces(elementLevel * 2);
218        // render element:
219        buffer.append(spacing).append("<li>\n");
220        buffer.append(spacing).append("  <a href=\"");
221        buffer.append(m_jsp.link(element.getResourceName()));
222        buffer.append("\" title=\"");
223        buffer.append(element.getNavText());
224        buffer.append("\"");
225        if (elementLevel == 1) {
226            buffer.append(" class=\"bold\"");
227        }
228        buffer.append(">");
229        buffer.append(element.getNavText());
230        buffer.append("</a>\n");
231
232        // peek at the next (list is depth - first by contract)
233        if (!navElements.isEmpty()) {
234            CmsJspNavElement child = navElements.get(0);
235            int childLevel = child.getNavTreeLevel();
236            if (elementLevel < childLevel) {
237                // next one goes down a level: it is a child by tree means
238                buffer.append(spacing).append("  <ul>\n");
239            } else if (elementLevel == childLevel) {
240                // it is a sibling: close our list item, no recursion
241                buffer.append(spacing).append("</li>\n");
242            } else {
243                // next element gets up one layer
244                // this has to happen because of the depth-first contract!
245                buffer.append(spacing).append("  </li>\n").append(spacing).append("</ul>\n");
246            }
247            createNavigationInternal(buffer, navElements);
248        } else {
249            // no more next elements: get back and close all lists (by using the recursion we are in)
250            buffer.append(spacing).append("  </li>\n").append(spacing).append("</ul>\n");
251        }
252
253    }
254}