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