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