/******************************************************************************* * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.help.internal.webapp.data; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.help.IToc; import org.eclipse.help.ITopic; import org.eclipse.help.UAContentFilter; import org.eclipse.help.base.AbstractHelpScope; import org.eclipse.help.internal.HelpPlugin; import org.eclipse.help.internal.base.HelpBasePlugin; import org.eclipse.help.internal.base.HelpEvaluationContext; import org.eclipse.help.internal.base.remote.RemoteHelp; import org.eclipse.help.internal.base.scope.ScopeUtils; import org.eclipse.help.internal.webapp.servlet.TocFragmentServlet; /** * Helper class for tocView.jsp initialization */ public class TocData extends ActivitiesData { public static final String COMPLETE_PATH_PARAM = "cp"; //$NON-NLS-1$ // Request parameters private String tocParameter; private String topicHref; private String expandPathParam; private String completePath; // help form of selected topic href private String topicHelpHref; // Selected TOC private int selectedToc = -1; // path from TOC to the root topic of the TOC fragment private int[] rootPath = null; // path from TOC to the selected topic, excluding TOC; private ITopic[] topicPath = null; // String representing the topic path as numbers separated by underscores e.g. 2_4_3 private String numericPath; // List of TOC's, unfiltered private IToc[] tocs; // images directory private String imagesDirectory; // Scope private AbstractHelpScope scope; /** * Constructs the xml data for the contents page. * * @param context * @param request */ public TocData(ServletContext context, HttpServletRequest request, HttpServletResponse response) { super(context, request, response); this.tocParameter = request.getParameter("toc"); //$NON-NLS-1$ this.topicHref = request.getParameter("topic"); //$NON-NLS-1$ this.completePath = request.getParameter(COMPLETE_PATH_PARAM); this.expandPathParam = request.getParameter("expandPath"); //$NON-NLS-1$ this.scope = RequestScope.getScope(request, response, false); if (tocParameter != null && tocParameter.length() == 0) tocParameter = null; if (topicHref != null && topicHref.length() == 0) topicHref = null; if (expandPathParam != null && expandPathParam.length() == 0) expandPathParam = null; if (completePath != null && completePath.length() == 0) completePath = null; String anchor = request.getParameter("anchor"); //$NON-NLS-1$ if (topicHref != null && anchor != null) { topicHref = topicHref + '#' + anchor; } // initialize rootPath String pathStr = request.getParameter("path"); //$NON-NLS-1$ if (pathStr != null && pathStr.length() > 0) { String[] paths = pathStr.split("_", -1); //$NON-NLS-1$ int[] indexes = new int[paths.length]; boolean indexesOK = true; for (int i = 0; i < paths.length; i++) { try { indexes[i] = Integer.parseInt(paths[i]); } catch (NumberFormatException nfe) { indexesOK = false; break; } if (indexesOK) { rootPath = indexes; } } } imagesDirectory = preferences.getImagesDirectory(); loadTocs(); } public boolean isRemoteHelpError() { boolean isError = (RemoteHelp.getError() != null); if (isError) { RemoteHelp.clearError(); } return isError; } // Accessor methods to avoid exposing help classes directly to JSP. // Note: this seems ok for now, but maybe we need to reconsider this // and allow help classes in JSP's. public int getTocCount() { return tocs.length; } public String getTocLabel(int i) { return tocs[i].getLabel(); } public String getTocHref(int i) { return tocs[i].getHref(); } public String getTocDescriptionTopic(int i) { return UrlUtil.getHelpURL(tocs[i].getTopic(null).getHref()); } /** * Returns the selected TOC * * @return int */ public int getSelectedToc() { return selectedToc; } /** * Returns the topic to display. If there is a TOC, return its topic * description. Return null if no topic is specified and there is no toc * description. * * @return String */ public String getSelectedTopic() { if (topicHref != null && topicHref.length() > 0) return UrlUtil.getHelpURL(topicHref); else if (selectedToc == -1) return null; IToc toc = tocs[selectedToc]; ITopic tocDescription = toc.getTopic(null); if (tocDescription != null) return UrlUtil.getHelpURL(tocDescription.getHref()); return UrlUtil.getHelpURL(null); } /** * Returns the topic to display. If there is a TOC, return its topic * description. Return null if no topic is specified and there is no toc * description. * * @return String */ public String getSelectedTopicWithPath() { String href = getSelectedTopic(); if ( completePath != null ) { href = TocFragmentServlet.fixupHref(href, completePath); } return href; } /** * Returns a list of all the TOC's as xml elements. Individual TOC's are not * loaded yet. * * @return IToc[] */ public IToc[] getTocs() { return tocs; } /** * Check if given TOC is visible * * @param toc * @return true if TOC should be visible */ public boolean isEnabled(int toc) { return ScopeUtils.showInTree(tocs[toc], scope); } /** * Check if given TOC is visible * * @param toc * @return true if TOC should be visible */ private boolean isEnabled(IToc toc) { if(!isAdvancedUI()){ // activities never filtered for basic browsers return true; } return HelpBasePlugin.getActivitySupport().isEnabled(toc.getHref()) && !UAContentFilter.isFiltered(toc, HelpEvaluationContext.getContext()); } private void loadTocs() { tocs = HelpPlugin.getTocManager().getTocs(getLocale()); // Find the requested TOC selectedToc = -1; if (isExpandPath()) { getEnabledTopicPath(); } else if (tocParameter != null && tocParameter.length() > 0) { for (int i = 0; selectedToc == -1 && i < tocs.length; i++) { if (tocParameter.equals(tocs[i].getHref())) { selectedToc = i; } } } else if ( completePath != null ) { // obtain the TOC from the complete path TopicFinder finder = new TopicFinder("/nav/" + completePath, tocs, scope); //$NON-NLS-1$ topicPath = finder.getTopicPath(); selectedToc = finder.getSelectedToc(); numericPath = finder.getNumericPath(); } else { // toc not specified as parameter // try obtaining the TOC from the topic TopicFinder finder = new TopicFinder(topicHref, tocs, scope); topicPath = finder.getTopicPath(); selectedToc = finder.getSelectedToc(); numericPath = finder.getNumericPath(); } } private void getEnabledTopicPath() { int[] path = UrlUtil.splitPath(expandPathParam); if (path != null) { // path[0] is the index of enabled TOCS, convert to an index into all TOCS int enabled = path[0] + 1; for (int i = 0; enabled > 0 && i < tocs.length; i++) { if (ScopeUtils.showInTree(tocs[i], scope)) { enabled--; if (enabled == 0) { selectedToc = i; } } } if (selectedToc != -1) { topicPath = decodePath(path, tocs[selectedToc], scope); } } else { selectedToc = -1; } } public static ITopic[] decodePath(int[] path, IToc toc, AbstractHelpScope scope) { ITopic[] topicPath = new ITopic[path.length - 1]; try { if (path.length > 1) { ITopic[] topics = toc.getTopics(); ITopic[] enabledTopics = ScopeUtils.inScopeTopics(topics, scope); topicPath[0] = enabledTopics[path[1]]; } for (int i = 1; i < topicPath.length; i++) { ITopic[] topics = topicPath[i-1].getSubtopics(); ITopic[] enabledTopics = ScopeUtils.inScopeTopics(topics, scope); topicPath[i] = enabledTopics[path[i+1]]; } } catch (RuntimeException e) { return null; } return topicPath; } /** * Generates the HTML code (a tree) for a TOC. * * @param toc * @param out * @throws IOException */ public void generateBasicToc(int toc, Writer out) throws IOException { ITopic[] topics = getEnabledSubtopics(tocs[toc]); for (ITopic topic : topics) { generateBasicTopic(topic, out); } } private void generateBasicTopic(ITopic topic, Writer out) throws IOException { out.write("<li>"); //$NON-NLS-1$ ITopic[] topics = getEnabledSubtopics(topic); boolean hasNodes = topics.length > 0; if (hasNodes) { out.write("<nobr>"); //$NON-NLS-1$ out.write("<a "); //$NON-NLS-1$ if (getSelectedTopicHelpHref().equals(topic.getHref())) { out.write("name=\"selectedItem\" "); //$NON-NLS-1$ } out.write("href="+"\"" + UrlUtil.getHelpURL(topic.getHref())+"\"" + ">"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ out.write("<img src='"); //$NON-NLS-1$ out.write(imagesDirectory); out.write("/container_obj.gif' alt=\"\" border=0> "); //$NON-NLS-1$ out.write(UrlUtil.htmlEncode(topic.getLabel())); out.write("</a>"); //$NON-NLS-1$ out.write("</nobr>"); //$NON-NLS-1$ out.write("<ul>\n"); //$NON-NLS-1$ for (ITopic topic2 : topics) { generateBasicTopic(topic2, out); } out.write("</ul>\n"); //$NON-NLS-1$ } else { out.write("<nobr>"); //$NON-NLS-1$ out.write("<a "); //$NON-NLS-1$ if (getSelectedTopicHelpHref().equals(topic.getHref())) { out.write("name=\"selectedItem\" "); //$NON-NLS-1$ } out.write("href="+"\"" + UrlUtil.getHelpURL(topic.getHref()) +"\""+ ">"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ out.write("<img src='"); //$NON-NLS-1$ out.write(imagesDirectory); out.write("/topic.gif' alt=\"\" border=0> "); //$NON-NLS-1$ out.write(UrlUtil.htmlEncode(topic.getLabel())); out.write("</a>"); //$NON-NLS-1$ out.write("</nobr>"); //$NON-NLS-1$ } out.write("</li>\n"); //$NON-NLS-1$ } /** * @return String - help form of selected topic URL, or "" */ private String getSelectedTopicHelpHref() { if (topicHelpHref == null) { String topic = getSelectedTopic(); if (topic == null || topic.length() == 0) { topicHelpHref = ""; //$NON-NLS-1$ return topicHelpHref; } int index = topic.indexOf("/topic/"); //$NON-NLS-1$ if (index != -1) topic = topic.substring(index + 6); index = topic.indexOf('?'); if (index != -1) topic = topic.substring(0, index); topicHelpHref = topic; if (topic == null) { topicHelpHref = ""; //$NON-NLS-1$ } } return topicHelpHref; } /** * Obtains children topics for a given navigation element. Topics from TOCs * not matching enabled activities are filtered out. * * @param element ITopic or IToc * @return ITopic[] */ public ITopic[] getEnabledSubtopics(Object element) { List<ITopic> topics = getEnabledSubtopicList(element); return topics.toArray(new ITopic[topics.size()]); } /** * Obtains children topics for a given navigation element. Topics from TOCs * not matching enabled activities are filtered out. * * @param navigationElement * @return List of ITopic */ private List<ITopic> getEnabledSubtopicList(Object element) { if (element instanceof IToc && !isEnabled((IToc) element)) return Collections.emptyList(); List<ITopic> children; if (element instanceof IToc) { children = Arrays.asList(((IToc)element).getTopics()); } else if (element instanceof ITopic) { children = Arrays.asList(((ITopic)element).getSubtopics()); } else { // unknown element type return Collections.emptyList(); } List<ITopic> childTopics = new ArrayList<>(children.size()); for (ITopic iTopic : children) { Object c = iTopic; if ((c instanceof ITopic)) { // add topic only if it will not end up being an empty // container if (((((ITopic) c).getHref() != null && ((ITopic) c) .getHref().length() > 0) || getEnabledSubtopicList(c).size() > 0) && !UAContentFilter.isFiltered(c, HelpEvaluationContext.getContext())) { childTopics.add((ITopic) c); } } else { // it is a Toc, Anchor or Link, // which may have children attached to it. childTopics.addAll(getEnabledSubtopicList(c)); } } return childTopics; } private void generateTopicLinks(ITopic topic, Writer w, int indent) { String topicHref = topic.getHref(); try { if (indent == 0) w.write("<b>"); //$NON-NLS-1$ for (int tab = 0; tab < indent; tab++) { w.write("  "); //$NON-NLS-1$ } if (topicHref != null && topicHref.length() > 0) { w.write("<a href=\""); //$NON-NLS-1$ if ('/' == topicHref.charAt(0)) { w.write("topic"); //$NON-NLS-1$ } w.write(topicHref); w.write("\">"); //$NON-NLS-1$ w.write(UrlUtil.htmlEncode(topic.getLabel())); w.write("</a>"); //$NON-NLS-1$ } else { w.write(UrlUtil.htmlEncode(topic.getLabel())); } w.write("<br>\n"); //$NON-NLS-1$ if (indent == 0) w.write("</b>"); //$NON-NLS-1$ } catch (IOException ioe) { } ITopic[] topics = topic.getSubtopics(); for (ITopic topic2 : topics) { generateTopicLinks(topic2, w, indent + 1); } } public void generateLinks(Writer out) { for (IToc toc : tocs) { ITopic tocTopic = toc.getTopic(null); generateTopicLinks(tocTopic, out, 0); ITopic[] topics = toc.getTopics(); for (ITopic topic : topics) { generateTopicLinks(topic, out, 1); } } } public ITopic[] getTopicPathFromRootPath(IToc toc) { ITopic[] topicPath; // Determine the topicPath from the path passed in as a parameter int[] rootPath = getRootPath(); if (rootPath == null) { return null; } int pathLength = rootPath.length; topicPath = new ITopic[pathLength]; ITopic[] children = toc.getTopics(); for (int i = 0; i < pathLength; i++) { int index = rootPath[i]; if (index < children.length) { topicPath[i] = children[index]; children = topicPath[i].getSubtopics(); } else { return null; // Mismatch between expected and actual children } } return topicPath; } public ITopic[] getTopicPath() { return topicPath; } public int[] getRootPath() { return rootPath; } public String getTopicHref() { return topicHref; } public String getNumericPath() { return numericPath; } public boolean isExpandPath() { return expandPathParam != null; } }