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 (ituberInit(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 (isArtifactscriptMetaClass = script.getGriffonClass().getMetaClass()
148                 if (!(scriptMetaClass instanceof UberInterceptorMetaClass)) {
149                     MetaClass uberMetaClass = new UberInterceptorMetaClass(scriptMetaClass, this)
150                     script.setMetaClass(uberMetaClass)
151                     if (isArtifactscript.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.errorEnabledLOG.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 }