FXMetaComponentFactory.groovy
001 /*
002  * Copyright 2008-2017 the original author or authors.
003  *
004  * Licensed under the Apache License, Version 2.0 (the "License");
005  * you may not use this file except in compliance with the License.
006  * You may obtain a copy of the License at
007  *
008  *     http://www.apache.org/licenses/LICENSE-2.0
009  *
010  * Unless required by applicable law or agreed to in writing, software
011  * distributed under the License is distributed on an "AS IS" BASIS,
012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  * See the License for the specific language governing permissions and
014  * limitations under the License.
015  */
016 package griffon.builder.javafx.factory
017 
018 import griffon.core.ApplicationEvent
019 import griffon.core.GriffonApplication
020 import griffon.core.RunnableWithArgs
021 import griffon.core.mvc.MVCGroup
022 import griffon.core.mvc.MVCGroupManager
023 import groovyx.javafx.factory.AbstractNodeFactory
024 import javafx.scene.Node
025 
026 import javax.annotation.Nullable
027 
028 import static org.codehaus.griffon.runtime.groovy.mvc.GroovyAwareMVCGroup.CURRENT_MVCGROUP
029 
030 /**
031  * Enables MVC groups to be used as component nodes
032  *
033  @author Andres Almiray
034  @author Alexander Klein
035  @since 2.12.0
036  */
037 @SuppressWarnings("rawtypes")
038 class FXMetaComponentFactory extends AbstractNodeFactory {
039     FXMetaComponentFactory() {
040         super(Node, false)
041     }
042 
043     @Override
044     Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) {
045         Map attrs = resolveAttributes(attributes)
046         String mvcType = resolveMvcType(name, value, attrs)
047         String mvcId = resolveMvcId(builder, name, value, attrs)
048         Map mvcArgs = resolveMvcArgs(attrs)
049         Map mvcArgsCopy = [*: mvcArgs]
050         attributes.clear()
051         attributes.putAll(attrs)
052 
053         MVCGroup parentGroup = builder.getVariables().get(CURRENT_MVCGROUP)
054         def receiver = parentGroup ?: builder.application.mvcGroupManager
055         MVCGroup mvcGroup = receiver.createMVCGroup(mvcType, mvcId, mvcArgs)
056         def root = mvcGroup.rootNode
057 
058         new DestroyEventHandler(mvcId, mvcGroup, builder.application)
059 
060         builder.context.root = root
061         builder.context.mvcGroup = mvcGroup
062         builder.context.mvcArgs = mvcArgsCopy
063         root
064     }
065 
066     protected Map resolveAttributes(Map attributes) {
067         [*: attributes]
068     }
069 
070     protected String resolveMvcType(Object name, Object value, Map attributes) {
071         String mvcType = ''
072         if (value != null && value instanceof CharSequence) {
073             return value.toString()
074         }
075         throw new IllegalArgumentException("In $name value must be an MVC group type")
076     }
077 
078     protected String resolveMvcId(FactoryBuilderSupport builder, Object name, String mvcType, Map attributes) {
079         String mvcId = attributes.remove('mvcId') ?: mvcType
080 
081         MVCGroupManager mvcGroupManager = builder.application.mvcGroupManager
082         if (mvcGroupManager.findGroup(mvcId)) {
083             mvcId += '-' + UUID.randomUUID().toString()
084         }
085         mvcId
086     }
087 
088     protected Map resolveMvcArgs(Map attributes) {
089         (attributes.remove('mvcArgs') ?: [:]) [metaComponentArgs: attributes]
090     }
091 
092     private static class DestroyEventHandler implements RunnableWithArgs {
093         private final String parentId
094         private final MVCGroup childGroup
095         private final GriffonApplication application
096 
097         DestroyEventHandler(String parentId, MVCGroup childGroup, GriffonApplication application) {
098             this.parentId = parentId
099             this.childGroup = childGroup
100             this.application = application
101             application.eventRouter.addEventListener(ApplicationEvent.DESTROY_MVC_GROUP.name, this)
102         }
103 
104         @Override
105         void run(@Nullable Object... args) {
106             Object destroyedGroup = args[0]
107             if (destroyedGroup.mvcId == parentId) {
108                 childGroup.destroy()
109                 application.eventRouter.removeEventListener(ApplicationEvent.DESTROY_MVC_GROUP.name, this)
110             }
111         }
112     }
113 
114     @Override
115     boolean onHandleNodeAttributes(FactoryBuilderSupport builder, Object node, Map attributes) {
116         try {
117             boolean defaultBehavior = builder.context.mvcGroup.controller.metaClass.invokeMethod(builder.context.mvcGroup.controller, 'onHandleNodeAttributes', builder, node, attributes)
118             super.onHandleNodeAttributes(builder, node, attributes)
119             return defaultBehavior
120         catch (MissingMethodException e) {
121             return false
122         }
123     }
124 
125     @Override
126     boolean onNodeChildren(FactoryBuilderSupport builder, Object node, Closure childContent) {
127         def root = builder.context.root
128         builder = builder.context.mvcGroup.builder
129         Closure handleChildContent = builder.getVariables().get('handleChildContent')
130         if (handleChildContent != null) {
131             handleChildContent(childContent)
132         else {
133             builder.container(root, childContent)
134         }
135         false
136     }
137 
138     boolean isHandlesNodeChildren() {
139         false
140     }
141 
142     @Override
143     void setChild(FactoryBuilderSupport builder, Object parent, Object child) {
144         safeInvoke(builder.context.mvcGroup.controller, 'setChild', builder, parent, child)
145         safeInvoke(builder.parentContext.mvcGroup.builder, 'setChild', builder, parent, child)
146         super.setChild(builder, parent, child)
147     }
148 
149     @Override
150     void setParent(FactoryBuilderSupport builder, Object parent, Object child) {
151         safeInvoke(builder.context.mvcGroup.controller, 'setParent', builder, parent, child)
152         safeInvoke(builder.context.mvcGroup.builder, 'setParent', builder, parent, child)
153         super.setParent(builder, parent, child)
154     }
155 
156     @Override
157     void onNodeCompleted(FactoryBuilderSupport builder, Object parent, Object node) {
158         safeInvoke(builder.context.mvcGroup.controller, 'onNodeCompleted', builder, parent, node)
159         safeInvoke(builder.context.mvcGroup.builder, 'onNodeCompleted', builder, parent, node)
160         super.onNodeCompleted(builder, parent, node)
161     }
162 
163     static protected def safeInvoke(Object obj, String method, Object... args) {
164         try {
165             return obj.metaClass.invokeMethod(obj, method, args)
166         catch (MissingMethodException e) {
167             return null
168         }
169     }
170 }