FXMetaComponentFactory.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.core.ApplicationEvent
021 import griffon.core.GriffonApplication
022 import griffon.core.RunnableWithArgs
023 import griffon.core.mvc.MVCGroup
024 import griffon.core.mvc.MVCGroupManager
025 import griffon.javafx.artifact.ContentProvider
026 import groovyx.javafx.factory.AbstractNodeFactory
027 import javafx.scene.Node
028 
029 import javax.annotation.Nullable
030 
031 import static org.codehaus.griffon.runtime.groovy.mvc.GroovyAwareMVCGroup.CURRENT_MVCGROUP
032 
033 /**
034  * Enables MVC groups to be used as component nodes
035  *
036  @author Andres Almiray
037  @author Alexander Klein
038  @since 2.12.0
039  */
040 @SuppressWarnings("rawtypes")
041 class FXMetaComponentFactory extends AbstractNodeFactory {
042     FXMetaComponentFactory() {
043         super(Node, false)
044     }
045 
046     @Override
047     Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) {
048         Map attrs = resolveAttributes(attributes)
049         String mvcType = resolveMvcType(name, value, attrs)
050         String mvcId = resolveMvcId(builder, name, value, attrs)
051         Map mvcArgs = resolveMvcArgs(attrs)
052         Map mvcArgsCopy = [*: mvcArgs]
053         attributes.clear()
054         attributes.putAll(attrs)
055 
056         MVCGroup parentGroup = builder.getVariables().get(CURRENT_MVCGROUP)
057         def receiver = parentGroup ?: builder.application.mvcGroupManager
058         MVCGroup mvcGroup = receiver.createMVCGroup(mvcType, mvcId, mvcArgs)
059         def view = mvcGroup.view
060         def root = view instanceof ContentProvider? view.content : mvcGroup.rootNode
061 
062         new DestroyEventHandler(mvcId, mvcGroup, builder.application)
063 
064         builder.context.root = root
065         builder.context.mvcGroup = mvcGroup
066         builder.context.mvcArgs = mvcArgsCopy
067         root
068     }
069 
070     protected Map resolveAttributes(Map attributes) {
071         [*: attributes]
072     }
073 
074     protected String resolveMvcType(Object name, Object value, Map attributes) {
075         String mvcType = ''
076         if (value != null && value instanceof CharSequence) {
077             return value.toString()
078         }
079         throw new IllegalArgumentException("In $name value must be an MVC group type")
080     }
081 
082     protected String resolveMvcId(FactoryBuilderSupport builder, Object name, String mvcType, Map attributes) {
083         String mvcId = attributes.remove('mvcId') ?: mvcType
084 
085         MVCGroupManager mvcGroupManager = builder.application.mvcGroupManager
086         if (mvcGroupManager.findGroup(mvcId)) {
087             mvcId += '-' + UUID.randomUUID().toString()
088         }
089         mvcId
090     }
091 
092     protected Map resolveMvcArgs(Map attributes) {
093         (attributes.remove('mvcArgs') ?: [:]) [metaComponentArgs: attributes]
094     }
095 
096     private static class DestroyEventHandler implements RunnableWithArgs {
097         private final String parentId
098         private final MVCGroup childGroup
099         private final GriffonApplication application
100 
101         DestroyEventHandler(String parentId, MVCGroup childGroup, GriffonApplication application) {
102             this.parentId = parentId
103             this.childGroup = childGroup
104             this.application = application
105             application.eventRouter.addEventListener(ApplicationEvent.DESTROY_MVC_GROUP.name, this)
106         }
107 
108         @Override
109         void run(@Nullable Object... args) {
110             Object destroyedGroup = args[0]
111             if (destroyedGroup.mvcId == parentId) {
112                 childGroup.destroy()
113                 application.eventRouter.removeEventListener(ApplicationEvent.DESTROY_MVC_GROUP.name, this)
114             }
115         }
116     }
117 
118     @Override
119     boolean onHandleNodeAttributes(FactoryBuilderSupport builder, Object node, Map attributes) {
120         try {
121             boolean defaultBehavior = builder.context.mvcGroup.controller.metaClass.invokeMethod(builder.context.mvcGroup.controller, 'onHandleNodeAttributes', builder, node, attributes)
122             super.onHandleNodeAttributes(builder, node, attributes)
123             return defaultBehavior
124         catch (MissingMethodException e) {
125             return false
126         }
127     }
128 
129     @Override
130     boolean onNodeChildren(FactoryBuilderSupport builder, Object node, Closure childContent) {
131         def root = builder.context.root
132         builder = builder.context.mvcGroup.builder
133         Closure handleChildContent = builder.getVariables().get('handleChildContent')
134         if (handleChildContent != null) {
135             handleChildContent(childContent)
136         else {
137             builder.container(root, childContent)
138         }
139         false
140     }
141 
142     boolean isHandlesNodeChildren() {
143         false
144     }
145 
146     @Override
147     void setChild(FactoryBuilderSupport builder, Object parent, Object child) {
148         safeInvoke(builder.context.mvcGroup.controller, 'setChild', builder, parent, child)
149         safeInvoke(builder.parentContext.mvcGroup.builder, 'setChild', builder, parent, child)
150         super.setChild(builder, parent, child)
151     }
152 
153     @Override
154     void setParent(FactoryBuilderSupport builder, Object parent, Object child) {
155         safeInvoke(builder.context.mvcGroup.controller, 'setParent', builder, parent, child)
156         safeInvoke(builder.context.mvcGroup.builder, 'setParent', builder, parent, child)
157         super.setParent(builder, parent, child)
158     }
159 
160     @Override
161     void onNodeCompleted(FactoryBuilderSupport builder, Object parent, Object node) {
162         safeInvoke(builder.context.mvcGroup.controller, 'onNodeCompleted', builder, parent, node)
163         safeInvoke(builder.context.mvcGroup.builder, 'onNodeCompleted', builder, parent, node)
164         super.onNodeCompleted(builder, parent, node)
165     }
166 
167     static protected def safeInvoke(Object obj, String method, Object... args) {
168         try {
169             return obj.metaClass.invokeMethod(obj, method, args)
170         catch (MissingMethodException e) {
171             return null
172         }
173     }
174 }