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