KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > intro > impl > html > IntroHTMLGenerator


1 /*******************************************************************************
2  * Copyright (c) 2004, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.ui.internal.intro.impl.html;
12
13 import java.io.BufferedReader JavaDoc;
14 import java.io.IOException JavaDoc;
15 import java.io.InputStream JavaDoc;
16 import java.io.InputStreamReader JavaDoc;
17 import java.io.PrintWriter JavaDoc;
18 import java.io.StringWriter JavaDoc;
19 import java.net.URL JavaDoc;
20
21 import org.eclipse.core.runtime.Platform;
22 import org.eclipse.ui.internal.intro.impl.IIntroConstants;
23 import org.eclipse.ui.internal.intro.impl.IntroPlugin;
24 import org.eclipse.ui.internal.intro.impl.model.AbstractBaseIntroElement;
25 import org.eclipse.ui.internal.intro.impl.model.AbstractIntroElement;
26 import org.eclipse.ui.internal.intro.impl.model.AbstractIntroPage;
27 import org.eclipse.ui.internal.intro.impl.model.IntroContentProvider;
28 import org.eclipse.ui.internal.intro.impl.model.IntroGroup;
29 import org.eclipse.ui.internal.intro.impl.model.IntroHTML;
30 import org.eclipse.ui.internal.intro.impl.model.IntroHead;
31 import org.eclipse.ui.internal.intro.impl.model.IntroImage;
32 import org.eclipse.ui.internal.intro.impl.model.IntroInjectedIFrame;
33 import org.eclipse.ui.internal.intro.impl.model.IntroLink;
34 import org.eclipse.ui.internal.intro.impl.model.IntroPageTitle;
35 import org.eclipse.ui.internal.intro.impl.model.IntroSeparator;
36 import org.eclipse.ui.internal.intro.impl.model.IntroText;
37 import org.eclipse.ui.internal.intro.impl.model.loader.ContentProviderManager;
38 import org.eclipse.ui.internal.intro.impl.model.util.BundleUtil;
39 import org.eclipse.ui.internal.intro.impl.util.Log;
40 import org.eclipse.ui.intro.config.IIntroContentProvider;
41 import org.eclipse.ui.intro.config.IIntroContentProviderSite;
42
43 public class IntroHTMLGenerator {
44
45     private AbstractIntroPage introPage;
46
47     private IIntroContentProviderSite providerSite;
48
49     /**
50      * Generates the HTML code that will be presented in the browser widget for the provided intro
51      * page.
52      *
53      * @param page
54      * the page to generate HTML for
55      * @param presentation
56      * the presentation associated with this page.
57      */

58     public HTMLElement generateHTMLforPage(AbstractIntroPage page, IIntroContentProviderSite providerSite) {
59         if (page == null)
60             return null;
61         this.introPage = page;
62         this.providerSite = providerSite;
63
64         // generate and add the appropriate encoding to the top of the document
65
// generateEncoding();
66
// create the main HTML element, and all of its contents.
67
return generateHTMLElement();
68     }
69
70     /*
71      * private HTMLElement generateEncoding() { HTMLElement encoding = new HTMLElement("");
72      * //$NON-NLS-1$ // TODO: figure out how to handle locale based encoding // As far as the HTML
73      * generator is concerned, this is probably as // simple as asking the model for the information
74      * return encoding; }
75      */

76
77     /**
78      * Generates the HTML element and its content:
79      *
80      * <pre>
81      *
82      * &lt;HTML&gt;
83      * &lt;HEAD&gt;
84      * head content
85      * &lt;/HEAD&gt;
86      * &lt;BODY&gt;
87      * body content
88      * &lt;/BODY&gt;
89      * &lt;/HTML&gt;
90      *
91      * </pre>
92      *
93      * @return the html HTMLElement
94      */

95     private HTMLElement generateHTMLElement() {
96         // this is the outermost element, so it has no indent
97
int indentLevel = 0;
98         HTMLElement html = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_HTML, indentLevel, true);
99         HTMLElement head = generateHeadElement(indentLevel + 1);
100         HTMLElement body = generateBodyElement(indentLevel + 1, head);
101         html.addContent(head);
102         html.addContent(body);
103         return html;
104     }
105
106     /**
107      * Generates the HEAD element and its content:
108      *
109      * <pre>
110      *
111      *
112      * &lt;HEAD&gt;
113      * &lt;BASE HREF=&quot;base_plugin_location&gt;
114      * &lt;style type=&quot;text/css&quot;&gt;HTML, IMG { border: 0px; } &lt;/style&gt;
115      * &lt;TITLE&gt;page title &lt;/TITLE&gt;
116      * &lt;LINK HREF=&quot;style sheet&quot;&gt;
117      * additional head content, if specified
118      * &lt;/HEAD&gt;
119      *
120      * </pre>
121      *
122      * @param indentLevel
123      * the number of indents to insert before the element when it is printed
124      * @return the head HTMLElement
125      */

