CompositeBuilder.java
001 /*
002  * Copyright 2008-2014 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 package griffon.util;
017 
018 import groovy.lang.Closure;
019 import groovy.lang.MissingMethodException;
020 import groovy.lang.MissingPropertyException;
021 import groovy.util.Factory;
022 import groovy.util.FactoryBuilderSupport;
023 
024 import javax.annotation.Nonnull;
025 import java.util.ArrayList;
026 import java.util.List;
027 import java.util.Map;
028 
029 import static java.util.Objects.requireNonNull;
030 
031 /**
032  @author Andres Almiray
033  @since 2.0.0
034  */
035 @SuppressWarnings("rawtypes")
036 public class CompositeBuilder extends FactoryBuilderSupport {
037     public CompositeBuilder(@Nonnull BuilderCustomizer[] customizers) {
038         super(false);
039         requireNonNull(customizers, "Argument 'customizers' must not be null");
040         for (int i = 0; i < customizers.length; i++) {
041             BuilderCustomizer customizer = customizers[customizers.length - - i];
042             doRegisterVariables(customizer);
043             doRegisterFactories(customizer);
044             doRegisterMethods(customizer);
045             doRegisterProps(customizer);
046         }
047 
048         final List<Closure> methodMissingDelegates = new ArrayList<>();
049         final List<Closure> propertyMissingDelegates = new ArrayList<>();
050         for (BuilderCustomizer customizer : customizers) {
051             doRegisterAttributeDelegates(customizer);
052             doRegisterPreInstantiateDelegates(customizer);
053             doRegisterPostInstantiateDelegates(customizer);
054             doRegisterPostNodeCompletionDelegates(customizer);
055             doDisposalClosures(customizer);
056             if (customizer.getMethodMissingDelegate() != null) {
057                 methodMissingDelegates.add(customizer.getMethodMissingDelegate());
058             }
059             if (customizer.getPropertyMissingDelegate() != null) {
060                 propertyMissingDelegates.add(customizer.getPropertyMissingDelegate());
061             }
062         }
063 
064         final Closure originalMethodMissingDelegate = getMethodMissingDelegate();
065         setMethodMissingDelegate(new Closure<Object>(this) {
066             private static final long serialVersionUID = -6901410680736336645L;
067 
068             protected Object doCall(Object[] args) {
069                 String methodName = String.valueOf(args[0]);
070                 for (Closure methodMissingDelegate : methodMissingDelegates) {
071                     try {
072                         return methodMissingDelegate.call(args);
073                     catch (MissingMethodException mme) {
074                         if (!methodName.equals(mme.getMethod())) throw mme;
075                     }
076                 }
077 
078                 if (originalMethodMissingDelegate != null) {
079                     try {
080                         return originalMethodMissingDelegate.call(args);
081                     catch (MissingMethodException mme) {
082                         if (!methodName.equals(mme.getMethod())) throw mme;
083                     }
084                 }
085 
086                 Object[] argsCopy = new Object[args.length - 1];
087                 System.arraycopy(args, 1, argsCopy, 0, argsCopy.length);
088                 throw new MissingMethodException(methodName, CompositeBuilder.class, argsCopy);
089             }
090         });
091 
092         final Closure originalPropertyMissingDelegate = getMethodMissingDelegate();
093         setPropertyMissingDelegate(new Closure<Object>(this) {
094             private static final long serialVersionUID = 1055591497264374109L;
095 
096             protected Object doCall(Object[] args) {
097                 String propertyName = String.valueOf(args[0]);
098                 for (Closure propertyMissingDelegate : propertyMissingDelegates) {
099                     try {
100                         return propertyMissingDelegate.call(args);
101                     catch (MissingMethodException mme) {
102                         if (!propertyName.equals(mme.getMethod()))
103                             throw mme;
104                     }
105                 }
106 
107                 if (originalPropertyMissingDelegate != null) {
108                     try {
109                         return originalPropertyMissingDelegate.call(args);
110                     catch (MissingMethodException mme) {
111                         if (!propertyName.equals(mme.getMethod()))
112                             throw mme;
113                     }
114                 }
115 
116                 throw new MissingPropertyException(propertyName, CompositeBuilder.class);
117             }
118         });
119     }
120 
121     private void doRegisterVariables(@Nonnull BuilderCustomizer customizer) {
122         for (Map.Entry<String, Object> entry : customizer.getVariables().entrySet()) {
123             setVariable(entry.getKey(), entry.getValue());
124         }
125     }
126 
127     private void doRegisterFactories(@Nonnull BuilderCustomizer customizer) {
128         for (Map.Entry<String, Factory> entry : customizer.getFactories().entrySet()) {
129             registerFactory(entry.getKey(), entry.getValue());
130         }
131     }
132 
133     private void doRegisterMethods(@Nonnull BuilderCustomizer customizer) {
134         for (Map.Entry<String, Closure> e : customizer.getMethods().entrySet()) {
135             registerExplicitMethod(e.getKey(), e.getValue());
136         }
137     }
138 
139     private void doRegisterProps(@Nonnull BuilderCustomizer customizer) {
140         for (Map.Entry<String, Closure[]> entry : customizer.getProps().entrySet()) {
141             Closure[] accessors = entry.getValue();
142             Closure getter = accessors[0];
143             Closure setter = accessors[1];
144             registerExplicitProperty(entry.getKey(), getter, setter);
145         }
146     }
147 
148     private void doRegisterAttributeDelegates(@Nonnull BuilderCustomizer customizer) {
149         for (Closure c : customizer.getAttributeDelegates()) {
150             addAttributeDelegate(c);
151         }
152     }
153 
154     private void doRegisterPreInstantiateDelegates(@Nonnull BuilderCustomizer customizer) {
155         for (Closure c : customizer.getPreInstantiateDelegates()) {
156             addPreInstantiateDelegate(c);
157         }
158     }
159 
160     private void doRegisterPostInstantiateDelegates(@Nonnull BuilderCustomizer customizer) {
161         for (Closure c : customizer.getPostInstantiateDelegates()) {
162             addPostInstantiateDelegate(c);
163         }
164     }
165 
166     private void doRegisterPostNodeCompletionDelegates(@Nonnull BuilderCustomizer customizer) {
167         for (Closure c : customizer.getPostNodeCompletionDelegates()) {
168             addPostNodeCompletionDelegate(c);
169         }
170     }
171 
172     private void doDisposalClosures(@Nonnull BuilderCustomizer customizer) {
173         for (Closure c : customizer.getDisposalClosures()) {
174             addDisposalClosure(c);
175         }
176     }
177 }