1 /*
   2  * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 package com.oracle.javafx.scenebuilder.kit.fxom;
  33 
  34 import com.oracle.javafx.scenebuilder.kit.fxom.glue.GlueElement;
  35 import com.oracle.javafx.scenebuilder.kit.metadata.util.PrefixedValue;
  36 import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
  37 import com.oracle.javafx.scenebuilder.kit.util.JavaLanguage;
  38 import java.net.URL;
  39 import java.util.ArrayList;
  40 import java.util.Collections;
  41 import java.util.Iterator;
  42 import java.util.LinkedHashMap;
  43 import java.util.List;
  44 import java.util.Map;
  45 import java.util.Set;
  46 import javafx.fxml.FXMLLoader;
  47 
  48 /**
  49  *
  50  *
  51  */
  52 public class FXOMInstance extends FXOMObject {
  53 
  54     private final Map<PropertyName, FXOMProperty> properties = new LinkedHashMap<>();
  55     private Class<?> declaredClass;
  56 
  57 
  58     FXOMInstance(
  59             FXOMDocument fxomDocument,
  60             GlueElement glueElement,
  61             Class<?> declaredClass,
  62             Object sceneGraphObject,
  63             List<FXOMProperty> properties) {
  64         super(fxomDocument, glueElement, sceneGraphObject);
  65 
  66         assert declaredClass != null;
  67         assert glueElement.getTagName().equals("fx:root")
  68                 || glueElement.getTagName().equals(PropertyName.makeClassFullName(declaredClass))
  69                 || glueElement.getTagName().equals(declaredClass.getCanonicalName());
  70         assert sceneGraphObject != null;
  71         assert properties != null;
  72 
  73         this.declaredClass = declaredClass;
  74         for (FXOMProperty p : properties) {
  75             this.properties.put(p.getName(), p);
  76             p.setParentInstance(this);
  77         }
  78     }
  79 
  80     FXOMInstance(
  81             FXOMDocument fxomDocument,
  82             GlueElement glueElement,
  83             List<FXOMProperty> properties) {
  84         super(fxomDocument, glueElement, null);
  85 
  86         assert properties != null;
  87 
  88         this.declaredClass = null;
  89         for (FXOMProperty p : properties) {
  90             this.properties.put(p.getName(), p);
  91             p.setParentInstance(this);
  92         }
  93     }
  94 
  95     public FXOMInstance(FXOMDocument fxomDocument, Class<?> declaredClass) {
  96         super(fxomDocument, PropertyName.makeClassFullName(declaredClass));
  97         this.declaredClass = declaredClass;
  98     }
  99 
 100     public FXOMInstance(FXOMDocument fxomDocument, String tagName) {
 101         super(fxomDocument, tagName);
 102         this.declaredClass = null; // This is an unresolved instance
 103     }
 104 
 105     public Class<?> getDeclaredClass() {
 106         return declaredClass;
 107     }
 108 
 109     public void setDeclaredClass(Class<?> declaredClass) {
 110         this.declaredClass = declaredClass;
 111     }
 112 
 113     public Map<PropertyName, FXOMProperty> getProperties() {
 114         return Collections.unmodifiableMap(properties);
 115     }
 116 
 117     public boolean isFxRoot() {
 118         return getGlueElement().getTagName().equals("fx:root");
 119     }
 120 
 121     public void toggleFxRoot() {
 122 
 123         if (isFxRoot()) {
 124             assert getType() != null;
 125             getGlueElement().setTagName(getType());
 126             getGlueElement().getAttributes().remove(FXMLLoader.ROOT_TYPE_ATTRIBUTE);
 127         } else {
 128             assert getType() == null;
 129             getGlueElement().getAttributes().put(FXMLLoader.ROOT_TYPE_ATTRIBUTE, getGlueElement().getTagName());
 130             getGlueElement().setTagName("fx:root");
 131         }
 132     }
 133 
 134     public String getType() {
 135         return getGlueElement().getAttributes().get(FXMLLoader.ROOT_TYPE_ATTRIBUTE);
 136     }
 137 
 138     /*
 139      * FXOMObject
 140      */
 141 
 142     @Override
 143     public void addToParentCollection(int index, FXOMCollection newParentCollection) {
 144         super.addToParentCollection(index, newParentCollection);
 145 
 146         // May be this object was root : fx:root, type properties must be reset.
 147         resetRootProperties();
 148     }
 149 
 150     @Override
 151     public void addToParentProperty(int index, FXOMPropertyC newParentProperty) {
 152         super.addToParentProperty(index, newParentProperty); //To change body of generated methods, choose Tools | Templates.
 153 
 154         // May be this object was root : fx:root, type properties must be reset.
 155         resetRootProperties();
 156     }
 157 
 158 
 159     @Override
 160     public List<FXOMObject> getChildObjects() {
 161         final List<FXOMObject> result = new ArrayList<>();
 162 
 163         for (FXOMProperty p : properties.values()) {
 164             if (p instanceof FXOMPropertyC) {
 165                 final FXOMPropertyC pc = (FXOMPropertyC) p;
 166                 result.addAll(pc.getValues());
 167             }
 168         }
 169         return result;
 170     }
 171 
 172 
 173     @Override
 174     public FXOMObject searchWithSceneGraphObject(Object sceneGraphObject) {
 175         FXOMObject result;
 176 
 177         result = super.searchWithSceneGraphObject(sceneGraphObject);
 178         if (result == null) {
 179             final Iterator<FXOMProperty> it = properties.values().iterator();
 180             while ((result == null) && it.hasNext()) {
 181                 final FXOMProperty property = it.next();
 182                 if (property instanceof FXOMPropertyC) {
 183                     FXOMPropertyC propertyC = (FXOMPropertyC) property;
 184                     final Iterator<FXOMObject> itValue = propertyC.getValues().iterator();
 185                     while ((result == null) && itValue.hasNext()) {
 186                         final FXOMObject value = itValue.next();
 187                         result = value.searchWithSceneGraphObject(sceneGraphObject);
 188                     }
 189                 }
 190             }
 191         }
 192 
 193         return result;
 194     }
 195 
 196     @Override
 197     public FXOMObject searchWithFxId(String fxId) {
 198         FXOMObject result;
 199 
 200         result = super.searchWithFxId(fxId);
 201         if (result == null) {
 202             final Iterator<FXOMProperty> it = properties.values().iterator();
 203             while ((result == null) && it.hasNext()) {
 204                 final FXOMProperty property = it.next();
 205                 if (property instanceof FXOMPropertyC) {
 206                     FXOMPropertyC propertyC = (FXOMPropertyC) property;
 207                     final Iterator<FXOMObject> itValue = propertyC.getValues().iterator();
 208                     while ((result == null) && itValue.hasNext()) {
 209                         final FXOMObject value = itValue.next();
 210                         result = value.searchWithFxId(fxId);
 211                     }
 212                 }
 213             }
 214         }
 215 
 216         return result;
 217     }
 218 
 219     @Override
 220     protected void collectDeclaredClasses(Set<Class<?>> result) {
 221         assert result != null;
 222 
 223         if (declaredClass != null) {
 224             result.add(declaredClass);
 225         }
 226 
 227         for (FXOMProperty p : properties.values()) {
 228             if (p instanceof FXOMPropertyC) {
 229                 for (FXOMObject v : ((FXOMPropertyC)p).getValues()) {
 230                     v.collectDeclaredClasses(result);
 231                 }
 232             }
 233         }
 234     }
 235 
 236     @Override
 237     protected void collectProperties(PropertyName propertyName, List<FXOMProperty> result) {
 238         assert propertyName != null;
 239         assert result != null;
 240 
 241         for (FXOMProperty p : properties.values()) {
 242             if (p.getName().equals(propertyName)) {
 243                 result.add(p);
 244             }
 245             if (p instanceof FXOMPropertyC) {
 246                 for (FXOMObject v : ((FXOMPropertyC)p).getValues()) {
 247                     v.collectProperties(propertyName, result);
 248                 }
 249             }
 250         }
 251     }
 252 
 253     @Override
 254     protected void collectNullProperties(List<FXOMPropertyT> result) {
 255         assert result != null;
 256 
 257         for (FXOMProperty p : properties.values()) {
 258             if (p instanceof FXOMPropertyT) {
 259                 final FXOMPropertyT tp = (FXOMPropertyT) p;
 260                 if (tp.getValue().equals("$null")) {
 261                     result.add(tp);
 262                 }
 263             } else {
 264                 assert p instanceof FXOMPropertyC;
 265                 for (FXOMObject v : ((FXOMPropertyC)p).getValues()) {
 266                     v.collectNullProperties(result);
 267                 }
 268             }
 269         }
 270     }
 271 
 272     @Override
 273     protected void collectPropertiesT(List<FXOMPropertyT> result) {
 274         assert result != null;
 275 
 276         for (FXOMProperty p : properties.values()) {
 277             if (p instanceof FXOMPropertyT) {
 278                 final FXOMPropertyT tp = (FXOMPropertyT) p;
 279                 result.add(tp);
 280             } else {
 281                 assert p instanceof FXOMPropertyC;
 282                 for (FXOMObject v : ((FXOMPropertyC)p).getValues()) {
 283                     v.collectPropertiesT(result);
 284                 }
 285             }
 286         }
 287     }
 288 
 289     @Override
 290     protected void collectReferences(String source, List<FXOMIntrinsic> result) {
 291         for (FXOMProperty p : properties.values()) {
 292             if (p instanceof FXOMPropertyC) {
 293                 for (FXOMObject v : ((FXOMPropertyC)p).getValues()) {
 294                     v.collectReferences(source, result);
 295                 }
 296             }
 297         }
 298     }
 299 
 300     @Override
 301     protected void collectReferences(String source, FXOMObject scope, List<FXOMNode> result) {
 302         if ((scope == null) || (scope != this)) {
 303             for (FXOMProperty p : properties.values()) {
 304                 if (p instanceof FXOMPropertyC) {
 305                     for (FXOMObject v : ((FXOMPropertyC)p).getValues()) {
 306                         v.collectReferences(source, scope, result);
 307                     }
 308                 } else if (p instanceof FXOMPropertyT) {
 309                     final FXOMPropertyT pt = (FXOMPropertyT) p;
 310                     final PrefixedValue pv = new PrefixedValue(pt.getValue());
 311                     if (pv.isExpression()) {
 312                         final String suffix = pv.getSuffix();
 313                         if (JavaLanguage.isIdentifier(suffix)) {
 314                             if ((source == null) || source.equals(suffix)) {
 315                                 result.add(pt);
 316                             }
 317                         }
 318                     }
 319                 }
 320             }
 321         }
 322     }
 323 
 324     @Override
 325     protected void collectIncludes(String source, List<FXOMIntrinsic> result) {
 326         for (FXOMProperty p : properties.values()) {
 327             if (p instanceof FXOMPropertyC) {
 328                 for (FXOMObject v : ((FXOMPropertyC)p).getValues()) {
 329                     v.collectIncludes(source, result);
 330                 }
 331             }
 332         }
 333     }
 334 
 335     @Override
 336     protected void collectFxIds(Map<String, FXOMObject> result) {
 337         final String fxId = getFxId();
 338         if (fxId != null) {
 339             result.put(fxId, this);
 340         }
 341 
 342         for (FXOMProperty p : properties.values()) {
 343             if (p instanceof FXOMPropertyC) {
 344                 for (FXOMObject v : ((FXOMPropertyC)p).getValues()) {
 345                     v.collectFxIds(result);
 346                 }
 347             }
 348         }
 349     }
 350 
 351     @Override
 352     protected void collectObjectWithSceneGraphObjectClass(Class<?> sceneGraphObjectClass, List<FXOMObject> result) {
 353         if (getSceneGraphObject() != null) {
 354             if (getSceneGraphObject().getClass() == sceneGraphObjectClass) {
 355                 result.add(this);
 356             }
 357             for (FXOMProperty p : properties.values()) {
 358                 if (p instanceof FXOMPropertyC) {
 359                     for (FXOMObject v : ((FXOMPropertyC)p).getValues()) {
 360                         v.collectObjectWithSceneGraphObjectClass(sceneGraphObjectClass, result);
 361                     }
 362                 }
 363             }
 364         }
 365     }
 366 
 367     @Override
 368     protected void collectEventHandlers(List<FXOMPropertyT> result) {
 369         if (getSceneGraphObject() != null) {
 370             for (FXOMProperty p : properties.values()) {
 371                 if (p instanceof FXOMPropertyT) {
 372                     final FXOMPropertyT pt = (FXOMPropertyT) p;
 373                     if (pt.getName().getName().startsWith("on") && pt.getValue().startsWith("#")) {
 374                         result.add(pt);
 375                     }
 376                 }
 377             }
 378             for (FXOMProperty p : properties.values()) {
 379                 if (p instanceof FXOMPropertyC) {
 380                     for (FXOMObject v : ((FXOMPropertyC)p).getValues()) {
 381                         v.collectEventHandlers(result);
 382                     }
 383                 }
 384             }
 385         }
 386     }
 387 
 388     /*
 389      * FXOMNode
 390      */
 391 
 392     @Override
 393     protected void changeFxomDocument(FXOMDocument destination) {
 394 
 395         super.changeFxomDocument(destination);
 396         for (FXOMProperty p : properties.values()) {
 397             p.changeFxomDocument(destination);
 398         }
 399     }
 400 
 401     @Override
 402     public void documentLocationWillChange(URL newLocation) {
 403         for (FXOMProperty p : properties.values()) {
 404             p.documentLocationWillChange(newLocation);
 405         }
 406     }
 407 
 408 
 409     /*
 410      * Package
 411      */
 412 
 413     /* For FXOMProperty.addToParentInstance() private use only */
 414     void addProperty(FXOMProperty property) {
 415         assert property.getParentInstance() == this;
 416         assert properties.get(property.getName()) == null;
 417         properties.put(property.getName(), property);
 418     }
 419 
 420     /* For FXOMProperty.removeFromParentInstance() private use only */
 421     void removeProperty(FXOMProperty property) {
 422         assert property.getParentInstance() == null;
 423         assert properties.get(property.getName()) == property;
 424         properties.remove(property.getName());
 425 
 426     }
 427 
 428 
 429     /*
 430      * Private
 431      */
 432 
 433     private void resetRootProperties() {
 434         if (isFxRoot()) {
 435             toggleFxRoot();
 436         }
 437     }
 438 }