126     private HTMLElement generateHeadElement(int indentLevel) {
127         HTMLElement head = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_HEAD, indentLevel, true);
128         // add the title
129
head.addContent(generateTitleElement(null, indentLevel + 1));
130         // create the BASE element
131
String JavaDoc basePath = BundleUtil.getResolvedResourceLocation(introPage.getBase(), introPage.getBundle());
132         HTMLElement base = generateBaseElement(indentLevel + 1, basePath);
133         if (base != null)
134             head.addContent(base);
135         // create the HTML style block
136
head.addContent(generateStyleElement(indentLevel + 1));
137         // add the presentation style
138
String JavaDoc[] presentationStyles = IntroPlugin.getDefault().getIntroModelRoot().getPresentation()
139                 .getImplementationStyles();
140         if (presentationStyles != null && introPage.injectSharedStyle()) {
141             for (int i=0; i<presentationStyles.length; i++)
142                 head.addContent(generateLinkElement(presentationStyles[i], indentLevel + 1));
143         }
144         String JavaDoc pageStyle = introPage.getStyle();
145         if (pageStyle != null)
146             head.addContent(generateLinkElement(pageStyle, indentLevel + 1));
147         // add javascript
148
head.addContent(generateJavascriptElement(indentLevel + 1));
149
150         // add the page's inherited style(s)
151
String JavaDoc[] pageStyles = introPage.getStyles();
152         for (int i = 0; i < pageStyles.length; i++) {
153             pageStyle = pageStyles[i];
154             if (pageStyle != null)
155                 head.addContent(generateLinkElement(pageStyle, indentLevel + 1));
156         }
157         // if there is additional head conent specified in an external file,
158
// include it. Additional head content can be specified at the
159
// implementation level (which would apply to ALL pages) and at the
160
// page level (which would apply only to that particular page).
161
// For the implementation's head contribution:
162
StringBuffer JavaDoc content = null;
163         IntroHead introHead = IntroPlugin.getDefault().getIntroModelRoot().getPresentation().getHead();
164         if (introHead != null) {
165             content = readFromFile(introHead.getSrc(), introHead.getInlineEncoding());
166             if (content != null)
167                 head.addContent(content);
168         }
169         // For the page's head contribution:
170
// TODO: there should only be one of these at the page level, not a
171
// collection..
172
IntroHead[] htmlHeads = introPage.getHTMLHeads();
173         for (int i = 0; i < htmlHeads.length; i++) {
174             introHead = htmlHeads[i];
175             if (introHead != null) {
176                 content = readFromFile(introHead.getSrc(), introHead.getInlineEncoding());
177                 if (content != null)
178                     head.addContent(content);
179             }
180         }
181         return head;
182     }
183
184     private HTMLElement generateJavascriptElement(int indentLevel) {
185         String JavaDoc rel = "javascript/common.js"; //$NON-NLS-1$
186
String JavaDoc abs = BundleUtil.getResolvedResourceLocation(rel, IntroPlugin.getDefault().getBundle());
187         HTMLElement jselement = new FormattedHTMLElement("script", indentLevel, false); //$NON-NLS-1$
188
jselement.addAttribute("type", "text/javascript"); //$NON-NLS-1$ //$NON-NLS-2$
189
jselement.addAttribute("src", abs); //$NON-NLS-1$
190
return jselement;
191     }
192
193     /**
194      * Generates the BODY element and its content:
195      *
196      * <pre>
197      *
198      *
199      * &lt;BODY&gt;
200      * &lt;DIV id=&quot;pageId&quot; class=&quot;pageClass&quot;&gt;
201      * page content
202      * &lt;/DIV&gt;
203      * &lt;/BODY&gt;
204      *
205      * </pre>
206      *
207      * @param indentLevel
208      * the number of indents to insert before the element when it is printed
209      * @return the body HTMLElement
210      */

211     private HTMLElement generateBodyElement(int indentLevel, HTMLElement head) {
212         HTMLElement body = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_BODY, indentLevel, true);
213         // Create the div that contains the page content
214
String JavaDoc pageId = (introPage.getId() != null) ? introPage.getId() : IIntroHTMLConstants.DIV_ID_PAGE;
215         HTMLElement pageContentDiv = generateDivElement(pageId, indentLevel + 1);
216         if (introPage.getStyleId() != null)
217             pageContentDiv.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, introPage.getStyleId());
218         if (introPage.getBackgroundImage() != null)
219             pageContentDiv.addAttribute(IIntroHTMLConstants.ATTRIBUTE_STYLE,
220                     "background-image : url(" + introPage.getBackgroundImage() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
221

222         // Add any children of the page, in the order they are defined
223
AbstractIntroElement[] children = introPage.getChildren();
224         for (int i = 0; i < children.length; i++) {
225             AbstractIntroElement child = children[i];
226             // use indentLevel + 2 here, since this element is contained within
227
// the pageContentDiv
228
HTMLElement childElement = generateIntroElement(child, indentLevel + 2);
229             if (childElement != null) {
230                 addMixinStyle(childElement, child.getMixinStyle());
231                 pageContentDiv.addContent(childElement);
232             }
233         }
234         body.addContent(pageContentDiv);
235         return body;
236     }
237
238     /**
239      * Given an IntroElement, generate the appropriate HTMLElement
240      *
241      * @param element
242      * the IntroElement
243      * @param indentLevel
244      * the number of indents to insert before the element when it is printed
245      * @return an HTMLElement
246      */

247     private HTMLElement generateIntroElement(AbstractIntroElement element, int indentLevel) {
248         if (element == null)
249             return null;
250         // check to see if this element should be filtered from the HTML
251
// presentation
252
if (filteredFromPresentation(element))
253             return null;
254         switch (element.getType()) {
255         case AbstractIntroElement.GROUP:
256             return generateIntroDiv((IntroGroup) element, indentLevel);
257         case AbstractIntroElement.LINK:
258             return generateIntroLink((IntroLink) element, indentLevel);
259         case AbstractIntroElement.HTML:
260             return generateIntroHTML((IntroHTML) element, indentLevel);
261         case AbstractIntroElement.CONTENT_PROVIDER:
262             return generateIntroContent((IntroContentProvider) element, indentLevel);
263         case AbstractIntroElement.IMAGE:
264             return generateIntroImage((IntroImage) element, indentLevel);
265         case AbstractIntroElement.HR:
266             return generateIntroSeparator((IntroSeparator) element, indentLevel);
267         case AbstractIntroElement.TEXT:
268             return generateIntroText((IntroText) element, indentLevel);
269         case AbstractIntroElement.PAGE_TITLE:
270             return generateIntroTitle((IntroPageTitle) element, indentLevel);
271         case AbstractIntroElement.INJECTED_IFRAME:
272             return generateIntroInjectedIFrame((IntroInjectedIFrame) element, indentLevel);
273         default:
274             return null;
275         }
276     }
277
278     /**
279      * Create a div element and its content from an IntroDiv:
280      *
281      * <pre>
282      *
283      *
284      * &lt;div id=&quot;attrvalue&quot;&gt;
285      * &lt;h4&gt;&lt;span class=&quot;div-label&quot;&gt;attrvalue&lt;/span&gt;&lt;h4&gt;
286      * any defined divs, links, html, images, text, includes
287      * &lt;/div&gt;
288      *
289      * </pre>
290      *
291      * @param element
292      * the IntroDiv
293      * @param indentLevel
294      * the number of indents to insert before the element when it is printed
295      * @return a div HTMLElement
296      */

