blob: fcbc602daae7f50a43052ed2cd8803d5652b0795 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2016 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.internal.intro.impl.model;
import java.util.ArrayList;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Vector;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IRegistryChangeEvent;
import org.eclipse.core.runtime.Platform;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.internal.intro.impl.IntroPlugin;
import org.eclipse.ui.internal.intro.impl.model.loader.ModelLoaderUtil;
import org.eclipse.ui.internal.intro.impl.model.util.ModelUtil;
import org.eclipse.ui.internal.intro.impl.presentations.BrowserIntroPartImplementation;
import org.eclipse.ui.internal.intro.impl.presentations.FormIntroPartImplementation;
import org.eclipse.ui.internal.intro.impl.presentations.TextIntroPartImplementation;
import org.eclipse.ui.internal.intro.impl.util.Log;
import org.eclipse.ui.internal.intro.impl.util.StringUtil;
import org.eclipse.ui.intro.IIntroPart;
/**
* This class models the presentation element contributed to a config extension point. The
* Presentation class delegates UI creation to the actual Implementation class, and passes the
* IntroPart along to this implementation class. Also, dynamic awarness is honored here.
* <p>
* Rules:
* <ul>
* <li>There is no model class for the "implementation" markup element. This presentation class
* inherits information from the implementation class that is picked (based on OS, ...).</li>
* <li>ID attribute of this model class is the id of the picked implementation element.</li>
* <li>Style attribute in this model class is the style of the picked implementation element.</li>
* <li>HTMLHeadContent in this model class is the HEAD element under the picked implementation
* element, only if the implementation element is a Browser implmenetation.</li>
* <li>The UI model class, AbstractIntroPartImplementation, that represents the IntroPart
* implementation is cached here for quick access. It is used by intro url actions for manipulation
* of UI.<br>
* INTRO:This really should be in a UI model class.
* <ul>
*/
public class IntroPartPresentation extends AbstractIntroElement {
protected static final String TAG_PRESENTATION = "presentation"; //$NON-NLS-1$
private static final String TAG_IMPLEMENTATION = "implementation"; //$NON-NLS-1$
private static final String ATT_KIND = "kind"; //$NON-NLS-1$
private static final String ATT_STYLE = "style"; //$NON-NLS-1$
private static final String ATT_OS = "os"; //$NON-NLS-1$
private static final String ATT_WS = "ws"; //$NON-NLS-1$
protected static final String ATT_HOME_PAGE_ID = "home-page-id"; //$NON-NLS-1$
protected static final String ATT_STANDBY_PAGE_ID = "standby-page-id"; //$NON-NLS-1$
public static final String BROWSER_IMPL_KIND = "html"; //$NON-NLS-1$
public static final String FORMS_IMPL_KIND = "swt"; //$NON-NLS-1$
// this implementation kind if not public api. Only used internally for
// debugging.
private static final String TEXT_IMPL_KIND = "text"; //$NON-NLS-1$
// private String title;
private String [] implementationStyles;
private String implementationKind;
private String homePageId;
private String standbyPageId;
// The Head contributions to this preentation (inherited from child
// implementation).
private IntroHead head;
private AbstractIntroPartImplementation implementation;
private IntroLaunchBarElement launchBar;
// CustomizableIntroPart and memento instances. Passed to the Implementation
// classes.
private IIntroPart introPart;
private IMemento memento;
/**
*
*/
IntroPartPresentation(IConfigurationElement element) {
super(element);
homePageId = element.getAttribute(ATT_HOME_PAGE_ID);
standbyPageId = element.getAttribute(ATT_STANDBY_PAGE_ID);
}
private void updatePresentationAttributes(IConfigurationElement element) {
if (element != null) {
// reset (ie: inherit) type and style to be implementation type and
// style. Then handle HEAD content for the case of HTML Browser.
String value = element.getAttribute(ATT_STYLE);
if (value!=null) {
IntroModelRoot root = getModelRoot();
ArrayList<String> list = new ArrayList<>();
StringTokenizer stok = new StringTokenizer(value, ","); //$NON-NLS-1$
for (;stok.hasMoreTokens();) {
String oneStyle = stok.nextToken().trim();
if (root!=null)
oneStyle = root.resolveVariables(oneStyle);
list.add(oneStyle);
}
implementationStyles = (String[])list.toArray(new String[list.size()]);
}
implementationKind = element.getAttribute(ATT_KIND);
// get Head contribution, regardless of implementation class.
// Implementation class is created lazily by UI.
head = getHead(element);
// Resolve.
if (implementationStyles!=null) {
for (int i=0; i<implementationStyles.length; i++) {
implementationStyles[i] = ModelUtil.resolveURL(implementationStyles[i], element);
}
}
}
}
/**
* Returns the styles associated with the Presentation. May be null if no shared presentation
* style is needed, or in the case of static HTML OOBE.
*
* @return Returns the array of styles or null if not defined.
*/
public String[] getImplementationStyles() {
return implementationStyles;
}
/**
* Returns the type attribute of the implementation picked by this presentation.
*
* @return Returns the implementationKind.
*/
public String getImplementationKind() {
return implementationKind;
}
public AbstractIntroPartImplementation getIntroPartImplementation() {
return implementation;
}
/**
* Returns the model class for the Head element under an implementation. Returns null if there
* is no head contribution.
*
* @param element
* @return
*/
private IntroHead getHead(IConfigurationElement element) {
try {
// There should only be one head element. Since elements where
// obtained by name, no point validating name.
IConfigurationElement[] headElements = element.getChildren(IntroHead.TAG_HEAD);
if (headElements.length == 0)
// no contributions. done.
return null;
IntroHead head = new IntroHead(headElements[0]);
head.setParent(this);
return head;
} catch (Exception e) {
Log.error(e.getMessage(), e);
return null;
}
}
/**
* Returns the launch bar element if defined in this presentation, or <code>null</code>
* otherwise.
*
* @since 3.1
* @return
*/
public IntroLaunchBarElement getLaunchBarElement() {
if (launchBar != null)
return launchBar;
IConfigurationElement[] children = getCfgElement().getChildren("launchBar"); //$NON-NLS-1$
if (children.length > 0) {
launchBar = new IntroLaunchBarElement(children[0]);
launchBar.setParent(this);
if (children.length > 1)
Log
.warning("Mutiple Intro Launch bars defined when only one is allowed. Only first one was loaded. "); //$NON-NLS-1$
}
return launchBar;
}
/**
* @param introPart
*/
public void init(IIntroPart introPart, IMemento memento) {
// REVISIT: Called when the actual UI needs to be created. Incomplete
// separation of model / UI. Will change later. should not get here if
// there is no valid implementation.
this.introPart = introPart;
this.memento = memento;
}
/**
* Creates the UI based on the implementation class.
*
* @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
*/
public void createPartControl(Composite parent) {
Vector validImplementations = getValidImplementationElements(getCfgElement());
IConfigurationElement implementationElement = null;
for (int i = 0; i < validImplementations.size(); i++) {
implementationElement = (IConfigurationElement) validImplementations.elementAt(i);
// you want to pass primed model.
updatePresentationAttributes(implementationElement);
try {
implementation = createIntroPartImplementation(getImplementationKind());
if (implementation == null)
// failed to create executable.
continue;
implementation.init(introPart, memento);
implementation.createPartControl(parent);
IntroModelRoot model = getModelRoot();
if (model != null && model.getConfigurer() != null) {
IntroTheme theme = model.getTheme();
Map<String, String> properties = theme != null ? theme.getProperties() : null;
model.getConfigurer().init(introPart.getIntroSite(), properties);
}
if (Log.logInfo)
Log.info("Loading Intro UI implementation from: " //$NON-NLS-1$
+ ModelLoaderUtil.getLogString(implementationElement, "kind")); //$NON-NLS-1$
break;
} catch (SWTError e) {
Log.warning("Failed to create Intro UI implementation from: " //$NON-NLS-1$
+ ModelLoaderUtil.getLogString(implementationElement, "kind") + e.getMessage()); //$NON-NLS-1$
implementation = null;
implementationElement = null;
} catch (Exception e) {
Log.error("Failed to create Intro UI implementation from: " //$NON-NLS-1$
+ ModelLoaderUtil.getLogString(implementationElement, "kind"), e); //$NON-NLS-1$
implementation = null;
implementationElement = null;
}
}
if (implementationElement == null) {
// worst case scenario. We failed in all cases.
implementation = new FormIntroPartImplementation();
try {
implementation.init(introPart, memento);
// simply set the presentation kind since all other attributes
// will be null.
implementationKind = FORMS_IMPL_KIND;
} catch (Exception e) {
// should never be here.
Log.error(e.getMessage(), e);
return;
}
implementation.createPartControl(parent);
Log.warning("Loaded UI Forms implementation as a default UI implementation."); //$NON-NLS-1$
}
}
/**
* Retruns a list of valid implementation elements of the config. Choose correct implementation
* element based on os atrributes. Rules: get current OS, choose first contributrion, with os
* that matches OS. Otherwise, choose first contribution with no os. Returns null if no valid
* implementation is found.
*/
private Vector getValidImplementationElements(IConfigurationElement configElement) {
Vector<IConfigurationElement> validList = new Vector<>();
// There can be more than one implementation contribution. Add each
// valid one. First start with OS, then WS then no OS.
IConfigurationElement[] implementationElements = configElement.getChildren(TAG_IMPLEMENTATION);
// IConfigurationElement implementationElement = null;
if (implementationElements.length == 0)
// no contributions. done.
return validList;
String currentOS = Platform.getOS();
String currentWS = Platform.getWS();
// first loop through all to find one with matching OS, with or
// without WS.
for (int i = 0; i < implementationElements.length; i++) {
String os = implementationElements[i].getAttribute(ATT_OS);
if (os == null)
// no os, no match.
continue;
if (listValueHasValue(os, currentOS)) {
// found implementation with correct OS. Now try if WS
// matches.
String ws = implementationElements[i].getAttribute(ATT_WS);
if (ws == null) {
// good OS, and they do not care about WS. we have a
// match.
validList.add(implementationElements[i]);
} else {
// good OS, and we have WS.
if (listValueHasValue(ws, currentWS))
validList.add(implementationElements[i]);
}
}
}
// now loop through all to find one with no OS defined, but with a
// matching WS.
for (int i = 0; i < implementationElements.length; i++) {
String os = implementationElements[i].getAttribute(ATT_OS);
if (os == null) {
// found implementation with no OS. Now try if WS
// matches.
String ws = implementationElements[i].getAttribute(ATT_WS);
if (ws == null) {
// no OS, and they do not care about WS. we have a
// match.
validList.add(implementationElements[i]);
} else {
// no OS, and we have WS.
if (listValueHasValue(ws, currentWS))
validList.add(implementationElements[i]);
}
}
}
return validList;
}
/**
* Util method that searches for the given value in a comma separated list of values. The list
* is retrieved as an attribute value of OS, WS.
*
*/
private boolean listValueHasValue(String stringValue, String value) {
String[] attributeValues = StringUtil.split(stringValue, ","); //$NON-NLS-1$
for (int i = 0; i < attributeValues.length; i++) {
if (attributeValues[i].equalsIgnoreCase(value))
return true;
}
return false;
}
/**
* Util method to load shared style from given kind.
*/
public String getSharedStyle(String kind) {
// There can be more than one implementation contribution.
IConfigurationElement[] implementationElements = getCfgElement().getChildren(TAG_IMPLEMENTATION);
// IConfigurationElement implementationElement = null;
if (implementationElements.length == 0)
// no implementations. done.
return null;
// loop through all to find one with matching kind.
for (int i = 0; i < implementationElements.length; i++) {
String aKind = implementationElements[i].getAttribute(ATT_KIND);
if (aKind.equals(kind)) {
// found implementation with matching kind.
String style = implementationElements[i].getAttribute(ATT_STYLE);
return ModelUtil.resolveURL(style, getCfgElement());
}
}
return null;
}
/**
* Creates the actual implementation class. Returns null on failure.
*
*/
private AbstractIntroPartImplementation createIntroPartImplementation(String implementationType) {
// quick exits
if (implementationType == null)
return null;
if (!implementationType.equals(BROWSER_IMPL_KIND) && !implementationType.equals(FORMS_IMPL_KIND)
&& !implementationType.equals(TEXT_IMPL_KIND))
return null;
if (implementationType.equals(BROWSER_IMPL_KIND) && IntroPlugin.DEBUG_NO_BROWSER)
return null;
AbstractIntroPartImplementation implementation = null;
try {
if (implementationType.equals(BROWSER_IMPL_KIND))
implementation = //null;
new BrowserIntroPartImplementation();
else if (implementationType.equals(FORMS_IMPL_KIND))
implementation = new FormIntroPartImplementation();
else
implementation = new TextIntroPartImplementation();
} catch (Exception e) {
Log.error("Could not instantiate implementation " //$NON-NLS-1$
+ implementationType, e);
}
return implementation;
}
/**
* Returns the the Customizable Intro Part. may return null if init() has not been called yet on
* the presentation.
*
* @return Returns the introPart.
*/
public IIntroPart getIntroPart() {
return introPart;
}
/**
* Save the current state of the intro. Delegate to the implementation to do the work, as
* different implementations may have different requirements.
*
* @param memento
* the memento in which to store state information
*/
public void saveState(IMemento memento) {
if (implementation != null)
implementation.saveState(memento);
}
public void setFocus() {
if (implementation != null)
implementation.setFocus();
}
public void standbyStateChanged(boolean standby, boolean isStandbyPartNeeded) {
if (implementation != null)
implementation.standbyStateChanged(standby, isStandbyPartNeeded);
}
public void updateHistory(AbstractIntroPage page) {
if (implementation != null)
implementation.updateHistory(page);
}
public boolean navigateForward() {
if (implementation != null)
return implementation.navigateForward();
return false;
}
public boolean navigateBackward() {
if (implementation != null)
return implementation.navigateBackward();
return false;
}
public boolean navigateHome() {
if (implementation != null)
return implementation.navigateHome();
return false;
}
/**
* Called when the IntroPart is disposed. Forwards the call to the implementation class.
*/
public void dispose() {
if (implementation != null)
implementation.dispose();
}
/**
* Support dynamic awarness. Clear cached models first, then update UI by delegating to
* implementation.
*
* @see org.eclipse.core.runtime.IRegistryChangeListener#registryChanged(org.eclipse.core.runtime.IRegistryChangeEvent)
*/
public void registryChanged(IRegistryChangeEvent event) {
if (implementation != null)
implementation.registryChanged(event);
}
/**
* @return Returns the homePageId.
*/
public String getHomePageId() {
return homePageId;
}
/**
* @return Returns the homePageId.
*/
public String getStandbyPageId() {
return standbyPageId;
}
@Override
public int getType() {
return AbstractIntroElement.PRESENTATION;
}
/**
* @return Returns the HTML head conttent to be added to each dynamic html page in this
* presentation..
*/
public IntroHead getHead() {
return head;
}
}