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 (features) LOG.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.debugEnabled) LOG.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 (!mc) continue
107
108 if (LOG.debugEnabled) LOG.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.traceEnabled) LOG.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.traceEnabled) LOG.trace("Injected getter for ${beanName} on $partialTarget.key")
129 mc["get$beanName".toString()] = accessors[0]
130 }
131 if (accessors[1]) {
132 if (LOG.traceEnabled) LOG.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.traceEnabled) LOG.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 }
|