297     private HTMLElement generateIntroDiv(IntroGroup element, int indentLevel) {
298         // Create the outer div element
299
HTMLElement divElement = generateDivElement(element.getId(), indentLevel);
300         HTMLElement childContainer = divElement;
301         // if a div class was specified, add it
302
if (element.getStyleId() != null)
303             divElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, element.getStyleId());
304         // Create the div label, if specified
305
if (element.getLabel() != null) {
306             if (element.isExpandable()) {
307                 HTMLElement divLabel = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_SPAN,
308                         indentLevel + 2, false);
309                 divLabel.addContent(element.getLabel());
310                 divLabel.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS,
311                                                 "section-title");//$NON-NLS-1$
312
String JavaDoc clientId = element.getId() + "-content"; //$NON-NLS-1$
313
String JavaDoc toggleClosedId = element.getId() + "-toggle-closed"; //$NON-NLS-1$
314
String JavaDoc toggleOpenId = element.getId() + "-toggle-open"; //$NON-NLS-1$
315
String JavaDoc href = "#"; //$NON-NLS-1$
316
HTMLElement link = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_ANCHOR,
317                         indentLevel + 1, true);
318                 link.addAttribute(IIntroHTMLConstants.ATTRIBUTE_HREF, href);
319                 link.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, "section-title-link"); //$NON-NLS-1$
320
StringBuffer JavaDoc call = new StringBuffer JavaDoc();
321                 call.append("return (toggleSection('");//$NON-NLS-1$
322
call.append(clientId);
323                 call.append("','");//$NON-NLS-1$
324
call.append(toggleClosedId);
325                 call.append("','");//$NON-NLS-1$
326
call.append(toggleOpenId);
327                 call.append("'))"); //$NON-NLS-1$
328
link.addAttribute("onClick", call.toString()); //$NON-NLS-1$
329
link.addContent(divLabel);
330                 divElement.addContent(link);
331                 // Add toggle images
332
HTMLElement toggleImageClosed = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_IMG,
333                         indentLevel + 2, false, false);
334                 toggleImageClosed.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, toggleClosedId);
335                 toggleImageClosed.addAttribute(IIntroHTMLConstants.ATTRIBUTE_SRC, BundleUtil
336                         .getResolvedResourceLocation(IIntroHTMLConstants.IMAGE_SRC_BLANK,
337                                 IIntroConstants.PLUGIN_ID));
338                 toggleImageClosed.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, "section-toggle-image-closed"); //$NON-NLS-1$
339
if (element.isExpanded())
340                     toggleImageClosed.addAttribute(IIntroHTMLConstants.ATTRIBUTE_STYLE, "display: none"); //$NON-NLS-1$
341
link.addContent(toggleImageClosed);
342                 HTMLElement toggleImageOpen = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_IMG,
343                         indentLevel + 2, false, false);
344                 toggleImageOpen.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, toggleOpenId);
345                 toggleImageOpen.addAttribute(IIntroHTMLConstants.ATTRIBUTE_SRC, BundleUtil
346                         .getResolvedResourceLocation(IIntroHTMLConstants.IMAGE_SRC_BLANK,
347                                 IIntroConstants.PLUGIN_ID));
348                 toggleImageOpen.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, "section-toggle-image-open"); //$NON-NLS-1$
349
if (element.isExpanded())
350                     toggleImageOpen.addAttribute(IIntroHTMLConstants.ATTRIBUTE_STYLE, "display: inline"); //$NON-NLS-1$
351
link.addContent(toggleImageOpen);
352                 childContainer = generateDivElement(clientId, indentLevel + 1);
353                 childContainer.addAttribute("class", "section-body"); //$NON-NLS-1$//$NON-NLS-2$
354
if (element.isExpanded())
355                     childContainer.addAttribute(IIntroHTMLConstants.ATTRIBUTE_STYLE, "display: block"); //$NON-NLS-1$
356
divElement.addContent(childContainer);
357             } else {
358                 HTMLElement divLabel = generateTextElement(IIntroHTMLConstants.ELEMENT_H4, null,
359                         IIntroHTMLConstants.SPAN_CLASS_DIV_LABEL, element.getLabel(), indentLevel + 1);
360                 divElement.addContent(divLabel);
361             }
362         }
363         if (element.getBackgroundImage() != null) {
364             String JavaDoc imageUrl = element.getBackgroundImage();
365             imageUrl = BundleUtil.getResolvedResourceLocation(element.getBase(), imageUrl, element
366                     .getBundle());
367             String JavaDoc style;
368             if (Platform.getWS().equals(Platform.WS_WIN32) && imageUrl.toLowerCase().endsWith(".png")) { //$NON-NLS-1$
369
// IE 5.5+ does not handle alphas in PNGs without
370
// this hack. Remove when IE7 becomes widespread
371
style = "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + imageUrl + "', sizingMethod='crop');"; //$NON-NLS-1$ //$NON-NLS-2$
372
} else {
373                 style = "background-image : url(" + imageUrl + ")"; //$NON-NLS-1$ //$NON-NLS-2$
374
}
375             divElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_STYLE, style);
376         }
377         // Add any children of the div, in the order they are defined
378
AbstractIntroElement[] children = element.getChildren();
379         for (int i = 0; i < children.length; i++) {
380             AbstractIntroElement child = children[i];
381             HTMLElement childElement = generateIntroElement(child, indentLevel + 1);
382             if (childElement != null) {
383                 addMixinStyle(childElement, child.getMixinStyle());
384                 childContainer.addContent(childElement);
385             }
386         }
387         return divElement;
388     }
389
390     private void addMixinStyle(HTMLElement element, String JavaDoc mixinStyle) {
391         if (mixinStyle == null)
392             return;
393         String JavaDoc key = "class"; //$NON-NLS-1$
394
String JavaDoc original = (String JavaDoc) element.getElementAttributes().get(key);
395         if (original == null)
396             original = mixinStyle;
397         else
398             original += " " + mixinStyle; //$NON-NLS-1$
399
element.addAttribute(key, original);
400     }
401
402     /**
403      * Generates an anchor (link) element and its content from an IntroLink:
404      *
405      * <pre>
406      *
407      * &lt;A id=linkId class=&quot;link&quot; HREF=linkHref&gt;
408      * &lt;IMG SRC=&quot;blank.gif&quot;&gt;
409      * &lt;SPAN class=&quot;link-label&quot;&gt;linkLabel &lt;/SPAN&gt;
410      * &lt;P&gt;&lt;SPAN&gt;text&lt;/SPAN&gt;&lt;/P&gt;
411      * &lt;/A&gt;
412      *
413      * </pre>
414      *
415      * @param element
416      * the IntroLink
417      * @param indentLevel
418      * the number of indents to insert before the element when it is printed
419      * @return an anchor (&lt;A&gt;) HTMLElement
420      */

