diff options
author | Mazen Faraj | 2005-11-28 03:38:50 +0000 |
---|---|---|
committer | Mazen Faraj | 2005-11-28 03:38:50 +0000 |
commit | 94b0333b1532bbc381b6190eaa74cb37af4ece29 (patch) | |
tree | dd9abb481ea8c73b90f9f0222b64c7512b2d4cc9 /org.eclipse.help | |
parent | 3e91317d66d832f76be14e2f55fd57ba41bc8148 (diff) | |
download | eclipse.platform.ua-94b0333b1532bbc381b6190eaa74cb37af4ece29.tar.gz eclipse.platform.ua-94b0333b1532bbc381b6190eaa74cb37af4ece29.tar.xz eclipse.platform.ua-94b0333b1532bbc381b6190eaa74cb37af4ece29.zip |
*** empty log message ***
Diffstat (limited to 'org.eclipse.help')
11 files changed, 1059 insertions, 116 deletions
diff --git a/org.eclipse.help/META-INF/MANIFEST.MF b/org.eclipse.help/META-INF/MANIFEST.MF index f99303ff1..a12eff88f 100644 --- a/org.eclipse.help/META-INF/MANIFEST.MF +++ b/org.eclipse.help/META-INF/MANIFEST.MF @@ -13,6 +13,7 @@ Export-Package: org.eclipse.help, org.eclipse.help.internal.model;x-friends:="org.eclipse.help.base,org.eclipse.help.ui,org.eclipse.help.webapp", org.eclipse.help.internal.protocols;x-friends:="org.eclipse.help.base,org.eclipse.help.ui,org.eclipse.help.webapp", org.eclipse.help.internal.toc;x-friends:="org.eclipse.help.base,org.eclipse.help.ui,org.eclipse.help.webapp", - org.eclipse.help.internal.util;x-friends:="org.eclipse.help.base,org.eclipse.help.ui,org.eclipse.help.webapp" + org.eclipse.help.internal.util;x-friends:="org.eclipse.help.base,org.eclipse.help.ui,org.eclipse.help.webapp", + org.eclipse.help.internal.xhtml;x-friends:="org.eclipse.help.ui" Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.1.0,4.0.0)" Eclipse-LazyStart: true diff --git a/org.eclipse.help/plugin.xml b/org.eclipse.help/plugin.xml index 47e42c42a..ddcb4154e 100644 --- a/org.eclipse.help/plugin.xml +++ b/org.eclipse.help/plugin.xml @@ -7,4 +7,5 @@ <extension-point id="contentProducer" name="%content_producer_extension_point_name" schema="schema/contentProducer.exsd"/> <extension-point id="index" name="%index_extension_point_name"/> + </plugin> diff --git a/org.eclipse.help/src/org/eclipse/help/internal/util/ResourceLocator.java b/org.eclipse.help/src/org/eclipse/help/internal/util/ResourceLocator.java index 4d7ce1e3e..68ca4595c 100644 --- a/org.eclipse.help/src/org/eclipse/help/internal/util/ResourceLocator.java +++ b/org.eclipse.help/src/org/eclipse/help/internal/util/ResourceLocator.java @@ -1,25 +1,25 @@ -/******************************************************************************* - * Copyright (c) 2000, 2004 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 +/*************************************************************************************************** + * Copyright (c) 2000, 2004 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 - *******************************************************************************/ + * Contributors: IBM Corporation - initial API and implementation + **************************************************************************************************/ package org.eclipse.help.internal.util; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; -import java.util.*; import java.util.ArrayList; +import java.util.Enumeration; import java.util.HashMap; +import java.util.HashSet; import java.util.Hashtable; import java.util.Locale; import java.util.Map; +import java.util.Set; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; @@ -36,6 +36,7 @@ import org.eclipse.help.internal.HelpPlugin; import org.osgi.framework.Bundle; public class ResourceLocator { + public static final String CONTENTPRODUCER_XP_NAME = "contentProducer"; //$NON-NLS-1$ public static final String CONTENTPRODUCER_XP_FULLNAME = HelpPlugin.PLUGIN_ID @@ -51,32 +52,31 @@ public class ResourceLocator { // Map of document content providers by plug-in ID; private static Map contentProducers = new HashMap(2, 0.5f); static { - Platform.getExtensionRegistry().addRegistryChangeListener( - new IRegistryChangeListener() { - /* - * (non-Javadoc) - * - * @see org.eclipse.core.runtime.IRegistryChangeListener#registryChanged(org.eclipse.core.runtime.IRegistryChangeEvent) - */ - public void registryChanged(IRegistryChangeEvent event) { - IExtensionDelta[] deltas = event.getExtensionDeltas( - HelpPlugin.PLUGIN_ID, CONTENTPRODUCER_XP_NAME); - for (int i = 0; i < deltas.length; i++) { - IExtension extension = deltas[i].getExtension(); - String affectedPlugin = extension.getNamespace(); - // reset producer for the affected plugin, - // it will be recreated on demand - synchronized (contentProducers) { - contentProducers.remove(affectedPlugin); - } - } + Platform.getExtensionRegistry().addRegistryChangeListener(new IRegistryChangeListener() { + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.IRegistryChangeListener#registryChanged(org.eclipse.core.runtime.IRegistryChangeEvent) + */ + public void registryChanged(IRegistryChangeEvent event) { + IExtensionDelta[] deltas = event.getExtensionDeltas(HelpPlugin.PLUGIN_ID, + CONTENTPRODUCER_XP_NAME); + for (int i = 0; i < deltas.length; i++) { + IExtension extension = deltas[i].getExtension(); + String affectedPlugin = extension.getNamespace(); + // reset producer for the affected plugin, + // it will be recreated on demand + synchronized (contentProducers) { + contentProducers.remove(affectedPlugin); } - }); + } + } + }); } /** - * Obtains content proivider for a documentation plug-in, creates one if - * necessary. + * Obtains content proivider for a documentation plug-in, creates one if necessary. * * @param pluginId * @return ITopicContentProvider or null @@ -107,8 +107,7 @@ public class ResourceLocator { * @return ITopicContentProvider or null */ private static IHelpContentProducer createContentProducer(String pluginId) { - IExtensionPoint xp = Platform.getExtensionRegistry().getExtensionPoint( - CONTENTPRODUCER_XP_FULLNAME); + IExtensionPoint xp = Platform.getExtensionRegistry().getExtensionPoint(CONTENTPRODUCER_XP_FULLNAME); if (xp == null) { return null; } @@ -117,15 +116,13 @@ public class ResourceLocator { if (!extensions[i].getNamespace().equals(pluginId)) { continue; } - IConfigurationElement[] elements = extensions[i] - .getConfigurationElements(); + IConfigurationElement[] elements = extensions[i].getConfigurationElements(); for (int j = 0; j < elements.length; j++) { if (!CONTENTPRODUCER_XP_NAME.equals(elements[j].getName())) { continue; } try { - Object o = elements[j] - .createExecutableExtension("producer"); //$NON-NLS-1$ + Object o = elements[j].createExecutableExtension("producer"); //$NON-NLS-1$ if (o instanceof IHelpContentProducer) { return (IHelpContentProducer) o; } @@ -140,13 +137,10 @@ public class ResourceLocator { } /** - * Opens an input stream to a file contained in a plugin. This includes NL - * lookup. + * Opens an input stream to a file contained in a plugin. This includes NL lookup. */ - public static InputStream openFromProducer(Bundle pluginDesc, String file, - String locale) { - IHelpContentProducer producer = getContentProducer(pluginDesc - .getSymbolicName()); + public static InputStream openFromProducer(Bundle pluginDesc, String file, String locale) { + IHelpContentProducer producer = getContentProducer(pluginDesc.getSymbolicName()); if (producer == null) { return null; } @@ -165,23 +159,19 @@ public class ResourceLocator { } /** - * Opens an input stream to a file contained in a plugin. This includes - * includes OS, WS and NL lookup. + * Opens an input stream to a file contained in a plugin. This includes includes OS, WS and NL + * lookup. * * @param pluginId - * the plugin id of the plugin that contains the file you are - * trying to find + * the plugin id of the plugin that contains the file you are trying to find * @param file * the relative path of the file to find * @param locale - * the locale used as an override or <code>null</code> to use - * the default locale + * the locale used as an override or <code>null</code> to use the default locale * - * @return an InputStream to the file or <code>null</code> if the file - * wasn't found + * @return an InputStream to the file or <code>null</code> if the file wasn't found */ - public static InputStream openFromPlugin(String pluginId, String file, - String locale) { + public static InputStream openFromPlugin(String pluginId, String file, String locale) { Bundle bundle = Platform.getBundle(pluginId); if (bundle != null) return openFromPlugin(bundle, file, locale); @@ -189,32 +179,29 @@ public class ResourceLocator { } /** - * Opens an input stream to a file contained in a zip in a plugin. This - * includes OS, WS and NL lookup. + * Opens an input stream to a file contained in a zip in a plugin. This includes OS, WS and NL + * lookup. * * @param pluginDesc - * the plugin description of the plugin that contains the file - * you are trying to find + * the plugin description of the plugin that contains the file you are trying to find * @param file * the relative path of the file to find * @param locale - * the locale used as an override or <code>null</code> to use - * the default locale + * the locale used as an override or <code>null</code> to use the default locale * - * @return an InputStream to the file or <code>null</code> if the file - * wasn't found + * @return an InputStream to the file or <code>null</code> if the file wasn't found */ public static InputStream openFromZip(Bundle pluginDesc, String zip, String file, String locale) { - + String pluginID = pluginDesc.getSymbolicName(); Map cache = zipCache; ArrayList pathPrefix = getPathPrefix(locale); - + for (int i = 0; i < pathPrefix.size(); i++) { - + // finds the zip file by either using a cached location, or // calling Platform.find - the result is cached for future use. - Object cached = cache.get(pluginID + '/'+ pathPrefix.get(i) + zip); + Object cached = cache.get(pluginID + '/' + pathPrefix.get(i) + zip); if (cached == null) { try { URL url = Platform.find(pluginDesc, new Path(pathPrefix.get(i) + zip)); @@ -229,11 +216,11 @@ public class ResourceLocator { } // cache it cache.put(pluginID + '/' + pathPrefix.get(i) + zip, cached); - } + } if (cached == ZIP_NOT_FOUND || cached.toString().startsWith("jar:")) //$NON-NLS-1$ continue; - + // cached should be a zip file that is actually on the filesystem // now check if the file is in this zip try { @@ -246,32 +233,29 @@ public class ResourceLocator { // a file not found exception is an io exception continue; } - + } // end for loop - + // we didn't find the file in any zip return null; } /** - * Opens an input stream to a file contained in a plugin. This includes - * includes OS, WS and NL lookup. + * Opens an input stream to a file contained in a plugin. This includes includes OS, WS and NL + * lookup. * * @param pluginDesc - * the plugin description of the plugin that contains the file - * you are trying to find + * the plugin description of the plugin that contains the file you are trying to find * @param file * the relative path of the file to find * @param locale - * the locale used as an override or <code>null</code> to use - * the default locale + * the locale used as an override or <code>null</code> to use the default locale * - * @return an InputStream to the file or <code>null</code> if the file - * wasn't found + * @return an InputStream to the file or <code>null</code> if the file wasn't found */ public static InputStream openFromPlugin(Bundle pluginDesc, String file, String locale) { + ArrayList pathPrefix = getPathPrefix(locale); - URL flatFileURL = find(pluginDesc, new Path(file), pathPrefix); if (flatFileURL != null) try { @@ -282,79 +266,75 @@ public class ResourceLocator { return null; } + + /* - * Search the ws, os then nl for a resource. Platform.find can't be used - * directly with $nl$, $os$ or $ws$ becuase the root directory will be - * searched too early. + * Search the ws, os then nl for a resource. Platform.find can't be used directly with $nl$, + * $os$ or $ws$ becuase the root directory will be searched too early. */ - private static URL find(Bundle pluginDesc, IPath flatFilePath, ArrayList pathPrefix) { - - // try to find the actual file. + public static URL find(Bundle pluginDesc, IPath flatFilePath, ArrayList pathPrefix) { + + // try to find the actual file. for (int i = 0; i < pathPrefix.size(); i++) { - URL url = Platform.find(pluginDesc, new Path((String)pathPrefix.get(i) + flatFilePath)); + URL url = Platform.find(pluginDesc, new Path((String) pathPrefix.get(i) + flatFilePath)); if (url != null) return url; } - return null; + return null; } - + public static void clearZipCache() { zipCache = new Hashtable(); } - + /* * Gets an ArrayList that has the path prefixes to search. * - * @param locale - * the locale used as an override or <code>null</code> to use - * the default locale - * @return an ArrayList that has path prefixes that need to be search. The - * returned ArrayList will have an entry for the root of the plugin. + * @param locale the locale used as an override or <code>null</code> to use the default locale + * @return an ArrayList that has path prefixes that need to be search. The returned ArrayList + * will have an entry for the root of the plugin. */ public static ArrayList getPathPrefix(String locale) { - ArrayList pathPrefix = new ArrayList(5); - // TODO add override for ws and os similar to how it's done with locale now + ArrayList pathPrefix = new ArrayList(5); + // TODO add override for ws and os similar to how it's done with locale + // now String ws = Platform.getWS(); String os = Platform.getOS(); if (locale == null) locale = Platform.getNL(); - - if (ws != null) + + if (ws != null) pathPrefix.add("ws/" + ws + '/'); //$NON-NLS-1$ - + if (os != null && !os.equals("OS_UNKNOWN")) //$NON-NLS-1$ pathPrefix.add("os/" + os + '/'); //$NON-NLS-1$ - - if (locale != null && locale.length() >= 5) + + if (locale != null && locale.length() >= 5) pathPrefix.add("nl/" + locale.substring(0, 2) + '/' + locale.substring(3, 5) + '/'); //$NON-NLS-1$ - + if (locale != null && locale.length() >= 2) pathPrefix.add("nl/" + locale.substring(0, 2) + '/'); //$NON-NLS-1$ - + // the plugin root - pathPrefix.add(""); //$NON-NLS-1$ - + pathPrefix.add(""); //$NON-NLS-1$ + return pathPrefix; } /** - * Finds all topics under specified directory (recursively). This includes - * includes OS, WS and NL lookup. + * Finds all topics under specified directory (recursively). This includes includes OS, WS and + * NL lookup. * * @param pluginDesc - * the plugin description of the plugin that contains the file - * you are trying to find + * the plugin description of the plugin that contains the file you are trying to find * @param directory * the relative path of the directory * @param locale - * the locale used as an override or <code>null</code> to use - * the default locale + * the locale used as an override or <code>null</code> to use the default locale * - * @return an InputStream to the file or <code>null</code> if the file - * wasn't found + * @return an InputStream to the file or <code>null</code> if the file wasn't found */ - public static Set findTopicPaths(Bundle pluginDesc, String directory, - String locale) { + public static Set findTopicPaths(Bundle pluginDesc, String directory, String locale) { Set ret = new HashSet(); findTopicPaths(pluginDesc, directory, locale, ret); return ret; @@ -366,8 +346,7 @@ public class ResourceLocator { * @param locale * @param paths */ - private static void findTopicPaths(Bundle pluginDesc, String directory, - String locale, Set paths) { + private static void findTopicPaths(Bundle pluginDesc, String directory, String locale, Set paths) { if (directory.endsWith("/")) //$NON-NLS-1$ directory = directory.substring(0, directory.length() - 1); ArrayList pathPrefix = getPathPrefix(locale); diff --git a/org.eclipse.help/src/org/eclipse/help/internal/xhtml/AbstractUAElement.java b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/AbstractUAElement.java new file mode 100644 index 000000000..8f85f11b2 --- /dev/null +++ b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/AbstractUAElement.java @@ -0,0 +1,94 @@ +/*************************************************************************************************** + * Copyright (c) 2004, 2005 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.xhtml; + +import org.osgi.framework.Bundle; +import org.w3c.dom.Element; + + +public abstract class AbstractUAElement { + + private Bundle bundle; + + AbstractUAElement() { + } + + + /** + * Constructor used when model elements are being loaded from an xml content file. Bundle is + * propagated down the model to enable resolving resources relative to the base of the bundle. + * + * @param element + * @param pd + */ + AbstractUAElement(Element element, Bundle bundle) { + this.bundle = bundle; + } + + + /** + * Constructor used when model elements are being loaded from an xml content file. Bundle AND + * base is propagated down the model to enable resolving resources relative to the xml content + * file. The base is set to point to the relative location of the parent folder that holds the + * content file. In the case of a configExtension, it is set to point to the relative position + * of the parent folder that holds the extension. Only when needed, the base field is stored in + * a model element. This saves memory. + * + * @param element + * @param pd + */ + AbstractUAElement(Element element, Bundle bundle, String base) { + this(element, bundle); + } + + + + /** + * DOM getAttribute retruns an empty string (not null) if attribute is not defined. Override + * this behavior. + * + * @param element + * @param att + * @return + */ + protected String getAttribute(Element element, String att) { + if (element.hasAttribute(att)) + return element.getAttribute(att); + return null; + } + + /** + * Util method to parse a comma separated list of values + * + * @param element + * @param att + * @return + */ + protected String[] getAttributeList(Element element, String att) { + if (element.hasAttribute(att)) + return element.getAttribute(att).split(","); //$NON-NLS-1$ + return null; + } + + + /** + * Returns the plugin descriptor of the plugin from which this element was loaded. In the case + * of extension, returns the plugin descriptor of the plugin defining the extension. + * + * @return + */ + public Bundle getBundle() { + return bundle; + } + + + + +}
\ No newline at end of file diff --git a/org.eclipse.help/src/org/eclipse/help/internal/xhtml/BundleUtil.java b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/BundleUtil.java new file mode 100644 index 000000000..3268c5bfb --- /dev/null +++ b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/BundleUtil.java @@ -0,0 +1,32 @@ +/*************************************************************************************************** + * Copyright (c) 2004, 2005 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.xhtml; + +import java.net.URL; + +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.osgi.framework.Bundle; + +/** + * Bundle convenience methods. + */ +public class BundleUtil { + + /* + * Util method to return an URL to a plugin relative resource. + */ + public static URL getResourceAsURL(String resource, String pluginId) { + Bundle bundle = Platform.getBundle(pluginId); + URL localLocation = localLocation = Platform.find(bundle, new Path(resource)); + return localLocation; + } + +} diff --git a/org.eclipse.help/src/org/eclipse/help/internal/xhtml/DOMUtil.java b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/DOMUtil.java new file mode 100644 index 000000000..bde650ec9 --- /dev/null +++ b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/DOMUtil.java @@ -0,0 +1,239 @@ +/*************************************************************************************************** + * Copyright (c) 2004, 2005 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.xhtml; + +import java.util.Enumeration; +import java.util.Properties; +import java.util.Vector; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + + +/** + * Util class for model. Has methods for resolving model attributes, and methods for manipulating + * XHTML DOM. + */ +public class DOMUtil { + + public static String TAG_BODY = "body"; //$NON-NLS-1$ + private static String TAG_HEAD = "head"; //$NON-NLS-1$ + public static String TAG_BASE = "base"; //$NON-NLS-1$ + public static String TAG_DIV = "div"; //$NON-NLS-1$ + public static String TAG_HEAD_LINK = "link"; //$NON-NLS-1$ + public static String TAG_FILTER = "filter"; //$NON-NLS-1$ + public static String ATT_HREF = "href"; //$NON-NLS-1$ + public static String ATT_REL = "rel"; //$NON-NLS-1$ + public static String ATT_TYPE = "type"; //$NON-NLS-1$ + public static String ATT_FILTER = "filter"; //$NON-NLS-1$ + + + + + public static void insertBase(Document dom, String baseURL) { + // there should only be one head and one base element dom. + NodeList headList = dom.getElementsByTagName(TAG_HEAD); + Element head = (Element) headList.item(0); + NodeList baseList = head.getElementsByTagName(TAG_BASE); + if (baseList.getLength() == 0) { + // insert a base element, since one is not defined already. + Element base = dom.createElement(TAG_BASE); + base.setAttribute(ATT_HREF, baseURL); + head.insertBefore(base, head.getFirstChild()); + } + } + + + public static Element getBase(Document dom) { + // there should only be one head and one base element dom. + NodeList headList = dom.getElementsByTagName(TAG_HEAD); + Element head = (Element) headList.item(0); + NodeList baseList = head.getElementsByTagName(TAG_BASE); + if (baseList.getLength() == 0) + // no base defined, signal failure. + return null; + + return (Element) baseList.item(baseList.getLength() - 1); + + } + + + // <link rel="stylesheet" href="shared.css" type="text/css" /> + public static void insertStyle(Document dom, String cssUrl) { + // there should only be one head and one base element dom. + NodeList headList = dom.getElementsByTagName(TAG_HEAD); + Element head = null; + // Element base = getBase(dom); + NodeList styleList = null; + // there can be more than one style. DO not add style if it exists. + if (headList.getLength() >= 1) { + head = (Element) headList.item(0); + styleList = head.getElementsByTagName(TAG_HEAD_LINK); + for (int i = 0; i < styleList.getLength(); i++) { + Element style = (Element) styleList.item(0); + String styleString = style.getAttribute(ATT_HREF); + if (styleString.equals(cssUrl)) + return; + } + } + + // insert the style, since it is not defined. + Element styleToAdd = dom.createElement(TAG_HEAD_LINK); + styleToAdd.setAttribute(ATT_HREF, cssUrl); + styleToAdd.setAttribute(ATT_REL, "stylesheet"); //$NON-NLS-1$ + styleToAdd.setAttribute(ATT_TYPE, "text/css"); //$NON-NLS-1$ + if (styleList != null && styleList.getLength() >= 1) + styleList.item(0).getParentNode().insertBefore(styleToAdd, styleList.item(0)); + else + head.appendChild(styleToAdd); + + } + + /** + * Returns a reference to the body of the DOM. + * + * @param dom + * @return + */ + public static Element getBodyElement(Document dom) { + // there should only be one body element dom. + NodeList bodyList = dom.getElementsByTagName(TAG_BODY); + Element body = (Element) bodyList.item(0); + return body; + } + + + + public static Element createElement(Document dom, String elementName, Properties attributes) { + + // make sure to create element with any namespace uri to enable finding + // it again using Dom.getElementsByTagNameNS() + Element element = dom.createElementNS("", elementName); + if (attributes != null) { + Enumeration e = attributes.keys(); + while (e.hasMoreElements()) { + String key = (String) e.nextElement(); + element.setAttribute(key, attributes.getProperty(key)); + } + } + return element; + } + + public static Element createAndAppendChild(Element parentElement, String elementName, + Properties attributes) { + + Element element = createElement(parentElement.getOwnerDocument(), elementName, attributes); + parentElement.appendChild(element); + return element; + } + + + + /** + * Returns an Element array of all first level descendant Elements with a given tag name, in the + * order in which they are encountered in the DOM. Unlike the JAXP apis, which returns preorder + * traversal of this Element tree, this method filters out children deeper than first level + * child nodes. + */ + public static Element[] getElementsByTagName(Element parent, String tagName) { + NodeList allChildElements = parent.getElementsByTagName(tagName); + Vector vector = new Vector(); + for (int i = 0; i < allChildElements.getLength(); i++) { + // we know that the nodelist is of elements. + Element aElement = (Element) allChildElements.item(i); + if (aElement.getParentNode().equals(parent)) + // first level child element. add it. + vector.add(aElement); + } + Element[] filteredElements = new Element[vector.size()]; + vector.copyInto(filteredElements); + return filteredElements; + } + + /** + * Same as getElementsByTagName(Element parent, String tagName) but the parent element is + * assumed to be the root of the document. + * + * @see getElementsByTagName(Element parent, String tagName) + */ + public static Element[] getElementsByTagName(Document dom, String tagName) { + NodeList allChildElements = dom.getElementsByTagName(tagName); + Vector vector = new Vector(); + for (int i = 0; i < allChildElements.getLength(); i++) { + // we know that the nodelist is of elements. + Element aElement = (Element) allChildElements.item(i); + if (aElement.getParentNode().equals(dom.getDocumentElement())) + // first level child element. add it. Cant use getParent + // here. + vector.add(aElement); + } + Element[] filteredElements = new Element[vector.size()]; + vector.copyInto(filteredElements); + return filteredElements; + } + + + /* + * Util method similar to DOM getElementById() method, but it works without an id attribute + * being specified. Deep searches all children in this container's DOM for the first child with + * the given id. The element retrieved must have the passed local name. Note that in an XHTML + * file (aka DOM) elements should have a unique id within the scope of a document. We use local + * name because this allows for finding anchors and includes regardless of whether or not an + * xmlns was used in the xml. + */ + public static Element getElementById(Document dom, String id, String localElementName) { + + NodeList children = dom.getElementsByTagNameNS("*", localElementName); //$NON-NLS-1$ + for (int i = 0; i < children.getLength(); i++) { + Element element = (Element) children.item(i); + if (element.getAttribute("id").equals(id)) //$NON-NLS-1$ + return element; + } + // non found. + return null; + + } + + + + + /** + * Returns an array version of the passed NodeList. Used to work around DOM design issues. + */ + public static Node[] getArray(NodeList nodeList) { + Node[] nodes = new Node[nodeList.getLength()]; + for (int i = 0; i < nodeList.getLength(); i++) + nodes[i] = nodeList.item(i); + return nodes; + } + + + /** + * Remove all instances of the element from the DOM. + * + */ + public static void removeAllElements(Document dom, String elementLocalName) { + // get all elements in DOM and remove them. + NodeList elements = dom.getElementsByTagNameNS("*", //$NON-NLS-1$ + elementLocalName); + // get the array version of the nodelist to work around DOM api design. + Node[] elementsArray = DOMUtil.getArray(elements); + for (int i = 0; i < elementsArray.length; i++) { + Node element = elementsArray[i]; + element.getParentNode().removeChild(element); + } + + } + + + +} diff --git a/org.eclipse.help/src/org/eclipse/help/internal/xhtml/UAContentFilterProcessor.java b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/UAContentFilterProcessor.java new file mode 100644 index 000000000..0efa772d3 --- /dev/null +++ b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/UAContentFilterProcessor.java @@ -0,0 +1,135 @@ +/*************************************************************************************************** + * Copyright (c) 2004, 2005 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.xhtml; + +import org.eclipse.core.runtime.Platform; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Handles content manipulation. Filters filter content in as opposed to filtering out. ie: if a + * filter passes, content is displayed. + * + */ +public class UAContentFilterProcessor { + + public Document applyFilters(Document dom) { + Element body = DOMUtil.getBodyElement(dom); + NodeList allChildElements = body.getChildNodes(); + for (int i = 0; i < allChildElements.getLength(); i++) { + // not all nodes are Elements. + Node node = (Node) allChildElements.item(i); + if (!(node instanceof Element)) + continue; + applyFilters((Element) node); + + } + return dom; + } + + public void applyFilters(Element element) { + if (hasFilterAttribute(element)) { + boolean filteredIn = false; + filteredIn = processFilterAttribute(element); + if (!filteredIn) + // element failed filter, and is filtered out. + return; + } + + NodeList allChildElements = element.getChildNodes(); + for (int i = 0; i < allChildElements.getLength(); i++) { + // not all nodes are Elements. + Node node = (Node) allChildElements.item(i); + if (!(node instanceof Element)) + continue; + Element childElement = (Element) node; + applyFilters(childElement); + } + } + + private static boolean hasFilterAttribute(Element element) { + // getAttribute returns an empty string if att is not defined. + if (element.getAttribute(DOMUtil.ATT_FILTER).equals("")) + return false; + return true; + } + + /** + * Returns true is filter passes and Element is to be included. + * + * @param element + * @return + */ + private boolean processFilterAttribute(Element element) { + String filterString = element.getAttribute(DOMUtil.ATT_FILTER); + String[] parsedFilterString = filterString.split("="); + String filter = parsedFilterString[0]; + String value = parsedFilterString[1]; + + boolean filtered_in = isFilteredIn(filter, value); + + if (!filtered_in) + element.getParentNode().removeChild(element); + return false; + } + + /** + * FIltering capabilities. Can be overiden by subclasses to add more filtering capabilities. + * + * @param filter + * @param value + * @return + */ + protected boolean isFilteredIn(String filter, String value) { + boolean filtered_in = false; + if (filter.equals("ws")) { + filtered_in = filterByWS(value); + } else if (filter.equals("os")) { + filtered_in = filterByOS(value); + } else + filtered_in = filterBySystemProperty(filter, value); + + return filtered_in; + } + + /** + * evaluates WS filter. + */ + private static boolean filterByWS(String ws) { + String currentWS = Platform.getWS(); + if (currentWS.equals(ws)) + return true; + return false; + } + + /** + * evaluates OS filter. + */ + private static boolean filterByOS(String os) { + String currentOS = Platform.getOS(); + if (currentOS.equals(os)) + return true; + return false; + } + + + /** + * evaluates system property filter. + */ + private static boolean filterBySystemProperty(String property, String value) { + String currentValue = System.getProperty(property); + if (currentValue != null && currentValue.equals(value)) + return true; + return false; + } + +} diff --git a/org.eclipse.help/src/org/eclipse/help/internal/xhtml/UAContentMergeProcessor.java b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/UAContentMergeProcessor.java new file mode 100644 index 000000000..c591d4038 --- /dev/null +++ b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/UAContentMergeProcessor.java @@ -0,0 +1,104 @@ +/*************************************************************************************************** + * Copyright (c) 2004, 2005 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.xhtml; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; + +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.help.internal.HelpPlugin; +import org.eclipse.help.internal.util.ResourceLocator; +import org.osgi.framework.Bundle; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + + +/** + * Handles content manipulation to resolve includes. + * + */ +public class UAContentMergeProcessor { + + + public Document resolveIncludes(Document document, String locale) { + + NodeList includes = document.getElementsByTagNameNS("*", "include"); + Node[] nodes = getArray(includes); + for (int i = 0; i < nodes.length; i++) { + Element includeElement = (Element) nodes[i]; + UAInclude include = new UAInclude(includeElement); + Element targetElement = findIncludeTarget(include, locale); + if (targetElement == null) { + String message = "Could not resolve following include: "; //$NON-NLS-1$; + HelpPlugin.logWarning(message); + return null; + } + Node targetNode = document.importNode(targetElement, true); + includeElement.getParentNode().replaceChild(targetNode, includeElement); + } + return document; + } + + + + + /** + * Find the target Element pointed to by the path in the include. It is assumed that configId + * always points to an external config, and not the same config of the inlcude. + * + * @param include + * @param path + * @return + */ + private Element findIncludeTarget(UAInclude include, String locale) { + String path = include.getPath(); + int index = path.indexOf("/"); + if (index < 0) + return null; + String pluginID = path.substring(0, index); + int lastIndex = path.lastIndexOf("/"); + String pluginRelativePath = path.substring(index + 1, lastIndex); + String include_id = path.substring(lastIndex + 1, path.length()); + + Bundle bundle = Platform.getBundle(pluginID); + ArrayList pathPrefix = ResourceLocator.getPathPrefix(locale); + URL flatFileURL = ResourceLocator.find(bundle, new Path(pluginRelativePath), pathPrefix); + if (flatFileURL != null) + try { + InputStream inputStream = flatFileURL.openStream(); + UAContentParser parser = new UAContentParser(inputStream); + Document dom = parser.getDocument(); + return DOMUtil.getElementById(dom, include_id, "*"); + } catch (IOException e) { + return null; + } + return null; + } + + + + /** + * Returns an array version of the passed NodeList. Used to work around DOM design issues. + */ + public static Node[] getArray(NodeList nodeList) { + Node[] nodes = new Node[nodeList.getLength()]; + for (int i = 0; i < nodeList.getLength(); i++) + nodes[i] = nodeList.item(i); + return nodes; + } + + + +} diff --git a/org.eclipse.help/src/org/eclipse/help/internal/xhtml/UAContentParser.java b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/UAContentParser.java new file mode 100644 index 000000000..26bef5426 --- /dev/null +++ b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/UAContentParser.java @@ -0,0 +1,194 @@ +/*************************************************************************************************** + * Copyright (c) 2004, 2005 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.xhtml; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Hashtable; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.eclipse.help.internal.HelpPlugin; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + + +/** + * + */ +public class UAContentParser { + + private static String TAG_HTML = "html"; //$NON-NLS-1$ + protected static String XHTML1_TRANSITIONAL = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"; //$NON-NLS-1$ + protected static String XHTML1_STRICT = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"; //$NON-NLS-1$ + protected static String XHTML1_FRAMESET = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"; //$NON-NLS-1$ + + + protected static Hashtable dtdMap = new Hashtable(); + + static { + String dtdBaseLocation = "dtds/xhtml1-20020801/"; //$NON-NLS-1$ + + String dtdLocation = dtdBaseLocation + "xhtml1-transitional.dtd"; //$NON-NLS-1$ + URL dtdURL_T = BundleUtil.getResourceAsURL(dtdLocation, "org.eclipse.ui.intro"); + dtdMap.put(XHTML1_TRANSITIONAL, dtdURL_T); + + dtdLocation = dtdBaseLocation + "xhtml1-strict.dtd"; //$NON-NLS-1$ + URL dtdURL_S = BundleUtil.getResourceAsURL(dtdLocation, "org.eclipse.ui.intro"); + dtdMap.put(XHTML1_STRICT, dtdURL_S); + + dtdLocation = dtdBaseLocation + "xhtml1-frameset.dtd"; //$NON-NLS-1$ + URL dtdURL_F = BundleUtil.getResourceAsURL(dtdLocation, "org.eclipse.ui.intro"); + dtdMap.put(XHTML1_FRAMESET, dtdURL_F); + } + + + + private Document document; + private boolean hasXHTMLContent; + + public UAContentParser(String content) { + parseDocument(content); + } + + public UAContentParser(InputStream content) { + parseDocument(content); + } + + /** + * Creates a config parser assuming that the passed content represents a URL to the content + * file. + */ + public void parseDocument(Object content) { + try { + document = doParse(content); + if (document != null) { + // xml file is loaded. check that it is XHTML + Element rootElement = document.getDocumentElement(); + // DocumentType docType = document.getDoctype(); + if (rootElement.getTagName().equals(TAG_HTML)) { + // rely on root element to detect if we have an XHTML file + // and not on doctype. We need to support xhtml files with + // no doctype. + hasXHTMLContent = true; + } else + // not XHTML. + document = null; + } + } catch (Exception e) { + HelpPlugin.logError("Could not load content file: " + content, e); //$NON-NLS-1$ + } + } + + private DocumentBuilder createParser() { + + try { + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + docFactory.setValidating(false); + // if this is not set, Document.getElementsByTagNameNS() will fail. + docFactory.setNamespaceAware(true); + docFactory.setExpandEntityReferences(false); + DocumentBuilder parser = docFactory.newDocumentBuilder(); + + parser.setEntityResolver(new EntityResolver() { + + public InputSource resolveEntity(String publicId, String systemId) throws SAXException, + IOException { + + if (systemId.equals(XHTML1_TRANSITIONAL) || systemId.equals(XHTML1_STRICT) + || systemId.equals(XHTML1_FRAMESET)) { + + // be carefull here to support running as a jarred + // plugin. + URL dtdURL = (URL) dtdMap.get(systemId); + InputSource in = new InputSource(dtdURL.openStream()); + in.setSystemId(dtdURL.toExternalForm()); + return in; + } + return null; + } + }); + return parser; + } catch (ParserConfigurationException pce) { + // Parser with specified options can't be built + HelpPlugin.logError(pce.getMessage(), pce); + } + return null; + } + + + /** + * General parser method that can accept both String and InputStream for parsing. + * + * @param fileObject + * @return + */ + private Document doParse(Object fileObject) { + Document document = null; + try { + DocumentBuilder parser = createParser(); + if (fileObject instanceof String) + document = parser.parse((String) fileObject); + else if (fileObject instanceof InputStream) + document = parser.parse((InputStream) fileObject); + + return document; + + } catch (SAXParseException spe) { + StringBuffer buffer = new StringBuffer("Parser error in line "); //$NON-NLS-1$ + buffer.append(spe.getLineNumber()); + buffer.append(", uri "); //$NON-NLS-1$ + buffer.append(spe.getSystemId()); + buffer.append("\n"); //$NON-NLS-1$ + buffer.append(spe.getMessage()); + + // Use the contained exception. + Exception x = spe; + if (spe.getException() != null) + x = spe.getException(); + HelpPlugin.logError(buffer.toString(), x); + + } catch (SAXException sxe) { + Exception x = sxe; + if (sxe.getException() != null) + x = sxe.getException(); + HelpPlugin.logError(x.getMessage(), x); + + } catch (IOException ioe) { + HelpPlugin.logError(ioe.getMessage(), ioe); + } + return null; + } + + /** + * Returned the DOM representing the xml content file. May return null if parsing the file + * failed. + * + * @return Returns the document. + */ + public Document getDocument() { + return document; + } + + public boolean hasXHTMLContent() { + return hasXHTMLContent; + } + + + + +} diff --git a/org.eclipse.help/src/org/eclipse/help/internal/xhtml/UAInclude.java b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/UAInclude.java new file mode 100644 index 000000000..2681005b4 --- /dev/null +++ b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/UAInclude.java @@ -0,0 +1,50 @@ +/*************************************************************************************************** + * Copyright (c) 2004, 2005 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.xhtml; + +import org.w3c.dom.Element; + + +public class UAInclude extends AbstractUAElement { + + protected static final String TAG_INCLUDE = "include"; //$NON-NLS-1$ + protected static final String ATT_PATH = "path"; //$NON-NLS-1$ + /** + * boolean attribute, default is false. + */ + private static final String ATT_MERGE_STYLE = "merge-style"; //$NON-NLS-1$ + + private String path; + private boolean mergeStyle = false; + + public UAInclude(Element element) { + path = getAttribute(element, ATT_PATH); + String mergeStyleString = getAttribute(element, ATT_MERGE_STYLE); + mergeStyle = (mergeStyleString != null && mergeStyleString.equalsIgnoreCase("true")) ? true : false; //$NON-NLS-1$ + } + + + /** + * @return Returns the mergeStyle. + */ + public boolean getMergeStyle() { + return mergeStyle; + } + + /** + * @return Returns the path. + */ + public String getPath() { + return path; + } + + + +} diff --git a/org.eclipse.help/src/org/eclipse/help/internal/xhtml/UATransformManager.java b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/UATransformManager.java new file mode 100644 index 000000000..76f421793 --- /dev/null +++ b/org.eclipse.help/src/org/eclipse/help/internal/xhtml/UATransformManager.java @@ -0,0 +1,114 @@ +/*************************************************************************************************** + * Copyright (c) 2004, 2005 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.xhtml; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.eclipse.help.internal.HelpPlugin; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; + + +/** + * Handles all XSL transforms applied to XHTML UA content. + */ +public class UATransformManager { + + + private static Transformer createTransformer(Document document) { + try { + // identity xslt. + TransformerFactory tFactory = TransformerFactory.newInstance(); + Transformer transformer = tFactory.newTransformer(); + + // setup properties, for doctype. + DocumentType docType = document.getDoctype(); + if (docType != null) { + String value = docType.getSystemId(); + // transformer.clearParameters(); + transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, value); + value = document.getDoctype().getPublicId(); + transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, value); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$ + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); //$NON-NLS-1$ + // transformer.setOutputProperty(OutputKeys.MEDIA_TYPE, + // "text/html"); + // transformer + // .setOutputProperty(OutputKeys.ENCODING, "iso-8859-1"); + } else + ; + + return transformer; + + } catch (TransformerConfigurationException tce) { + // Error generated by the parser + HelpPlugin.logError("Transformer Config error: " + tce.getMessage(), null); //$NON-NLS-1$ + // Use the contained exception, if any + Throwable x = tce; + if (tce.getException() != null) + x = tce.getException(); + HelpPlugin.logError("Transformer Stack trace: ", x); //$NON-NLS-1$ + System.out.println(x); + } + return null; + } + + public static String convertToString(Document document) { + try { + // identity xslt. + Transformer transformer = createTransformer(document); + + DOMSource source = new DOMSource(document); + + StringWriter stringBuffer = new StringWriter(); + StreamResult result = new StreamResult(stringBuffer); + + transformer.transform(source, result); + return stringBuffer.toString(); + + } catch (TransformerException te) { + // Error generated by the parser + HelpPlugin.logError("Transformer error: " + te.getMessage(), te); //$NON-NLS-1$ + // Use the contained exception, if any + Throwable x = te; + if (te.getException() != null) + x = te.getException(); + HelpPlugin.logError("Transformer Stack trace: ", x); //$NON-NLS-1$ + System.out.println(x); + } + return null; + + } + + public static InputStream getAsInputStream(Document document) { + byte[] ba = null; + String xhtml = convertToString(document); + try { + ba = xhtml.getBytes("UTF-8"); + } catch (UnsupportedEncodingException ue) { + ; + } + ByteArrayInputStream is = new ByteArrayInputStream(ba); + return is; + } + + +} |