/*******************************************************************************
* Copyright (c) 2006, 2009 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.servlet;
import java.io.IOException;
import java.util.Locale;
import java.util.Map;
import java.util.WeakHashMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.help.IToc;
import org.eclipse.help.ITopic;
import org.eclipse.help.internal.Topic;
import org.eclipse.help.internal.toc.Toc;
import org.eclipse.help.internal.webapp.WebappResources;
import org.eclipse.help.internal.webapp.data.EnabledTopicUtils;
import org.eclipse.help.internal.webapp.data.IconFinder;
import org.eclipse.help.internal.webapp.data.TocData;
import org.eclipse.help.internal.webapp.data.UrlUtil;
/*
* Creates xml representing selected parts of one or more TOCs depending on the parameters
* With no parameters the head of each toc is included
* With parameter "href" the node and all its ancestors and siblings is included, corresponds to show in toc
* With parameter "toc" and optionally "path" the node, its ancestors and children are included
*/
public class TocFragmentServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static Map locale2Response = new WeakHashMap();
private boolean isErrorSuppress;
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String locale = UrlUtil.getLocale(req, resp);
req.setCharacterEncoding("UTF-8"); //$NON-NLS-1$
resp.setContentType("application/xml; charset=UTF-8"); //$NON-NLS-1$
resp.setHeader("Cache-Control","no-cache"); //$NON-NLS-1$//$NON-NLS-2$
resp.setHeader("Pragma","no-cache"); //$NON-NLS-1$ //$NON-NLS-2$
resp.setDateHeader ("Expires", 0); //$NON-NLS-1$
TocData data = new TocData(this.getServletContext(), req, resp);
readParameters(req);
Serializer serializer = new Serializer(data, req.getLocale());
String response = serializer.generateTreeXml();
locale2Response.put(locale, response);
resp.getWriter().write(response);
}
private void readParameters(HttpServletRequest req) {
String errorSuppressParam = req.getParameter("errorSuppress"); //$NON-NLS-1$
isErrorSuppress = "true".equalsIgnoreCase(errorSuppressParam); //$NON-NLS-1$
}
/*
* Class which creates the xml file based upon the request parameters
*/
private class Serializer {
private TocData tocData;
private StringBuffer buf;
private int requestKind;
private Locale locale;
private static final int REQUEST_SHOW_IN_TOC = 1; // Get the path to an element an element based on its href
private static final int REQUEST_SHOW_TOCS = 2; // Show all the tocs but not their children
private static final int REQUEST_SHOW_CHILDREN = 3; // Show the children of a node
private static final int REQUEST_EXPAND_PATH = 4; // Get all the nodes requires to expand a path in the tree
public Serializer(TocData data, Locale locale) {
tocData = data;
buf = new StringBuffer();
this.locale = locale;
if (tocData.isExpandPath()) {
requestKind = REQUEST_EXPAND_PATH;
} else if (tocData.getTopicHref() != null) {
requestKind = REQUEST_SHOW_IN_TOC;
} else if (tocData.getSelectedToc() == -1) {
requestKind = REQUEST_SHOW_TOCS;
} else {
requestKind = REQUEST_SHOW_CHILDREN;
}
}
public String generateTreeXml() {
buf.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); //$NON-NLS-1$
buf.append("<tree_data>\n"); //$NON-NLS-1$
if (tocData.isRemoteHelpError()) {
addError(WebappResources.getString("remoteHelpErrorMessage", locale)); //$NON-NLS-1$
}
// Return an error for show in toc if topic was not found in toc
if ((requestKind == REQUEST_SHOW_IN_TOC || requestKind == REQUEST_EXPAND_PATH) && tocData.getTopicPath() == null) {
addError(WebappResources.getString("CannotSync", locale)); //$NON-NLS-1$
} else if (requestKind == REQUEST_SHOW_IN_TOC) {
generateNumericPath();
} else {
serializeTocs();
}
buf.append("</tree_data>\n"); //$NON-NLS-1$
return buf.toString();
}
private void generateNumericPath() {
int selectedToc = tocData.getSelectedToc();
if (selectedToc < 0) {
addError(WebappResources.getString("CannotSync", locale)); //$NON-NLS-1$
} else {
// Count the number of enabled tocs
int enabled = 0;
for (int i = 0; i <= selectedToc; i++) {
if (EnabledTopicUtils.isEnabled(tocData.getTocs()[i])) {
enabled++;
}
}
String fullNumericPath = "" + (enabled - 1); //$NON-NLS-1$
String numericPath = tocData.getNumericPath();
if (numericPath != null) {
fullNumericPath = fullNumericPath + '_' + numericPath;
}
buf.append("<numeric_path path=\"" + fullNumericPath + "\"/>\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
private void addError(String message) {
if (!isErrorSuppress) {
buf.append("<error>"); //$NON-NLS-1$
buf.append(XMLGenerator.xmlEscape(message));
buf.append("</error>"); //$NON-NLS-1$
}
}
private void serializeTocs() {
ITopic[] topicPath = tocData.getTopicPath();
int selectedToc = tocData.getSelectedToc();
// Iterate over all tocs - if there is a selected toc only generate that
// toc, otherwise generate the root of every toc.
for (int toc=0; toc< tocData.getTocCount(); toc++) {
boolean shouldLoad = requestKind == REQUEST_SHOW_TOCS || toc == selectedToc;
if(!tocData.isEnabled(toc)){
shouldLoad = false;
}
if (shouldLoad) {
boolean isSelected = false; // Should this node be selected in the tree
if (requestKind == REQUEST_SHOW_TOCS) {
isSelected = toc == 0;
} else if (requestKind == REQUEST_SHOW_CHILDREN) {
isSelected = tocData.getRootPath() == null;
}
serializeToc(tocData.getTocs()[toc], toc, topicPath, isSelected);
}
}
}
private void serializeToc(IToc toc, int tocIndex, ITopic[] topicPath, boolean isSelected) {
if (!EnabledTopicUtils.isEnabled(toc)) {
// do not generate toc when there are no leaf topics
return;
}
ITopic[] topics = toc.getTopics();
if (requestKind == REQUEST_SHOW_CHILDREN) {
topicPath = tocData.getTopicPathFromRootPath(toc);
}
buf.append("<node"); //$NON-NLS-1$
if (toc.getLabel() != null) {
buf.append('\n' + " title=\"" + XMLGenerator.xmlEscape(toc.getLabel()) + '"'); //$NON-NLS-1$
}
buf.append('\n' + " id=\"" + XMLGenerator.xmlEscape(toc.getHref()) + "\""); //$NON-NLS-1$ //$NON-NLS-2$
String href = fixupHref(toc.getTopic(null).getHref(), "" + tocIndex); //$NON-NLS-1$
buf.append('\n' + " href=\"" + XMLGenerator.xmlEscape(UrlUtil.getHelpURL(href)) + "\""); //$NON-NLS-1$ //$NON-NLS-2$
buf.append(createTocImageTag(toc));
boolean serializeChildren = true;
if (requestKind == REQUEST_SHOW_TOCS) {
serializeChildren = false;
}
if (requestKind == REQUEST_EXPAND_PATH && topicPath.length == 0) {
serializeChildren = false;
buf.append('\n' + " is_selected=\"true\"" ); //$NON-NLS-1$
buf.append('\n' + " is_highlighted=\"true\"" ); //$NON-NLS-1$
}
buf.append(">\n"); //$NON-NLS-1$
if (serializeChildren) {
serializeChildTopics(topics, topicPath, "", isSelected); //$NON-NLS-1$
}
buf.append("</node>\n"); //$NON-NLS-1$
}
private void serializeTopic(ITopic topic, ITopic[] topicPath, boolean isSelected, String parentPath) {
ITopic[] subtopics = topic.getSubtopics();
boolean isLeaf = !EnabledTopicUtils.hasEnabledSubtopic(topic);
buf.append("<node"); //$NON-NLS-1$
if (topic.getLabel() != null) {
buf.append('\n' + " title=\"" + XMLGenerator.xmlEscape(topic.getLabel()) + '"'); //$NON-NLS-1$
}
buf.append('\n' + " id=\"" + parentPath + "\""); //$NON-NLS-1$ //$NON-NLS-2$
String href = topic.getHref();
href = fixupHref(href, "" + tocData.getSelectedToc() + '_' + parentPath); //$NON-NLS-1$
buf.append('\n' + " href=\"" + XMLGenerator.xmlEscape( //$NON-NLS-1$
UrlUtil.getHelpURL(href)) + '"');
if (isLeaf ) {
buf.append('\n' + " is_leaf=\"true\"" ); //$NON-NLS-1$
}
if (isSelected && requestKind == REQUEST_EXPAND_PATH) {
buf.append('\n' + " is_selected=\"true\"" ); //$NON-NLS-1$
buf.append('\n' + " is_highlighted=\"true\"" ); //$NON-NLS-1$
}
String imageTags = createTopicImageTags(topic, isLeaf);
buf.append(imageTags);
buf.append(">\n"); //$NON-NLS-1$
serializeChildTopics(subtopics, topicPath, parentPath, isSelected);
buf.append("</node>\n"); //$NON-NLS-1$
}
private String createTocImageTag(IToc toc) {
if (toc instanceof Toc) {
String icon = ((Toc) toc).getIcon();
if (IconFinder.isIconDefined(icon)) {
String openIcon = IconFinder.getImagePathFromId(icon, IconFinder.TYPEICON_OPEN);
String closedIcon = IconFinder.getImagePathFromId(icon, IconFinder.TYPEICON_CLOSED);
String imageTags = '\n' + " openImage=\"/"+ openIcon + "\""; //$NON-NLS-1$ //$NON-NLS-2$
if (!openIcon.equals(closedIcon)) {
imageTags += '\n' + " closedImage=\"/" + closedIcon + "\""; //$NON-NLS-1$ //$NON-NLS-2$
}
String altText = IconFinder.getIconAltFromId(icon);
if(altText != null) {
imageTags += '\n' + " imageAlt=\""+ altText + "\""; //$NON-NLS-1$ //$NON-NLS-2$
}
return imageTags;
}
}
return '\n' + " image=\"toc_closed\""; //$NON-NLS-1$
}
private String createTopicImageTags(ITopic topic, boolean isLeaf) {
if (topic instanceof Topic) {
String icon = ((Topic) topic).getIcon();
String altText = IconFinder.getIconAltFromId(icon);
if (IconFinder.isIconDefined(icon)) {
String imageTags;
if (isLeaf) {
imageTags = '\n' + " openImage=\"/" +IconFinder.getImagePathFromId(icon, IconFinder.TYPEICON_LEAF) + "\""; //$NON-NLS-1$//$NON-NLS-2$
} else {
String openIcon = IconFinder.getImagePathFromId(icon, IconFinder.TYPEICON_OPEN);
String closedIcon = IconFinder.getImagePathFromId(icon, IconFinder.TYPEICON_CLOSED);
imageTags = '\n' + " openImage=\"/" + openIcon+ "\""; //$NON-NLS-1$ //$NON-NLS-2$
if (!openIcon.equals(closedIcon)) {
imageTags += '\n' + " closedImage=\"/" + closedIcon + "\""; //$NON-NLS-1$ //$NON-NLS-2$
}
}
if(altText != null) {
imageTags += '\n' + " imageAlt=\""+ altText + "\""; //$NON-NLS-1$ //$NON-NLS-2$
}
return imageTags;
}
}
String icon;
if (isLeaf) {
icon = "topic"; //$NON-NLS-1$
} else if (topic.getHref() == null) {
icon = "container_obj"; //$NON-NLS-1$
} else {
icon = "container_topic"; //$NON-NLS-1$
}
String imageTags = '\n' + " image=\"" + icon + "\""; //$NON-NLS-1$ //$NON-NLS-2$
return imageTags;
}
private void serializeChildTopics(ITopic[] childTopics, ITopic[] topicPath, String parentPath, boolean parentIsSelected) {
if (parentIsSelected && requestKind == REQUEST_SHOW_CHILDREN) {
// Show the children of this node
for (int subtopic = 0; subtopic < childTopics.length; subtopic++) {
ITopic childTopic = childTopics[subtopic];
if (EnabledTopicUtils.isEnabled(childTopic)) {
serializeTopic(childTopic, null, false, addSuffix(parentPath, subtopic));
}
}
} else if (topicPath != null) {
for (int subtopic = 0; subtopic < childTopics.length; subtopic++) {
ITopic childTopic = childTopics[subtopic];
if (EnabledTopicUtils.isEnabled(childTopic)) {
if (topicPath[0].getLabel().equals(childTopic.getLabel())) {
ITopic[] newPath = null;
if (topicPath.length > 1) {
newPath = new ITopic[topicPath.length - 1];
System.arraycopy(topicPath, 1, newPath, 0, topicPath.length - 1);
}
serializeTopic(childTopic, newPath, topicPath.length == 1, addSuffix(parentPath, subtopic));
} else {
serializeTopic(childTopic, null, false, addSuffix(parentPath, subtopic));
}
}
}
}
}
private String addSuffix(String parentPath, int subtopic) {
if (parentPath.length() == 0) {
return parentPath + subtopic;
}
return parentPath + '_' + subtopic;
}
}
/*
* If the href contains an anchor add a parameter containing the anchor value
* href cannot be null. Static for testing purposes
*/
public static String fixupHref(String href, String path) {
if (href == null) {
return "/../nav/" + path; //$NON-NLS-1$
}
int aIndex = href.indexOf('#');
if (aIndex > 0) {
int questionIndex = href.indexOf('?');
if (questionIndex > 0 && questionIndex < aIndex) {
return href.substring(0, aIndex) + "&path=" + path + href.substring(aIndex); //$NON-NLS-1$
} else {
return href.substring(0, aIndex) + "?path=" + path + href.substring(aIndex); //$NON-NLS-1$
}
}
return href;
}
}
|