| /******************************************************************************* |
| * Copyright (c) 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ui.internal.intro.impl.html; |
| import java.io.*; |
| import java.net.*; |
| |
| import org.eclipse.ui.internal.intro.impl.*; |
| import org.eclipse.ui.internal.intro.impl.model.*; |
| import org.eclipse.ui.internal.intro.impl.util.*; |
| public class IntroHTMLGenerator { |
| private AbstractIntroPage introPage; |
| |
| /** |
| * 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 title |
| * the title of the intro presentation, or null |
| */ |
| public HTMLElement generateHTMLforPage(AbstractIntroPage page) { |
| if (page == null) |
| return null; |
| this.introPage = page; |
| // 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 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> |
| * |
| * <HTML> |
| * <HEAD> |
| * head content |
| * </HEAD> |
| * <BODY> |
| * body content |
| * </BODY> |
| * </HTML> |
| * |
| * </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); |
| html.addContent(head); |
| html.addContent(body); |
| return html; |
| } |
| /** |
| * Generates the HEAD element and its content: |
| * |
| * <pre> |
| * |
| * <HEAD> |
| * <BASE href="base_plugin_location> |
| * <style type="text/css">HTML, IMG { border: 0px; } </style> |
| * <TITLE>page title </TITLE> |
| * <LINK href="style sheet"> |
| * additional head content, if specified |
| * </HEAD> |
| * |
| * </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); |
| // add the title |
| head.addContent(generateTitleElement(null, indentLevel + 1)); |
| // create the BASE element |
| String basePath = HTMLUtil.getResolvedBundleLocation(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 style = IntroPlugin.getDefault().getIntroModelRoot() |
| .getPresentation().getImplementationStyle(); |
| if (style != null) |
| head.addContent(generateLinkElement(style, indentLevel + 1)); |
| //TODO: Should introPage.getStyles() return the main page style as |
| // well? |
| style = introPage.getStyle(); |
| if (style != null) |
| head.addContent(generateLinkElement(style, indentLevel + 1)); |
| // add the page's inherited style(s) |
| String[] pageStyles = introPage.getStyles(); |
| for (int i = 0; i < pageStyles.length; i++) { |
| style = pageStyles[i]; |
| if (style != null) |
| head.addContent(generateLinkElement(style, 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()); |
| 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()); |
| if (content != null) |
| head.addContent(content); |
| } |
| } |
| return head; |
| } |
| /** |
| * Generates the BODY element and its content: |
| * |
| * <pre> |
| * |
| * <BODY> |
| * <DIV id="pageId" class="pageClass"> |
| * page content |
| * </DIV> |
| * </BODY> |
| * |
| * </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 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()); |
| // 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]; |
| // check to see if this element should be filtered from the HTML |
| // presentation |
| if(!filteredFromPresentation(child)){ |
| // use indentLevel + 2 here, since this element is contained within |
| // the pageContentDiv |
| HTMLElement childElement = generateIntroElement(child, |
| indentLevel + 2); |
| if (childElement != null) |
| 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; |
| 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.IMAGE : |
| return generateIntroImage((IntroImage) element, indentLevel); |
| case AbstractIntroElement.TEXT : |
| return generateIntroText((IntroText) element, indentLevel); |
| case AbstractIntroElement.PAGE_TITLE : |
| return generateIntroTitle((IntroPageTitle) element, indentLevel); |
| default : |
| return null; |
| } |
| } |
| /** |
| * Create a div element and its content from an IntroDiv: |
| * |
| * <pre> |
| * |
| * <div id="attrvalue"> |
| * <h4><span class="div-label">attrvalue</span><h4> |
| * any defined divs, links, html, images, text, includes |
| * </div> |
| * |
| * </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); |
| // 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) { |
| HTMLElement divLabel = generateTextElement( |
| IIntroHTMLConstants.ELEMENT_H4, null, |
| IIntroHTMLConstants.SPAN_CLASS_DIV_LABEL, element |
| .getLabel(), indentLevel + 1); |
| divElement.addContent(divLabel); |
| } |
| // 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) |
| divElement.addContent(childElement); |
| } |
| return divElement; |
| } |
| /** |
| * Generates an anchor (link) element and its content from an IntroLink: |
| * |
| * <pre> |
| * |
| * <A id=linkId class="link" href=linkHref> |
| * <IMG src="blank.gif"> |
| * <SPAN class="link-label">linkLabel </SPAN> |
| * <P><SPAN>text</SPAN></P> |
| * </A> |
| * |
| * </pre> |
| * |
| * @param element |
| * the IntroLink |
| * @param indentLevel |
| * the number of indents to insert before the element when it is |
| * printed |
| * @return an anchor (<A>) HTMLElement |
| */ |
| private HTMLElement generateIntroLink(IntroLink element, int indentLevel) { |
| HTMLElement anchor = generateAnchorElement(element, indentLevel); |
| // add <IMG src="blank.gif"> |
| String blankImageURL = IntroModelRoot.getPluginLocation( |
| IIntroHTMLConstants.IMAGE_SRC_BLANK, IIntroConstants.PLUGIN_ID); |
| if (blankImageURL != null) { |
| anchor.addContent(generateImageElement(blankImageURL, null, |
| IIntroHTMLConstants.IMAGE_CLASS_BG, indentLevel + 1)); |
| } |
| // add link image, if one is specified |
| if(element.getImg() != null) { |
| HTMLElement img = generateIntroImage(element.getImg(), indentLevel + 1); |
| anchor.addContent(img); |
| } |
| // add <SPAN class="link-label">linkLabel</SPAN> |
| if (element.getLabel() != null) { |
| HTMLElement label = generateSpanElement( |
| IIntroHTMLConstants.SPAN_CLASS_LINK_LABEL, indentLevel + 1); |
| label.addContent(element.getLabel()); |
| anchor.addContent(label); |
| } |
| IntroText linkText = element.getIntroText(); |
| if (linkText != null && linkText.getText() != null) { |
| String classId = (linkText.getStyleId() != null) ? linkText |
| .getStyleId() : IIntroHTMLConstants.SPAN_CLASS_TEXT; |
| HTMLElement text = generateTextElement( |
| IIntroHTMLConstants.ELEMENT_PARAGRAPH, linkText.getId(), |
| classId, element.getText(), indentLevel + 1); |
| anchor.addContent(text); |
| } |
| return anchor; |
| } |
| /** |
| * 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); |
| else |
| return generateEmbeddedIntroHTML(element, indentLevel); |
| } |
| /** |
| * Generate an image element from an IntroImage: |
| * |
| * <pre> |
| * |
| * <IMG src=imageSrc id=imageId> |
| * |
| * </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.getStyleId(), indentLevel); |
| if (element.getId() != null) |
| imageElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, element |
| .getId()); |
| return imageElement; |
| } |
| /** |
| * Generate a paragraph (<P>) 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> |
| * |
| * <P><SPAN>spanContent</SPAN></P> |
| * |
| * </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 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> |
| * |
| * <div id="attrvalue" class="attrvalue2"> |
| * content from file specified in src attribute |
| * </div> |
| * |
| * </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) { |
| StringBuffer content = readFromFile(element.getSrc()); |
| 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; |
| } |
| /** |
| * 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> |
| * |
| * <OBJECT type="text/html" data="attrvalue"> |
| * alternative text in case the object can not be rendered |
| * </OBJECT> |
| * |
| * </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> |
| * <BASE href=baseURL> |
| * </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> |
| * |
| * <style type="text/css">HTML, IMG { border: 0px; } </style> |
| * |
| * </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); |
| return style; |
| } |
| /** |
| * Generates the title element and its content: |
| * |
| * <pre> |
| * |
| * <TITLE>intro title</TITLE> |
| * |
| * </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> |
| * |
| * <LINK rel="stylesheet" style="text/css" href="style sheet"> |
| * |
| * </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_STYLE, |
| IIntroHTMLConstants.LINK_STYLE); |
| if (href != null) |
| link.addAttribute(IIntroHTMLConstants.ATTRIBUTE_HREF, href); |
| return link; |
| } |
| /** |
| * Generate an anchor element: |
| * |
| * <pre> |
| * |
| * <A id=linkId class=linkClass href=linkHref> </A> |
| * |
| * </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 (<A>) 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> |
| * |
| * <DIV id=divId> |
| * <H><SPAN>spanContent </SPAN> </H> |
| * </DIV> |
| * |
| * </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> |
| * |
| * <P><SPAN>spanContent</SPAN></P> |
| * |
| * </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); |
| // Create the enclosing text element: <P><SPAN>spanContent</SPAN></P> |
| HTMLElement text = new FormattedHTMLElement(type, indentLevel, false); |
| text.addContent(span); |
| return text; |
| } |
| /** |
| * 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> |
| * |
| * <IMG src=imageSrc alt=altText> |
| * |
| * </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 imageClass, int indentLevel) { |
| HTMLElement image = new FormattedHTMLElement( |
| IIntroHTMLConstants.ELEMENT_IMG, indentLevel, true, false); |
| image.addAttribute(IIntroHTMLConstants.ATTRIBUTE_SRC, imageSrc); |
| if (altText == null) |
| altText = ""; //$NON-NLS-1$ |
| image.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ALT, altText); |
| if(imageClass != null) |
| image.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, imageClass); |
| return image; |
| } |
| /** |
| * Generate a span element |
| * |
| * <pre> |
| * |
| * <SPAN class=spanClass> </SPAN> |
| * |
| * </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; |
| } |
| |
| private boolean filteredFromPresentation(AbstractIntroElement element) { |
| if (element.isOfType(AbstractIntroElement.BASE_ELEMENT)) |
| return ((AbstractBaseIntroElement) element).isFiltered(); |
| else |
| 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 |
| * @return a StringBuffer containing the content in the file, or null |
| */ |
| private StringBuffer readFromFile(String src) { |
| 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())); |
| reader = new BufferedReader(new InputStreamReader(stream)); |
| 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(char tokenBegin, BufferedReader bufferedreader){ |
| reader = bufferedreader; |
| tokenContent = new StringBuffer(tokenBegin); |
| pluginId = new StringBuffer(); |
| } |
| |
| 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(); |
| } |
| else { |
| return pluginPath; |
| } |
| } |
| else { |
| // 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 = HTMLUtil.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; |
| } |
| } |
| } |
| } |