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.util.Deprecation;
  35 import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
  36 import com.sun.javafx.fxml.LoadListener;
  37 import java.io.ByteArrayInputStream;
  38 import java.io.IOException;
  39 import java.io.InputStream;
  40 import java.nio.charset.Charset;
  41 import javafx.fxml.FXMLLoader;
  42 
  43 
  44 /**
  45  *
  46  * 
  47  */
  48 class FXOMLoader implements LoadListener {
  49     
  50     private final FXOMDocument document;
  51     private TransientNode currentTransientNode;
  52     private GlueCursor glueCursor;
  53     
  54     /*
  55      * FXOMLoader
  56      */
  57     
  58     public FXOMLoader(FXOMDocument document) {
  59         assert document != null;
  60         assert document.getGlue().getRootElement() != null;
  61         this.document = document;
  62     }
  63     
  64     public void load(String fxmlText) throws java.io.IOException {
  65         assert fxmlText != null;
  66         
  67         final ClassLoader classLoader;
  68         if (document.getClassLoader() != null) {
  69             classLoader = document.getClassLoader();
  70         } else {
  71             classLoader = FXMLLoader.getDefaultClassLoader();
  72         }
  73         
  74         FXMLLoader fxmlLoader = new FXMLLoader();
  75         
  76         fxmlLoader.setLocation(document.getLocation());
  77         fxmlLoader.setResources(new ResourceKeyCollector(document.getResources()));
  78         fxmlLoader.setClassLoader(new TransientClassLoader(classLoader));
  79         fxmlLoader.setBuilderFactory(new FXOMBuilderFactory(classLoader));
  80         Deprecation.setStaticLoad(fxmlLoader, true);
  81         Deprecation.setLoadListener(fxmlLoader, this);
  82         
  83         final Charset utf8 = Charset.forName("UTF-8");
  84         try (final InputStream is = new ByteArrayInputStream(fxmlText.getBytes(utf8))) {
  85             glueCursor = new GlueCursor(document.getGlue());
  86             currentTransientNode = null;
  87             assert is.markSupported();
  88             is.reset();
  89             document.setSceneGraphRoot(fxmlLoader.load(is));
  90 //            assert document.isConsistent(); // TODO Eric - Returns true on Preview
  91         } catch(RuntimeException | IOException x) {
  92             throw new IOException(x);
  93         }
  94     }
  95     
  96     public FXOMDocument getDocument() {
  97         return document;
  98     }
  99     
 100     
 101     /*
 102      * LoadListener
 103      */
 104     
 105     @Override
 106     public void readImportProcessingInstruction(String data) {
 107     }
 108 
 109     @Override
 110     public void readLanguageProcessingInstruction(String data) {
 111     }
 112 
 113     @Override
 114     public void readComment(String string) {
 115     }
 116 
 117     @Override
 118     public void beginInstanceDeclarationElement(Class<?> declaredClass) {
 119         assert declaredClass != null;
 120         assert glueCursor.getCurrentElement().getTagName().equals(PropertyName.makeClassFullName(declaredClass)) ||
 121                glueCursor.getCurrentElement().getTagName().equals(declaredClass.getCanonicalName());
 122         
 123         final TransientObject transientInstance
 124                 = new TransientObject(currentTransientNode, 
 125                 declaredClass, glueCursor.getCurrentElement());
 126         
 127         currentTransientNode = transientInstance;
 128         glueCursor.moveToNextElement();
 129     }
 130 
 131     @Override
 132     public void beginUnknownTypeElement(String unknownClassName) {
 133         assert unknownClassName != null;
 134         assert glueCursor.getCurrentElement().getTagName().equals(unknownClassName);
 135         
 136         final TransientObject transientInstance
 137                 = new TransientObject(currentTransientNode, 
 138                 unknownClassName, glueCursor.getCurrentElement());
 139         
 140         currentTransientNode = transientInstance;
 141         glueCursor.moveToNextElement();
 142     }
 143 
 144     @Override
 145     public void beginIncludeElement() {
 146         assert glueCursor.getCurrentElement().getTagName().equals("fx:include");
 147         
 148         final TransientIntrinsic transientIntrinsic
 149                 = new TransientIntrinsic(currentTransientNode,
 150                 FXOMIntrinsic.Type.FX_INCLUDE, glueCursor.getCurrentElement());
 151         
 152         currentTransientNode = transientIntrinsic;
 153         glueCursor.moveToNextElement();
 154     }
 155     
 156     @Override
 157     public void beginReferenceElement() {
 158         assert glueCursor.getCurrentElement().getTagName().equals("fx:reference");
 159         
 160         final TransientIntrinsic transientIntrinsic
 161                 = new TransientIntrinsic(currentTransientNode,
 162                 FXOMIntrinsic.Type.FX_REFERENCE, glueCursor.getCurrentElement());
 163         
 164         currentTransientNode = transientIntrinsic;
 165         glueCursor.moveToNextElement();
 166     }
 167 
 168     @Override
 169     public void beginCopyElement() {
 170         assert glueCursor.getCurrentElement().getTagName().equals("fx:copy");
 171         
 172         final TransientIntrinsic transientIntrinsic
 173                 = new TransientIntrinsic(currentTransientNode,
 174                 FXOMIntrinsic.Type.FX_COPY, glueCursor.getCurrentElement());
 175         
 176         currentTransientNode = transientIntrinsic;
 177         glueCursor.moveToNextElement();
 178     }
 179 
 180     @Override
 181     public void beginRootElement() {
 182         assert glueCursor.getCurrentElement().getTagName().equals("fx:root");
 183         
 184         final TransientObject transientInstance
 185                 = new TransientObject(currentTransientNode, 
 186                 glueCursor.getCurrentElement());
 187         
 188         currentTransientNode = transientInstance;
 189         glueCursor.moveToNextElement();
 190     }
 191 
 192     @Override
 193     public void beginPropertyElement(String name, Class<?> staticClass) {
 194         assert name != null;
 195 
 196         final TransientProperty transientProperty
 197                 = new TransientProperty(currentTransientNode,
 198                     new PropertyName(name, staticClass), 
 199                     glueCursor.getCurrentElement());
 200         
 201         currentTransientNode = transientProperty;
 202         glueCursor.moveToNextElement();
 203     }
 204 
 205     @Override
 206     public void beginUnknownStaticPropertyElement(String string) {
 207         currentTransientNode = new TransientIgnored(currentTransientNode);
 208         glueCursor.moveToNextElement();
 209     }
 210 
 211     @Override
 212     public void beginScriptElement() {
 213         currentTransientNode = new TransientIgnored(currentTransientNode);
 214         glueCursor.moveToNextElement();
 215     }
 216 
 217     @Override
 218     public void beginDefineElement() {
 219         currentTransientNode = new TransientIgnored(currentTransientNode);
 220         glueCursor.moveToNextElement();
 221     }
 222 
 223     @Override
 224     public void readInternalAttribute(String attrName, String attrValue) {
 225         assert currentTransientNode instanceof TransientObject ||
 226                currentTransientNode instanceof TransientIntrinsic;
 227         
 228         if (attrName.equals("type")) {
 229             assert currentTransientNode instanceof TransientObject;
 230             final TransientObject transientObject = (TransientObject) currentTransientNode;
 231             transientObject.setFxRootType(attrValue);
 232         }
 233     }
 234 
 235     @Override
 236     public void readPropertyAttribute(String name, Class<?> staticClass, String fxmlValue) {
 237         assert currentTransientNode instanceof TransientObject
 238                 || currentTransientNode instanceof TransientIntrinsic
 239                 || currentTransientNode instanceof TransientProperty;
 240         
 241         assert name != null;
 242 
 243         final PropertyName pname = new PropertyName(name, staticClass);
 244         final FXOMPropertyT fxomProperty = new FXOMPropertyT(document, pname, null, null, fxmlValue);
 245 
 246         if (currentTransientNode instanceof TransientObject) {
 247             final TransientObject transientInstance = (TransientObject) currentTransientNode;
 248             transientInstance.getProperties().add(fxomProperty);
 249         } else if (currentTransientNode instanceof TransientProperty) {
 250             final TransientProperty transientProperty = (TransientProperty) currentTransientNode;
 251             transientProperty.getCollectedProperties().add(fxomProperty);
 252         } else {
 253             // TODO(elp): for now, we ignore properties declared in fx:include.
 254             // To be implemented later.
 255         }
 256     }
 257 
 258     @Override
 259     public void readUnknownStaticPropertyAttribute(String string, String string1) {
 260         // TODO(elp) : implement FXOMLoader.readUnknownStaticPropertyAttribute.
 261     }
 262 
 263     @Override
 264     public void readEventHandlerAttribute(String name, String hashStatement) {
 265         // Same as readPropertyAttribute() 
 266         readPropertyAttribute(name, null, hashStatement);
 267     }
 268 
 269     @Override
 270     public void endElement(Object sceneGraphObject) {
 271         
 272         currentTransientNode.setSceneGraphObject(sceneGraphObject);
 273         
 274         if (currentTransientNode instanceof TransientObject) {
 275             final TransientObject currentInstance = (TransientObject) currentTransientNode;
 276             final FXOMObject currentFxomObject = currentInstance.makeFxomObject(document);
 277             final TransientNode currentParent = currentInstance.getParentNode();
 278             if (currentParent instanceof TransientProperty) {
 279                 final TransientProperty parentProperty = (TransientProperty) currentParent;
 280                 parentProperty.getValues().add(currentFxomObject);
 281             } else if (currentParent instanceof TransientObject) {
 282                 final TransientObject parentInstance = (TransientObject) currentParent;
 283                 parentInstance.getCollectedItems().add(currentFxomObject);
 284             } else if (currentParent instanceof TransientIgnored) {
 285                 // currentObject is an object inside an fx:define section
 286                 // Nothing to do for now
 287             } else {
 288                 assert currentParent == null;
 289                 document.updateRoots(currentFxomObject, currentFxomObject.getSceneGraphObject());
 290             }
 291             
 292         } else if (currentTransientNode instanceof TransientIntrinsic) {
 293             final TransientIntrinsic currentIntrinsic = (TransientIntrinsic) currentTransientNode;
 294             final FXOMIntrinsic currentFxomIntrinsic = currentIntrinsic.makeFxomIntrinsic(document);
 295             final TransientNode currentParent = currentIntrinsic.getParentNode();
 296             
 297             if (currentParent instanceof TransientProperty) {
 298                 final TransientProperty parentProperty = (TransientProperty) currentParent;
 299                 parentProperty.getValues().add(currentFxomIntrinsic);
 300             } else if (currentParent instanceof TransientObject) {
 301                 final TransientObject parentInstance = (TransientObject) currentParent;
 302                 parentInstance.getCollectedItems().add(currentFxomIntrinsic);
 303             } else if (currentParent instanceof TransientIgnored) {
 304                 // currentObject is an object inside an fx:define section
 305                 // Nothing to do for now
 306             } else {
 307                 assert currentParent == null;
 308                 document.updateRoots(currentFxomIntrinsic, currentFxomIntrinsic.getSceneGraphObject());
 309             }
 310         } else if (currentTransientNode instanceof TransientProperty) {
 311             final TransientProperty currentProperty = (TransientProperty) currentTransientNode;
 312             final TransientNode currentParent = currentProperty.getParentNode();
 313             final FXOMProperty currentFxomProperty = currentProperty.makeFxomProperty(document);
 314             assert currentParent instanceof TransientObject;
 315             final TransientObject parentObject = (TransientObject) currentParent;
 316             parentObject.getProperties().add(currentFxomProperty);
 317             // We ignore sceneGraphObject
 318         } else {
 319                 assert currentTransientNode instanceof TransientIgnored;
 320                 // Nothing to do in this case
 321         }
 322         
 323         currentTransientNode = currentTransientNode.getParentNode();
 324     }
 325 }