1 /*******************************************************************************
2  * Copyright (c) 2004, 2017 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 
15 package org.eclipse.ui.internal.intro.impl.model;
16 
17 import org.eclipse.core.runtime.IConfigurationElement;
18 import org.eclipse.ui.internal.intro.impl.model.util.BundleUtil;
19 import org.osgi.framework.Bundle;
20 import org.w3c.dom.Element;
21 
22 /**
23  * An intro config component. All config components can get to their defining
24  * config element or bundle depending from where the element was loaded.
25  * <p>
26  * Class Rules:
27  * <ul>
28  * <li>If an element does not appear as a child under any node, then that
29  * element does not need a type to be defined.</li>
30  * <li>Each subclass must ensure that it properly supports cloning. This means
31  * that if a deep copy is needed, the subclass must override the base behavior
32  * here.</li>
33  * <li>if cloning is not needed, override clone method and throw an unsupported
34  * cloning exception. For now, only pages and targets of includes are cloneable.
35  * </li>
36  * </ul>
37  * <p>
38  * Note: This is an abstract base class for all classes in the Intro Model. <br>
39  * Clients are not expected to implement or subclass this class, or any of its
40  * subclasses.
41  */
42 public abstract class AbstractIntroElement implements Cloneable {
43 
44 	/**
45 	 * Type constant which identifies an IntroModelRoot element.
46 	 */
47 	public static final int MODEL_ROOT = 1;
48 
49 	/**
50 	 * Type constant which identifies an IntroPartPresentation element.
51 	 */
52 	public static final int PRESENTATION = 1 << 1;
53 
54 	/**
55 	 * Type constant which identifies an IntroHomePage element.
56 	 */
57 	public static final int HOME_PAGE = 1 << 2;
58 
59 	/**
60 	 * Type constant which identifies the IntroPage element.
61 	 */
62 	public static final int PAGE = 1 << 3;
63 
64 	/**
65 	 * Type constant which identifies the AbstractIntroPage element.
66 	 */
67 	public static final int ABSTRACT_PAGE = HOME_PAGE | PAGE;
68 
69 	/**
70 	 * Type constant which identifies an IntroDiv element.
71 	 */
72 	public static final int GROUP = 1 << 4;
73 
74 	/**
75 	 * Type constant which identifies the AbstractIntroContainer element.
76 	 */
77 	public static final int ABSTRACT_CONTAINER = ABSTRACT_PAGE | GROUP
78 			| MODEL_ROOT;
79 
80 	/**
81 	 * Type constant which identifies the IntroHtml element.
82 	 */
83 	public static final int HTML = 1 << 5;
84 
85 	/**
86 	 * Type constant which identifies the IntroLink element.
87 	 */
88 	public static final int LINK = 1 << 6;
89 
90 	/**
91 	 * Type constant which identifies the IntroImage element.
92 	 */
93 	public static final int IMAGE = 1 << 7;
94 
95 	/**
96 	 * Type constant which identifies the IntroInclude element.
97 	 */
98 	public static final int INCLUDE = 1 << 8;
99 
100 	/**
101 	 * Type constant which identifies the IntroText element.
102 	 */
103 	public static final int TEXT = 1 << 9;
104 
105 	/**
106 	 * Type constant which identifies the IntroContainerExtension element.
107 	 */
108 	public static final int CONTAINER_EXTENSION = 1 << 10;
109 
110 	/**
111 	 * Type constant which identifies the IntroHead element.
112 	 */
113 	public static final int HEAD = 1 << 11;
114 
115 	/**
116 	 * Type constant which identifies the IntroHead element.
117 	 */
118 	public static final int PAGE_TITLE = 1 << 12;
119 
120 	/**
121 	 * Type constant which identifies the IntroAnchor element.
122 	 */
123 	public static final int ANCHOR = 1 << 13;
124 
125 	/**
126 	 * Type constant which identifies the IntroContentProvider element.
127 	 */
128 	public static final int CONTENT_PROVIDER = 1 << 14;
129 
130 	/**
131 	 * Type constant which identifies the LaunchBarElement.
132 	 */
133 	public static final int LAUNCH_BAR = 1 << 15;
134 
135 	/**
136 	 * Type constant which identifies the launch bar shortcut.
137 	 */
138 	public static final int LAUNCH_BAR_SHORTCUT = 1 << 16;
139 
140 	/**
141 	 * Type constant which identifies am injected IFrame model element.
142 	 */
143 	public static final int INJECTED_IFRAME = 1 << 17;
144 
145 	/**
146 	 * Type constant for the theme element.
147 	 */
148 	public static final int THEME = 1 << 18;
149 
150 	/**
151 	 * Type constant for the hr element.
152 	 */
153 	public static final int HR = 1 << 19;
154 
155 
156 	/**
157 	 * Type constant which identifies the AbstractText element.
158 	 */
159 	public static final int ABSTRACT_TEXT = HTML | LINK | CONTENT_PROVIDER;
160 
161 	/**
162 	 * Type constant which identifies the AbstractCommonIntroElement element.
163 	 */
164 	public static final int BASE_ELEMENT = ABSTRACT_CONTAINER | ABSTRACT_TEXT
165 			| IMAGE | TEXT | PAGE_TITLE;
166 
167 	/**
168 	 * Type constant which identifies any element in the Intro Model which can
169 	 * have an id. Note: eventhough IntroStandbyContentPart has an id, it does
170 	 * not appear as a child in the model, and so it does not have a type.
171 	 */
172 	public static final int ID_ELEMENT = BASE_ELEMENT | ANCHOR;
173 
174 	/**
175 	 * Type constant which identifies any element in the Intro Model.
176 	 */
177 	public static final int ELEMENT = ID_ELEMENT | CONTAINER_EXTENSION | HEAD
178 			| INCLUDE | PRESENTATION | LAUNCH_BAR | LAUNCH_BAR_SHORTCUT;
179 
180 
181 
182 	private AbstractIntroElement parent;
183 	private Object cfgElement;
184 	private Bundle bundle;
185 	private String mixinStyle;
186 
187 
188 	/**
189 	 * Constructor used when model elements are being loaded from plugin.xml.
190 	 */
AbstractIntroElement(IConfigurationElement element)191 	AbstractIntroElement(IConfigurationElement element) {
192 		cfgElement = element;
193 		bundle = BundleUtil.getBundleFromConfigurationElement(element);
194 	}
195 
196 
197 	/**
198 	 * Constructor used when model elements are being loaded from an xml content
199 	 * file. Bundle is propagated down the model to enable resolving resources
200 	 * relative to the base of the bundle.
201 	 *
202 	 * @param element
203 	 * @param pd
204 	 */
AbstractIntroElement(Element element, Bundle bundle)205 	AbstractIntroElement(Element element, Bundle bundle) {
206 		this.cfgElement = element;
207 		this.bundle = bundle;
208 	}
209 
210 
211 	/**
212 	 * Constructor used when model elements are being loaded from an xml content
213 	 * file. Bundle AND base is propagated down the model to enable resolving
214 	 * resources relative to the xml content file. The base is set to point to
215 	 * the relative location of the parent folder that holds the content file.
216 	 * In the case of a configExtension, it is set to point to the relative
217 	 * position of the parent folder that holds the extension. Only when needed,
218 	 * the base field is stored in a model element. This saves memory.
219 	 *
220 	 * @param element
221 	 * @param pd
222 	 */
AbstractIntroElement(Element element, Bundle bundle, String base)223 	AbstractIntroElement(Element element, Bundle bundle, String base) {
224 		this(element, bundle);
225 	}
226 
227 
228 
229 
230 	/**
231 	 * Returns the configuration element from which this intro element was
232 	 * loaded. In the case of extension, returns the configuration element of
233 	 * the defining extension.
234 	 *
235 	 * @return
236 	 */
getCfgElement()237 	public IConfigurationElement getCfgElement() {
238 		return cfgElement instanceof IConfigurationElement?(IConfigurationElement)cfgElement:null;
239 	}
240 
getElement()241 	public Element getElement() {
242 		return cfgElement instanceof Element?(Element)cfgElement:null;
243 	}
244 
245 	/**
246 	 * DOM getAttribute retruns an empty string (not null) if attribute is not
247 	 * defined. Override this behavior to be consistent with Intro Model, and
248 	 * IConfiguration element.
249 	 *
250 	 * @param element
251 	 * @param att
252 	 * @return
253 	 */
getAttribute(Element element, String att)254 	protected String getAttribute(Element element, String att) {
255 		if (element.hasAttribute(att)) {
256 			String value = element.getAttribute(att);
257 			if (value!=null) {
258 				IntroModelRoot root = getModelRoot();
259 				if (root!=null)
260 					return root.resolveVariables(value);
261 				return value;
262 			}
263 		}
264 		return null;
265 	}
266 
267 	/**
268 	 * Util method to parse a comma separated list of values
269 	 *
270 	 * @param element
271 	 * @param att
272 	 * @return
273 	 */
getAttributeList(Element element, String att)274 	protected String[] getAttributeList(Element element, String att) {
275 		if (element.hasAttribute(att)) {
276 			String value = element.getAttribute(att);
277 			if (value!=null) {
278 				String[] splitValues = value.split(",");  //$NON-NLS-1$
279 				IntroModelRoot root = getModelRoot();
280 				if (root!=null) {
281 					for (int i = 0; i < splitValues.length; i++) {
282 						splitValues[i] = root.resolveVariables(splitValues[i]);
283 					}
284 				}
285 				return splitValues;
286 			}
287 		}
288 		/*
289 		if (element.hasAttribute(att))
290 			return element.getAttribute(att).split(","); //$NON-NLS-1$
291 			*/
292 		return null;
293 	}
294 
loadFromParent()295 	protected void loadFromParent() {
296 	}
297 
298 
299 	/**
300 	 * Returns the plugin descriptor of the plugin from which this intro element
301 	 * was loaded. In the case of extension, returns the plugin descriptor of
302 	 * the plugin defining the extension.
303 	 *
304 	 * @return
305 	 */
getBundle()306 	public Bundle getBundle() {
307 		return bundle;
308 	}
309 
310 
311 
312 	/**
313 	 * Returns the specific model type of this intro element. To be implemented
314 	 * by all subclasses.
315 	 *
316 	 * @return returns one of the model class types defined in this class.
317 	 */
getType()318 	public abstract int getType();
319 
320 
321 	/**
322 	 * Returns the parent of this intro element.
323 	 * <p>
324 	 * Rules:
325 	 * <ul>
326 	 * <li>For the model root, it retruns null.</li>
327 	 * <li>For the introPart presentation it returns a model root.</li>
328 	 * <li>For Pages, it returns an intro model root.</li>
329 	 * <li>For all other elements, it retruns a subclass of abstract container.
330 	 * </li>
331 	 * <li>for divs that are children of configs (shared divs), it returns the
332 	 * holding model root.</li>
333 	 * <li>for Head elements that are children of Implementation elements
334 	 * (shared Heads), it returns the holding presentation element.</li>
335 	 * </ul>
336 	 *
337 	 * @return returns the parent of this intro element. Null only for model
338 	 *         root.
339 	 */
getParent()340 	public AbstractIntroElement getParent() {
341 		return parent;
342 	}
343 
344 	/**
345 	 * @param parent
346 	 *            The parent to set.
347 	 */
setParent(AbstractIntroElement parent)348 	public void setParent(AbstractIntroElement parent) {
349 		this.parent = parent;
350 		if (parent!=null)
351 			loadFromParent();
352 	}
353 
setBundle(Bundle bundle)354 	public void setBundle(Bundle bundle) {
355 		this.bundle = bundle;
356 	}
357 
358 	/**
359 	 * Returns the parent page holding this intro element. For the model root
360 	 * and the introPart presentation it returns null. For Pages, it returns the
361 	 * page itself. For all other element, returns the holding page.
362 	 * <p>
363 	 * Exceptions:
364 	 * <ul>
365 	 * <li>for divs that are children of configs (shared divs), it returns
366 	 * null.</li>
367 	 * <li>for Head elements that are children of Implementation elements
368 	 * (shared Heads), it returns null.</li>
369 	 * </ul>
370 	 */
getParentPage()371 	public AbstractIntroPage getParentPage() {
372 		// return yourself if you are a page.
373 		if (isOfType(AbstractIntroElement.ABSTRACT_PAGE))
374 			return (AbstractIntroPage) this;
375 
376 		AbstractIntroElement parent = getParent();
377 		if (parent == null)
378 			return null;
379 
380 		while (parent != null && parent.getParent() != null
381 				&& !parent.isOfType(AbstractIntroElement.ABSTRACT_PAGE))
382 			parent = parent.getParent();
383 		if (parent.isOfType(ABSTRACT_PAGE))
384 			return (AbstractIntroPage) parent;
385 		return null;
386 	}
387 
getModelRoot()388 	public IntroModelRoot getModelRoot() {
389 		// return yourself if you are a model root.
390 		if (isOfType(AbstractIntroElement.MODEL_ROOT))
391 			return (IntroModelRoot) this;
392 
393 		AbstractIntroElement parent = getParent();
394 		if (parent == null)
395 			return null;
396 
397 		while (parent != null && parent.getParent() != null
398 				&& !parent.isOfType(AbstractIntroElement.MODEL_ROOT))
399 			parent = parent.getParent();
400 		if (parent.isOfType(MODEL_ROOT))
401 			return (IntroModelRoot) parent;
402 		return null;
403 	}
404 
405 
406 	/**
407 	 * Returns whether the element is among the specified element types. An
408 	 * example of an element mask is as follows:
409 	 * <p>
410 	 * <code>
411 	 *  	int elementMask = IntroElement.ABSTRACT_CONTAINER;
412 	 * 		int elementMask = IntroElement.DIV | IntroElement.DEFAULT_LINK;
413 	 * </code>
414 	 *
415 	 * @param elementMask
416 	 *            element mask formed by bitwise OR of element type constants
417 	 *            defined in this class.
418 	 * @return <code>true</code> if this element has a matching type, and
419 	 *         <code>false</code> otherwise.
420 	 */
isOfType(int elementMask)421 	public boolean isOfType(int elementMask) {
422 		return (getType() & elementMask) != 0;
423 	}
424 
425 	/**
426 	 * Returns whether the types of all the elements in the given array are
427 	 * among the specified element types. <br>
428 	 * An example of an element mask is as follows:
429 	 * <p>
430 	 * <code>
431 	 * int elementMask = IntroElement.DIV | IntroElement.DEFAULT_LINK;
432 	 * </code>
433 	 *
434 	 * @return <code>true</code> if all elements are of the right type, and
435 	 *         <code>false</code> if the list is empty, or at least one
436 	 *         element is not of the specified types.
437 	 */
allElementsAreOfType( AbstractIntroElement[] elements, int elementMask)438 	public static final boolean allElementsAreOfType(
439 			AbstractIntroElement[] elements, int elementMask) {
440 		// if we have an empty list, no point going on.
441 		if (elements.length == 0)
442 			return false;
443 
444 		for (int i = 0; i < elements.length; i++) {
445 			AbstractIntroElement element = elements[i];
446 			if (!element.isOfType(elementMask))
447 				return false;
448 		}
449 		return true;
450 	}
451 
452 	/**
453 	 * Shallow copy. The design of cloning this model assumes that when a
454 	 * container is cloned, all its children must be cloned and reparented to
455 	 * it, hence one clone of this container object. This is why we have a
456 	 * shallow copy here.
457 	 */
458 	@Override
clone()459 	public Object clone() throws CloneNotSupportedException {
460 		return super.clone();
461 	}
462 
463 
464 
getMixinStyle()465 	public String getMixinStyle() {
466 		return mixinStyle;
467 	}
468 
469 
470 
setMixinStyle(String mixinStyle)471 	public void setMixinStyle(String mixinStyle) {
472 		this.mixinStyle = mixinStyle;
473 	}
474 
475 
476 
477 }