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