CompositeBuilderHelper.groovy
001 /*
002  * Copyright 2008-2013 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 org.codehaus.griffon.runtime.builder
017 
018 import org.slf4j.Logger
019 import org.slf4j.LoggerFactory
020 
021 import griffon.core.GriffonApplication
022 import griffon.util.ApplicationClassLoader
023 import org.codehaus.griffon.runtime.util.AddonHelper
024 
025 /**
026  * Helper class that initializes a CompositeBuilder with the builder configuration read from the application.
027  *
028  @author Danno Ferrin
029  @author Andres Almiray
030  */
031 class CompositeBuilderHelper {
032     private static final Logger LOG = LoggerFactory.getLogger(CompositeBuilderHelper)
033     private final static CompositeBuilderCustomizer builderCustomizer
034 
035     static {
036         ClassLoader classLoader = ApplicationClassLoader.get()
037         try {
038             URL url = classLoader.getResource('META-INF/services/' + CompositeBuilderCustomizer.class.name)
039             String className = url.text.trim()
040             builderCustomizer = classLoader.loadClass(className).newInstance()
041         catch (Exception e) {
042             builderCustomizer = new DefaultCompositeBuilderCustomizer()
043         }
044     }
045 
046     static FactoryBuilderSupport createBuilder(GriffonApplication app, Map<String, MetaClass> targets) {
047         UberBuilder uberBuilder = new UberBuilder()
048         uberBuilder.setProperty('app', app)
049 
050         LOG.debug('Configuring builders with addon contributions')
051         AddonHelper.handleAddonsForBuilders(app, uberBuilder, targets)
052 
053         for (node in app.builderConfig) {
054             String nodeName = node.key
055             switch (nodeName) {
056                 case "features":
057                     handleFeatures(uberBuilder, node.value)
058                     break
059                 default:
060                     if (nodeName == "root"nodeName = ""
061                     node.value.each {builder ->
062                         handleLocalBuilder(uberBuilder, targets, nodeName, builder)
063                     }
064             }
065         }
066 
067         return uberBuilder
068     }
069 
070     static handleFeatures(UberBuilder uberBuilder, features) {
071         if (featuresLOG.debug("Applying 'features' config node to builders")
072         for (feature in features) {
073             switch (feature.key) {
074                 case ~/.*Delegates/:
075                     def delegateType = feature.key - "s"
076                     delegateType = delegateType[0].toUpperCase() + delegateType[1..-1]
077                     feature.value.each {delegateValue ->
078                         uberBuilder."add$delegateType"(delegateValue)
079                     }
080                     break
081                 case "factories":
082                     addFactories(uberBuilder, feature.value)
083                     break
084                 case "methods":
085                     addMethods(uberBuilder, feature.value)
086                     break
087                 case "props":
088                     addProperties(uberBuilder, feature.value)
089                     break
090             }
091         }
092     }
093 
094     static handleLocalBuilder(UberBuilder uberBuilder, Map<String, MetaClass> targets, String prefixName, builderClassName) {
095         Class builderClass = ApplicationClassLoader.get().loadClass(builderClassName.key//FIXME get correct classloader
096         if (!FactoryBuilderSupport.isAssignableFrom(builderClass)) {
097             return;
098         }
099         if (LOG.debugEnabledLOG.debug("Initializing builder ${builderClass.name}")
100         FactoryBuilderSupport localBuilder = uberBuilder.uberInit(prefixName, builderClass)
101         for (partialTarget in builderClassName.value) {
102             if (partialTarget.key == 'view') {
103                 // this needs special handling, skip it for now
104                 continue
105             }
106 
107             MetaClass mc = targets[partialTarget.key]
108             if (!mccontinue
109 
110             if (LOG.debugEnabledLOG.debug("Injecting builder contributions to $partialTarget.key using ${partialTarget.value}")
111             for (String injectionName in partialTarget.value) {
112                 def factories = localBuilder.getLocalFactories()
113                 def methods = localBuilder.getLocalExplicitMethods()
114                 def props = localBuilder.getLocalExplicitProperties()
115 
116                 Closure processInjection = {String injectedName ->
117                     String resolvedName = prefixName + injectedName
118                     if (methods.containsKey(injectedName)) {
119                         if (LOG.traceEnabledLOG.trace("Injected method ${resolvedName}() on $partialTarget.key")
120                         mc["$resolvedName".toString()] = methods[injectedName]
121                     else if (props.containsKey(injectedName)) {
122                         Closure[] accessors = props[injectedName]
123                         String beanName
124                         if (injectedName.length() 1) {
125                             beanName = injectedName[0].toUpperCase() + injectedName.substring(1)
126                         else {
127                             beanName = injectedName[0].toUpperCase()
128                         }
129                         if (accessors[0]) {
130                             if (LOG.traceEnabledLOG.trace("Injected getter for ${beanName} on $partialTarget.key")
131                             mc["get$beanName".toString()] = accessors[0]
132                         }
133                         if (accessors[1]) {
134                             if (LOG.traceEnabledLOG.trace("Injected setter for ${beanName} on $partialTarget.key")
135                             mc["set$beanName".toString()] = accessors[1]
136                         }
137                     else if (factories.containsKey(injectedName)) {
138                         if (LOG.traceEnabledLOG.trace("Injected factory ${resolvedName} on $partialTarget.key")
139                         mc[resolvedName{Object... args -> uberBuilder."$resolvedName"(* args)}
140                     }
141                 }
142 
143                 if (injectionName == "*") {
144                     for (group in localBuilder.getRegistrationGroups()) {
145                         localBuilder.getRegistrationGroupItems(group).each processInjection
146                     }
147                     continue
148                 }
149 
150                 def groupItems = localBuilder.getRegistrationGroupItems(injectionName)
151                 if (groupItems) {
152                     groupItems.each processInjection
153                 else {
154                     processInjection(injectionName)
155                 }
156             }
157         }
158     }
159 
160     private static addFactories(UberBuilder uberBuilder, groupedFactories) {
161         for (group in groupedFactories) {
162             String groupName = group.key
163             group.value.each {name, factory ->
164                 addFactory(uberBuilder, groupName, name, factory)
165             }
166         }
167     }
168 
169     static void addFactory(UberBuilder uberBuilder, String groupName, String name, Object factory) {
170         groupName = groupName == 'root' || groupName == '*' '' : groupName
171         uberBuilder.@registrationGroup.get(groupName, [] as TreeSet)
172 
173         if (Factory.class.isAssignableFrom(factory.getClass())) {
174             builderCustomizer.registerFactory(uberBuilder, name, groupName, factory)
175         else if (factory instanceof Class) {
176             builderCustomizer.registerBeanFactory(uberBuilder, name, groupName, factory)
177         else {
178             throw new IllegalArgumentException("[builder config] value of factory '$groupName:$name' is neither a Factory nor a Class instance.")
179         }
180     }
181 
182     private static addMethods(UberBuilder uberBuilder, groupedMethods) {
183         for (group in groupedMethods) {
184             String groupName = group.key
185             group.value.each {name, method ->
186                 if (method instanceof Closure) {
187                     addMethod(uberBuilder, groupName, name, method)
188                 else {
189                     throw new IllegalArgumentException("[builder config] value of method '$groupName:$name' is not a Closure.")
190                 }
191             }
192         }
193     }
194 
195     static void addMethod(UberBuilder uberBuilder, String groupName, String methodName, Closure method) {
196         groupName = groupName == 'root' || groupName == '*' '' : groupName
197         uberBuilder.@registrationGroup.get(groupName, [] as TreeSet)
198         builderCustomizer.registerExplicitMethod(uberBuilder, methodName, groupName, method)
199     }
200 
201     private static addProperties(UberBuilder uberBuilder, groupedProperties) {
202         for (group in groupedProperties) {
203             String groupName = group.key
204             group.value.each {name, propertyTuple ->
205                 addProperty(uberBuilder, name, groupName, propertyTuple.get, propertyTuple.set)
206             }
207         }
208     }
209 
210     static void addProperty(UberBuilder uberBuilder, String groupName, String propertyName, Closure getter, Closure setter) {
211         groupName = groupName == 'root' || groupName == '*' '' : groupName
212         uberBuilder.@registrationGroup.get(groupName, [] as TreeSet)
213         builderCustomizer.registerExplicitProperty(uberBuilder, propertyName, groupName, getter, setter)
214     }
215 }