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