KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > intro > impl > model > IntroModelRoot


1 /*******************************************************************************
2  * Copyright (c) 2004, 2007 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.model;
12
13 import java.util.Enumeration JavaDoc;
14 import java.util.Hashtable JavaDoc;
15 import java.util.Vector JavaDoc;
16
17 import org.eclipse.core.runtime.CoreException;
18 import org.eclipse.core.runtime.IConfigurationElement;
19 import org.eclipse.core.runtime.IPath;
20 import org.eclipse.core.runtime.ListenerList;
21 import org.eclipse.core.runtime.Path;
22 import org.eclipse.core.runtime.Platform;
23 import org.eclipse.core.runtime.Preferences;
24 import org.eclipse.core.runtime.SafeRunner;
25 import org.eclipse.help.UAContentFilter;
26 import org.eclipse.help.internal.UAElementFactory;
27 import org.eclipse.jface.util.SafeRunnable;
28 import org.eclipse.ui.IPropertyListener;
29 import org.eclipse.ui.internal.intro.impl.IntroPlugin;
30 import org.eclipse.ui.internal.intro.impl.model.loader.IntroContentParser;
31 import org.eclipse.ui.internal.intro.impl.model.loader.ModelLoaderUtil;
32 import org.eclipse.ui.internal.intro.impl.model.util.BundleUtil;
33 import org.eclipse.ui.internal.intro.impl.model.util.ModelUtil;
34 import org.eclipse.ui.internal.intro.impl.util.IntroEvaluationContext;
35 import org.eclipse.ui.internal.intro.impl.util.Log;
36 import org.eclipse.ui.internal.intro.impl.util.StringUtil;
37 import org.eclipse.ui.intro.config.IntroConfigurer;
38 import org.osgi.framework.Bundle;
39 import org.w3c.dom.Document JavaDoc;
40 import org.w3c.dom.Element JavaDoc;
41 import org.w3c.dom.Node JavaDoc;
42
43 /**
44  * The root class for the OOBE model. It loads the configuration into the
45  * appropriate classes.
46  *
47  * Model rules:
48  * <ol>
49  * <li>if an attribute is not included in the markup, its value will be null in
50  * the model.</li>
51  * <li>Resources in plugin.xml are not implicitly resolved against $nl$.
52  * Resources in pages are implicitly resolved against $nl$
53  * <li>the current page id is set silently when loading the model. You do not
54  * need the event notification on model load.</li>
55  * <li>Children of a given parent (ie: model root, page, or group) *must* have
56  * distinctive IDs otherwise resolving includes and extensions may fail.</li>
57  * <li>Containers have the concept of loading children and resolving children.
58  * At the model root level, resolving children means resolving ALL extensions of
59  * model. At the container level, resolving children means resolving includes.
60  * </li>
61  * <li>Extensions are resolved before includes at the container level to avoid
62  * race conditions. eg: if a page includes a shared group and an extension
63  * extends this shared group, you want the include to get the extended group and
64  * not the original group.</li>
65  * <li>Resolving extensions should not resolve includes. No need to load other
66  * models when we dont have to. Plus, extensions can only reference anchors, and
67  * so no need to resolve includes.</li>
68  * <li>Extensions can not target containers *after* they are resolved. For
69  * example, an extension can not target a shared group after it has been
70  * included in a page. It can target the initial shared group as a path, but not
71  * the group in the page as a path. Again this is because extensions extends
72  * anchors that already have a path, not a resolved path.</li>
73  * <li>Pages and shared groups that are contributed through extensions become
74  * children of the atrget configuration, and so any includes they may have will
75  * be resolved correctly.</li>
76  * <li>An infinite loop can occur if page A includes from page B and page B in
77  * turn includes from page A. ie: cyclic includes. For performnace, accept.
78  * </li>
79  * <li>When resolving includes, if the target is a container, it must be
80  * resolved to resolve its includes correctly. Otherwise, included includes will
81  * fail due to reparenting.</li>
82  * <li>unresolved includes are left as children of the parent container.</li>
83  * <li>Unresolved extensions are left as children of the targetted model.</li>
84  * <li>For dynamic awarness, the model is nulled and then reloaded. However, we
85  * need to preserve the presentation instance since the UI is already loaded.
86  * This is done by reloading the model, and directly resetting the presentation
87  * to what it was.</li>
88  * <li>Model classes should not have DOM classes as instance vars, and if this
89  * is a must, null the DOM class instance the minute you are done. This is
90  * because you want the VM to garbage collect the DOM model. Keeping a reference
91  * to the DOM model from the Intro model will prevent that.</li>
92  * </ol>
93  * <li>(since 3.0.2) several passes are used to resolve contributions to
94  * anchors that themselves where contributed through an extension. Each time a
95  * contribution is resolved, the model tries to resolve all unresolved
96  * contribution, recursively.
97  * </ul>
98  */

