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