blob: 5453202beea4e330f264bcd2b6d473357171a998 [file] [log] [blame]
/*******************************************************************************
* 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;
}
}
}
}