/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* For further information about Alkacon Software GmbH, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.opencms.workplace.help;
import org.opencms.file.CmsResource;
import org.opencms.jsp.CmsJspActionElement;
import org.opencms.jsp.CmsJspNavElement;
import org.opencms.main.CmsIllegalArgumentException;
import java.util.List;
import java.util.StringTokenizer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.PageContext;
/**
* Generates a simple TOC - list by using a navigation model
* obtained from a <code>{@link org.opencms.jsp.CmsJspNavBuilder}</code>. <p>
*
* This is a simpler facade to a fixed html - list based layout.
* Only a navigation root path and a desired depth have to be set.
* It is not specific to the online help.<p>
*
* @since 6.0.0
*/
public final class CmsHelpNavigationListView {
/** The depth (in levels) of the navigation. **/
private int m_depth;
/** The CmsJspActionElement to use. **/
private CmsJspActionElement m_jsp;
/** The root path where the navigation will start. **/
private String m_navRootPath;
/**
* Creates a <code>CmsNaviationListView</code> which uses the given
* <code>CmsJspActionElement</code> for accessing the underlying
* navigation API. <p>
*
* @param jsp the <code>CmsJspActionElement</code> to use
*/
public CmsHelpNavigationListView(CmsJspActionElement jsp) {
m_jsp = jsp;
m_navRootPath = m_jsp.getCmsObject().getRequestContext().getUri();
}
/**
* Creates a <code>CmsNaviationListView</code> which creates a
* <code>CmsJspActionElement</code> for accessing the underlying
* navigation API with the given arguments . <p>
*
* @param context the <code>PageContext</code> to use
* @param request the <code>HttpServletRequest</code> to use
* @param response the <code>HttpServletResponse</code> to use
*
*/
public CmsHelpNavigationListView(PageContext context, HttpServletRequest request, HttpServletResponse response) {
this(new CmsJspActionElement(context, request, response));
}
private static String getSpaces(int n) {
// avoid negative NegativeArraySizeException in case uri is missing
n = Math.max(n, 0);
StringBuffer result = new StringBuffer(n);
for (; n > 0; n--) {
result.append(' ');
}
return result.toString();
}
/**
* Returns a string containing the navigation created by using the internal members.<p>
*
* The navigation is a nested html list. <p>
*
* @return a string containing the navigation created by using the internal members
*/
public String createNavigation() {
StringBuffer buffer = new StringBuffer(2048);
int endlevel = calculateEndLevel();
String spaces = getSpaces((endlevel - m_depth) * 2);
if (m_navRootPath != null) {
buffer.append("\n").append(spaces).append("<p>\n");
buffer.append(spaces).append(" <ul>\n");
List navElements = m_jsp.getNavigation().getSiteNavigation(m_navRootPath, endlevel);
if (navElements.size() > 0) {
createNavigationInternal(buffer, navElements);
}
buffer.append(spaces).append(" </ul>\n");
buffer.append(spaces).append("</p>");
return buffer.toString();
} else {
CmsIllegalArgumentException ex = new CmsIllegalArgumentException(Messages.get().container(
Messages.GUI_HELP_ERR_SITEMAP_MISSING_PARAM_1,
"navRootPath"));
throw ex;
}
}
/**
* Returns the depth in levels of the navigation. <p>
*
* @return the depth in levels of the navigation.
*/
public int getDepth() {
return m_depth;
}
/**
* Returns the navigation root path of the navigation to generate a view for. <p>
*
* @return the navigation root path of the navigation to generate a view for.
*/
public String getSiteRootPath() {
return m_navRootPath;
}
/**
* Set the depth in level of the navigation to generate a view for. <p>
*
* @param depth the depth in level of the navigation to generate a view for to set
*/
public void setDepth(int depth) {
m_depth = depth;
}
/**
* Set the navigation root path of the navigation to generate a view for. <p>
*
* The navigation will start there. <p>
*
* @param navRootPath the navigation root path of the navigation to generate a view for to set
*/
public void setNavigationRootPath(String navRootPath) {
m_navRootPath = navRootPath;
}
private int calculateEndLevel() {
int result = 0;
if (m_navRootPath != null) {
// where are we? (start level)
StringTokenizer counter = new StringTokenizer(m_navRootPath, "/", false);
// one less as level 0 nav elements accepted is one level (depth 1).
result = counter.countTokens() - 1;
if (!CmsResource.isFolder(m_navRootPath)) {
// cut stuff like system/workpalce/locale/de/help/index.html
result--;
}
result += m_depth;
}
if (result < 0) {
result = 0;
}
return result;
}
private void createNavigationInternal(StringBuffer buffer, List navElements) {
// take the element to render.
CmsJspNavElement element = (CmsJspNavElement)navElements.remove(0);
int elementLevel = element.getNavTreeLevel();
String spacing = getSpaces(elementLevel * 2);
// render element:
buffer.append(spacing).append("<li>\n");
buffer.append(spacing).append(" <a href=\"");
buffer.append(m_jsp.link(element.getResourceName()));
buffer.append("\" title=\"");
buffer.append(element.getNavText());
buffer.append("\"");
if (elementLevel == 1) {
buffer.append(" class=\"bold\"");
}
buffer.append(">");
buffer.append(element.getNavText());
buffer.append("</a>\n");
// peek at the next (list is depth - first by contract)
if (!navElements.isEmpty()) {
CmsJspNavElement child = (CmsJspNavElement)navElements.get(0);
int childLevel = child.getNavTreeLevel();
if (elementLevel < childLevel) {
// next one goes down a level: it is a child by tree means
buffer.append(spacing).append(" <ul>\n");
} else if (elementLevel == childLevel) {
// it is a sibling: close our list item, no recursion
buffer.append(spacing).append("</li>\n");
} else {
// next element gets up one layer
// this has to happen because of the depth-first contract!
buffer.append(spacing).append(" </li>\n").append(spacing).append("</ul>\n");
}
createNavigationInternal(buffer, navElements);
} else {
// no more next elements: get back and close all lists (by using the recursion we are in)
buffer.append(spacing).append(" </li>\n").append(spacing).append("</ul>\n");
}
}
}