| /******************************************************************************* |
| * Copyright (c) 2004, 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.ui.internal.intro.impl.model; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Vector; |
| |
| import org.eclipse.core.runtime.FileLocator; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.ui.internal.intro.impl.model.loader.IntroContentParser; |
| import org.eclipse.ui.internal.intro.impl.model.util.BundleUtil; |
| import org.eclipse.ui.internal.intro.impl.model.util.ModelUtil; |
| 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; |
| |
| /** |
| * An intro container extension. If the content attribute is defined, then it is |
| * assumed that we have XHTML content in an external file. Load content from |
| * external DOM. No need to worry about caching here because this is a transient |
| * model class. It is used and then disregarded from the model.<br> |
| * Just like in a page, the styles and altStyles strings can be a comma |
| * separated list of styles. Handle this by storing styles just like pages. |
| */ |
| public class IntroExtensionContent extends AbstractIntroElement { |
| |
| protected static final String TAG_CONTAINER_EXTENSION = "extensionContent"; //$NON-NLS-1$ |
| protected static final String TAG_CONTAINER_REPLACE = "replacementContent"; //$NON-NLS-1$ |
| |
| public static final int TYPE_CONTRIBUTION = 0; |
| public static final int TYPE_REPLACEMENT = 1; |
| |
| protected static final String ATT_PATH = "path"; //$NON-NLS-1$ |
| protected static final String ATT_ID = "id"; //$NON-NLS-1$ |
| private static final String ATT_STYLE = "style"; //$NON-NLS-1$ |
| private static final String ATT_ALT_STYLE = "alt-style"; //$NON-NLS-1$ |
| private static final String ATT_CONTENT = "content"; //$NON-NLS-1$ |
| |
| private static final Element[] EMPTY_ELEMENT_ARRAY = new Element[0]; |
| |
| private String path; |
| private String content; |
| private String contentFile; |
| private String contentId; |
| private String anchorId; |
| |
| private Element element; |
| private String base; |
| |
| private Vector<String> styles = new Vector<>(); |
| private Map<String, Bundle> altStyles = new HashMap<>(); |
| |
| IntroExtensionContent(Element element, Bundle bundle, String base, IConfigurationElement configExtElement) { |
| super(element, bundle); |
| path = getAttribute(element, ATT_PATH); |
| content = getAttribute(element, ATT_CONTENT); |
| anchorId = getAttribute(element, ATT_ID); |
| this.element = element; |
| this.base = base; |
| |
| // load and resolve styles, first. |
| init(element, bundle, base); |
| |
| // if content is not null we have XHTML extension. |
| if (content != null) { |
| // BASE: since content is being loaded from another XHTML file and |
| // not this xml file, point the base of this page to be relative to |
| // the new xml file location. |
| IPath subBase = ModelUtil.getParentFolderPath(content); |
| String newBase = new Path(base).append(subBase).toString(); |
| extractFileAndId(bundle); |
| contentFile = BundleUtil.getResolvedResourceLocation(base, contentFile, |
| bundle); |
| this.base = newBase; |
| } |
| |
| // Save the mapping between plugin registry id and base/anchor id |
| String contributor = configExtElement.getContributor().getName(); |
| ExtensionMap.getInstance().putPluginId(anchorId, contributor); |
| } |
| |
| public String getId() { |
| return anchorId; |
| } |
| |
| |
| /** |
| * Initialize styles. Take first style in style attribute and make it the |
| * page style. Then put other styles in styles vectors. Make sure to resolve |
| * each style. |
| * |
| * @param element |
| * @param bundle |
| */ |
| private void init(Element element, Bundle bundle, String base) { |
| String[] styleValues = getAttributeList(element, ATT_STYLE); |
| if (styleValues != null && styleValues.length > 0) { |
| for (int i = 0; i < styleValues.length; i++) { |
| String style = styleValues[i]; |
| style = BundleUtil.getResolvedResourceLocation(base, style, |
| bundle); |
| addStyle(style); |
| } |
| } |
| |
| String[] altStyleValues = getAttributeList(element, ATT_ALT_STYLE); |
| if (altStyleValues != null && altStyleValues.length > 0) { |
| for (int i = 0; i < altStyleValues.length; i++) { |
| String style = altStyleValues[i]; |
| style = BundleUtil.getResolvedResourceLocation(base, style, |
| bundle); |
| addAltStyle(style, bundle); |
| } |
| } |
| } |
| |
| /** |
| * Adds the given style to the list. Style is not added if it already exists |
| * in the list. |
| * |
| * @param style |
| */ |
| protected void addStyle(String style) { |
| if (styles.contains(style)) |
| return; |
| styles.add(style); |
| } |
| |
| |
| /** |
| * Adds the given style to the list.Style is not added if it already exists |
| * in the list. |
| * |
| * @param altStyle |
| */ |
| protected void addAltStyle(String altStyle, Bundle bundle) { |
| if (altStyles.containsKey(altStyle)) |
| return; |
| altStyles.put(altStyle, bundle); |
| } |
| |
| /** |
| * Returns the extension type; either contribution into an anchor or replacement |
| * of an element. |
| */ |
| public int getExtensionType() { |
| return TAG_CONTAINER_REPLACE.equals(element.getNodeName()) ? TYPE_REPLACEMENT : TYPE_CONTRIBUTION; |
| } |
| |
| /** |
| * @return Returns the path. |
| */ |
| public String getPath() { |
| return path; |
| } |
| |
| @Override |
| public int getType() { |
| return AbstractIntroElement.CONTAINER_EXTENSION; |
| } |
| |
| protected Element[] getChildren() { |
| NodeList nodeList = element.getChildNodes(); |
| Vector<Node> vector = new Vector<>(); |
| for (int i = 0; i < nodeList.getLength(); i++) { |
| Node node = nodeList.item(i); |
| if (node.getNodeType() == Node.ELEMENT_NODE) |
| vector.add(node); |
| } |
| Element[] filteredElements = new Element[vector.size()]; |
| vector.copyInto(filteredElements); |
| // free DOM model for memory performance. |
| this.element = null; |
| return filteredElements; |
| } |
| |
| public boolean isXHTMLContent() { |
| return content != null ? true : false; |
| } |
| |
| /** |
| * Returns the elements loaded from the content attribute. This is the content |
| * that should be inserted for the extension. If it is a file, all child elements |
| * of body are returned. If it is a file with an id, only the element with the id |
| * is returned. |
| * |
| * @return the elements to be inserted |
| */ |
| public Element[] getElements() { |
| // only applicable when content attribute is specified |
| if (isXHTMLContent()) { |
| IntroContentParser parser = new IntroContentParser(contentFile); |
| Document dom = parser.getDocument(); |
| if (dom != null) { |
| // parser content should be XHTML because defining content here |
| // means that we want XHTML extension. |
| if (parser.hasXHTMLContent()) { |
| if (contentId != null) { |
| // id specified, only get that element |
| return new Element[] { ModelUtil.getElementById(dom, contentId) }; |
| } |
| else { |
| // no id specified, use the whole body |
| Element extensionBody = ModelUtil.getBodyElement(dom); |
| return ModelUtil.getElementsByTagName(extensionBody, "*"); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| return EMPTY_ELEMENT_ARRAY; |
| } |
| |
| /** |
| * @return Returns the altStyle. |
| */ |
| protected Map<String, Bundle> getAltStyles() { |
| return altStyles; |
| } |
| |
| /** |
| * @return Returns the style. |
| */ |
| protected String[] getStyles() { |
| String[] stylesArray = new String[styles.size()]; |
| styles.copyInto(stylesArray); |
| return stylesArray; |
| } |
| |
| /** |
| * @return Returns the content. |
| */ |
| public String getContent() { |
| return content; |
| } |
| |
| public String getBase() { |
| return base; |
| } |
| |
| /** |
| * Extracts the file and id parts of the content attribute. This attribute has two modes - |
| * if you specify a file, it will include the body of that file (minus the body element itself). |
| * If you append an id after the file, only the element with that id will be included. However |
| * we need to know which mode we're in. |
| * |
| * @param bundle the bundle that contributed this extension |
| */ |
| private void extractFileAndId(Bundle bundle) { |
| // look for the file |
| IPath resourcePath = new Path(base + content); |
| if (FileLocator.find(bundle, resourcePath, null) != null) { |
| // found it, assume it's a file |
| contentFile = content; |
| } |
| else { |
| // didn't find the file, assume the last segment is an id |
| int lastSlashIndex = content.lastIndexOf('/'); |
| if (lastSlashIndex != -1) { |
| contentFile = content.substring(0, lastSlashIndex); |
| contentId = content.substring(lastSlashIndex + 1); |
| } |
| else { |
| // there was no slash, it must be a file |
| contentFile = content; |
| } |
| } |
| } |
| } |