`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 (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 }`