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 }
|