001: /*******************************************************************************
002: * Copyright (c) 2004, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.intro.impl.model;
011:
012: import java.util.ArrayList;
013: import java.util.Map;
014: import java.util.StringTokenizer;
015: import java.util.Vector;
016:
017: import org.eclipse.core.runtime.IConfigurationElement;
018: import org.eclipse.core.runtime.IRegistryChangeEvent;
019: import org.eclipse.core.runtime.Platform;
020: import org.eclipse.swt.SWTError;
021: import org.eclipse.swt.widgets.Composite;
022: import org.eclipse.ui.IMemento;
023: import org.eclipse.ui.internal.intro.impl.IntroPlugin;
024: import org.eclipse.ui.internal.intro.impl.model.loader.ModelLoaderUtil;
025: import org.eclipse.ui.internal.intro.impl.model.util.ModelUtil;
026: import org.eclipse.ui.internal.intro.impl.presentations.BrowserIntroPartImplementation;
027: import org.eclipse.ui.internal.intro.impl.presentations.FormIntroPartImplementation;
028: import org.eclipse.ui.internal.intro.impl.presentations.TextIntroPartImplementation;
029: import org.eclipse.ui.internal.intro.impl.util.Log;
030: import org.eclipse.ui.internal.intro.impl.util.StringUtil;
031: import org.eclipse.ui.intro.IIntroPart;
032:
033: /**
034: * This class models the presentation element contributed to a config extension point. The
035: * Presentation class delegates UI creation to the actual Implementation class, and passes the
036: * IntroPart along to this implementation class. Also, dynamic awarness is honored here.
037: * <p>
038: * Rules:
039: * <ul>
040: * <li>There is no model class for the "implementation" markup element. This presentation class
041: * inherits information from the implementation class that is picked (based on OS, ...).</li>
042: * <li>ID attribute of this model class is the id of the picked implementation element.</li>
043: * <li>Style attribute in this model class is the style of the picked implementation element.</li>
044: * <li>HTMLHeadContent in this model class is the HEAD element under the picked implementation
045: * element, only if the implementation element is a Browser implmenetation.</li>
046: * <li>The UI model class, AbstractIntroPartImplementation, that represents the IntroPart
047: * implementation is cached here for quick access. It is used by intro url actions for manipulation
048: * of UI.<br>
049: * INTRO:This really should be in a UI model class.
050: * <ul>
051: */
052: public class IntroPartPresentation extends AbstractIntroElement {
053:
054: protected static final String TAG_PRESENTATION = "presentation"; //$NON-NLS-1$
055: private static final String TAG_IMPLEMENTATION = "implementation"; //$NON-NLS-1$
056:
057: private static final String ATT_KIND = "kind"; //$NON-NLS-1$
058: private static final String ATT_STYLE = "style"; //$NON-NLS-1$
059: private static final String ATT_OS = "os"; //$NON-NLS-1$
060: private static final String ATT_WS = "ws"; //$NON-NLS-1$
061: protected static final String ATT_HOME_PAGE_ID = "home-page-id"; //$NON-NLS-1$
062: protected static final String ATT_STANDBY_PAGE_ID = "standby-page-id"; //$NON-NLS-1$
063:
064: public static final String BROWSER_IMPL_KIND = "html"; //$NON-NLS-1$
065: public static final String FORMS_IMPL_KIND = "swt"; //$NON-NLS-1$
066: // this implementation kind if not public api. Only used internally for
067: // debugging.
068: private static final String TEXT_IMPL_KIND = "text"; //$NON-NLS-1$
069:
070: // private String title;
071: private String[] implementationStyles;
072: private String implementationKind;
073: private String homePageId;
074: private String standbyPageId;
075:
076: // The Head contributions to this preentation (inherited from child
077: // implementation).
078: private IntroHead head;
079:
080: private AbstractIntroPartImplementation implementation;
081:
082: private IntroLaunchBarElement launchBar;
083:
084: // CustomizableIntroPart and memento instances. Passed to the Implementation
085: // classes.
086: private IIntroPart introPart;
087: private IMemento memento;
088:
089: /**
090: *
091: */
092: IntroPartPresentation(IConfigurationElement element) {
093: super (element);
094: homePageId = element.getAttribute(ATT_HOME_PAGE_ID);
095: standbyPageId = element.getAttribute(ATT_STANDBY_PAGE_ID);
096: }
097:
098: private void updatePresentationAttributes(
099: IConfigurationElement element) {
100: if (element != null) {
101: // reset (ie: inherit) type and style to be implementation type and
102: // style. Then handle HEAD content for the case of HTML Browser.
103: String value = element.getAttribute(ATT_STYLE);
104: if (value != null) {
105: IntroModelRoot root = getModelRoot();
106: ArrayList list = new ArrayList();
107: StringTokenizer stok = new StringTokenizer(value, ","); //$NON-NLS-1$
108: for (; stok.hasMoreTokens();) {
109: String oneStyle = stok.nextToken().trim();
110: if (root != null)
111: oneStyle = root.resolveVariables(oneStyle);
112: list.add(oneStyle);
113: }
114: implementationStyles = (String[]) list
115: .toArray(new String[list.size()]);
116: }
117: implementationKind = element.getAttribute(ATT_KIND);
118: // get Head contribution, regardless of implementation class.
119: // Implementation class is created lazily by UI.
120: head = getHead(element);
121: // Resolve.
122: if (implementationStyles != null) {
123: for (int i = 0; i < implementationStyles.length; i++) {
124: implementationStyles[i] = ModelUtil.resolveURL(
125: implementationStyles[i], element);
126: }
127: }
128: }
129: }
130:
131: /**
132: * Returns the styles associated with the Presentation. May be null if no shared presentation
133: * style is needed, or in the case of static HTML OOBE.
134: *
135: * @return Returns the array of styles or null if not defined.
136: */
137: public String[] getImplementationStyles() {
138: return implementationStyles;
139: }
140:
141: /**
142: * Returns the type attribute of the implementation picked by this presentation.
143: *
144: * @return Returns the implementationKind.
145: */
146: public String getImplementationKind() {
147: return implementationKind;
148: }
149:
150: public AbstractIntroPartImplementation getIntroPartImplementation() {
151: return implementation;
152: }
153:
154: /**
155: * Returns the model class for the Head element under an implementation. Returns null if there
156: * is no head contribution.
157: *
158: * @param element
159: * @return
160: */
161: private IntroHead getHead(IConfigurationElement element) {
162: try {
163: // There should only be one head element. Since elements where
164: // obtained by name, no point validating name.
165: IConfigurationElement[] headElements = element
166: .getChildren(IntroHead.TAG_HEAD);
167: if (headElements.length == 0)
168: // no contributions. done.
169: return null;
170: IntroHead head = new IntroHead(headElements[0]);
171: head.setParent(this );
172: return head;
173: } catch (Exception e) {
174: Log.error(e.getMessage(), e);
175: return null;
176: }
177: }
178:
179: /**
180: * Returns the launch bar element if defined in this presentation, or <code>null</code>
181: * otherwise.
182: *
183: * @since 3.1
184: * @return
185: */
186:
187: public IntroLaunchBarElement getLaunchBarElement() {
188: if (launchBar != null)
189: return launchBar;
190: IConfigurationElement[] children = getCfgElement().getChildren(
191: "launchBar"); //$NON-NLS-1$
192: if (children.length > 0) {
193: launchBar = new IntroLaunchBarElement(children[0]);
194: launchBar.setParent(this );
195: if (children.length > 1)
196: Log
197: .warning("Mutiple Intro Launch bars defined when only one is allowed. Only first one was loaded. "); //$NON-NLS-1$
198: }
199: return launchBar;
200: }
201:
202: /**
203: * @param introPart
204: */
205: public void init(IIntroPart introPart, IMemento memento) {
206: // REVISIT: Called when the actual UI needs to be created. Incomplete
207: // separation of model / UI. Will change later. should not get here if
208: // there is no valid implementation.
209: this .introPart = introPart;
210: this .memento = memento;
211: }
212:
213: /**
214: * Creates the UI based on the implementation class.
215: *
216: * @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
217: */
218: public void createPartControl(Composite parent) {
219: Vector validImplementations = getValidImplementationElements(getCfgElement());
220: IConfigurationElement implementationElement = null;
221: for (int i = 0; i < validImplementations.size(); i++) {
222: implementationElement = (IConfigurationElement) validImplementations
223: .elementAt(i);
224: // you want to pass primed model.
225: updatePresentationAttributes(implementationElement);
226: try {
227: implementation = createIntroPartImplementation(getImplementationKind());
228: if (implementation == null)
229: // failed to create executable.
230: continue;
231:
232: implementation.init(introPart, memento);
233: implementation.createPartControl(parent);
234: IntroModelRoot model = getModelRoot();
235: if (model != null && model.getConfigurer() != null) {
236: IntroTheme theme = model.getTheme();
237: Map properties = theme != null ? theme
238: .getProperties() : null;
239: model.getConfigurer().init(
240: introPart.getIntroSite(), properties);
241: }
242: if (Log.logInfo)
243: Log.info("Loading Intro UI implementation from: " //$NON-NLS-1$
244: + ModelLoaderUtil.getLogString(
245: implementationElement, "kind")); //$NON-NLS-1$
246: break;
247: } catch (SWTError e) {
248: Log
249: .warning("Failed to create Intro UI implementation from: " //$NON-NLS-1$
250: + ModelLoaderUtil.getLogString(
251: implementationElement, "kind") + e.getMessage()); //$NON-NLS-1$
252: implementation = null;
253: implementationElement = null;
254: } catch (Exception e) {
255: Log
256: .error(
257: "Failed to create Intro UI implementation from: " //$NON-NLS-1$
258: + ModelLoaderUtil.getLogString(
259: implementationElement,
260: "kind"), e); //$NON-NLS-1$
261: implementation = null;
262: implementationElement = null;
263: }
264: }
265:
266: if (implementationElement == null) {
267: // worst case scenario. We failed in all cases.
268: implementation = new FormIntroPartImplementation();
269: try {
270: implementation.init(introPart, memento);
271: // simply set the presentation kind since all other attributes
272: // will be null.
273: implementationKind = FORMS_IMPL_KIND;
274: } catch (Exception e) {
275: // should never be here.
276: Log.error(e.getMessage(), e);
277: return;
278: }
279: implementation.createPartControl(parent);
280: Log
281: .warning("Loaded UI Forms implementation as a default UI implementation."); //$NON-NLS-1$
282: }
283: }
284:
285: /**
286: * Retruns a list of valid implementation elements of the config. Choose correct implementation
287: * element based on os atrributes. Rules: get current OS, choose first contributrion, with os
288: * that matches OS. Otherwise, choose first contribution with no os. Returns null if no valid
289: * implementation is found.
290: */
291: private Vector getValidImplementationElements(
292: IConfigurationElement configElement) {
293:
294: Vector validList = new Vector();
295:
296: // There can be more than one implementation contribution. Add each
297: // valid one. First start with OS, then WS then no OS.
298: IConfigurationElement[] implementationElements = configElement
299: .getChildren(TAG_IMPLEMENTATION);
300: // IConfigurationElement implementationElement = null;
301:
302: if (implementationElements.length == 0)
303: // no contributions. done.
304: return validList;
305:
306: String currentOS = Platform.getOS();
307: String currentWS = Platform.getWS();
308:
309: // first loop through all to find one with matching OS, with or
310: // without WS.
311: for (int i = 0; i < implementationElements.length; i++) {
312: String os = implementationElements[i].getAttribute(ATT_OS);
313: if (os == null)
314: // no os, no match.
315: continue;
316:
317: if (listValueHasValue(os, currentOS)) {
318: // found implementation with correct OS. Now try if WS
319: // matches.
320: String ws = implementationElements[i]
321: .getAttribute(ATT_WS);
322: if (ws == null) {
323: // good OS, and they do not care about WS. we have a
324: // match.
325: validList.add(implementationElements[i]);
326: } else {
327: // good OS, and we have WS.
328: if (listValueHasValue(ws, currentWS))
329: validList.add(implementationElements[i]);
330: }
331: }
332: }
333:
334: // now loop through all to find one with no OS defined, but with a
335: // matching WS.
336: for (int i = 0; i < implementationElements.length; i++) {
337: String os = implementationElements[i].getAttribute(ATT_OS);
338: if (os == null) {
339: // found implementation with no OS. Now try if WS
340: // matches.
341: String ws = implementationElements[i]
342: .getAttribute(ATT_WS);
343: if (ws == null) {
344: // no OS, and they do not care about WS. we have a
345: // match.
346: validList.add(implementationElements[i]);
347: } else {
348: // no OS, and we have WS.
349: if (listValueHasValue(ws, currentWS))
350: validList.add(implementationElements[i]);
351: }
352:
353: }
354: }
355:
356: return validList;
357:
358: }
359:
360: /**
361: * Util method that searches for the given value in a comma separated list of values. The list
362: * is retrieved as an attribute value of OS, WS.
363: *
364: */
365: private boolean listValueHasValue(String stringValue, String value) {
366: String[] attributeValues = StringUtil.split(stringValue, ","); //$NON-NLS-1$
367: for (int i = 0; i < attributeValues.length; i++) {
368: if (attributeValues[i].equalsIgnoreCase(value))
369: return true;
370: }
371: return false;
372: }
373:
374: /**
375: * Util method to load shared style from given kind.
376: */
377: public String getSharedStyle(String kind) {
378: // There can be more than one implementation contribution.
379: IConfigurationElement[] implementationElements = getCfgElement()
380: .getChildren(TAG_IMPLEMENTATION);
381: // IConfigurationElement implementationElement = null;
382:
383: if (implementationElements.length == 0)
384: // no implementations. done.
385: return null;
386:
387: // loop through all to find one with matching kind.
388: for (int i = 0; i < implementationElements.length; i++) {
389: String aKind = implementationElements[i]
390: .getAttribute(ATT_KIND);
391: if (aKind.equals(kind)) {
392: // found implementation with matching kind.
393: String style = implementationElements[i]
394: .getAttribute(ATT_STYLE);
395: return ModelUtil.resolveURL(style, getCfgElement());
396: }
397: }
398: return null;
399: }
400:
401: /**
402: * Creates the actual implementation class. Returns null on failure.
403: *
404: */
405: private AbstractIntroPartImplementation createIntroPartImplementation(
406: String implementationType) {
407: // quick exits
408: if (implementationType == null)
409: return null;
410: if (!implementationType.equals(BROWSER_IMPL_KIND)
411: && !implementationType.equals(FORMS_IMPL_KIND)
412: && !implementationType.equals(TEXT_IMPL_KIND))
413: return null;
414: if (implementationType.equals(BROWSER_IMPL_KIND)
415: && IntroPlugin.DEBUG_NO_BROWSER)
416: return null;
417:
418: AbstractIntroPartImplementation implementation = null;
419: try {
420: if (implementationType.equals(BROWSER_IMPL_KIND))
421: implementation = //null;
422: new BrowserIntroPartImplementation();
423: else if (implementationType.equals(FORMS_IMPL_KIND))
424: implementation = new FormIntroPartImplementation();
425: else
426: implementation = new TextIntroPartImplementation();
427: } catch (Exception e) {
428: Log.error("Could not instantiate implementation " //$NON-NLS-1$
429: + implementationType, e);
430: }
431: return implementation;
432: }
433:
434: /**
435: * Returns the the Customizable Intro Part. may return null if init() has not been called yet on
436: * the presentation.
437: *
438: * @return Returns the introPart.
439: */
440: public IIntroPart getIntroPart() {
441: return introPart;
442: }
443:
444: /**
445: * Save the current state of the intro. Delegate to the implementation to do the work, as
446: * different implementations may have different requirements.
447: *
448: * @param memento
449: * the memento in which to store state information
450: */
451: public void saveState(IMemento memento) {
452: if (implementation != null)
453: implementation.saveState(memento);
454: }
455:
456: public void setFocus() {
457: if (implementation != null)
458: implementation.setFocus();
459: }
460:
461: public void standbyStateChanged(boolean standby,
462: boolean isStandbyPartNeeded) {
463: if (implementation != null)
464: implementation.standbyStateChanged(standby,
465: isStandbyPartNeeded);
466: }
467:
468: public void updateHistory(AbstractIntroPage page) {
469: if (implementation != null)
470: implementation.updateHistory(page);
471: }
472:
473: public boolean navigateForward() {
474: if (implementation != null)
475: return implementation.navigateForward();
476: return false;
477: }
478:
479: public boolean navigateBackward() {
480: if (implementation != null)
481: return implementation.navigateBackward();
482: return false;
483: }
484:
485: public boolean navigateHome() {
486: if (implementation != null)
487: return implementation.navigateHome();
488: return false;
489: }
490:
491: /**
492: * Called when the IntroPart is disposed. Forwards the call to the implementation class.
493: */
494: public void dispose() {
495: if (implementation != null)
496: implementation.dispose();
497: }
498:
499: /**
500: * Support dynamic awarness. Clear cached models first, then update UI by delegating to
501: * implementation.
502: *
503: * @see org.eclipse.core.runtime.IRegistryChangeListener#registryChanged(org.eclipse.core.runtime.IRegistryChangeEvent)
504: */
505: public void registryChanged(IRegistryChangeEvent event) {
506: if (implementation != null)
507: implementation.registryChanged(event);
508: }
509:
510: /**
511: * @return Returns the homePageId.
512: */
513: public String getHomePageId() {
514: return homePageId;
515: }
516:
517: /**
518: * @return Returns the homePageId.
519: */
520: public String getStandbyPageId() {
521: return standbyPageId;
522: }
523:
524: /*
525: * (non-Javadoc)
526: *
527: * @see org.eclipse.ui.internal.intro.impl.model.IntroElement#getType()
528: */
529: public int getType() {
530: return AbstractIntroElement.PRESENTATION;
531: }
532:
533: /**
534: * @return Returns the HTML head conttent to be added to each dynamic html page in this
535: * presentation..
536: */
537: public IntroHead getHead() {
538: return head;
539: }
540:
541: }
|