FXMLFactory.groovy
001 /*
002  * SPDX-License-Identifier: Apache-2.0
003  *
004  * Copyright 2008-2017 the original author or authors.
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     http://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 package griffon.builder.javafx.factory
019 
020 import griffon.javafx.support.JavaFXUtils
021 import groovyx.javafx.factory.AbstractNodeFactory
022 import javafx.fxml.FXMLLoader
023 import javafx.scene.Group
024 import javafx.scene.Node
025 
026 /**
027  @author jimclarke
028  @author Andres Almiray
029  @since 2.4.0
030  */
031 class FXMLFactory extends AbstractNodeFactory {
032 
033     private FXMLLoader loader
034 
035     FXMLFactory() {
036         super(Node)
037     }
038 
039     FXMLFactory(Class<Node> beanClass) {
040         super(beanClass)
041     }
042 
043     public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributesthrows InstantiationException, IllegalAccessException {
044         Node result
045         if (value != null) {
046             result = processValue(value)
047             if (result == null)
048                 throw new Exception("In $name value must be an instanceof InputStream or one of its subclasses, java.net.URL, java.net.URI or a String  to be used as embedded content.")
049         else if (attributes.containsKey("location"|| attributes.containsKey("url")) {
050             def location = attributes.remove("location")
051             if (location == null) {
052                 location = attributes.remove("url")
053             }
054             if (location instanceof String)
055                 location = new URL(location)
056             result = loadInput(location)
057         else if (attributes.containsKey("uri")) {
058             def uri = attributes.remove("uri")
059             if (uri instanceof String)
060                 uri = new URI(uri)
061             result = loadInput(uri.toURL())
062         else if (attributes.containsKey("xml")) {
063             def xml = attributes.remove("xml")
064             result = loadXML(xml)
065         else if (attributes.containsKey("input")) {
066             def input = attributes.remove("input")
067             result = loadInput(input)
068         else // default case
069             result = new Group()
070         }
071 
072         return result
073     }
074 
075     private Node processValue(Object value) {
076         Node result = null
077         switch (value) {
078             case Node:
079                 result = value
080                 break
081             case CharSequence:
082                 try {
083                     URL url = new URL(value.toString())
084                     result = loadInput(url)
085                 catch (MalformedURLException mfe) {
086                     result = loadXML(value.toString())
087                 }
088                 break
089             case InputStream:
090                 result = loadInput(value)
091                 break
092             case URL:
093                 result = loadInput(value)
094                 break
095             case URI:
096                 result = loadInput(value.toURL())
097                 break
098         }
099         result
100     }
101 
102 
103     private Object loadXML(String xml) {
104         this.@loader new FXMLLoader()
105         def ins = new ByteArrayInputStream(xml.getBytes())
106         try {
107             return loader.load(ins)
108         finally {
109             ins.close()
110         }
111     }
112 
113     private Object loadInput(input) {
114         this.@loader new FXMLLoader()
115         return loader.load(input)
116     }
117 
118     @Override
119     public void setChild(FactoryBuilderSupport builder, Object parent, Object child) {
120         Node childNode = processValue(child)
121         if (childNode != null) {
122             parent.children.add(childNode)
123         else {
124             super.setChild(builder, parent, child)
125         }
126     }
127 
128     @Override
129     boolean onNodeChildren(FactoryBuilderSupport builder, Object node, Closure childContent) {
130         childContent.delegate = new FXMLDelegate(loader, node, childContent.delegate)
131         childContent.call()
132         return false
133     }
134 
135     @Override
136     boolean isHandlesNodeChildren() {
137         return true
138     }
139 }
140 
141 class FXMLDelegate {
142     FXMLDelegate(FXMLLoader loader, Node node, GroovyObject superObject) {
143         this.loader = loader
144         this.node = node
145         this.superObject = superObject
146     }
147 
148     private FXMLLoader loader
149     private Node node
150     private GroovyObject superObject
151 
152     @Override
153     def getProperty(String property) {
154         return this.@loader.namespace[property?: this.@node.lookup("#$property"?: JavaFXUtils.findElement(this.@node, property?: this.@superObject.getProperty(property)
155     }
156 }