blob: 9097980611fed4d09211d30f061d9542a4369732 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 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;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.eclipse.core.runtime.IProduct;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.IPreferencesService;
import org.eclipse.help.internal.util.ProductPreferences;
import org.osgi.framework.Bundle;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/*
* Interface to the help data file contents defined in plugin_customization.ini under
* key org.eclipse.help/HELP_DATA.
*/
public class HelpData {
private static final String ELEMENT_TOC_ORDER = "tocOrder"; //$NON-NLS-1$
private static final String ELEMENT_HIDDEN = "hidden"; //$NON-NLS-1$
private static final String ELEMENT_TOC = "toc"; //$NON-NLS-1$
private static final String ELEMENT_CATEGORY = "category"; //$NON-NLS-1$
private static final String ELEMENT_INDEX = "index"; //$NON-NLS-1$
private static final String ATTRIBUTE_ID = "id"; //$NON-NLS-1$
private static final String ELEMENT_OTHER_TOCS = "otherTocs"; //$NON-NLS-1$
private static final String ATTRIBUTE_SORT = "sort"; //$NON-NLS-1$
private static final String PLUGINS_ROOT_SLASH = "PLUGINS_ROOT/"; //$NON-NLS-1$
private static HelpData productHelpData;
private URL url;
private List<String> tocOrder;
private Set<String> hiddenTocs;
private Set<String> hiddenIndexes;
private String sortMode;
/*
* Get the active product's help data, or null if the product doesn't have
* help data or there is no active product.
*/
public static synchronized HelpData getProductHelpData() {
if (productHelpData == null) {
String pluginId = null;
IProduct product = Platform.getProduct();
if (product != null) {
pluginId = product.getDefiningBundle().getSymbolicName();
}
String helpDataFile = Platform.getPreferencesService().getString(HelpPlugin.PLUGIN_ID, HelpPlugin.HELP_DATA_KEY, "", null); //$NON-NLS-1$
if (helpDataFile.length() > 0) {
if (helpDataFile.startsWith(PLUGINS_ROOT_SLASH)) {
int nextSlash = helpDataFile.indexOf('/', PLUGINS_ROOT_SLASH.length());
if (nextSlash > 0) {
pluginId = helpDataFile.substring(PLUGINS_ROOT_SLASH.length(), nextSlash);
helpDataFile = helpDataFile.substring(nextSlash + 1);
}
}
}
if (helpDataFile.length() > 0 && pluginId != null) {
Bundle bundle = Platform.getBundle(pluginId);
if (bundle != null) {
URL helpDataUrl = bundle.getEntry(helpDataFile);
productHelpData = new HelpData(helpDataUrl);
}
}
if (productHelpData == null) {
productHelpData = new HelpData(null);
}
}
return productHelpData;
}
/*
* For testing
*/
public static void clearProductHelpData() {
productHelpData = null;
}
/*
* Constructs help data from the XML at the given URL.
*/
public HelpData(URL url) {
this.url = url;
}
/*
* Returns a list of strings which are the IDs of the tocs/categories listed
* in tocOrder.
*/
public synchronized List<String> getTocOrder() {
if (tocOrder == null) {
loadHelpData();
}
return tocOrder;
}
/*
* Returns a set of strings which are the IDs of the tocs/categories listed
* in the hidden section.
*/
public synchronized Set<String> getHiddenTocs() {
if (hiddenTocs == null) {
loadHelpData();
}
return hiddenTocs;
}
/*
* Returns a set of strings which are the IDs of the indexes listed
* in the hidden section.
*/
public synchronized Set<String> getHiddenIndexes() {
if (hiddenIndexes == null) {
loadHelpData();
}
return hiddenIndexes;
}
/*
* Returns true if tocs not specified in the toc order are sorted
*/
public synchronized boolean isSortOthers() {
if (sortMode == null) {
loadHelpData();
}
return "true".equals(sortMode); //$NON-NLS-1$
}
/*
* Allow unit tests to override for providing test data.
*/
public InputStream getHelpDataFile(String filePath) throws IOException {
IProduct product = Platform.getProduct();
Bundle definingBundle = product != null ? product.getDefiningBundle() : null;
URL entry = definingBundle != null ? definingBundle.getEntry(filePath) : null;
if(entry == null) {
throw new IOException("No entry to '"+filePath+"' could be found or caller does not have the appropriate permissions.");//$NON-NLS-1$ //$NON-NLS-2$
}
return entry.openStream();
}
/*
* Loads and parses the file and populates the data structures.
*/
private void loadHelpData() {
tocOrder = new ArrayList<>();
hiddenTocs = new HashSet<>();
hiddenIndexes = new HashSet<>();
sortMode = "true"; //$NON-NLS-1$
if (url != null) {
try {
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
InputStream in = url.openStream();
parser.parse(in, new Handler());
}
catch (Throwable t) {
String msg = "Error loading help data file \"" + url + "\""; //$NON-NLS-1$ //$NON-NLS-2$
HelpPlugin.logError(msg, t);
}
} else {
// Derive information from preferences
IPreferencesService preferencesService = Platform.getPreferencesService();
String baseTocs = preferencesService.getString(HelpPlugin.PLUGIN_ID, HelpPlugin.BASE_TOCS_KEY, "", null); //$NON-NLS-1$
String ignoredTocs = preferencesService.getString(HelpPlugin.PLUGIN_ID, HelpPlugin.IGNORED_TOCS_KEY, "", null); //$NON-NLS-1$
String ignoredIndexes = preferencesService.getString(HelpPlugin.PLUGIN_ID, HelpPlugin.IGNORED_INDEXES_KEY, "", null); //$NON-NLS-1$
tocOrder = ProductPreferences.tokenize(baseTocs);
hiddenTocs.addAll(ProductPreferences.tokenize(ignoredTocs));
hiddenIndexes.addAll(ProductPreferences.tokenize(ignoredIndexes));
}
}
/*
* SAX Handler for parsing the help data file.
*/
private class Handler extends DefaultHandler {
private boolean inTocOrder;
private boolean inHidden;
/* (non-Javadoc)
* @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
*/
@Override
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
if (ELEMENT_TOC_ORDER.equals(name)) {
inTocOrder = true;
}
else if (ELEMENT_HIDDEN.equals(name)) {
inHidden = true;
}
else if (ELEMENT_TOC.equals(name) || ELEMENT_CATEGORY.equals(name)) {
String id = attributes.getValue(ATTRIBUTE_ID);
if (id != null) {
if (inTocOrder) {
tocOrder.add(id);
}
else if (inHidden) {
hiddenTocs.add(id);
}
}
}
else if (ELEMENT_INDEX.equals(name) && inHidden) {
String id = attributes.getValue(ATTRIBUTE_ID);
if (id != null) {
hiddenIndexes.add(id);
}
}
else if (ELEMENT_OTHER_TOCS.equals(name)) {
String sortAttribute = attributes.getValue(ATTRIBUTE_SORT);
if (sortAttribute != null) {
sortMode = sortAttribute.toLowerCase();
}
}
}
/* (non-Javadoc)
* @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public void endElement(String uri, String localName, String name) throws SAXException {
if (ELEMENT_TOC_ORDER.equals(name)) {
inTocOrder = false;
}
else if (ELEMENT_HIDDEN.equals(name)) {
inHidden = false;
}
}
/*
* Note: throws clause does not declare IOException due to a bug in
* sun jdk: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6327149
*
* @see org.xml.sax.helpers.DefaultHandler#resolveEntity(java.lang.String, java.lang.String)
*/
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
return new InputSource(new StringReader("")); //$NON-NLS-1$
}
}
}