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