001: /*******************************************************************************
002: * Copyright (c) 2004, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.intro.impl.model;
011:
012: import java.util.Hashtable;
013: import java.util.Vector;
014:
015: import org.eclipse.core.runtime.FileLocator;
016: import org.eclipse.core.runtime.IConfigurationElement;
017: import org.eclipse.core.runtime.IPath;
018: import org.eclipse.core.runtime.Path;
019: import org.eclipse.ui.internal.intro.impl.model.loader.IntroContentParser;
020: import org.eclipse.ui.internal.intro.impl.model.util.BundleUtil;
021: import org.eclipse.ui.internal.intro.impl.model.util.ModelUtil;
022: import org.osgi.framework.Bundle;
023: import org.w3c.dom.Document;
024: import org.w3c.dom.Element;
025: import org.w3c.dom.Node;
026: import org.w3c.dom.NodeList;
027:
028: /**
029: * An intro container extension. If the content attribute is defined, then it is
030: * assumed that we have XHTML content in an external file. Load content from
031: * external DOM. No need to worry about caching here because this is a transient
032: * model class. It is used and then disregarded from the model.<br>
033: * Just like in a page, the styles and altStyles strings can be a comma
034: * separated list of styles. Handle this by storing styles just like pages.
035: */
036: public class IntroExtensionContent extends AbstractIntroElement {
037:
038: protected static final String TAG_CONTAINER_EXTENSION = "extensionContent"; //$NON-NLS-1$
039: protected static final String TAG_CONTAINER_REPLACE = "replacementContent"; //$NON-NLS-1$
040:
041: public static final int TYPE_CONTRIBUTION = 0;
042: public static final int TYPE_REPLACEMENT = 1;
043:
044: protected static final String ATT_PATH = "path"; //$NON-NLS-1$
045: protected static final String ATT_ID = "id"; //$NON-NLS-1$
046: private static final String ATT_STYLE = "style"; //$NON-NLS-1$
047: private static final String ATT_ALT_STYLE = "alt-style"; //$NON-NLS-1$
048: private static final String ATT_CONTENT = "content"; //$NON-NLS-1$
049:
050: private static final Element[] EMPTY_ELEMENT_ARRAY = new Element[0];
051:
052: private String path;
053: private String content;
054: private String contentFile;
055: private String contentId;
056: private String anchorId;
057:
058: private Element element;
059: private String base;
060:
061: private Vector styles = new Vector();
062: private Hashtable altStyles = new Hashtable();
063:
064: IntroExtensionContent(Element element, Bundle bundle, String base,
065: IConfigurationElement configExtElement) {
066: super (element, bundle);
067: path = getAttribute(element, ATT_PATH);
068: content = getAttribute(element, ATT_CONTENT);
069: anchorId = getAttribute(element, ATT_ID);
070: this .element = element;
071: this .base = base;
072:
073: // load and resolve styles, first.
074: init(element, bundle, base);
075:
076: // if content is not null we have XHTML extension.
077: if (content != null) {
078: // BASE: since content is being loaded from another XHTML file and
079: // not this xml file, point the base of this page to be relative to
080: // the new xml file location.
081: IPath subBase = ModelUtil.getParentFolderPath(content);
082: String newBase = new Path(base).append(subBase).toString();
083: extractFileAndId(bundle);
084: contentFile = BundleUtil.getResolvedResourceLocation(base,
085: contentFile, bundle);
086: this .base = newBase;
087: }
088:
089: // Save the mapping between plugin registry id and base/anchor id
090: String contributor = configExtElement.getContributor()
091: .getName();
092: ExtensionMap.getInstance().putPluginId(anchorId, contributor);
093: }
094:
095: public String getId() {
096: return anchorId;
097: }
098:
099: /**
100: * Initialize styles. Take first style in style attribute and make it the
101: * page style. Then put other styles in styles vectors. Make sure to resolve
102: * each style.
103: *
104: * @param element
105: * @param bundle
106: */
107: private void init(Element element, Bundle bundle, String base) {
108: String[] styleValues = getAttributeList(element, ATT_STYLE);
109: if (styleValues != null && styleValues.length > 0) {
110: for (int i = 0; i < styleValues.length; i++) {
111: String style = styleValues[i];
112: style = BundleUtil.getResolvedResourceLocation(base,
113: style, bundle);
114: addStyle(style);
115: }
116: }
117:
118: String[] altStyleValues = getAttributeList(element,
119: ATT_ALT_STYLE);
120: if (altStyleValues != null && altStyleValues.length > 0) {
121: for (int i = 0; i < altStyleValues.length; i++) {
122: String style = altStyleValues[i];
123: style = BundleUtil.getResolvedResourceLocation(base,
124: style, bundle);
125: addAltStyle(style, bundle);
126: }
127: }
128: }
129:
130: /**
131: * Adds the given style to the list. Style is not added if it already exists
132: * in the list.
133: *
134: * @param style
135: */
136: protected void addStyle(String style) {
137: if (styles.contains(style))
138: return;
139: styles.add(style);
140: }
141:
142: /**
143: * Adds the given style to the list.Style is not added if it already exists
144: * in the list.
145: *
146: * @param altStyle
147: */
148: protected void addAltStyle(String altStyle, Bundle bundle) {
149: if (altStyles.containsKey(altStyle))
150: return;
151: altStyles.put(altStyle, bundle);
152: }
153:
154: /**
155: * Returns the extension type; either contribution into an anchor or replacement
156: * of an element.
157: */
158: public int getExtensionType() {
159: return TAG_CONTAINER_REPLACE.equals(element.getNodeName()) ? TYPE_REPLACEMENT
160: : TYPE_CONTRIBUTION;
161: }
162:
163: /**
164: * @return Returns the path.
165: */
166: public String getPath() {
167: return path;
168: }
169:
170: /*
171: * (non-Javadoc)
172: *
173: * @see org.eclipse.ui.internal.intro.impl.model.IntroElement#getType()
174: */
175: public int getType() {
176: return AbstractIntroElement.CONTAINER_EXTENSION;
177: }
178:
179: protected Element[] getChildren() {
180: NodeList nodeList = element.getChildNodes();
181: Vector vector = new Vector();
182: for (int i = 0; i < nodeList.getLength(); i++) {
183: Node node = nodeList.item(i);
184: if (node.getNodeType() == Node.ELEMENT_NODE)
185: vector.add(node);
186: }
187: Element[] filteredElements = new Element[vector.size()];
188: vector.copyInto(filteredElements);
189: // free DOM model for memory performance.
190: this .element = null;
191: return filteredElements;
192: }
193:
194: public boolean isXHTMLContent() {
195: return content != null ? true : false;
196: }
197:
198: /**
199: * Returns the elements loaded from the content attribute. This is the content
200: * that should be inserted for the extension. If it is a file, all child elements
201: * of body are returned. If it is a file with an id, only the element with the id
202: * is returned.
203: *
204: * @return the elements to be inserted
205: */
206: public Element[] getElements() {
207: // only applicable when content attribute is specified
208: if (isXHTMLContent()) {
209: IntroContentParser parser = new IntroContentParser(
210: contentFile);
211: Document dom = parser.getDocument();
212: if (dom != null) {
213: // parser content should be XHTML because defining content here
214: // means that we want XHTML extension.
215: if (parser.hasXHTMLContent()) {
216: if (contentId != null) {
217: // id specified, only get that element
218: return new Element[] { ModelUtil
219: .getElementById(dom, contentId) };
220: } else {
221: // no id specified, use the whole body
222: Element extensionBody = ModelUtil
223: .getBodyElement(dom);
224: return ModelUtil.getElementsByTagName(
225: extensionBody, "*"); //$NON-NLS-1$
226: }
227: }
228: }
229: }
230: return EMPTY_ELEMENT_ARRAY;
231: }
232:
233: /**
234: * @return Returns the altStyle.
235: */
236: protected Hashtable getAltStyles() {
237: return altStyles;
238: }
239:
240: /**
241: * @return Returns the style.
242: */
243: protected String[] getStyles() {
244: String[] stylesArray = new String[styles.size()];
245: styles.copyInto(stylesArray);
246: return stylesArray;
247: }
248:
249: /**
250: * @return Returns the content.
251: */
252: public String getContent() {
253: return content;
254: }
255:
256: public String getBase() {
257: return base;
258: }
259:
260: /**
261: * Extracts the file and id parts of the content attribute. This attribute has two modes -
262: * if you specify a file, it will include the body of that file (minus the body element itself).
263: * If you append an id after the file, only the element with that id will be included. However
264: * we need to know which mode we're in.
265: *
266: * @param bundle the bundle that contributed this extension
267: */
268: private void extractFileAndId(Bundle bundle) {
269: // look for the file
270: IPath resourcePath = new Path(base + content);
271: if (FileLocator.find(bundle, resourcePath, null) != null) {
272: // found it, assume it's a file
273: contentFile = content;
274: } else {
275: // didn't find the file, assume the last segment is an id
276: int lastSlashIndex = content.lastIndexOf('/');
277: if (lastSlashIndex != -1) {
278: contentFile = content.substring(0, lastSlashIndex);
279: contentId = content.substring(lastSlashIndex + 1);
280: } else {
281: // there was no slash, it must be a file
282: contentFile = content;
283: }
284: }
285: }
286: }
|