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 (it) uberInit(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 (isArtifact) scriptMetaClass = script.getGriffonClass().getMetaClass()
153 if (!(scriptMetaClass instanceof UberInterceptorMetaClass)) {
154 MetaClass uberMetaClass = new UberInterceptorMetaClass(scriptMetaClass, this)
155 script.setMetaClass(uberMetaClass)
156 if (isArtifact) script.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.errorEnabled) LOG.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 }
|