421     private HTMLElement generateIntroLink(IntroLink element, int indentLevel) {
422         HTMLElement anchor = generateAnchorElement(element, indentLevel);
423         // add <IMG SRC="blank.gif">
424
String JavaDoc blankImageURL = BundleUtil.getResolvedResourceLocation(IIntroHTMLConstants.IMAGE_SRC_BLANK,
425                 IIntroConstants.PLUGIN_ID);
426         if (blankImageURL != null) {
427             anchor.addContent(generateImageElement(blankImageURL, null, IIntroHTMLConstants.IMAGE_CLASS_BG,
428                     indentLevel + 1));
429         }
430         // add link image, if one is specified
431
if (element.getImg() != null) {
432             HTMLElement img = generateIntroElement(element.getImg(), indentLevel + 1);
433             if (img != null)
434                 anchor.addContent(img);
435         }
436         HTMLElement imageDiv = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_DIV, indentLevel+1, false);
437         imageDiv.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS,
438                 IIntroHTMLConstants.LINK_EXTRA_DIV);
439         anchor.addContent(imageDiv);
440         // add <SPAN class="link-label">linkLabel</SPAN>
441
if (element.getLabel() != null) {
442             HTMLElement label = generateSpanElement(IIntroHTMLConstants.SPAN_CLASS_LINK_LABEL,
443                     indentLevel + 1);
444             label.addContent(element.getLabel());
445             anchor.addContent(label);
446         }
447         IntroText linkText = element.getIntroText();
448         if (linkText != null && linkText.getText() != null) {
449             HTMLElement text = generateIntroElement(linkText, indentLevel + 1);
450             if (text != null)
451                 anchor.addContent(text);
452         }
453         return anchor;
454     }
455
456     /**
457      * Generate the appropriate HTML from an IntroHTML. If the IntroHTML type is "inline", then the
458      * content from the referenced file is emitted as-is into a div element. If the type is "embed",
459      * an OBJECT html element is created whose <code>data</code> attribute is equal to the
460      * IntroHTML's <code>src</code> value
461      *
462      * @param element
463      * the IntroHTML
464      * @param indentLevel
465      * the number of indents to insert before the element when it is printed
466      * @return an HTMLElement
467      */

468     private HTMLElement generateIntroHTML(IntroHTML element, int indentLevel) {
469         if (element.isInlined())
470             return generateInlineIntroHTML(element, indentLevel);
471
472         return generateEmbeddedIntroHTML(element, indentLevel);
473     }
474
475     /**
476      * Generate an image element from an IntroImage:
477      *
478      * <pre>
479      *
480      * &lt;IMG SRC=imageSrc id=imageId&gt;
481      *
482      * </pre>
483      *
484      * @param element
485      * the IntroImage
486      * @param indentLevel
487      * the number of indents to insert before the element when it is printed
488      * @return an img HTMLElement
489      */

490     private HTMLElement generateIntroImage(IntroImage element, int indentLevel) {
491         HTMLElement imageElement = generateImageElement(element.getSrc(), element.getAlt(), element
492                 .getStyleId(), indentLevel);
493         if (element.getId() != null)
494             imageElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, element.getId());
495         return imageElement;
496     }
497     
498     private HTMLElement generateIntroSeparator(IntroSeparator element, int indentLevel) {
499         HTMLElement hrElement = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_HR, indentLevel, false);
500         if (element.getId() != null)
501             hrElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, element.getId());
502         if (element.getStyleId() != null)
503             hrElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_STYLE, element.getStyleId());
504         return hrElement;
505     }
506
507     /**
508      * Generate a paragraph (&lt;P&gt;) element from an IntroText. The paragraph element will
509      * contain a span element that will contain the actual text. Providing the span element provides
510      * additional flexibility for CSS designers.
511      *
512      * <pre>
513      *
514      *
515      * &lt;P&gt;&lt;SPAN&gt;spanContent&lt;/SPAN&gt;&lt;/P&gt;
516      *
517      * </pre>
518      *
519      * @param element
520      * the IntroText
521      * @param indentLevel
522      * the number of indents to insert before the element when it is printed
523      * @return a paragraph HTMLElement
524      */

525     private HTMLElement generateIntroText(IntroText element, int indentLevel) {
526         String JavaDoc spanClass = (element.getStyleId() != null) ? element.getStyleId()
527                 : IIntroHTMLConstants.SPAN_CLASS_TEXT;
528         HTMLElement textElement = generateTextElement(IIntroHTMLConstants.ELEMENT_PARAGRAPH, element.getId(),
529                 spanClass, element.getText(), indentLevel);
530         return textElement;
531     }
532
533     /**
534      * @param element
535      * @param indentLevel
536      * @return
537      */

538     private HTMLElement generateIntroInjectedIFrame(IntroInjectedIFrame element, int indentLevel) {
539         HTMLElement iframe = generateIFrameElement(element.getIFrameURL(), "0", //$NON-NLS-1$
540
"auto", indentLevel); //$NON-NLS-1$
541
return iframe;
542     }
543
544     /**
545      * @param element
546      * @param indentLevel
547      * @return
548      */

549     private HTMLElement generateIntroTitle(IntroPageTitle element, int indentLevel) {
550         HTMLElement titleElement = generateHeaderDiv(element.getId(), element.getStyleId(),
551                 IIntroHTMLConstants.ELEMENT_H1, element.getTitle(), indentLevel);
552         return titleElement;
553     }
554
555     /**
556      * Generate "inline" content from an IntroHTML. The content from the file referenced by the
557      * IntroHTML's <code>src</code> attribute is emitted as-is into a div element:
558      *
559      * <pre>
560      *
561      *
562      * &lt;div id=&quot;attrvalue&quot; class=&quot;attrvalue2&quot;&gt;
563      * content from file specified in src attribute
564      * &lt;/div&gt;
565      *
566      * </pre>
567      *
568      * @param element
569      * the IntroHTML
570      * @param indentLevel
571      * the number of indents to insert before the element when it is printed
572      * @return a div HTMLElement, or null if there was a problem reading from the file
573      */

574     private HTMLElement generateInlineIntroHTML(IntroHTML element, int indentLevel) {
575         // make sure to ask model for encoding. If encoding is null (ie: not
576
// specified in
577
// markup, local encoding is used.
578
StringBuffer JavaDoc content = readFromFile(element.getSrc(), element.getInlineEncoding());
579         if (content != null && content.length() > 0) {
580             // Create the outer div element
581
String JavaDoc divClass = (element.getStyleId() != null) ? element.getStyleId()
582                     : IIntroHTMLConstants.DIV_CLASS_INLINE_HTML;
583             HTMLElement divElement = generateDivElement(element.getId(), divClass, indentLevel);
584             // add the content of the specified file into the div element
585
divElement.addContent(content);
586             return divElement;
587         }
588         return null;
589     }
590
591     /**
592      * Includes HTML content that is created by an IIntroContentProvider implementation.
593      *
594      * @param element
595      * @param indentLevel
596      * @return
597      */

