`UberBuilder.groovy`
`001 /* 002 * Copyright 2007-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 017 package org.codehaus.griffon.runtime.builder 018 019 import griffon.core.GriffonArtifact 020 import griffon.util.GriffonExceptionHandler 021 import org.slf4j.Logger 022 import org.slf4j.LoggerFactory 023 024 /** 025 * @author Danno.Ferrin 026 * Date: Nov 7, 2007 027 * Time: 2:50:58 PM 028 */ 029 class UberBuilder extends FactoryBuilderSupport { 030 private static final Logger LOG = LoggerFactory.getLogger(UberBuilder) 031 protected final Map builderLookup = [:] 032 protected final List<UberBuilderRegistration> builderRegistration = [] as LinkedList 033 034 public UberBuilder() { 035 loadBuilderLookups() 036 } 037 038 public UberBuilder(Object[] builders) { 039 this() 040 builders.each {if (it) uberInit(null, it)} 041 } 042 043 protected Object loadBuilderLookups() {} 044 045 public final uberInit(Object prefix, Map builders) { 046 if (prefix) { 047 throw new IllegalArgumentException("Prefixed maps not supported") 048 } else { 049 return builders.collect {k, v -> uberInit(k, v)} 050 } 051 } 052 053 public final uberInit(Object prefix, Object[] builders) { 054 if (prefix) { 055 throw new IllegalArgumentException("Prefixed maps not supported") 056 } else { 057 return builders.collect {v -> uberInit(prefix, v)} 058 } 059 } 060 061 public final uberInit(Object prefix, Object builderKey) { 062 def builder = builderLookup[builderKey] 063 // make sure we won't self-loop 064 if (builder && (builder != builderKey)) { 065 // if we get more than one, we have more than this base case, so look it up 066 return uberInit(prefix, builder) 067 } else { 068 throw new IllegalArgumentException("Cannot uberinit indirectly via key '$builderKey'") 069 } 070 } 071 072 protected uberInit(Object prefix, Class klass) { 073 if (builderLookup.containsKey(klass)) { 074 return uberInit(prefix, builderLookup[klass]) 075 } else if (FactoryBuilderSupport.isAssignableFrom(klass)) { 076 return uberInit(prefix, klass.newInstance()) 077 } else { 078 throw new IllegalArgumentException("Cannot uberinit indirectly from class'${klass.name}'") 079 } 080 } 081 082 protected uberInit(Object prefix, FactoryBuilderSupport fbs) { 083 builderRegistration.add(new UberBuilderRegistration(prefix, fbs)) 084 getVariables().putAll(fbs.variables) 085 fbs.variables.clear() 086 for (Closure delegate in fbs.attributeDelegates) { 087 delegate.delegate = fbs 088 proxyBuilder.@attributeDelegates.add(delegate) 089 } 090 for (Closure delegate in fbs.preInstantiateDelegates) { 091 delegate.delegate = fbs 092 proxyBuilder.@preInstantiateDelegates.add(delegate) 093 } 094 for (Closure delegate in fbs.postInstantiateDelegates) { 095 delegate.delegate = fbs 096 proxyBuilder.@postInstantiateDelegates.add(delegate) 097 } 098 for (Closure delegate in fbs.postNodeCompletionDelegates) { 099 delegate.delegate = fbs 100 proxyBuilder.@postNodeCompletionDelegates.add(delegate) 101 } 102 103 fbs.setProxyBuilder(this) 104 return fbs 105 } 106 107 protected uberInit(Object prefix, Factory factory) { 108 builderRegistration.add(new UberBuilderRegistration(prefix, factory)) 109 } 110 111 Factory resolveFactory(Object name, Map attributes, Object value) { 112 for (UberBuilderRegistration ubr in builderRegistration) { 113 Factory factory = ubr.nominateFactory(name) 114 if (factory) { 115 if (ubr.builder) { 116 getProxyBuilder().getContext().put(CHILD_BUILDER, ubr.builder) 117 } else { 118 getProxyBuilder().getContext().put(CHILD_BUILDER, proxyBuilder) 119 } 120 121 return factory 122 } 123 } 124 return super.resolveFactory(name, attributes, value) 125 } 126 127 protected Closure resolveExplicitMethod(String methodName, Object args) { 128 for (UberBuilderRegistration ubr in builderRegistration) { 129 Closure explcitMethod = ubr.nominateExplicitMethod(methodName) 130 if (explcitMethod) { 131 return explcitMethod 132 } 133 } 134 return super.resolveExplicitMethod(methodName, args) 135 } 136 137 protected void setClosureDelegate(Closure closure, Object node) { 138 closure.setDelegate(currentBuilder) 139 } 140 141 public Object build(Script script) { 142 synchronized (script) { 143 Object oldScriptName = builder.variables[FactoryBuilderSupport.SCRIPT_CLASS_NAME] 144 try { 145 MetaClass scriptMetaClass = script.getMetaClass() 146 boolean isArtifact = script instanceof GriffonArtifact 147 if (isArtifact) scriptMetaClass = script.getGriffonClass().getMetaClass() 148 if (!(scriptMetaClass instanceof UberInterceptorMetaClass)) { 149 MetaClass uberMetaClass = new UberInterceptorMetaClass(scriptMetaClass, this) 150 script.setMetaClass(uberMetaClass) 151 if (isArtifact) script.getGriffonClass().setMetaClass(uberMetaClass) 152 } 153 builder[FactoryBuilderSupport.SCRIPT_CLASS_NAME] = script.getClass().name 154 script.binding = this 155 return script.run() 156 } catch (x) { 157 if (LOG.errorEnabled) LOG.error("An error occurred while building $script", GriffonExceptionHandler.sanitize(x)) 158 throw x 159 } finally { 160 if (oldScriptName != null) { 161 builder[FactoryBuilderSupport.SCRIPT_CLASS_NAME] = oldScriptName 162 } else { 163 builder.variables.remove(FactoryBuilderSupport.SCRIPT_CLASS_NAME) 164 } 165 } 166 167 } 168 } 169 170 public Object getProperty(String property) { 171 for (UberBuilderRegistration ubr in builderRegistration) { 172 Closure[] accessors = ubr.nominateExplicitProperty(property) 173 if (accessors) { 174 if (accessors[0] == null) { 175 // write only property 176 throw new MissingPropertyException(property + " is declared as write only") 177 } else { 178 return accessors[0].call() 179 } 180 } 181 } 182 return super.getProperty(property) 183 } 184 185 public void setProperty(String property, Object newValue) { 186 for (UberBuilderRegistration ubr in builderRegistration) { 187 Closure[] accessors = ubr.nominateExplicitProperty(property) 188 if (accessors) { 189 if (accessors[1] == null) { 190 // read only property 191 throw new MissingPropertyException(property + " is declared as read only") 192 } else { 193 accessors[1].call(newValue) 194 } 195 } 196 } 197 super.setProperty(property, newValue) 198 } 199 200 public void dispose() { 201 builderRegistration.each {UberBuilderRegistration ubr -> 202 try { 203 ubr.builder.dispose() 204 } catch (UnsupportedOperationException uoe) { 205 // Sometimes an UOE may appear due to a TriggerBinding 206 // see http://jira.codehaus.org/browse/GRIFFON-165 207 // however there is little that can be done so we 208 // ignore the exception for the time being 209 } 210 } 211 super.dispose() 212 } 213 }`