99 public class IntroModelRoot extends AbstractIntroContainer {
100
101     /**
102      * Model constants that fire property change event when they are changed in
103      * the model.
104      */

105     public static final int CURRENT_PAGE_PROPERTY_ID = 1;
106
107     private static final String JavaDoc ATT_CONTENT = "content"; //$NON-NLS-1$
108
private static final String JavaDoc ATT_CONFIGURER = "configurer"; //$NON-NLS-1$
109
private static final String JavaDoc VAR_THEME = "theme"; //$NON-NLS-1$
110

111     // False if there is no valid contribution to the
112
// org.eclipse.ui.into.config extension point. Start off with true, and set
113
// to false whenever something bad happens.
114
private boolean hasValidConfig = true;
115     private boolean isdynamicIntro;
116     private IntroConfigurer configurer;
117     private IntroTheme theme;
118     private IntroPartPresentation introPartPresentation;
119     private IntroHomePage homePage;
120     private String JavaDoc currentPageId;
121     private IntroHomePage standbyPage;
122
123     // the config extensions for this model.
124
private IConfigurationElement[] configExtensionElements;
125
126     // maintain listener list for model changes.
127
public ListenerList propChangeListeners = new ListenerList();
128
129     // a hashtable to hold all loaded DOMs until resolving all configExtensions
130
// is done. Key is one extensionContent DOM element, while value is the
131
// IConfigurationElement from where it was loaded. This is needed to extract
132
// the base for the xml content file.
133
private Hashtable JavaDoc unresolvedConfigExt = new Hashtable JavaDoc();
134
135
136     /**
137      * Model root. Takes a configElement that represents <config>in the
138      * plugin.xml markup AND all the extension contributed to this model through
139      * the configExtension point.
140      */

141     public IntroModelRoot(IConfigurationElement configElement,
142             IConfigurationElement[] configExtensionElements) {
143         // the config element that represents the correct model root.
144
super(configElement);
145         this.configExtensionElements = configExtensionElements;
146
147     }
148
149     public void loadModel() {
150         getChildren();
151     }
152
153     /**
154      * Loads the full model. The children of a model root are the presentation,
155      * followed by all pages, and all shared groups. Then if the model has
156      * extension, its the unresolved container extensions, followed by all
157      * extension pages and groups. The presentation is loaded from the
158      * IConfiguration element representing the config. All else is loaded from
159      * xml content file.
160      *
161      */

162     protected void loadChildren() {
163         children = new Vector JavaDoc();
164         if (Log.logInfo)
165             Log.info("Creating Intro plugin model...."); //$NON-NLS-1$
166

167         // load presentation first and create the model class for it. If there
168
// is more than one presentation, load first one, and log rest.
169
IConfigurationElement presentationElement = loadPresentation();
170         if (presentationElement == null) {
171             // no presentations at all, exit.
172
setModelState(true, false, false);
173             Log.warning("Could not find presentation element in intro config."); //$NON-NLS-1$
174
return;
175         }
176         
177         loadTheme();
178         loadConfigurer();
179
180         introPartPresentation = new IntroPartPresentation(presentationElement);
181         children.add(introPartPresentation);
182         // set parent.
183
introPartPresentation.setParent(this);
184
185         // now load all children of the config. There should only be pages and
186
// groups here. And order is not important. These elements are loaded
187
// from the content file DOM.
188
Document JavaDoc document = loadDOM(getCfgElement());
189         if (document == null) {
190             // we failed to parse the content file. Intro Parser would have
191
// logged the fact. Parser would also have checked to see if the
192
// content file has the correct root tag.
193
setModelState(true, false, false);
194             return;
195         }
196
197         // set base for this model.
198
this.base = getBase(getCfgElement());
199
200         // now load content.
201
loadPages(document, getBundle());
202         loadSharedGroups(document, getBundle());
203
204         // Attributes of root page decide if we have a static or dynamic case.
205
setModelState(true, true, getHomePage().isDynamic());
206     }
207
208     /**
209      * Sets the presentation to the given presentation. The model always has the
210      * presentation as the first child, so use that fact. This method is used
211      * for dynamic awarness to enable replacing the new presentation with the
212      * existing one after a model refresh.
213      *
214      * @param presentation
215      */

216     public void setPresentation(IntroPartPresentation presentation) {
217         this.introPartPresentation = presentation;
218         presentation.setParent(this);
219         children.set(0, presentation);
220     }
221
222     /**
223      * Resolve contributions into this container's children.
224      */

225     protected void resolveChildren() {
226         // now handle config extension.
227
resolveConfigExtensions();
228         resolved = true;
229     }
230
231     private IConfigurationElement loadPresentation() {
232         // If there is more than one presentation, load first one, and log
233
// rest.
234
IConfigurationElement[] presentationElements = getCfgElement()
235             .getChildren(IntroPartPresentation.TAG_PRESENTATION);
236
237         IConfigurationElement presentationElement = ModelLoaderUtil
238             .validateSingleContribution(presentationElements,
239                 IntroPartPresentation.ATT_HOME_PAGE_ID);
240         return presentationElement;
241     }
242     
243     private void loadConfigurer() {
244         String JavaDoc cname = getCfgElement().getAttribute(ATT_CONFIGURER);
245         if (cname!=null) {
246             try {
247                 Object JavaDoc obj = getCfgElement().createExecutableExtension(ATT_CONFIGURER);
248                 if (obj instanceof IntroConfigurer)
249                     configurer = (IntroConfigurer)obj;
250             }
251             catch (CoreException e) {
252                 Log.error("Error loading intro configurer", e); //$NON-NLS-1$
253
}
254         }
255     }
256     
257     private void loadTheme() {
258         Preferences pref = IntroPlugin.getDefault().getPluginPreferences();
259         String JavaDoc pid = Platform.getProduct().getId();
260         String JavaDoc themeId = pref.getString(pid+"_INTRO_THEME"); //$NON-NLS-1$
261
if (themeId.length()==0)
262             themeId = pref.getString("INTRO_THEME"); //$NON-NLS-1$
263
IConfigurationElement [] elements = Platform.getExtensionRegistry().getConfigurationElementsFor("org.eclipse.ui.intro.configExtension"); //$NON-NLS-1$
264
IConfigurationElement themeElement=null;
265         for (int i=0; i<elements.length; i++) {
266             if (elements[i].getName().equals("theme")) { //$NON-NLS-1$
267
String JavaDoc id = elements[i].getAttribute("id"); //$NON-NLS-1$
268
if (themeId!=null) {
269                     if (id!=null && themeId.equals(id)) {
270                         // use this one
271
themeElement = elements[i];
272                         break;
273                     }
274                 }
275                 else {
276                     // see if this one is the default
277
String JavaDoc value = elements[i].getAttribute("default"); //$NON-NLS-1$
278
if (value!=null && value.equalsIgnoreCase("true")) { //$NON-NLS-1$
279
themeElement = elements[i];
280                         break;
281                     }
282                 }
283             }
284         }
285         if (themeElement!=null) {
286             theme = new IntroTheme(themeElement);
287         }
288     }
289
290     /**
291      * Loads all pages defined in this config from the xml content file.
292      */

293     private void loadPages(Document JavaDoc dom, Bundle bundle) {
294         String JavaDoc homePageId = getPresentation().getHomePageId();
295         String JavaDoc standbyPageId = getPresentation().getStandbyPageId();
296         Element[] pages = ModelUtil.getElementsByTagName(dom,
297             AbstractIntroPage.TAG_PAGE);
298         for (int i = 0; i < pages.length; i++) {
299             Element pageElement = pages[i];
300             if (pageElement.getAttribute(AbstractIntroIdElement.ATT_ID).equals(
301                 homePageId)) {
302                 // Create the model class for the Root Page.
303
homePage = new IntroHomePage(pageElement, bundle, base);
304                 homePage.setParent(this);
305                 currentPageId = homePage.getId();
306                 children.add(homePage);
307             } else if (pageElement.getAttribute(AbstractIntroIdElement.ATT_ID)
308                 .equals(standbyPageId)) {
309                 // Create the model class for the standby Page.
310
standbyPage = new IntroHomePage(pageElement, bundle, base);
311                 standbyPage.setParent(this);
312                 // signal that it is a standby page.
313
standbyPage.setStandbyPage(true);
314                 children.add(standbyPage);
315             } else {
316                 // Create the model class for an intro Page.
317
IntroPage page = new IntroPage(pageElement, bundle, base);
318                 page.setParent(this);
319                 children.add(page);
320             }
321         }
322     }
323
324     /**
325      * Loads all shared groups defined in this config, from the DOM.
326      */

327     private void loadSharedGroups(Document JavaDoc dom, Bundle bundle) {
328         Element[] groups = ModelUtil.getElementsByTagName(dom,
329             IntroGroup.TAG_GROUP);
330         for (int i = 0; i < groups.length; i++) {
331             IntroGroup group = new IntroGroup(groups[i], bundle, base);
332             group.setParent(this);
333             children.add(group);
334         }
335     }
336
337     /**
338      * Handles all the configExtensions to this current model. Resolving
339      * configExts means finding target anchor and inserting extension content at
340      * target. Also, several passes are used to resolve as many extensions as
341      * possible. This allows for resolving nested anchors (ie: anchors to
342      * anchors in contributions).
343      */

344     private void resolveConfigExtensions() {
345         for (int i = 0; i < configExtensionElements.length; i++)
346             resolveConfigExtension(configExtensionElements[i]);
347
348         // now add all unresolved extensions as model children and log fact.
349
Enumeration JavaDoc keys = unresolvedConfigExt.keys();
350         while (keys.hasMoreElements()) {
351             Element configExtensionElement = (Element) keys.nextElement();
352             IConfigurationElement configExtConfigurationElement = (IConfigurationElement) unresolvedConfigExt
353                 .get(configExtensionElement);
354             Bundle bundle = BundleUtil
355                 .getBundleFromConfigurationElement(configExtConfigurationElement);
356             String JavaDoc base = getBase(configExtConfigurationElement);
357             children.add(new IntroExtensionContent(configExtensionElement,
358                 bundle, base, configExtConfigurationElement));
359
360             // INTRO: fix log strings.
361
Log
362                 .warning("Could not resolve the following configExtension: " //$NON-NLS-1$
363
+ ModelLoaderUtil.getLogString(bundle,
364                             configExtensionElement,
365                             IntroExtensionContent.ATT_PATH));
366         }
367     }
368
369     private void resolveConfigExtension(IConfigurationElement configExtElement) {
370         // This call will extract the parent folder if needed.
371
Document JavaDoc dom = loadDOM(configExtElement);
372         if (dom == null)
373             // we failed to parse the content file. Intro Parser would
374
// have logged the fact. Parser would also have checked to
375
// see if the content file has the correct root tag.
376
return;
377         resolveConfigExtension(dom, configExtElement);
378     }
379
380
381     private void resolveConfigExtension(Document JavaDoc dom,
382             IConfigurationElement configExtElement) {
383
384         // Find the target of this container extension, and add all its
385
// children to target. Make sure to pass correct bundle and base to
386
// propagate to all children.
387
String JavaDoc base = getBase(configExtElement);
388         Element extensionContentElement = loadExtensionContent(dom,
389             configExtElement, base);
390         if (extensionContentElement == null)
391             // no extension content defined, ignore extension completely.
392
return;
393
394         if (extensionContentElement.hasAttribute("failed")) { //$NON-NLS-1$
395
// we failed to resolve this configExtension, because target
396
// could not be found or is not an anchor, add the extension to the
397
// list of unresolved configExtensions.
398
// INTRO: an extensionContent is used as a key, instead of the whole
399
// DOM. This is usefull if we need to support multiple extension
400
// contents in one file.
401
if (!unresolvedConfigExt.containsKey(extensionContentElement))
402                 unresolvedConfigExt.put(extensionContentElement,
403                     configExtElement);
404             return;
405         }
406
407         // We resolved a contribution. Now load all pages and shared groups
408
// from this config extension. No point adding pages that will never
409
// be referenced. Get the bundle from the extensions since they are
410
// defined in other plugins.
411
Bundle bundle = BundleUtil
412             .getBundleFromConfigurationElement(configExtElement);
413
414         Element[] pages = ModelUtil.getElementsByTagName(dom,
415             AbstractIntroPage.TAG_PAGE);
416         for (int j = 0; j < pages.length; j++) {
417             // Create the model class for an intro Page.
418
IntroPage page = new IntroPage(pages[j], bundle, base);
419             page.setParent(this);
420             children.add(page);
421         }
422
423         // load all shared groups from all configExtensions to this model.
424
loadSharedGroups(dom, bundle);
425
426         // since we resolved a contribution, try resolving some of the
427
// unresolved ones before going on.
428
unresolvedConfigExt.remove(extensionContentElement);
429         tryResolvingExtensions();
430     }
431
432
433     private void tryResolvingExtensions() {
434         Enumeration JavaDoc keys = unresolvedConfigExt.keys();
435         while (keys.hasMoreElements()) {
436             Element extensionContentElement = (Element) keys.nextElement();
437             resolveConfigExtension(extensionContentElement.getOwnerDocument(),
438                 (IConfigurationElement) unresolvedConfigExt
439                     .get(extensionContentElement));
440         }
441     }
442
443
444     /**
445      * load the extension content of this configExtension into model classes,
446      * and insert them at target. A config extension can have only ONE extension
447      * content. This is because if the extension fails, we need to be able to
448      * not include the page and group contributions as part of the model. If
449      * extension content has XHTML content (ie: content attribute is defined) we
450      * load extension DOM into target page dom.
451      *
452      * note: the extension Element is returned to enable creating a child model
453      * element on failure.
454      *
455      * @param
456      * @return
457      */

458     private Element loadExtensionContent(Document JavaDoc dom,
459             IConfigurationElement configExtElement, String JavaDoc base) {
460
461         // get the bundle from the extensions since they are defined in
462
// other plugins.
463
Bundle bundle = BundleUtil
464             .getBundleFromConfigurationElement(configExtElement);
465
466         Element[] extensionContents = ModelUtil.getElementsByTagName(dom,
467             IntroExtensionContent.TAG_CONTAINER_EXTENSION);
468         if (extensionContents.length == 0) {
469             extensionContents = ModelUtil.getElementsByTagName(dom,
470                     IntroExtensionContent.TAG_CONTAINER_REPLACE);
471         }
472
473         // INTRO: change this. we may need to load more than one extension
474
// content here.
475
// There should only be one container extension. (ver3.0)
476
Element extensionContentElement = ModelLoaderUtil
477             .validateSingleContribution(bundle, extensionContents,
478                 IntroExtensionContent.ATT_PATH);
479         if (extensionContentElement == null)
480             // no extensionContent defined.
481
return null;
482         if (UAContentFilter.isFiltered(UAElementFactory.newElement(extensionContentElement), IntroEvaluationContext.getContext())) {
483             // whole extension was filtered
484
return null;
485         }
486         
487         // Create the model class for extension content.
488
IntroExtensionContent extensionContent = new IntroExtensionContent(
489             extensionContentElement, bundle, base, configExtElement);
490         boolean success = false;
491         if (extensionContent.isXHTMLContent())
492             success = loadXHTMLExtensionContent(extensionContent);
493         else
494             success = load3_0ExtensionContent(extensionContent);
495
496         if (success) {
497             if (extensionContentElement.hasAttribute("failed")) //$NON-NLS-1$
498
extensionContentElement.removeAttribute("failed"); //$NON-NLS-1$
499
} else
500             extensionContentElement.setAttribute("failed", "true"); //$NON-NLS-1$ //$NON-NLS-2$
501

502         return extensionContentElement;
503     }
504
505
506
507     /**
508      * Insert the extension content into the target.
509      *
510      * @param extensionContent
511      * @return
512      */

513     private boolean loadXHTMLExtensionContent(
514             IntroExtensionContent extensionContent) {
515         String JavaDoc path = extensionContent.getPath();
516         // path must be pageId/anchorID in the case of anchors in XHTML pages.
517
String JavaDoc[] pathSegments = StringUtil.split(path, "/"); //$NON-NLS-1$
518
if (pathSegments.length != 2)
519             // path does not have correct format.
520
return false;
521         AbstractIntroPage targetPage = (AbstractIntroPage) findChild(
522             pathSegments[0], ABSTRACT_PAGE);
523         if (targetPage == null)
524             // target could not be found. Signal failure.
525
return false;
526
527         // Insert all children of this extension before the target element. Anchors need
528
// to stay in DOM, even after all extensions have been resolved, to enable other
529
// plugins to contribute. Find the target node.
530
Document JavaDoc pageDom = targetPage.getDocument();
531         Element targetElement = targetPage.findDomChild(pathSegments[1], "*"); //$NON-NLS-1$
532
if (targetElement == null)
533             return false;
534
535         // get extension content
536
Element[] elements = extensionContent.getElements();
537         // insert all children before anchor in page body.
538
for (int i = 0; i < elements.length; i++) {
539             Node JavaDoc targetNode = pageDom.importNode(elements[i], true);
540             // update the src attribute of this node, if defined by w3
541
// specs.
542

543             ModelUtil.updateResourceAttributes((Element) targetNode,
544                 extensionContent);
545             targetElement.getParentNode().insertBefore(targetNode, targetElement);
546         }
547
548         if (extensionContent.getExtensionType() == IntroExtensionContent.TYPE_REPLACEMENT) {
549             targetElement.getParentNode().removeChild(targetElement);
550         }
551         
552         // now handle style inheritance.
553
// Update the parent page styles. skip style if it is null;
554
String JavaDoc[] styles = extensionContent.getStyles();
555         if (styles != null) {
556             for (int i = 0; i < styles.length; i++)
557                 ModelUtil.insertStyle(pageDom, styles[i]);
558         }
559
560         return true;
561
562     }
563
564
565
566     /**
567      * Insert the extension content (3.0 format) into the target.
568      *
569      * @param extensionContent
570      * @return
571      */

572     private boolean load3_0ExtensionContent(IntroExtensionContent extensionContent) {
573         String JavaDoc path = extensionContent.getPath();
574         int type = extensionContent.getExtensionType();
575         AbstractIntroElement target = findTarget(this, path, extensionContent.getId());
576         if (target != null && target.isOfType(AbstractIntroElement.ANCHOR) == (type == IntroExtensionContent.TYPE_CONTRIBUTION)) {
577             // insert all children of this extension before the target element/anchor.
578
insertExtensionChildren(target, extensionContent, extensionContent.getBundle(), extensionContent.getBase());
579             // anchors need to stay around to receive other contributions
580
if (type == IntroExtensionContent.TYPE_REPLACEMENT) {
581                 AbstractIntroContainer parent = (AbstractIntroContainer)target.getParent();
582                 parent.removeChild(target);
583             }
584             handleExtensionStyleInheritence(target, extensionContent);
585             return true;
586         }
587         // appropriate target could not be found. Signal failure.
588
return false;
589     }
590
591     private void insertExtensionChildren(AbstractIntroElement target,
592             IntroExtensionContent extensionContent, Bundle bundle, String JavaDoc base) {
593         AbstractIntroContainer parent = (AbstractIntroContainer)target.getParent();
594         // insert the elements of the extension before the target
595
String JavaDoc mixinStyle = getMixinStyle(extensionContent);
596         Element [] children = extensionContent.getChildren();
597         parent.insertElementsBefore(children, bundle, base, target, mixinStyle);
598     }
599     
600     private String JavaDoc getMixinStyle(IntroExtensionContent extensionContent) {
601         String JavaDoc path = extensionContent.getPath();
602         if (!path.endsWith("/@")) //$NON-NLS-1$
603
return null;
604         String JavaDoc pageId = path.substring(0, path.length()-2);
605         IntroModelRoot modelRoot = getModelRoot();
606         if (modelRoot==null)
607             return null;
608         IntroConfigurer configurer = modelRoot.getConfigurer();
609         if (configurer==null)
610             return null;
611         String JavaDoc extensionId = extensionContent.getId();
612         // if this is a replace, take the mixin style as what is being replaced
613
if (extensionContent.getExtensionType() == IntroExtensionContent.TYPE_REPLACEMENT) {
614             IPath ipath = new Path(extensionContent.getPath());
615             String JavaDoc s2 = ipath.segment(1);
616             if (s2 != null && s2.startsWith("@") && s2.length() > 1) { //$NON-NLS-1$
617
extensionId = s2.substring(1);
618             }
619         }
620         return configurer.getMixinStyle(pageId, extensionId);
621     }
622
623
624     /**
625      * Updates the inherited styles based on the style attribtes defined in the
626      * configExtension. If we are extending a shared group do nothing. For
627      * inherited alt-styles, we have to cache the bundle from which we inherited
628      * the styles to be able to access resources in that plugin.
629      *
630      * @param include
631      * @param target
632      */

633     private void handleExtensionStyleInheritence(AbstractIntroElement target,
634             IntroExtensionContent extension) {
635
636         AbstractIntroContainer targetContainer = (AbstractIntroContainer)target.getParent();
637         if (targetContainer.getType() == AbstractIntroElement.GROUP
638                 && targetContainer.getParent().getType() == AbstractIntroElement.MODEL_ROOT)
639             // if we are extending a shared group, defined under a config, we
640
// can not include styles.
641
return;
642
643         // Update the parent page styles. skip style if it is null;
644
String JavaDoc[] styles = extension.getStyles();
645         if (styles != null)
646             targetContainer.getParentPage().addStyles(styles);
647
648         // for alt-style cache bundle for loading resources.
649
Hashtable JavaDoc altStyles = extension.getAltStyles();
650         if (altStyles != null)
651             targetContainer.getParentPage().addAltStyles(altStyles);
652     }
653
654     /**
655      * Sets the model state based on all the model classes. Dynamic nature of
656      * the model is always setto false when we fail to load model for any
657      * reason.
658      */

659     private void setModelState(boolean loaded, boolean hasValidConfig,
660             boolean isdynamicIntro) {
661         this.loaded = loaded;
662         this.hasValidConfig = hasValidConfig;
663         this.isdynamicIntro = isdynamicIntro;
664     }
665
666     /**
667      * Returns true if there is a valid contribution to
668      * org.eclipse.ui.intro.config extension point, with a valid Presentation,
669      * and pages.
670      *
671      * @return Returns the hasValidConfig.
672      */

673     public boolean hasValidConfig() {
674         return hasValidConfig;
675     }
676
677     /**
678      * @return Returns the introPartPresentation.
679      */

680     public IntroPartPresentation getPresentation() {
681         return introPartPresentation;
682     }
683     
684     public IntroConfigurer getConfigurer() {
685         return configurer;
686     }
687
688     /**
689      * @return Returns the rootPage.
690      */

691     public IntroHomePage getHomePage() {
692         return homePage;
693     }
694
695     /**
696      * @return Returns the standby Page. May return null if standby page is not
697      * defined.
698      */

699     public IntroHomePage getStandbyPage() {
700         return standbyPage;
701     }
702
703     /**
704      * @return all pages *excluding* the Home Page. If all pages are needed,
705      * call <code>(AbstractIntroPage[])
706      * getChildrenOfType(IntroElement.ABSTRACT_PAGE);</code>
707      */

708     public IntroPage[] getPages() {
709         return (IntroPage[]) getChildrenOfType(AbstractIntroElement.PAGE);
710     }
711
712     /**
713      * @return Returns the isdynamicIntro.
714      */

715     public boolean isDynamic() {
716         return isdynamicIntro;
717     }
718
719     /**
720      * @return Returns the currentPageId.
721      */

722     public String JavaDoc getCurrentPageId() {
723         return currentPageId;
724     }
725
726
727     /**
728      * Sets the current page. If the model does not have a page with the passed
729      * id, the message is logged, and the model retains its old current page.
730      *
731      * @param currentPageId
732      * The currentPageId to set. *
733      * @param fireEvent
734      * flag to indicate if event notification is needed.
735      * @return true if the model has a page with the passed id, false otherwise.
736      * If the method fails, the current page remains the same as the
737      * last state.
738      */

739     public boolean setCurrentPageId(String JavaDoc pageId, boolean fireEvent) {
740         if (pageId.equals(currentPageId))
741             // setting to the same page does nothing. Return true because we did
742
// not actually fail. just a no op.
743
return true;
744
745         AbstractIntroPage page = (AbstractIntroPage) findChild(pageId,
746             ABSTRACT_PAGE);
747         if (page == null) {
748             // not a page. Test for root page.
749
if (!pageId.equals(homePage.getId())) {
750                 // not a page nor the home page.
751
Log
752                     .warning("Could not set current page to Intro page with id: " + pageId); //$NON-NLS-1$
753
return false;
754             }
755         }
756
757         currentPageId = pageId;
758         if (fireEvent)
759             firePropertyChange(CURRENT_PAGE_PROPERTY_ID);
760         return true;
761     }
762
763     public boolean setCurrentPageId(String JavaDoc pageId) {
764         return setCurrentPageId(pageId, true);
765     }
766
767     public void addPropertyListener(IPropertyListener l) {
768         propChangeListeners.add(l);
769     }
770
771     /**
772      * Fires a property changed event. Made public because it can be used to
773      * trigger a UI refresh.
774      *
775      * @param propertyId
776      * the id of the property that changed
777      */

778     public void firePropertyChange(final int propertyId) {
779         Object JavaDoc[] array = propChangeListeners.getListeners();
780         for (int i = 0; i < array.length; i++) {
781             final IPropertyListener l = (IPropertyListener) array[i];
782             SafeRunner.run(new SafeRunnable() {
783
784                 public void run() {
785                     l.propertyChanged(this, propertyId);
786                 }
787
788                 public void handleException(Throwable JavaDoc e) {
789                     super.handleException(e);
790                     // If an unexpected exception happens, remove it
791
// to make sure the workbench keeps running.
792
propChangeListeners.remove(l);
793                 }
794             });
795         }
796     }
797
798     public void removePropertyListener(IPropertyListener l) {
799         propChangeListeners.remove(l);
800     }
801
802     /**
803      * @return Returns the currentPage. return null if page is not found, or if
804      * we are not in a dynamic intro mode.
805      */

806     public AbstractIntroPage getCurrentPage() {
807         if (!isdynamicIntro)
808             return null;
809
810         AbstractIntroPage page = (AbstractIntroPage) findChild(currentPageId,
811             ABSTRACT_PAGE);
812         if (page != null)
813             return page;
814         // not a page. Test for root page.
815
if (currentPageId.equals(homePage.getId()))
816             return homePage;
817         // return null if page is not found.
818
return null;
819     }
820
821     /*
822      * (non-Javadoc)
823      *
824      * @see org.eclipse.ui.internal.intro.impl.model.IntroElement#getType()
825      */

826     public int getType() {
827         return AbstractIntroElement.MODEL_ROOT;
828     }
829
830
831     /**
832      * Assumes that the passed config element has a "content" attribute. Reads
833      * it and loads a DOM based on that attribute value. It does not explicitly
834      * resolve the resource because this method only loads the introContent and
835      * the configExt content files. ie: in plugin.xml. <br>
836      * This method also sets the base attribute on the root element in the DOM
837      * to enable resolving all resources relative to this DOM.
838      *
839      * @return
840      */

841     protected Document JavaDoc loadDOM(IConfigurationElement cfgElement) {
842         String JavaDoc content = cfgElement.getAttribute(ATT_CONTENT);
843
844         // To support jarring, extract parent folder of where the intro content
845
// file is. It is expected that all intro content is in that one parent
846
// folder. This works for both content files and configExtension content
847
// files.
848
Bundle domBundle = BundleUtil
849             .getBundleFromConfigurationElement(cfgElement);
850         ModelUtil.ensureFileURLsExist(domBundle, content);
851
852         // Resolve.
853
content = BundleUtil.getResourceLocation(content, cfgElement);
854         Document JavaDoc document = new IntroContentParser(content).getDocument();
855
856         return document;
857     }
858
859
860     private String JavaDoc getBase(IConfigurationElement configElement) {
861         String JavaDoc content = configElement.getAttribute(ATT_CONTENT);
862         return ModelUtil.getParentFolderToString(content);
863     }
864     
865     public String JavaDoc resolveVariables(String JavaDoc text) {
866         if (text==null) return null;
867         if (text.indexOf('$')== -1)
868             return text;
869         // resolve
870
boolean inVariable=false;
871         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
872         int vindex=0;
873         for (int i=0; i<text.length(); i++) {
874             char c = text.charAt(i);
875             if (c=='$') {
876                 if (!inVariable) {
877                     inVariable=true;
878                     vindex=i+1;
879                     continue;
880                 }
881                 inVariable=false;
882                 String JavaDoc variable=text.substring(vindex, i);
883                 String JavaDoc value = getVariableValue(variable);
884                 if (value==null)
885                     value = "$"+variable+"$"; //$NON-NLS-1$ //$NON-NLS-2$
886
buf.append(value);
887                 continue;
888             }
889             else if (!inVariable)
890                 buf.append(c);
891         }
892         return buf.toString();
893     }
894
895     private String JavaDoc getVariableValue(String JavaDoc variable) {
896         if (variable.equals(VAR_THEME)) {
897             if (theme!=null)
898                 return theme.getPath();
899         }
900         if (configurer!=null)
901             return configurer.getVariable(variable);
902         return null;
903     }
904     
905     public String JavaDoc resolvePath(String JavaDoc extensionId, String JavaDoc path) {
906         if (configurer==null) return null;
907         return configurer.resolvePath(extensionId, path);
908     }
909
910     
911     public IntroTheme getTheme() {
912         return theme;
913     }
914 }
Popular Tags