598     private HTMLElement generateIntroContent(IntroContentProvider element, int indentLevel) {
599         // create a new div to wrap the content
600
HTMLElement divElement = generateDivElement(element.getId(),
601                 IIntroHTMLConstants.DIV_CLASS_PROVIDED_CONTENT, indentLevel);
602
603         // If we've already loaded the content provider for this element,
604
// retrieve it, otherwise load the class
605
IIntroContentProvider providerClass = ContentProviderManager.getInst().getContentProvider(element);
606         if (providerClass == null)
607             // content provider never created before, create it.
608
providerClass = ContentProviderManager.getInst().createContentProvider(element, providerSite);
609
610         if (providerClass != null) {
611             StringWriter JavaDoc stringWriter = new StringWriter JavaDoc();
612             PrintWriter JavaDoc pw = new PrintWriter JavaDoc(stringWriter);
613             // create the specialized content
614
providerClass.createContent(element.getId(), pw);
615             // add the content of the specified file into the div element
616
stringWriter.flush();
617             divElement.addContent(stringWriter.toString());
618             pw.close();
619         } else {
620             // we couldn't load the content provider, so add any alternate
621
// text content if there is any
622
IntroText htmlText = element.getIntroText();
623             if (htmlText != null && htmlText.getText() != null) {
624                 String JavaDoc textClass = (htmlText.getStyleId() != null) ? htmlText.getStyleId()
625                         : IIntroHTMLConstants.SPAN_CLASS_TEXT;
626                 HTMLElement text = generateTextElement(IIntroHTMLConstants.ELEMENT_PARAGRAPH, htmlText
627                         .getId(), textClass, element.getText(), indentLevel);
628                 if (text != null)
629                     divElement.addContent(text);
630             }
631         }
632         return divElement;
633     }
634
635     /**
636      * Generate "embedded" content from an IntroHTML. An OBJECT html element is created whose
637      * <code>data</code> attribute is equal to the IntroHTML's <code>src</code> value.
638      *
639      * <pre>
640      *
641      * &lt;OBJECT type=&quot;text/html&quot; data=&quot;attrvalue&quot;&gt;
642      * alternative text in case the object can not be rendered
643      * &lt;/OBJECT&gt;
644      *
645      * </pre>
646      *
647      * @param element
648      * the IntroHTML
649      * @param indentLevel
650      * the number of indents to insert before the element when it is printed
651      * @return an object HTMLElement
652      */

653     private HTMLElement generateEmbeddedIntroHTML(IntroHTML element, int indentLevel) {
654         HTMLElement objectElement = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_OBJECT, indentLevel,
655                 true);
656         objectElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_TYPE, IIntroHTMLConstants.OBJECT_TYPE);
657         if (element.getId() != null)
658             objectElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, element.getId());
659         if (element.getSrc() != null)
660             objectElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_DATA, element.getSrc());
661         if (element.getStyleId() != null)
662             objectElement.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, element.getStyleId());
663         // The alternative content is added in case the browser can not render
664
// the specified content.
665
IntroText htmlText = element.getIntroText();
666         if (htmlText != null && htmlText.getText() != null) {
667             String JavaDoc textClass = (htmlText.getStyleId() != null) ? htmlText.getStyleId()
668                     : IIntroHTMLConstants.SPAN_CLASS_TEXT;
669             HTMLElement text = generateTextElement(IIntroHTMLConstants.ELEMENT_PARAGRAPH, htmlText.getId(),
670                     textClass, element.getText(), indentLevel);
671             if (text != null)
672                 objectElement.addContent(text);
673         }
674         if (element.getIntroImage() != null) {
675             HTMLElement img = generateIntroImage(element.getIntroImage(), indentLevel);
676             if (img != null)
677                 objectElement.addContent(img);
678         }
679         return objectElement;
680     }
681
682     /**
683      * Generates the BASE element for the head of the html document. Each document can have only one
684      * base element
685      *
686      * <pre>
687      *
688      *
689      * &lt;BASE HREF=baseURL&gt;
690      * </pre>
691      *
692      * @param indentLevel
693      * @param baseURL
694      * @return
695      */

696     private HTMLElement generateBaseElement(int indentLevel, String JavaDoc baseURL) {
697         HTMLElement base = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_BASE, indentLevel, true,
698                 false);
699         if (baseURL != null)
700             base.addAttribute(IIntroHTMLConstants.ATTRIBUTE_HREF, baseURL);
701         return base;
702     }
703
704     /**
705      * Generates the style element that goes into HEAD:
706      *
707      * <pre>
708      *
709      * &lt;style type=&quot;text/css&quot;&gt;HTML, IMG { border: 0px; } &lt;/style&gt;
710      *
711      * </pre>
712      *
713      * @param indentLevel
714      * the number of indents to insert before the element when it is printed
715      * @return the style HTMLElement
716      */

717     private HTMLElement generateStyleElement(int indentLevel) {
718         HTMLElement style = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_STYLE, indentLevel, false);
719         style.addAttribute(IIntroHTMLConstants.ATTRIBUTE_TYPE, IIntroHTMLConstants.LINK_STYLE);
720         style.addContent(IIntroHTMLConstants.STYLE_HTML);
721         return style;
722     }
723
724     /**
725      * Generates the title element and its content:
726      *
727      * <pre>
728      *
729      * &lt;TITLE&gt;intro title&lt;/TITLE&gt;
730      *
731      * </pre>
732      *
733      * @param title
734      * the title of this intro page
735      * @param indentLevel
736      * the number of indents to insert before the element when it is printed
737      * @return the title HTMLElement
738      */

739     private HTMLElement generateTitleElement(String JavaDoc title, int indentLevel) {
740         HTMLElement titleElement = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_TITLE, indentLevel,
741                 false);
742         if (title != null)
743             titleElement.addContent(title);
744         return titleElement;
745     }
746
747     /**
748      * Generates a link element that refers to a cascading style sheet (CSS):
749      *
750      * <pre>
751      *
752      *
753      * &lt;LINK rel=&quot;stylesheet&quot; style=&quot;text/css&quot; HREF=&quot;style sheet&quot;&gt;
754      * </pre>
755      *
756      * @param href
757      * the value of the href attribute for this link element
758      * @param indentLevel
759      * the number of indents to insert before the element when it is printed
760      * @return a link HTMLElement
761      */

762     private HTMLElement generateLinkElement(String JavaDoc href, int indentLevel) {
763         HTMLElement link = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_LINK, indentLevel, true,
764                 false);
765         link.addAttribute(IIntroHTMLConstants.ATTRIBUTE_RELATIONSHIP, IIntroHTMLConstants.LINK_REL);
766         link.addAttribute(IIntroHTMLConstants.ATTRIBUTE_TYPE, IIntroHTMLConstants.LINK_STYLE);
767         if (href != null)
768             link.addAttribute(IIntroHTMLConstants.ATTRIBUTE_HREF, href);
769         return link;
770     }
771
772     /**
773      * Generate an anchor element:
774      *
775      * <pre>
776      *
777      * &lt;A id=linkId class=linkClass HREF=linkHref&gt; &lt;/A&gt;
778      *
779      * </pre>
780      *
781      * @param link
782      * the IntroLink element that contains the value for the id and href attributes
783      * @param indentLevel
784      * the number of indents to insert before the element when it is printed
785      * @return an anchor (&lt;A&gt;) HTMLElement
786      */

