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