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