blob: 13adf8cf79ed65deb42f0b619cf74d45eb9eb2bb [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 org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.ui.internal.intro.impl.model.util.BundleUtil;
import org.eclipse.ui.internal.intro.impl.util.StringUtil;
import org.osgi.framework.Bundle;
import org.w3c.dom.Element;
/**
* An intro config component. All config components can get to their defining
* config element or bundle depending from where the element was loaded.
* <p>
* Class Rules:
* <ul>
* <li>If an element does not appear as a child under any node, then that
* element does not need a type to be defined.</li>
* <li>Each subclass must ensure that it properly supports cloning. This means
* that if a deep copy is needed, the subclass must override the base behavior
* here.</li>
* <li>if cloning is not needed, override clone method and throw an unsupported
* cloning exception. For now, only pages and targets of includes are cloneable.
* </li>
* </ul>
* <p>
* Note: This is an abstract base class for all classes in the Intro Model. <br>
* Clients are not expected to implement or subclass this class, or any of its
* subclasses.
*/
public abstract class AbstractIntroElement implements Cloneable {
/**
* Type constant which identifies an IntroModelRoot element.
*/
public static final int MODEL_ROOT = 1;
/**
* Type constant which identifies an IntroPartPresentation element.
*/
public static final int PRESENTATION = 1 << 1;
/**
* Type constant which identifies an IntroHomePage element.
*/
public static final int HOME_PAGE = 1 << 2;
/**
* Type constant which identifies the IntroPage element.
*/
public static final int PAGE = 1 << 3;
/**
* Type constant which identifies the AbstractIntroPage element.
*/
public static final int ABSTRACT_PAGE = HOME_PAGE | PAGE;
/**
* Type constant which identifies an IntroDiv element.
*/
public static final int GROUP = 1 << 4;
/**
* Type constant which identifies the AbstractIntroContainer element.
*/
public static final int ABSTRACT_CONTAINER = ABSTRACT_PAGE | GROUP
| MODEL_ROOT;
/**
* Type constant which identifies the IntroHtml element.
*/
public static final int HTML = 1 << 5;
/**
* Type constant which identifies the IntroLink element.
*/
public static final int LINK = 1 << 6;
/**
* Type constant which identifies the IntroImage element.
*/
public static final int IMAGE = 1 << 7;
/**
* Type constant which identifies the IntroInclude element.
*/
public static final int INCLUDE = 1 << 8;
/**
* Type constant which identifies the IntroText element.
*/
public static final int TEXT = 1 << 9;
/**
* Type constant which identifies the IntroContainerExtension element.
*/
public static final int CONTAINER_EXTENSION = 1 << 10;
/**
* Type constant which identifies the IntroHead element.
*/
public static final int HEAD = 1 << 11;
/**
* Type constant which identifies the IntroHead element.
*/
public static final int PAGE_TITLE = 1 << 12;
/**
* Type constant which identifies the IntroAnchor element.
*/
public static final int ANCHOR = 1 << 13;
/**
* Type constant which identifies the IntroContentProvider element.
*/
public static final int CONTENT_PROVIDER = 1 << 14;
/**
* Type constant which identifies the LaunchBarElement.
*/
public static final int LAUNCH_BAR = 1 << 15;
/**
* Type constant which identifies the launch bar shortcut.
*/
public static final int LAUNCH_BAR_SHORTCUT = 1 << 16;
/**
* Type constant which identifies am injected IFrame model element.
*/
public static final int INJECTED_IFRAME = 1 << 17;
/**
* Type constant for the theme element.
*/
public static final int THEME = 1 << 18;
/**
* Type constant for the hr element.
*/
public static final int HR = 1 << 19;
/**
* Type constant which identifies the AbstractText element.
*/
public static final int ABSTRACT_TEXT = HTML | LINK | CONTENT_PROVIDER;
/**
* Type constant which identifies the AbstractCommonIntroElement element.
*/
public static final int BASE_ELEMENT = ABSTRACT_CONTAINER | ABSTRACT_TEXT
| IMAGE | TEXT | PAGE_TITLE;
/**
* Type constant which identifies any element in the Intro Model which can
* have an id. Note: eventhough IntroStandbyContentPart has an id, it does
* not appear as a child in the model, and so it does not have a type.
*/
public static final int ID_ELEMENT = BASE_ELEMENT | ANCHOR;
/**
* Type constant which identifies any element in the Intro Model.
*/
public static final int ELEMENT = ID_ELEMENT | CONTAINER_EXTENSION | HEAD
| INCLUDE | PRESENTATION | LAUNCH_BAR | LAUNCH_BAR_SHORTCUT;
private AbstractIntroElement parent;
private Object cfgElement;
private Bundle bundle;
private String mixinStyle;
/**
* Constructor used when model elements are being loaded from plugin.xml.
*/
AbstractIntroElement(IConfigurationElement element) {
cfgElement = element;
bundle = BundleUtil.getBundleFromConfigurationElement(element);
}
/**
* Constructor used when model elements are being loaded from an xml content
* file. Bundle is propagated down the model to enable resolving resources
* relative to the base of the bundle.
*
* @param element
* @param pd
*/
AbstractIntroElement(Element element, Bundle bundle) {
this.cfgElement = element;
this.bundle = bundle;
}
/**
* Constructor used when model elements are being loaded from an xml content
* file. Bundle AND base is propagated down the model to enable resolving
* resources relative to the xml content file. The base is set to point to
* the relative location of the parent folder that holds the content file.
* In the case of a configExtension, it is set to point to the relative
* position of the parent folder that holds the extension. Only when needed,
* the base field is stored in a model element. This saves memory.
*
* @param element
* @param pd
*/
AbstractIntroElement(Element element, Bundle bundle, String base) {
this(element, bundle);
}
/**
* Returns the configuration element from which this intro element was
* loaded. In the case of extension, returns the configuration element of
* the defining extension.
*
* @return
*/
public IConfigurationElement getCfgElement() {
return cfgElement instanceof IConfigurationElement?(IConfigurationElement)cfgElement:null;
}
public Element getElement() {
return cfgElement instanceof Element?(Element)cfgElement:null;
}
/**
* DOM getAttribute retruns an empty string (not null) if attribute is not
* defined. Override this behavior to be consistent with Intro Model, and
* IConfiguration element.
*
* @param element
* @param att
* @return
*/
protected String getAttribute(Element element, String att) {
if (element.hasAttribute(att)) {
String value = element.getAttribute(att);
if (value!=null) {
IntroModelRoot root = getModelRoot();
if (root!=null)
return root.resolveVariables(value);
return value;
}
}
return null;
}
/**
* Util method to parse a comma separated list of values
*
* @param element
* @param att
* @return
*/
protected String[] getAttributeList(Element element, String att) {
if (element.hasAttribute(att)) {
String value = element.getAttribute(att);
if (value!=null) {
String[] splitValues = StringUtil.split(value, ","); //$NON-NLS-1$
IntroModelRoot root = getModelRoot();
if (root!=null) {
for (int i = 0; i < splitValues.length; i++) {
splitValues[i] = root.resolveVariables(splitValues[i]);
}
}
return splitValues;
}
}
/*
if (element.hasAttribute(att))
return element.getAttribute(att).split(","); //$NON-NLS-1$
*/
return null;
}
protected void loadFromParent() {
}
/**
* Returns the plugin descriptor of the plugin from which this intro element
* was loaded. In the case of extension, returns the plugin descriptor of
* the plugin defining the extension.
*
* @return
*/
public Bundle getBundle() {
return bundle;
}
/**
* Returns the specific model type of this intro element. To be implemented
* by all subclasses.
*
* @return returns one of the model class types defined in this class.
*/
public abstract int getType();
/**
* Returns the parent of this intro element.
* <p>
* Rules:
* <ul>
* <li>For the model root, it retruns null.</li>
* <li>For the introPart presentation it returns a model root.</li>
* <li>For Pages, it returns an intro model root.</li>
* <li>For all other elements, it retruns a subclass of abstract container.
* </li>
* <li>for divs that are children of configs (shared divs), it returns the
* holding model root.</li>
* <li>for Head elements that are children of Implementation elements
* (shared Heads), it returns the holding presentation element.</li>
* </ul>
*
* @return returns the parent of this intro element. Null only for model
* root.
*/
public AbstractIntroElement getParent() {
return parent;
}
/**
* @param parent
* The parent to set.
*/
public void setParent(AbstractIntroElement parent) {
this.parent = parent;
if (parent!=null)
loadFromParent();
}
public void setBundle(Bundle bundle) {
this.bundle = bundle;
}
/**
* Returns the parent page holding this intro element. For the model root
* and the introPart presentation it returns null. For Pages, it returns the
* page itself. For all other element, returns the holding page.
* <p>
* Exceptions:
* <ul>
* <li>for divs that are children of configs (shared divs), it returns
* null.</li>
* <li>for Head elements that are children of Implementation elements
* (shared Heads), it returns null.</li>
* </ul>
*/
public AbstractIntroPage getParentPage() {
// return yourself if you are a page.
if (isOfType(AbstractIntroElement.ABSTRACT_PAGE))
return (AbstractIntroPage) this;
AbstractIntroElement parent = getParent();
if (parent == null)
return null;
while (parent != null && parent.getParent() != null
&& !parent.isOfType(AbstractIntroElement.ABSTRACT_PAGE))
parent = parent.getParent();
if (parent.isOfType(ABSTRACT_PAGE))
return (AbstractIntroPage) parent;
return null;
}
public IntroModelRoot getModelRoot() {
// return yourself if you are a model root.
if (isOfType(AbstractIntroElement.MODEL_ROOT))
return (IntroModelRoot) this;
AbstractIntroElement parent = getParent();
if (parent == null)
return null;
while (parent != null && parent.getParent() != null
&& !parent.isOfType(AbstractIntroElement.MODEL_ROOT))
parent = parent.getParent();
if (parent.isOfType(MODEL_ROOT))
return (IntroModelRoot) parent;
return null;
}
/**
* Returns whether the element is among the specified element types. An
* example of an element mask is as follows:
* <p>
* <code>
* int elementMask = IntroElement.ABSTRACT_CONTAINER;
* int elementMask = IntroElement.DIV | IntroElement.DEFAULT_LINK;
* </code>
*
* @param elementMask
* element mask formed by bitwise OR of element type constants
* defined in this class.
* @return <code>true</code> if this element has a matching type, and
* <code>false</code> otherwise.
*/
public boolean isOfType(int elementMask) {
return (getType() & elementMask) != 0;
}
/**
* Returns whether the types of all the elements in the given array are
* among the specified element types. <br>
* An example of an element mask is as follows:
* <p>
* <code>
* int elementMask = IntroElement.DIV | IntroElement.DEFAULT_LINK;
* </code>
*
* @return <code>true</code> if all elements are of the right type, and
* <code>false</code> if the list is empty, or at least one
* element is not of the specified types.
*/
public static final boolean allElementsAreOfType(
AbstractIntroElement[] elements, int elementMask) {
// if we have an empty list, no point going on.
if (elements.length == 0)
return false;
for (int i = 0; i < elements.length; i++) {
AbstractIntroElement element = elements[i];
if (!element.isOfType(elementMask))
return false;
}
return true;
}
/**
* Shallow copy. The design of cloning this model assumes that when a
* container is cloned, all its children must be cloned and reparented to
* it, hence one clone of this container object. This is why we have a
* shallow copy here.
*/
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getMixinStyle() {
return mixinStyle;
}
public void setMixinStyle(String mixinStyle) {
this.mixinStyle = mixinStyle;
}
}