blob: c5489cd35aa3b36f931bfb1a449ea2d5e475c782 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2009 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.html;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Map;
import org.eclipse.core.runtime.Platform;
import org.eclipse.help.internal.util.ProductPreferences;
import org.eclipse.swt.browser.Browser;
import org.eclipse.ui.internal.intro.impl.FontSelection;
import org.eclipse.ui.internal.intro.impl.IIntroConstants;
import org.eclipse.ui.internal.intro.impl.IntroPlugin;
import org.eclipse.ui.internal.intro.impl.model.AbstractBaseIntroElement;
import org.eclipse.ui.internal.intro.impl.model.AbstractIntroElement;
import org.eclipse.ui.internal.intro.impl.model.AbstractIntroPage;
import org.eclipse.ui.internal.intro.impl.model.IntroContentProvider;
import org.eclipse.ui.internal.intro.impl.model.IntroGroup;
import org.eclipse.ui.internal.intro.impl.model.IntroHTML;
import org.eclipse.ui.internal.intro.impl.model.IntroHead;
import org.eclipse.ui.internal.intro.impl.model.IntroImage;
import org.eclipse.ui.internal.intro.impl.model.IntroInjectedIFrame;
import org.eclipse.ui.internal.intro.impl.model.IntroLink;
import org.eclipse.ui.internal.intro.impl.model.IntroPageTitle;
import org.eclipse.ui.internal.intro.impl.model.IntroSeparator;
import org.eclipse.ui.internal.intro.impl.model.IntroText;
import org.eclipse.ui.internal.intro.impl.model.IntroTheme;
import org.eclipse.ui.internal.intro.impl.model.loader.ContentProviderManager;
import org.eclipse.ui.internal.intro.impl.model.util.BundleUtil;
import org.eclipse.ui.internal.intro.impl.presentations.BrowserIntroPartImplementation;
import org.eclipse.ui.internal.intro.impl.util.Log;
import org.eclipse.ui.intro.config.IIntroContentProvider;
import org.eclipse.ui.intro.config.IIntroContentProviderSite;
public class IntroHTMLGenerator {
private AbstractIntroPage introPage;
private IIntroContentProviderSite providerSite;
private boolean backgroundSizeWorks;
/**
* Generates the HTML code that will be presented in the browser widget for the provided intro
* page.
*
* @param page
* the page to generate HTML for
* @param presentation
* the presentation associated with this page.
*/
public HTMLElement generateHTMLforPage(AbstractIntroPage page, IIntroContentProviderSite providerSite) {
if (page == null)
return null;
this.introPage = page;
this.providerSite = providerSite;
initializeBackgroundSizeWorks();
// generate and add the appropriate encoding to the top of the document
// generateEncoding();
// create the main HTML element, and all of its contents.
return generateHTMLElement();
}
private void initializeBackgroundSizeWorks() {
// Internet Explorer <= 9 doesn't properly handle background-size
backgroundSizeWorks = true;
try {
if (isIE()) {
Class<?> ieClass = Class.forName("org.eclipse.swt.browser.IE"); //$NON-NLS-1$
Field field = ieClass.getDeclaredField("IEVersion"); //$NON-NLS-1$
field.setAccessible(true);
int value = field.getInt(ieClass);
// We specifically care about background-size which works in 9+
backgroundSizeWorks = value <= 0 || value >= 9;
}
} catch(Exception e) {
// IE not found
}
}
private boolean isIE() {
return getBrowser() != null && "ie".equals(getBrowser().getBrowserType()); //$NON-NLS-1$
}
/**
* Return the SWT Browser instance being used to render the intro.
*
* @return the browser or {@code null} if the browser could not be determined
*/
private Browser getBrowser() {
if (providerSite instanceof BrowserIntroPartImplementation) {
return ((BrowserIntroPartImplementation) providerSite).getBrowser();
}
return null;
}
/*
* private HTMLElement generateEncoding() { HTMLElement encoding = new HTMLElement("");
* //$NON-NLS-1$ // TODO: figure out how to handle locale based encoding // As far as the HTML
* generator is concerned, this is probably as // simple as asking the model for the information
* return encoding; }
*/
/**
* Generates the HTML element and its content:
*
* <pre>
*
* &lt;HTML&gt;
* &lt;HEAD&gt;
* head content
* &lt;/HEAD&gt;
* &lt;BODY&gt;
* body content
* &lt;/BODY&gt;
* &lt;/HTML&gt;
*
* </pre>
*
* @return the html HTMLElement
*/
private HTMLElement generateHTMLElement() {
// this is the outermost element, so it has no indent
int indentLevel = 0;
HTMLElement html = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_HTML, indentLevel, true);
HTMLElement head = generateHeadElement(indentLevel + 1);
HTMLElement body = generateBodyElement(indentLevel + 1, head);
html.addContent(head);
html.addContent(body);
return html;
}
/**
* Generates the HEAD element and its content:
*
* <pre>
*
*
* &lt;HEAD&gt;
* &lt;BASE href=&quot;base_plugin_location&gt;
* &lt;style type=&quot;text/css&quot;&gt;HTML, IMG { border: 0px; } &lt;/style&gt;
* &lt;TITLE&gt;page title &lt;/TITLE&gt;
* &lt;LINK href=&quot;style sheet&quot;&gt;
* additional head content, if specified
* &lt;/HEAD&gt;
*
* </pre>
*
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return the head HTMLElement
*/
private HTMLElement generateHeadElement(int indentLevel) {
HTMLElement head = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_HEAD, indentLevel, true);
addBrowserRenderingDirectives(head, indentLevel + 1);
// add the title
head.addContent(generateTitleElement(introPage.getTitle(), indentLevel + 1));
head.addContent(generateUTF8CharsetElement(indentLevel + 1));
// create the BASE element
String basePath = BundleUtil.getResolvedResourceLocation(introPage.getBase(), introPage.getBundle());
HTMLElement base = generateBaseElement(indentLevel + 1, basePath);
if (base != null)
head.addContent(base);
// create the HTML style block
head.addContent(generateStyleElement(indentLevel + 1));
// add the presentation style
String[] presentationStyles = IntroPlugin.getDefault().getIntroModelRoot().getPresentation()
.getImplementationStyles();
if (presentationStyles != null && introPage.injectSharedStyle()) {
for (int i=0; i<presentationStyles.length; i++)
head.addContent(generateLinkElement(presentationStyles[i], indentLevel + 1));
}
String pageStyle = introPage.getStyle();
if (pageStyle != null)
head.addContent(generateLinkElement(pageStyle, indentLevel + 1));
// add javascript
head.addContent(generateJavascriptElement(indentLevel + 1));
// add the page's inherited style(s)
String[] pageStyles = introPage.getStyles();
for (int i = 0; i < pageStyles.length; i++) {
pageStyle = pageStyles[i];
if (pageStyle != null)
head.addContent(generateLinkElement(pageStyle, indentLevel + 1));
}
// if there is additional head conent specified in an external file,
// include it. Additional head content can be specified at the
// implementation level (which would apply to ALL pages) and at the
// page level (which would apply only to that particular page).
// For the implementation's head contribution:
StringBuffer content = null;
IntroHead introHead = IntroPlugin.getDefault().getIntroModelRoot().getPresentation().getHead();
if (introHead != null) {
content = readFromFile(introHead.getSrc(), introHead.getInlineEncoding());
if (content != null)
head.addContent(content);
}
// For the page's head contribution:
// TODO: there should only be one of these at the page level, not a
// collection..
IntroHead[] htmlHeads = introPage.getHTMLHeads();
for (int i = 0; i < htmlHeads.length; i++) {
introHead = htmlHeads[i];
if (introHead != null) {
content = readFromFile(introHead.getSrc(), introHead.getInlineEncoding());
if (content != null)
head.addContent(content);
}
}
return head;
}
/**
* Add any browser-specific rendering quirks
*
* @param indentLevel
*/
private void addBrowserRenderingDirectives(HTMLElement head, int indentLevel) {
/* IE renders intranet content in Compat View by default */
if (isIE() && "html5".equals(getThemeProperty("standardSupport"))) { //$NON-NLS-1$ //$NON-NLS-2$
// "Edge mode tells Internet Explorer to display content in the highest mode available"
HTMLElement meta = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_META, indentLevel, true);
meta.addAttribute(IIntroHTMLConstants.ATTRIBUTE_HTTP_EQUIV, "X-UA-Compatible"); //$NON-NLS-1$
meta.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CONTENT, "IE=edge"); //$NON-NLS-1$
head.addContent(meta);
}
}
private String getThemeProperty(String key) {
IntroTheme theme = introPage.getModelRoot().getTheme();
if (theme == null) {
return null;
}
Map<String, String> properties = theme.getProperties();
if (properties == null) {
return null;
}
return properties.get(key);
}
private HTMLElement generateUTF8CharsetElement(int indentLevel) {
HTMLElement meta = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_META, indentLevel, false);
meta.addAttribute(IIntroHTMLConstants.ATTRIBUTE_HTTP_EQUIV, IIntroHTMLConstants.CONTENT_TYPE);
meta.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CONTENT, IIntroHTMLConstants.TYPE_HTML_UTF_8);
return meta;
}
private HTMLElement generateJavascriptElement(int indentLevel) {
String rel = "javascript/common.js"; //$NON-NLS-1$
String abs = BundleUtil.getResolvedResourceLocation(rel, IntroPlugin.getDefault().getBundle());
HTMLElement jselement = new FormattedHTMLElement("script", indentLevel, false); //$NON-NLS-1$
jselement.addAttribute("type", "text/javascript"); //$NON-NLS-1$ //$NON-NLS-2$
jselement.addAttribute("src", abs); //$NON-NLS-1$
return jselement;
}
/**
* Generates the BODY element and its content:
*
* <pre>
*
*
* &lt;BODY&gt;
* &lt;DIV id=&quot;pageId&quot; class=&quot;pageClass&quot;&gt;
* page content
* &lt;/DIV&gt;
* &lt;/BODY&gt;
*
* </pre>
*
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return the body HTMLElement
*/
private HTMLElement generateBodyElement(int indentLevel, HTMLElement head) {
HTMLElement body = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_BODY, indentLevel, true);
// Create the div that contains the page content
String pageId = (introPage.getId() != null) ? introPage.getId() : IIntroHTMLConstants.DIV_ID_PAGE;
HTMLElement pageContentDiv = generateDivElement(pageId, indentLevel + 1);
if (introPage.getStyleId() != null)
pageContentDiv.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, introPage.getStyleId());
if (introPage.getBackgroundImage() != null)
pageContentDiv.addAttribute(IIntroHTMLConstants.ATTRIBUTE_STYLE,
"background-image : url(" + introPage.getBackgroundImage() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
// Add any children of the page, in the order they are defined
AbstractIntroElement[] children = introPage.getChildren();
for (int i = 0; i < children.length; i++) {
AbstractIntroElement child = children[i];
// use indentLevel + 2 here, since this element is contained within
// the pageContentDiv
HTMLElement childElement = generateIntroElement(child, indentLevel + 2);
if (childElement != null) {
addMixinStyle(childElement, child.getMixinStyle());
pageContentDiv.addContent(childElement);
}
}
body.addContent(pageContentDiv);
return body;
}
/**
* Given an IntroElement, generate the appropriate HTMLElement
*
* @param element
* the IntroElement
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return an HTMLElement
*/
private HTMLElement generateIntroElement(AbstractIntroElement element, int indentLevel) {
if (element == null)
return null;
// check to see if this element should be filtered from the HTML
// presentation
if (filteredFromPresentation(element))
return null;
switch (element.getType()) {
case AbstractIntroElement.GROUP:
return generateIntroDiv((IntroGroup) element, indentLevel);
case AbstractIntroElement.LINK:
return generateIntroLink((IntroLink) element, indentLevel);
case AbstractIntroElement.HTML:
return generateIntroHTML((IntroHTML) element, indentLevel);
case AbstractIntroElement.CONTENT_PROVIDER:
return generateIntroContent((IntroContentProvider) element, indentLevel);
case AbstractIntroElement.IMAGE:
return generateIntroImage((IntroImage) element, indentLevel);
case AbstractIntroElement.HR:
return generateIntroSeparator((IntroSeparator) element, indentLevel);
case AbstractIntroElement.TEXT:
return generateIntroText((IntroText) element, indentLevel);
case AbstractIntroElement.PAGE_TITLE:
return generateIntroTitle((IntroPageTitle) element, indentLevel);
case AbstractIntroElement.INJECTED_IFRAME:
return generateIntroInjectedIFrame((IntroInjectedIFrame) element, indentLevel);
default:
return null;
}
}
/**
* Create a div element and its content from an IntroDiv:
*
* <pre>
*
*
* &lt;div id=&quot;attrvalue&quot;&gt;
* &lt;h4&gt;&lt;span class=&quot;div-label&quot;&gt;attrvalue&lt;/span&gt;&lt;h4&gt;
* any defined divs, links, html, images, text, includes
* &lt;/div&gt;
*
* </pre>
*
* @param element
* the IntroDiv
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return a div HTMLElement
*/
private HTMLElement generateIntroDiv(IntroGroup element, int indentLevel) {
// Create the outer div element
HTMLElement divElement = generateDivElement(element.getId(), indentLevel);
HTMLElement childContainer = divElement;
// if a div class was specified, add it
if (element.getStyleId() != null)
divElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, element.getStyleId());
// Create the div label, if specified
if (element.getLabel() != null) {
if (element.isExpandable()) {
HTMLElement divLabel = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_SPAN,
indentLevel + 2, false);
divLabel.addContent(element.getLabel());
divLabel.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS,
"section-title");//$NON-NLS-1$
String clientId = element.getId() + "-content"; //$NON-NLS-1$
String toggleClosedId = element.getId() + "-toggle-closed"; //$NON-NLS-1$
String toggleOpenId = element.getId() + "-toggle-open"; //$NON-NLS-1$
String href = "#"; //$NON-NLS-1$
HTMLElement link = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_ANCHOR,
indentLevel + 1, true);
link.addAttribute(IIntroHTMLConstants.ATTRIBUTE_HREF, href);
link.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, "section-title-link"); //$NON-NLS-1$
StringBuffer call = new StringBuffer();
call.append("return (toggleSection('");//$NON-NLS-1$
call.append(clientId);
call.append("','");//$NON-NLS-1$
call.append(toggleClosedId);
call.append("','");//$NON-NLS-1$
call.append(toggleOpenId);
call.append("'))"); //$NON-NLS-1$
link.addAttribute("onClick", call.toString()); //$NON-NLS-1$
link.addContent(divLabel);
divElement.addContent(link);
// Add toggle images
HTMLElement toggleImageClosed = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_IMG,
indentLevel + 2, false, false);
toggleImageClosed.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, toggleClosedId);
toggleImageClosed.addAttribute(IIntroHTMLConstants.ATTRIBUTE_SRC, BundleUtil
.getResolvedResourceLocation(IIntroHTMLConstants.IMAGE_SRC_BLANK,
IIntroConstants.PLUGIN_ID));
toggleImageClosed.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, "section-toggle-image-closed"); //$NON-NLS-1$
if (element.isExpanded())
toggleImageClosed.addAttribute(IIntroHTMLConstants.ATTRIBUTE_STYLE, "display: none"); //$NON-NLS-1$
link.addContent(toggleImageClosed);
HTMLElement toggleImageOpen = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_IMG,
indentLevel + 2, false, false);
toggleImageOpen.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, toggleOpenId);
toggleImageOpen.addAttribute(IIntroHTMLConstants.ATTRIBUTE_SRC, BundleUtil
.getResolvedResourceLocation(IIntroHTMLConstants.IMAGE_SRC_BLANK,
IIntroConstants.PLUGIN_ID));
toggleImageOpen.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, "section-toggle-image-open"); //$NON-NLS-1$
if (element.isExpanded())
toggleImageOpen.addAttribute(IIntroHTMLConstants.ATTRIBUTE_STYLE, "display: inline"); //$NON-NLS-1$
link.addContent(toggleImageOpen);
childContainer = generateDivElement(clientId, indentLevel + 1);
childContainer.addAttribute("class", "section-body"); //$NON-NLS-1$//$NON-NLS-2$
if (element.isExpanded())
childContainer.addAttribute(IIntroHTMLConstants.ATTRIBUTE_STYLE, "display: block"); //$NON-NLS-1$
divElement.addContent(childContainer);
} else {
HTMLElement divLabel = generateTextElement(IIntroHTMLConstants.ELEMENT_H4, null,
IIntroHTMLConstants.SPAN_CLASS_DIV_LABEL, element.getLabel(), indentLevel + 1);
divElement.addContent(divLabel);
}
}
if (element.getBackgroundImage() != null) {
String imageUrl = element.getBackgroundImage();
imageUrl = BundleUtil.getResolvedResourceLocation(element.getBase(), imageUrl, element
.getBundle());
String style;
if (Platform.getWS().equals(Platform.WS_WIN32) && !backgroundSizeWorks && imageUrl.toLowerCase().endsWith(".png")) { //$NON-NLS-1$
// IE 5.5+ does not handle alphas in PNGs without
// this hack. Remove when IE7 becomes widespread
style = "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + imageUrl + "', sizingMethod='crop');"; //$NON-NLS-1$ //$NON-NLS-2$
} else {
style = "background-image : url(" + imageUrl + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
divElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_STYLE, style);
}
// Add any children of the div, in the order they are defined
AbstractIntroElement[] children = element.getChildren();
for (int i = 0; i < children.length; i++) {
AbstractIntroElement child = children[i];
HTMLElement childElement = generateIntroElement(child, indentLevel + 1);
if (childElement != null) {
addMixinStyle(childElement, child.getMixinStyle());
childContainer.addContent(childElement);
}
}
return divElement;
}
private void addMixinStyle(HTMLElement element, String mixinStyle) {
if (mixinStyle == null)
return;
String key = "class"; //$NON-NLS-1$
String original = (String) element.getElementAttributes().get(key);
if (original == null)
original = mixinStyle;
else
original += " " + mixinStyle; //$NON-NLS-1$
element.addAttribute(key, original);
}
/**
* Generates an anchor (link) element and its content from an IntroLink:
*
* <pre>
*
* &lt;A id=linkId class=&quot;link&quot; href=linkHref&gt;
* &lt;IMG src=&quot;blank.gif&quot;&gt;
* &lt;SPAN class=&quot;link-label&quot;&gt;linkLabel &lt;/SPAN&gt;
* &lt;P&gt;&lt;SPAN&gt;text&lt;/SPAN&gt;&lt;/P&gt;
* &lt;/A&gt;
*
* </pre>
*
* @param element
* the IntroLink
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return an anchor (&lt;A&gt;) HTMLElement
*/
private HTMLElement generateIntroLink(IntroLink element, int indentLevel) {
String styleId = element.getStyleId();
boolean useTable = ProductPreferences.isRTL() && "content-link".equals(styleId); //$NON-NLS-1$
HTMLElement anchor1 = generateAnchorElement(element, indentLevel);
HTMLElement anchor2 = null;
HTMLElement labelAnchor = anchor1;
int indentBase = indentLevel;
if (useTable) {
indentBase = indentLevel + 1;
anchor2 = generateAnchorElement(element, indentLevel + 1);
labelAnchor = anchor2;
}
// add <IMG src="blank.gif">
String blankImageURL = BundleUtil.getResolvedResourceLocation(IIntroHTMLConstants.IMAGE_SRC_BLANK,
IIntroConstants.PLUGIN_ID);
if (blankImageURL != null) {
anchor1.addContent(generateImageElement(blankImageURL, null, null, IIntroHTMLConstants.IMAGE_CLASS_BG,
indentBase + 1));
}
// add link image, if one is specified
if (element.getImg() != null) {
HTMLElement img = generateIntroElement(element.getImg(), indentBase + 1);
if (img != null)
anchor1.addContent(img);
}
if (!useTable) {
HTMLElement imageDiv = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_DIV, indentBase+1, false);
imageDiv.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS,
IIntroHTMLConstants.LINK_EXTRA_DIV);
anchor1.addContent(imageDiv);
}
// add <SPAN class="link-label">linkLabel</SPAN>
if (element.getLabel() != null) {
HTMLElement label = generateSpanElement(IIntroHTMLConstants.SPAN_CLASS_LINK_LABEL,
indentBase + 2);
label.addContent(element.getLabel());
labelAnchor.addContent(label);
}
IntroText linkText = element.getIntroText();
if (linkText != null && linkText.getText() != null) {
HTMLElement text = generateIntroElement(linkText, indentBase + 3);
if (text != null)
labelAnchor.addContent(text);
}
if (!useTable) {
return anchor1;
}
HTMLElement table = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_TABLE, indentLevel, false);
HTMLElement tr = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_TR, indentLevel + 1, false);
table.addContent(tr);
HTMLElement td1 = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_TD, indentLevel + 1, false);
HTMLElement td2 = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_TD, indentLevel + 1, false);
tr.addContent(td1);
tr.addContent(td2);
td1.addContent(anchor1);
td2.addContent(anchor2);
return table;
}
/**
* Generate the appropriate HTML from an IntroHTML. If the IntroHTML type is "inline", then the
* content from the referenced file is emitted as-is into a div element. If the type is "embed",
* an OBJECT html element is created whose <code>data</code> attribute is equal to the
* IntroHTML's <code>src</code> value
*
* @param element
* the IntroHTML
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return an HTMLElement
*/
private HTMLElement generateIntroHTML(IntroHTML element, int indentLevel) {
if (element.isInlined())
return generateInlineIntroHTML(element, indentLevel);
return generateEmbeddedIntroHTML(element, indentLevel);
}
/**
* Generate an image element from an IntroImage:
*
* <pre>
*
* &lt;IMG src=imageSrc id=imageId&gt;
*
* </pre>
*
* @param element
* the IntroImage
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return an img HTMLElement
*/
private HTMLElement generateIntroImage(IntroImage element, int indentLevel) {
HTMLElement imageElement = generateImageElement(element.getSrc(), element.getAlt(),element.getTitle(), element
.getStyleId(), indentLevel);
if (element.getId() != null)
imageElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, element.getId());
return imageElement;
}
private HTMLElement generateIntroSeparator(IntroSeparator element, int indentLevel) {
HTMLElement hrElement = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_HR, indentLevel, false);
if (element.getId() != null)
hrElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, element.getId());
if (element.getStyleId() != null)
hrElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_STYLE, element.getStyleId());
return hrElement;
}
/**
* Generate a paragraph (&lt;P&gt;) element from an IntroText. The paragraph element will
* contain a span element that will contain the actual text. Providing the span element provides
* additional flexibility for CSS designers.
*
* <pre>
*
*
* &lt;P&gt;&lt;SPAN&gt;spanContent&lt;/SPAN&gt;&lt;/P&gt;
*
* </pre>
*
* @param element
* the IntroText
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return a paragraph HTMLElement
*/
private HTMLElement generateIntroText(IntroText element, int indentLevel) {
String spanClass = (element.getStyleId() != null) ? element.getStyleId()
: IIntroHTMLConstants.SPAN_CLASS_TEXT;
HTMLElement textElement = generateTextElement(IIntroHTMLConstants.ELEMENT_PARAGRAPH, element.getId(),
spanClass, element.getText(), indentLevel);
return textElement;
}
/**
* @param element
* @param indentLevel
* @return
*/
private HTMLElement generateIntroInjectedIFrame(IntroInjectedIFrame element, int indentLevel) {
HTMLElement iframe = generateIFrameElement(element.getIFrameURL(), "0", //$NON-NLS-1$
"auto", indentLevel); //$NON-NLS-1$
return iframe;
}
/**
* @param element
* @param indentLevel
* @return
*/
private HTMLElement generateIntroTitle(IntroPageTitle element, int indentLevel) {
HTMLElement titleElement = generateHeaderDiv(element.getId(), element.getStyleId(),
IIntroHTMLConstants.ELEMENT_H1, element.getTitle(), indentLevel);
return titleElement;
}
/**
* Generate "inline" content from an IntroHTML. The content from the file referenced by the
* IntroHTML's <code>src</code> attribute is emitted as-is into a div element:
*
* <pre>
*
*
* &lt;div id=&quot;attrvalue&quot; class=&quot;attrvalue2&quot;&gt;
* content from file specified in src attribute
* &lt;/div&gt;
*
* </pre>
*
* @param element
* the IntroHTML
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return a div HTMLElement, or null if there was a problem reading from the file
*/
private HTMLElement generateInlineIntroHTML(IntroHTML element, int indentLevel) {
// make sure to ask model for encoding. If encoding is null (ie: not
// specified in
// markup, local encoding is used.
StringBuffer content = readFromFile(element.getSrc(), element.getInlineEncoding());
if (content != null && content.length() > 0) {
// Create the outer div element
String divClass = (element.getStyleId() != null) ? element.getStyleId()
: IIntroHTMLConstants.DIV_CLASS_INLINE_HTML;
HTMLElement divElement = generateDivElement(element.getId(), divClass, indentLevel);
// add the content of the specified file into the div element
divElement.addContent(content);
return divElement;
}
return null;
}
/**
* Includes HTML content that is created by an IIntroContentProvider implementation.
*
* @param element
* @param indentLevel
* @return
*/
private HTMLElement generateIntroContent(IntroContentProvider element, int indentLevel) {
// create a new div to wrap the content
HTMLElement divElement = generateDivElement(element.getId(),
IIntroHTMLConstants.DIV_CLASS_PROVIDED_CONTENT, indentLevel);
// If we've already loaded the content provider for this element,
// retrieve it, otherwise load the class
IIntroContentProvider providerClass = ContentProviderManager.getInst().getContentProvider(element);
if (providerClass == null)
// content provider never created before, create it.
providerClass = ContentProviderManager.getInst().createContentProvider(element, providerSite);
if (providerClass != null) {
StringWriter stringWriter = new StringWriter();
PrintWriter pw = new PrintWriter(stringWriter);
// create the specialized content
providerClass.createContent(element.getId(), pw);
// add the content of the specified file into the div element
stringWriter.flush();
divElement.addContent(stringWriter.toString());
pw.close();
} else {
// we couldn't load the content provider, so add any alternate
// text content if there is any
IntroText htmlText = element.getIntroText();
if (htmlText != null && htmlText.getText() != null) {
String textClass = (htmlText.getStyleId() != null) ? htmlText.getStyleId()
: IIntroHTMLConstants.SPAN_CLASS_TEXT;
HTMLElement text = generateTextElement(IIntroHTMLConstants.ELEMENT_PARAGRAPH, htmlText
.getId(), textClass, element.getText(), indentLevel);
if (text != null)
divElement.addContent(text);
}
}
return divElement;
}
/**
* Generate "embedded" content from an IntroHTML. An OBJECT html element is created whose
* <code>data</code> attribute is equal to the IntroHTML's <code>src</code> value.
*
* <pre>
*
* &lt;OBJECT type=&quot;text/html&quot; data=&quot;attrvalue&quot;&gt;
* alternative text in case the object can not be rendered
* &lt;/OBJECT&gt;
*
* </pre>
*
* @param element
* the IntroHTML
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return an object HTMLElement
*/
private HTMLElement generateEmbeddedIntroHTML(IntroHTML element, int indentLevel) {
HTMLElement objectElement = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_OBJECT, indentLevel,
true);
objectElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_TYPE, IIntroHTMLConstants.OBJECT_TYPE);
if (element.getId() != null)
objectElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, element.getId());
if (element.getSrc() != null)
objectElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_DATA, element.getSrc());
if (element.getStyleId() != null)
objectElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, element.getStyleId());
// The alternative content is added in case the browser can not render
// the specified content.
IntroText htmlText = element.getIntroText();
if (htmlText != null && htmlText.getText() != null) {
String textClass = (htmlText.getStyleId() != null) ? htmlText.getStyleId()
: IIntroHTMLConstants.SPAN_CLASS_TEXT;
HTMLElement text = generateTextElement(IIntroHTMLConstants.ELEMENT_PARAGRAPH, htmlText.getId(),
textClass, element.getText(), indentLevel);
if (text != null)
objectElement.addContent(text);
}
if (element.getIntroImage() != null) {
HTMLElement img = generateIntroImage(element.getIntroImage(), indentLevel);
if (img != null)
objectElement.addContent(img);
}
return objectElement;
}
/**
* Generates the BASE element for the head of the html document. Each document can have only one
* base element
*
* <pre>
*
*
* &lt;BASE href=baseURL&gt;
* </pre>
*
* @param indentLevel
* @param baseURL
* @return
*/
private HTMLElement generateBaseElement(int indentLevel, String baseURL) {
HTMLElement base = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_BASE, indentLevel, true,
false);
if (baseURL != null)
base.addAttribute(IIntroHTMLConstants.ATTRIBUTE_HREF, baseURL);
return base;
}
/**
* Generates the style element that goes into HEAD:
*
* <pre>
*
* &lt;style type=&quot;text/css&quot;&gt;HTML, IMG { border: 0px; } &lt;/style&gt;
*
* </pre>
*
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return the style HTMLElement
*/
private HTMLElement generateStyleElement(int indentLevel) {
HTMLElement style = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_STYLE, indentLevel, false);
style.addAttribute(IIntroHTMLConstants.ATTRIBUTE_TYPE, IIntroHTMLConstants.LINK_STYLE);
style.addContent(IIntroHTMLConstants.STYLE_HTML);
IntroTheme theme = introPage.getModelRoot().getTheme();
if (theme != null && theme.isScalable()
&& FontSelection.FONT_RELATIVE.equals(FontSelection.getFontStyle())) {
String sizeStyle = FontSelection.generatePageFontStyle();
style.addContent(sizeStyle);
}
return style;
}
/**
* Generates the title element and its content:
*
* <pre>
*
* &lt;TITLE&gt;intro title&lt;/TITLE&gt;
*
* </pre>
*
* @param title
* the title of this intro page
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return the title HTMLElement
*/
private HTMLElement generateTitleElement(String title, int indentLevel) {
HTMLElement titleElement = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_TITLE, indentLevel,
false);
if (title != null)
titleElement.addContent(title);
return titleElement;
}
/**
* Generates a link element that refers to a cascading style sheet (CSS):
*
* <pre>
*
*
* &lt;LINK rel=&quot;stylesheet&quot; style=&quot;text/css&quot; href=&quot;style sheet&quot;&gt;
* </pre>
*
* @param href
* the value of the href attribute for this link element
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return a link HTMLElement
*/
private HTMLElement generateLinkElement(String href, int indentLevel) {
HTMLElement link = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_LINK, indentLevel, true,
false);
link.addAttribute(IIntroHTMLConstants.ATTRIBUTE_RELATIONSHIP, IIntroHTMLConstants.LINK_REL);
link.addAttribute(IIntroHTMLConstants.ATTRIBUTE_TYPE, IIntroHTMLConstants.LINK_STYLE);
if (href != null)
link.addAttribute(IIntroHTMLConstants.ATTRIBUTE_HREF, href);
return link;
}
/**
* Generate an anchor element:
*
* <pre>
*
* &lt;A id=linkId class=linkClass href=linkHref&gt; &lt;/A&gt;
*
* </pre>
*
* @param link
* the IntroLink element that contains the value for the id and href attributes
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return an anchor (&lt;A&gt;) HTMLElement
*/
private HTMLElement generateAnchorElement(IntroLink link, int indentLevel) {
HTMLElement anchor = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_ANCHOR, indentLevel, true);
if (link.getId() != null)
anchor.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, link.getId());
if (link.getUrl() != null)
anchor.addAttribute(IIntroHTMLConstants.ATTRIBUTE_HREF, link.getUrl());
if (link.getStyleId() != null)
anchor.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, link.getStyleId());
else
anchor.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, IIntroHTMLConstants.ANCHOR_CLASS_LINK);
return anchor;
}
/**
* Generates a div block that contains a header and span element:
*
* <pre>
*
*
* &lt;DIV id=divId&gt;
* &lt;H&gt;&lt;SPAN&gt;spanContent &lt;/SPAN&gt; &lt;/H&gt;
* &lt;/DIV&gt;
*
* </pre>
*
* @param divId
* the id of the div to create
* @param divClass
* the class of the div
* @param headerType
* what type of header to create (e.g., H1, H2, etc)
* @param spanContent
* the span content
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return a div HTMLElement that contains a header
*/
private HTMLElement generateHeaderDiv(String divId, String divClass, String headerType,
String spanContent, int indentLevel) {
// create the text element: <P><SPAN>spanContent</SPAN></P>
HTMLElement text = generateTextElement(headerType, null, null, spanContent, indentLevel + 1);
// create the containing div element
HTMLElement div = generateDivElement(divId, divClass, indentLevel);
div.addContent(text);
return div;
}
/**
* Generates a span element inside a text element, where the text element can be a P
* (paragraph), or any of the H (Header) elements. Providing the span element provides
* additional flexibility for CSS designers.
*
* <pre>
*
* &lt;P&gt;&lt;SPAN&gt;spanContent&lt;/SPAN&gt;&lt;/P&gt;
*
* </pre>
*
* @param type
* the type of text element to create (e.g., P, H1, H2, etc)
* @param spanID
* the id of the span element, or null
* @param spanClass
* the class of the span element, or null
* @param spanContent
* the span content
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return a text HTMLElement that contains a span element
*/
private HTMLElement generateTextElement(String type, String spanID, String spanClass, String spanContent,
int indentLevel) {
// Create the span: <SPAN>spanContent</SPAN>
HTMLElement span = new HTMLElement(IIntroHTMLConstants.ELEMENT_SPAN);
if (spanID != null)
span.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, spanID);
if (spanClass != null)
span.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, spanClass);
if (spanContent != null)
span.addContent(spanContent);
if (type != null) {
// Create the enclosing text element: <P><SPAN>spanContent</SPAN></P>
HTMLElement text = new FormattedHTMLElement(type, indentLevel, false);
text.addContent(span);
return text;
} else {
return span;
}
}
/**
* Generates a DIV element with the provided indent, id, and class.
*
* @param divId
* value for the div's id attribute
* @param divClass
* value for the div's class attribute
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return a div HTMLElement
*/
private HTMLElement generateDivElement(String divId, String divClass, int indentLevel) {
HTMLElement div = generateDivElement(divId, indentLevel);
div.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, divClass);
return div;
}
/**
* Generates a DIV element with the provided indent and id.
*
* @param divId
* value for the div's id attribute
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return a div HTMLElement
*/
private HTMLElement generateDivElement(String divId, int indentLevel) {
HTMLElement div = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_DIV, indentLevel, true);
if (divId != null)
div.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, divId);
return div;
}
/**
* Generates an IMG element:
*
* <pre>
*
*
* &lt;IMG src=imageSrc alt=altText&gt;
*
* </pre>
*
* @param imageSrc
* the value to be supplied to the src attribute
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return an img HTMLElement
*/
private HTMLElement generateImageElement(String imageSrc, String altText, String title, String imageClass,
int indentLevel) {
HTMLElement image = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_IMG, indentLevel, true,
false);
boolean pngOnWin32 = imageSrc != null && Platform.getWS().equals(Platform.WS_WIN32)
&& !backgroundSizeWorks && imageSrc.toLowerCase().endsWith(".png"); //$NON-NLS-1$
if (imageSrc == null || pngOnWin32) {
// we must handle PNGs here - IE does not support alpha blanding well.
// We will set the alpha image loader and load the real image
// that way. The 'src' attribute in the image itself will
// get the blank image.
String blankImageURL = BundleUtil.getResolvedResourceLocation(
IIntroHTMLConstants.IMAGE_SRC_BLANK, IIntroConstants.PLUGIN_ID);
if (blankImageURL != null) {
image.addAttribute(IIntroHTMLConstants.ATTRIBUTE_SRC, blankImageURL);
if (pngOnWin32) {
String style = "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + imageSrc + "', sizingMethod='image')"; //$NON-NLS-1$//$NON-NLS-2$
image.addAttribute(IIntroHTMLConstants.ATTRIBUTE_STYLE, style);
}
}
} else
image.addAttribute(IIntroHTMLConstants.ATTRIBUTE_SRC, imageSrc);
if (altText == null)
altText = ""; //$NON-NLS-1$
image.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ALT, altText);
if (title != null) {
image.addAttribute(IIntroHTMLConstants.ATTRIBUTE_TITLE, title);
}
if (imageClass != null)
image.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, imageClass);
return image;
}
/**
* Generate a span element
*
* <pre>
*
* &lt;SPAN class=spanClass&gt; &lt;/SPAN&gt;
*
*
* </pre>
*
* @param spanClass
* the value to be supplied to the class attribute
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return a span HTMLElement
*/
private HTMLElement generateSpanElement(String spanClass, int indentLevel) {
HTMLElement span = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_SPAN, indentLevel, false);
span.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, spanClass);
return span;
}
/**
* Generate a span element
*
* <pre>
*
* &lt;iframe src=&quot;localPage1.xhtml&quot; frameborder=&quot;1&quot; scrolling=&quot;auto&quot; longdesc=&quot;localPage1.xhtml&quot;&gt;
* </pre>
*
* @param spanClass
* the value to be supplied to the class attribute
* @param indentLevel
* the number of indents to insert before the element when it is printed
* @return a span HTMLElement
*/
private HTMLElement generateIFrameElement(String src, String frameborder, String scrolling,
int indentLevel) {
HTMLElement iframe = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_IFrame, indentLevel, false);
if (src != null)
iframe.addAttribute(IIntroHTMLConstants.ATTRIBUTE_SRC, src);
if (frameborder != null)
iframe.addAttribute(IIntroHTMLConstants.ATTRIBUTE_FRAMEBORDER, frameborder);
if (scrolling != null)
iframe.addAttribute(IIntroHTMLConstants.ATTRIBUTE_SCROLLING, scrolling);
return iframe;
}
private boolean filteredFromPresentation(AbstractIntroElement element) {
if (element.isOfType(AbstractIntroElement.BASE_ELEMENT))
return ((AbstractBaseIntroElement) element).isFiltered();
return false;
}
/**
* Reads the content of the file referred to by the <code>src</code> parameter and returns the
* content in the form of a StringBuffer. If the file read contains substitution segments of the
* form $plugin:plugin_id$ then this method will make the proper substitution (the segment will
* be replaced with the absolute path to the plugin with id plugin_id).
*
* @param src -
* the file that contains the target conent
* @param charsetName -
* the encoding of the file to be read. If null, local encoding is used. But the
* default of the model is UTF-8, so we should not get a null encoding.
* @return a StringBuffer containing the content in the file, or null
*/
private StringBuffer readFromFile(String src, String charsetName) {
if (src == null)
return null;
InputStream stream = null;
StringBuffer content = new StringBuffer();
BufferedReader reader = null;
try {
URL url = new URL(src);
stream = url.openStream();
// TODO: Do we need to worry about the encoding here? e.g.:
// reader = new BufferedReader(new InputStreamReader(stream,
// ResourcesPlugin.getEncoding()));
if (charsetName == null)
reader = new BufferedReader(new InputStreamReader(stream));
else
reader = new BufferedReader(new InputStreamReader(stream, charsetName));
while (true) {
int character = reader.read();
if (character == -1) // EOF
break; // done reading file
else if (character == PluginIdParser.SUBSTITUTION_BEGIN) { // possible
// substitution
PluginIdParser parser = new PluginIdParser(character, reader);
// If a valid plugin id was found in the proper format, text
// will be the absolute path to that plugin. Otherwise, text
// will simply be all characters read up to (but not
// including)
// the next dollar sign that follows the one just found.
String text = parser.parsePluginId();
if (text != null)
content.append(text);
} else {
// make sure character is in char range before making cast
if (character > 0x00 && character < 0xffff)
content.append((char) character);
else
content.append(character);
}
}
} catch (Exception exception) {
Log.error("Error reading from file", exception); //$NON-NLS-1$
} finally {
try {
if (reader != null)
reader.close();
if (stream != null)
stream.close();
} catch (IOException e) {
Log.error("Error closing input stream", e); //$NON-NLS-1$
return null;
}
}
return content;
}
/**
* A helper class to help identify substitution strings in a content file. A properly formatted
* substitution string is of the form: <code>$plugin:plugin_id$</code> where plugin_id is the
* valid id of an installed plugin. The substitution string will be replaced with the absolute
* path to the plugin.
*
* An example usage of the string substution: The html file <code>inline.html</code> is
* included in your intro via the html inline mechanism . This file needs to reference a
* resource that is located in another plugin. The following might be found in inline.html:
* <code>
* <a href="$plugin:test.plugin$html/test.html">link to file</a>
* </code> When this file
* is read in, the relevant section will be replaced as follows: <code>
* <a href="file:/install_path/plugins/test.plugin/html/test.html">link to file</a>
* </code>
*
*/
private static class PluginIdParser {
private BufferedReader reader;
private static final char SUBSTITUTION_BEGIN = '$';
private static final char SUBSTITUTION_END = '$';
// tokenContent will contain all characters read by the parser, starting
// with and including the initial $ token.
private StringBuffer tokenContent;
// pluginId will contain the content between the "$plugin:" segment
// and the closing "$" token
private StringBuffer pluginId;
protected PluginIdParser(int tokenBegin, BufferedReader bufferedreader) {
reader = bufferedreader;
tokenContent = new StringBuffer();
pluginId = new StringBuffer();
// make sure tokenBegin is in char range before making cast
if (tokenBegin > 0x00 && tokenBegin < 0xffff)
tokenContent.append((char) tokenBegin);
}
/**
* This method should be called after the initial substitution identifier has been read in
* (the substition string begins and ends with the "$" character). A properly formatted
* substitution string is of the form:</code> "$plugin:plugin_id$</code>- the initial "$"
* is immediately followed by the "plugin:" segment - the <code>plugin_id </code> refers to
* a valid, installed plugin - the substitution string is terminated by a closing "$" If the
* above conditions are not met, no substitution occurs. If the above conditions are met,
* the content between (and including) the opening and closing "$" characters will be
* replaced by the absolute path to the plugin
*
* @return
*/
protected String parsePluginId() {
if (reader == null || tokenContent == null || pluginId == null)
return null;
try {
// Mark the current position of the reader so we can roll
// back to this point if the proper "plugin:" segment is not
// found.
// Use 1024 as our readAheadLimit
reader.mark(0x400);
if (findValidPluginSegment()) {
String pluginPath = getPluginPath();
if (pluginPath == null) {
// Didn't find a valid plugin id.
// return tokenContent, which contains all characters
// read up to (not including) the last $. (if the
// last $ is part of a subsequent "$plugin:" segment
// it can still be processed properly)
return tokenContent.toString();
}
return pluginPath;
}
// The "plugin:" segment was not found. Reset the reader
// so we can continue reading character by character.
reader.reset();
return tokenContent.toString();
} catch (IOException exception) {
Log.error("Error reading from file", exception); //$NON-NLS-1$
return tokenContent.toString();
}
}
/**
* This method should be called after an initial substitution character has been found (that
* is, after a $). It looks at the subsequent characters in the input stream to determine if
* they match the expected <code>plugin:</code> segment of the substitution string. If the
* expected characters are found, they will be appended to the tokenContent StringBuffer and
* the method will return true. If they are not found, false is returned and the caller
* should reset the BufferedReader to the position it was in before this method was called.
*
* Resetting the reader ensures that the characters read in this method can be re-examined
* in case one of them happens to be the beginning of a valid substitution segment.
*
* @return true if the next characters match <code>plugin:</code>, and false otherwise.
*/
private boolean findValidPluginSegment() {
final char[] PLUGIN_SEGMENT = { 'p', 'l', 'u', 'g', 'i', 'n', ':' };
char[] streamContent = new char[PLUGIN_SEGMENT.length];
try {
int peek = reader.read(streamContent, 0, PLUGIN_SEGMENT.length);
if ((peek == PLUGIN_SEGMENT.length)
&& (HTMLUtil.equalCharArrayContent(streamContent, PLUGIN_SEGMENT))) {
// we have found the "$plugin:" segment
tokenContent.append(streamContent);
return true;
}
// The "plugin:" segment did not immediately follow the initial
// $.
return false;
} catch (IOException exception) {
Log.error("Error reading from file", exception); //$NON-NLS-1$
return false;
}
}
/**
* This method continues to read from the input stream until either the end of the file is
* reached, or until a character is found that indicates the end of the substitution. If the
* SUBSTITUTION_END character is found, the method looks up the plugin id that has been
* built up to see if it is a valid id. If so, return the absolute path to that plugin. If
* not, return null.
*
* This method assumes that the reader is positioned just after a valid <code>plugin:</code>
* segment in a substitution string.
*
* @return absolute path of the plugin id, if valid. null otherwise
*/
private String getPluginPath() {
try {
while (true) {
int nextChar = reader.read();
if (nextChar == -1) {
// reached EOF while looking for closing $
return null;
} else if (nextChar == SUBSTITUTION_END) { // end of plugin
// id
// look up the plugin id. If it is a valid id
// return the absolute path to this plugin.
// otherwise return null.
String path = BundleUtil.getResolvedBundleLocation(pluginId.toString());
// If the plugin id was not valid, reset reader to the
// previous mark. The mark should be at the character
// just before the last dollar sign.
if (path == null)
reader.reset();
return path;
} else { // we have a regular character
// mark the most recent non-dollar char in case we don't
// find a valid plugin id and have to roll back
// Use 1024 as our readAheadLimit
reader.mark(0x400);
// Add this character to the pluginId and tokenContent
// String.
// make sure we have a valid character before performing
// cast
if (nextChar > 0x00 && nextChar < 0xffff) {
tokenContent.append((char) nextChar);
// only include non-whitespace characters in plugin
// id
if (!Character.isWhitespace((char) nextChar))
pluginId.append((char) nextChar);
} else {
tokenContent.append(nextChar);
pluginId.append(nextChar);
}
}
}
} catch (IOException exception) {
Log.error("Error reading from file", exception); //$NON-NLS-1$
return null;
}
}
}
}