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