787     private HTMLElement generateAnchorElement(IntroLink link, int indentLevel) {
788         HTMLElement anchor = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_ANCHOR, indentLevel, true);
789         if (link.getId() != null)
790             anchor.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, link.getId());
791         if (link.getUrl() != null)
792             anchor.addAttribute(IIntroHTMLConstants.ATTRIBUTE_HREF, link.getUrl());
793         if (link.getStyleId() != null)
794             anchor.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, link.getStyleId());
795         else
796             anchor.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, IIntroHTMLConstants.ANCHOR_CLASS_LINK);
797         return anchor;
798     }
799
800     /**
801      * Generates a div block that contains a header and span element:
802      *
803      * <pre>
804      *
805      *
806      * &lt;DIV id=divId&gt;
807      * &lt;H&gt;&lt;SPAN&gt;spanContent &lt;/SPAN&gt; &lt;/H&gt;
808      * &lt;/DIV&gt;
809      *
810      * </pre>
811      *
812      * @param divId
813      * the id of the div to create
814      * @param divClass
815      * the class of the div
816      * @param headerType
817      * what type of header to create (e.g., H1, H2, etc)
818      * @param spanContent
819      * the span content
820      * @param indentLevel
821      * the number of indents to insert before the element when it is printed
822      * @return a div HTMLElement that contains a header
823      */

824     private HTMLElement generateHeaderDiv(String JavaDoc divId, String JavaDoc divClass, String JavaDoc headerType,
825             String JavaDoc spanContent, int indentLevel) {
826         // create the text element: <P><SPAN>spanContent</SPAN></P>
827
HTMLElement text = generateTextElement(headerType, null, null, spanContent, indentLevel + 1);
828         // create the containing div element
829
HTMLElement div = generateDivElement(divId, divClass, indentLevel);
830         div.addContent(text);
831         return div;
832     }
833
834     /**
835      * Generates a span element inside a text element, where the text element can be a P
836      * (paragraph), or any of the H (Header) elements. Providing the span element provides
837      * additional flexibility for CSS designers.
838      *
839      * <pre>
840      *
841      * &lt;P&gt;&lt;SPAN&gt;spanContent&lt;/SPAN&gt;&lt;/P&gt;
842      *
843      * </pre>
844      *
845      * @param type
846      * the type of text element to create (e.g., P, H1, H2, etc)
847      * @param spanID
848      * the id of the span element, or null
849      * @param spanClass
850      * the class of the span element, or null
851      * @param spanContent
852      * the span content
853      * @param indentLevel
854      * the number of indents to insert before the element when it is printed
855      * @return a text HTMLElement that contains a span element
856      */

857     private HTMLElement generateTextElement(String JavaDoc type, String JavaDoc spanID, String JavaDoc spanClass, String JavaDoc spanContent,
858             int indentLevel) {
859         // Create the span: <SPAN>spanContent</SPAN>
860
HTMLElement span = new HTMLElement(IIntroHTMLConstants.ELEMENT_SPAN);
861         if (spanID != null)
862             span.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, spanID);
863         if (spanClass != null)
864             span.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, spanClass);
865         if (spanContent != null)
866             span.addContent(spanContent);
867         // Create the enclosing text element: <P><SPAN>spanContent</SPAN></P>
868
HTMLElement text = new FormattedHTMLElement(type, indentLevel, false);
869         text.addContent(span);
870         return text;
871     }
872
873     /**
874      * Generates a DIV element with the provided indent, id, and class.
875      *
876      * @param divId
877      * value for the div's id attribute
878      * @param divClass
879      * value for the div's class attribute
880      * @param indentLevel
881      * the number of indents to insert before the element when it is printed
882      * @return a div HTMLElement
883      */

884     private HTMLElement generateDivElement(String JavaDoc divId, String JavaDoc divClass, int indentLevel) {
885         HTMLElement div = generateDivElement(divId, indentLevel);
886         div.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, divClass);
887         return div;
888     }
889
890     /**
891      * Generates a DIV element with the provided indent and id.
892      *
893      * @param divId
894      * value for the div's id attribute
895      * @param indentLevel
896      * the number of indents to insert before the element when it is printed
897      * @return a div HTMLElement
898      */

899     private HTMLElement generateDivElement(String JavaDoc divId, int indentLevel) {
900         HTMLElement div = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_DIV, indentLevel, true);
901         if (divId != null)
902             div.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ID, divId);
903         return div;
904     }
905
906     /**
907      * Generates an IMG element:
908      *
909      * <pre>
910      *
911      *
912      * &lt;IMG SRC=imageSrc alt=altText&gt;
913      *
914      * </pre>
915      *
916      * @param imageSrc
917      * the value to be supplied to the src attribute
918      * @param indentLevel
919      * the number of indents to insert before the element when it is printed
920      * @return an img HTMLElement
921      */

922     private HTMLElement generateImageElement(String JavaDoc imageSrc, String JavaDoc altText, String JavaDoc imageClass,
923             int indentLevel) {
924         HTMLElement image = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_IMG, indentLevel, true,
925                 false);
926         boolean pngOnWin32 = imageSrc != null && Platform.getWS().equals(Platform.WS_WIN32)
927                 && imageSrc.toLowerCase().endsWith(".png"); //$NON-NLS-1$
928
if (imageSrc == null || pngOnWin32) {
929             // we must handle PNGs here - IE does not support alpha blanding well.
930
// We will set the alpha image loader and load the real image
931
// that way. The 'src' attribute in the image itself will
932
// get the blank image.
933
String JavaDoc blankImageURL = BundleUtil.getResolvedResourceLocation(
934                     IIntroHTMLConstants.IMAGE_SRC_BLANK, IIntroConstants.PLUGIN_ID);
935             if (blankImageURL != null) {
936                 image.addAttribute(IIntroHTMLConstants.ATTRIBUTE_SRC, blankImageURL);
937                 if (pngOnWin32) {
938                     String JavaDoc style = "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + imageSrc + "', sizingMethod='image')"; //$NON-NLS-1$//$NON-NLS-2$
939
image.addAttribute(IIntroHTMLConstants.ATTRIBUTE_STYLE, style);
940                 }
941             }
942         } else
943             image.addAttribute(IIntroHTMLConstants.ATTRIBUTE_SRC, imageSrc);
944         if (altText == null)
945             altText = ""; //$NON-NLS-1$
946
image.addAttribute(IIntroHTMLConstants.ATTRIBUTE_ALT, altText);
947         if (imageClass != null)
948             image.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, imageClass);
949         return image;
950     }
951
952     /**
953      * Generate a span element
954      *
955      * <pre>
956      *
957      * &lt;SPAN class=spanClass&gt; &lt;/SPAN&gt;
958      *
959      *
960      * </pre>
961      *
962      * @param spanClass
963      * the value to be supplied to the class attribute
964      * @param indentLevel
965      * the number of indents to insert before the element when it is printed
966      * @return a span HTMLElement
967      */

