| /******************************************************************************* |
| * Copyright (c) 2006, 2015 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.extension; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExtensionRegistry; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.help.AbstractContentExtensionProvider; |
| import org.eclipse.help.IContentExtension; |
| import org.eclipse.help.internal.HelpPlugin; |
| import org.eclipse.help.internal.UAElementFactory; |
| |
| /* |
| * Manages content extensions (contributions into anchors and element |
| * replacements) for user assistance documents. |
| */ |
| public class ContentExtensionManager { |
| |
| private static final String EXTENSION_POINT_ID_CONTENT_EXTENSION = HelpPlugin.PLUGIN_ID + ".contentExtension"; //$NON-NLS-1$ |
| private static final String ELEMENT_NAME_CONTENT_EXTENSION_PROVIDER = "contentExtensionProvider"; //$NON-NLS-1$ |
| private static final String ATTRIBUTE_NAME_CLASS = "class"; //$NON-NLS-1$ |
| private static final ContentExtension[] EMPTY_ARRAY = new ContentExtension[0]; |
| |
| private AbstractContentExtensionProvider[] contentExtensionProviders; |
| private Map extensionsByPath; |
| private Map replacesByPath; |
| |
| /* |
| * Returns all known extensions for the given locale. |
| */ |
| public ContentExtension[] getExtensions(String locale) { |
| if (extensionsByPath == null) { |
| loadExtensions(locale); |
| } |
| List extensions = new ArrayList(); |
| Iterator iter = extensionsByPath.values().iterator(); |
| while (iter.hasNext()) { |
| extensions.addAll((Collection)iter.next()); |
| } |
| iter = replacesByPath.values().iterator(); |
| while (iter.hasNext()) { |
| extensions.addAll((Collection)iter.next()); |
| } |
| return (ContentExtension[])extensions.toArray(new ContentExtension[extensions.size()]); |
| } |
| |
| /* |
| * Get all extensions of the given type whose target matches the given path. |
| */ |
| public ContentExtension[] getExtensions(String path, int type, String locale) { |
| if (extensionsByPath == null) { |
| loadExtensions(locale); |
| } |
| Map map = (type == ContentExtension.CONTRIBUTION ? extensionsByPath : replacesByPath); |
| List extensions = (List)map.get(path); |
| if (extensions != null) { |
| return (ContentExtension[])extensions.toArray(new ContentExtension[extensions.size()]); |
| } |
| return EMPTY_ARRAY; |
| } |
| |
| /* |
| * Clears all cached data, forcing the manager to query the |
| * providers again next time a request is made. |
| */ |
| public void clearCache() { |
| if (extensionsByPath != null) { |
| extensionsByPath.clear(); |
| } |
| if (replacesByPath != null) { |
| replacesByPath.clear(); |
| } |
| } |
| |
| /* |
| * Retrieves all extensions from all providers and organizes them by |
| * type. |
| */ |
| private void loadExtensions(String locale) { |
| extensionsByPath = new HashMap(); |
| replacesByPath = new HashMap(); |
| contentExtensionProviders = getContentExtensionProviders(); |
| for (int i=0;i<contentExtensionProviders.length;++i) { |
| IContentExtension[] extensions = contentExtensionProviders[i].getContentExtensions(locale); |
| for (int j=0;j<extensions.length;++j) { |
| ContentExtension extension = (extensions[j] instanceof ContentExtension ? (ContentExtension)extensions[j] : (ContentExtension)UAElementFactory.newElement(extensions[j])); |
| String content = extension.getContent(); |
| String path = extension.getPath(); |
| if (content != null && path != null) { |
| int type = extension.getType(); |
| Map map = (type == IContentExtension.CONTRIBUTION ? extensionsByPath : replacesByPath); |
| content = normalizePath(content); |
| path = normalizePath(path); |
| extension.setContent(content); |
| extension.setPath(path); |
| List list = (List)map.get(path); |
| if (list == null) { |
| list = new ArrayList(); |
| map.put(path, list); |
| } |
| list.add(extension); |
| } |
| } |
| } |
| } |
| |
| /* |
| * Returns all registered content extension providers (potentially cached). |
| */ |
| private AbstractContentExtensionProvider[] getContentExtensionProviders() { |
| if (contentExtensionProviders == null) { |
| List providers = new ArrayList(); |
| IExtensionRegistry registry = Platform.getExtensionRegistry(); |
| IConfigurationElement[] elements = registry.getConfigurationElementsFor(EXTENSION_POINT_ID_CONTENT_EXTENSION); |
| for (int i=0;i<elements.length;++i) { |
| IConfigurationElement elem = elements[i]; |
| if (elem.getName().equals(ELEMENT_NAME_CONTENT_EXTENSION_PROVIDER)) { |
| try { |
| AbstractContentExtensionProvider provider = (AbstractContentExtensionProvider)elem.createExecutableExtension(ATTRIBUTE_NAME_CLASS); |
| providers.add(provider); |
| } |
| catch (CoreException e) { |
| // log and skip |
| String msg = "Error instantiating user assistance content extension provider class \"" + elem.getAttribute(ATTRIBUTE_NAME_CLASS) + '"'; //$NON-NLS-1$ |
| HelpPlugin.logError(msg, e); |
| } |
| } |
| } |
| contentExtensionProviders = (AbstractContentExtensionProvider[])providers.toArray(new AbstractContentExtensionProvider[providers.size()]); |
| } |
| return contentExtensionProviders; |
| } |
| |
| /* |
| * Normalizes the given path by adding a leading slash if one doesn't |
| * exist, and converting the final slash into a '#' if it is thought to |
| * separate the end of the document with the element (legacy form). |
| */ |
| private String normalizePath(String path) { |
| int bundleStart, bundleEnd; |
| int pathStart, pathEnd; |
| int elementStart, elementEnd; |
| |
| bundleStart = path.charAt(0) == '/' ? 1 : 0; |
| bundleEnd = path.indexOf('/', bundleStart); |
| |
| pathStart = bundleEnd + 1; |
| pathEnd = path.indexOf('#', pathStart); |
| if (pathEnd == -1) { |
| int lastSlash = path.lastIndexOf('/'); |
| if (lastSlash > 0) { |
| int secondLastSlash = path.lastIndexOf('/', lastSlash - 1); |
| if (secondLastSlash != -1) { |
| String secondLastToken = path.substring(secondLastSlash, lastSlash); |
| boolean hasDot = (secondLastToken.indexOf('.') != -1); |
| if (hasDot) { |
| pathEnd = lastSlash; |
| } |
| } |
| } |
| if (pathEnd == -1) { |
| pathEnd = path.length(); |
| } |
| } |
| |
| elementStart = Math.min(pathEnd + 1, path.length()); |
| elementEnd = path.length(); |
| |
| if (bundleEnd > bundleStart && pathStart > bundleEnd && pathEnd > pathStart && elementStart >= pathEnd && elementEnd >= elementStart) { |
| String bundleId = path.substring(bundleStart, bundleEnd); |
| String relativePath = path.substring(pathStart, pathEnd); |
| String elementId = path.substring(elementStart, elementEnd); |
| path = '/' + bundleId + '/' + relativePath; |
| if (elementId.length() > 0) { |
| path += '#' + elementId; |
| } |
| } |
| return path; |
| } |
| } |