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 }