968     private HTMLElement generateSpanElement(String JavaDoc spanClass, int indentLevel) {
969         HTMLElement span = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_SPAN, indentLevel, false);
970         span.addAttribute(IIntroHTMLConstants.ATTRIBUTE_CLASS, spanClass);
971         return span;
972     }
973
974     /**
975      * Generate a span element
976      *
977      * <pre>
978      *
979      * &lt;iframe SRC=&quot;localPage1.xhtml&quot; frameborder=&quot;1&quot; scrolling=&quot;auto&quot; longdesc=&quot;localPage1.xhtml&quot;&gt;
980      * </pre>
981      *
982      * @param spanClass
983      * the value to be supplied to the class attribute
984      * @param indentLevel
985      * the number of indents to insert before the element when it is printed
986      * @return a span HTMLElement
987      */

988     private HTMLElement generateIFrameElement(String JavaDoc src, String JavaDoc frameborder, String JavaDoc scrolling,
989             int indentLevel) {
990         HTMLElement iframe = new FormattedHTMLElement(IIntroHTMLConstants.ELEMENT_IFrame, indentLevel, false);
991         if (src != null)
992             iframe.addAttribute(IIntroHTMLConstants.ATTRIBUTE_SRC, src);
993         if (frameborder != null)
994             iframe.addAttribute(IIntroHTMLConstants.ATTRIBUTE_FRAMEBORDER, frameborder);
995         if (scrolling != null)
996             iframe.addAttribute(IIntroHTMLConstants.ATTRIBUTE_SCROLLING, scrolling);
997         return iframe;
998     }
999
1000
1001
1002
1003    private boolean filteredFromPresentation(AbstractIntroElement element) {
1004        if (element.isOfType(AbstractIntroElement.BASE_ELEMENT))
1005            return ((AbstractBaseIntroElement) element).isFiltered();
1006
1007        return false;
1008    }
1009
1010    /**
1011     * Reads the content of the file referred to by the <code>src</code> parameter and returns the
1012     * content in the form of a StringBuffer. If the file read contains substitution segments of the
1013     * form $plugin:plugin_id$ then this method will make the proper substitution (the segment will
1014     * be replaced with the absolute path to the plugin with id plugin_id).
1015     *
1016     * @param src -
1017     * the file that contains the target conent
1018     * @param charsetName -
1019     * the encoding of the file to be read. If null, local encoding is used. But the
1020     * default of the model is UTF-8, so we should not get a null encoding.
1021     * @return a StringBuffer containing the content in the file, or null
1022     */

1023    private StringBuffer JavaDoc readFromFile(String JavaDoc src, String JavaDoc charsetName) {
1024        if (src == null)
1025            return null;
1026        InputStream JavaDoc stream = null;
1027        StringBuffer JavaDoc content = new StringBuffer JavaDoc();
1028        BufferedReader JavaDoc reader = null;
1029        try {
1030            URL JavaDoc url = new URL JavaDoc(src);
1031            stream = url.openStream();
1032            // TODO: Do we need to worry about the encoding here? e.g.:
1033
// reader = new BufferedReader(new InputStreamReader(stream,
1034
// ResourcesPlugin.getEncoding()));
1035
if (charsetName == null)
1036                reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(stream));
1037            else
1038                reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(stream, charsetName));
1039            while (true) {
1040                int character = reader.read();
1041                if (character == -1) // EOF
1042
break; // done reading file
1043

1044                else if (character == PluginIdParser.SUBSTITUTION_BEGIN) { // possible
1045
// substitution
1046
PluginIdParser parser = new PluginIdParser(character, reader);
1047                    // If a valid plugin id was found in the proper format, text
1048
// will be the absolute path to that plugin. Otherwise, text
1049
// will simply be all characters read up to (but not
1050
// including)
1051
// the next dollar sign that follows the one just found.
1052
String JavaDoc text = parser.parsePluginId();
1053                    if (text != null)
1054                        content.append(text);
1055                } else {
1056                    // make sure character is in char range before making cast
1057
if (character > 0x00 && character < 0xffff)
1058                        content.append((char) character);
1059                    else
1060                        content.append(character);
1061                }
1062            }
1063        } catch (Exception JavaDoc exception) {
1064            Log.error("Error reading from file", exception); //$NON-NLS-1$
1065
} finally {
1066            try {
1067                if (reader != null)
1068                    reader.close();
1069                if (stream != null)
1070                    stream.close();
1071            } catch (IOException JavaDoc e) {
1072                Log.error("Error closing input stream", e); //$NON-NLS-1$
1073
return null;
1074            }
1075        }
1076        return content;
1077    }
1078
1079    /**
1080     * A helper class to help identify substitution strings in a content file. A properly formatted
1081     * substitution string is of the form: <code>$plugin:plugin_id$</code> where plugin_id is the
1082     * valid id of an installed plugin. The substitution string will be replaced with the absolute
1083     * path to the plugin.
1084     *
1085     * An example usage of the string substution: The html file <code>inline.html</code> is
1086     * included in your intro via the html inline mechanism . This file needs to reference a
1087     * resource that is located in another plugin. The following might be found in inline.html:
1088     * <code>
1089     * <a HREF="$plugin:test.plugin$html/test.html">link to file</a>
1090     * </code> When this file
1091     * is read in, the relevant section will be replaced as follows: <code>
1092     * <a HREF="file:/install_path/plugins/test.plugin/html/test.html">link to file</a>
1093     * </code>
1094     *
1095     */

1096    private static class PluginIdParser {
1097
1098        private BufferedReader JavaDoc reader;
1099
1100        private static final char SUBSTITUTION_BEGIN = '$';
1101
1102        private static final char SUBSTITUTION_END = '$';
1103
1104        // tokenContent will contain all characters read by the parser, starting
1105
// with and including the initial $ token.
1106
private StringBuffer JavaDoc tokenContent;
1107
1108        // pluginId will contain the content between the "$plugin:" segment
1109
// and the closing "$" token
1110
private StringBuffer JavaDoc pluginId;
1111
1112        protected PluginIdParser(char tokenBegin, BufferedReader JavaDoc bufferedreader) {
1113            reader = bufferedreader;
1114            tokenContent = new StringBuffer JavaDoc(tokenBegin);
1115            pluginId = new StringBuffer JavaDoc();
1116        }
1117
1118        protected PluginIdParser(int tokenBegin, BufferedReader JavaDoc bufferedreader) {
1119            reader = bufferedreader;
1120            tokenContent = new StringBuffer JavaDoc();
1121            pluginId = new StringBuffer JavaDoc();
1122            // make sure tokenBegin is in char range before making cast
1123
if (tokenBegin > 0x00 && tokenBegin < 0xffff)
1124                tokenContent.append((char) tokenBegin);
1125        }
1126
1127        /**
1128         * This method should be called after the initial substitution identifier has been read in
1129         * (the substition string begins and ends with the "$" character). A properly formatted
1130         * substitution string is of the form:</code> "$plugin:plugin_id$</code>- the initial "$"
1131         * is immediately followed by the "plugin:" segment - the <code>plugin_id </code> refers to
1132         * a valid, installed plugin - the substitution string is terminated by a closing "$" If the
1133         * above conditions are not met, no substitution occurs. If the above conditions are met,
1134         * the content between (and including) the opening and closing "$" characters will be
1135         * replaced by the absolute path to the plugin
1136         *
1137         * @return
1138         */

1139        protected String JavaDoc parsePluginId() {
1140            if (reader == null || tokenContent == null || pluginId == null)
1141                return null;
1142
1143            try {
1144                // Mark the current position of the reader so we can roll
1145
// back to this point if the proper "plugin:" segment is not
1146
// found.
1147
// Use 1024 as our readAheadLimit
1148
reader.mark(0x400);
1149                if (findValidPluginSegment()) {
1150                    String JavaDoc pluginPath = getPluginPath();
1151                    if (pluginPath == null) {
1152                        // Didn't find a valid plugin id.
1153
// return tokenContent, which contains all characters
1154
// read up to (not including) the last $. (if the
1155
// last $ is part of a subsequent "$plugin:" segment
1156
// it can still be processed properly)
1157
return tokenContent.toString();
1158                    }
1159                    return pluginPath;
1160                }
1161
1162                // The "plugin:" segment was not found. Reset the reader
1163
// so we can continue reading character by character.
1164
reader.reset();
1165                return tokenContent.toString();
1166
1167            } catch (IOException JavaDoc exception) {
1168                Log.error("Error reading from file", exception); //$NON-NLS-1$
1169
return tokenContent.toString();
1170            }
1171        }
1172
1173        /**
1174         * This method should be called after an initial substitution character has been found (that
1175         * is, after a $). It looks at the subsequent characters in the input stream to determine if
1176         * they match the expected <code>plugin:</code> segment of the substitution string. If the
1177         * expected characters are found, they will be appended to the tokenContent StringBuffer and
1178         * the method will return true. If they are not found, false is returned and the caller
1179         * should reset the BufferedReader to the position it was in before this method was called.
1180         *
1181         * Resetting the reader ensures that the characters read in this method can be re-examined
1182         * in case one of them happens to be the beginning of a valid substitution segment.
1183         *
1184         * @return true if the next characters match <code>plugin:</code>, and false otherwise.
1185         */

1186        private boolean findValidPluginSegment() {
1187            final char[] PLUGIN_SEGMENT = { 'p', 'l', 'u', 'g', 'i', 'n', ':' };
1188            char[] streamContent = new char[PLUGIN_SEGMENT.length];
1189            try {
1190                int peek = reader.read(streamContent, 0, PLUGIN_SEGMENT.length);
1191                if ((peek == PLUGIN_SEGMENT.length)
1192                        && (HTMLUtil.equalCharArrayContent(streamContent, PLUGIN_SEGMENT))) {
1193                    // we have found the "$plugin:" segment
1194
tokenContent.append(streamContent);
1195                    return true;
1196                }
1197                // The "plugin:" segment did not immediately follow the initial
1198
// $.
1199
return false;
1200            } catch (IOException JavaDoc exception) {
1201                Log.error("Error reading from file", exception); //$NON-NLS-1$
1202
return false;
1203            }
1204        }
1205
1206        /**
1207         * This method continues to read from the input stream until either the end of the file is
1208         * reached, or until a character is found that indicates the end of the substitution. If the
1209         * SUBSTITUTION_END character is found, the method looks up the plugin id that has been
1210         * built up to see if it is a valid id. If so, return the absolute path to that plugin. If
1211         * not, return null.
1212         *
1213         * This method assumes that the reader is positioned just after a valid <code>plugin:</code>
1214         * segment in a substitution string.
1215         *
1216         * @return absolute path of the plugin id, if valid. null otherwise
1217         */

1218        private String JavaDoc getPluginPath() {
1219            try {
1220                while (true) {
1221                    int nextChar = reader.read();
1222
1223                    if (nextChar == -1) {
1224                        // reached EOF while looking for closing $
1225
return null;
1226                    } else if (nextChar == SUBSTITUTION_END) { // end of plugin
1227
// id
1228
// look up the plugin id. If it is a valid id
1229
// return the absolute path to this plugin.
1230
// otherwise return null.
1231
String JavaDoc path = BundleUtil.getResolvedBundleLocation(pluginId.toString());
1232
1233                        // If the plugin id was not valid, reset reader to the
1234
// previous mark. The mark should be at the character
1235
// just before the last dollar sign.
1236
if (path == null)
1237                            reader.reset();
1238
1239                        return path;
1240                    } else { // we have a regular character
1241
// mark the most recent non-dollar char in case we don't
1242
// find a valid plugin id and have to roll back
1243
// Use 1024 as our readAheadLimit
1244
reader.mark(0x400);
1245                        // Add this character to the pluginId and tokenContent
1246
// String.
1247
// make sure we have a valid character before performing
1248
// cast
1249
if (nextChar > 0x00 && nextChar < 0xffff) {
1250                            tokenContent.append((char) nextChar);
1251                            // only include non-whitespace characters in plugin
1252
// id
1253
if (!Character.isWhitespace((char) nextChar))
1254                                pluginId.append((char) nextChar);
1255                        } else {
1256                            tokenContent.append(nextChar);
1257                            pluginId.append(nextChar);
1258                        }
1259                    }
1260                }
1261            } catch (IOException JavaDoc exception) {
1262                Log.error("Error reading from file", exception); //$NON-NLS-1$
1263
return null;
1264            }
1265        }
1266    }
1267
1268}
1269
Popular Tags