CompositeBuilder.java
001 /*
002  * SPDX-License-Identifier: Apache-2.0
003  *
004  * Copyright 2008-2017 the original author or authors.
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     http://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 package griffon.util;
019 
020 import groovy.lang.Closure;
021 import groovy.lang.MissingMethodException;
022 import groovy.lang.MissingPropertyException;
023 import groovy.util.Factory;
024 import groovy.util.FactoryBuilderSupport;
025 
026 import javax.annotation.Nonnull;
027 import java.util.ArrayList;
028 import java.util.List;
029 import java.util.Map;
030 
031 import static java.util.Objects.requireNonNull;
032 
033 /**
034  @author Andres Almiray
035  @since 2.0.0
036  */
037 @SuppressWarnings("rawtypes")
038 public class CompositeBuilder extends FactoryBuilderSupport {
039     public CompositeBuilder(@Nonnull BuilderCustomizer[] customizers) {
040         super(false);
041         requireNonNull(customizers, "Argument 'customizers' must not be null");
042         for (int i = 0; i < customizers.length; i++) {
043             BuilderCustomizer customizer = customizers[customizers.length - - i];
044             doRegisterVariables(customizer);
045             doRegisterFactories(customizer);
046             doRegisterMethods(customizer);
047             doRegisterProps(customizer);
048         }
049 
050         final List<Closure> methodMissingDelegates = new ArrayList<>();
051         final List<Closure> propertyMissingDelegates = new ArrayList<>();
052         for (BuilderCustomizer customizer : customizers) {
053             doRegisterAttributeDelegates(customizer);
054             doRegisterPreInstantiateDelegates(customizer);
055             doRegisterPostInstantiateDelegates(customizer);
056             doRegisterPostNodeCompletionDelegates(customizer);
057             doDisposalClosures(customizer);
058             if (customizer.getMethodMissingDelegate() != null) {
059                 methodMissingDelegates.add(customizer.getMethodMissingDelegate());
060             }
061             if (customizer.getPropertyMissingDelegate() != null) {
062                 propertyMissingDelegates.add(customizer.getPropertyMissingDelegate());
063             }
064         }
065 
066         final Closure originalMethodMissingDelegate = getMethodMissingDelegate();
067         setMethodMissingDelegate(new Closure<Object>(this) {
068             private static final long serialVersionUID = -6901410680736336645L;
069 
070             protected Object doCall(Object[] args) {
071                 String methodName = String.valueOf(args[0]);
072                 for (Closure methodMissingDelegate : methodMissingDelegates) {
073                     try {
074                         return methodMissingDelegate.call(args);
075                     catch (MissingMethodException mme) {
076                         if (!methodName.equals(mme.getMethod())) throw mme;
077                     }
078                 }
079 
080                 if (originalMethodMissingDelegate != null) {
081                     try {
082                         return originalMethodMissingDelegate.call(args);
083                     catch (MissingMethodException mme) {
084                         if (!methodName.equals(mme.getMethod())) throw mme;
085                     }
086                 }
087 
088                 Object[] argsCopy = new Object[args.length - 1];
089                 System.arraycopy(args, 1, argsCopy, 0, argsCopy.length);
090                 throw new MissingMethodException(methodName, CompositeBuilder.class, argsCopy);
091             }
092         });
093 
094         final Closure originalPropertyMissingDelegate = getMethodMissingDelegate();
095         setPropertyMissingDelegate(new Closure<Object>(this) {
096             private static final long serialVersionUID = 1055591497264374109L;
097 
098             protected Object doCall(Object[] args) {
099                 String propertyName = String.valueOf(args[0]);
100                 for (Closure propertyMissingDelegate : propertyMissingDelegates) {
101                     try {
102                         return propertyMissingDelegate.call(args);
103                     catch (MissingMethodException mme) {
104                         if (!propertyName.equals(mme.getMethod()))
105                             throw mme;
106                     }
107                 }
108 
109                 if (originalPropertyMissingDelegate != null) {
110                     try {
111                         return originalPropertyMissingDelegate.call(args);
112                     catch (MissingMethodException mme) {
113                         if (!propertyName.equals(mme.getMethod()))
114                             throw mme;
115                     }
116                 }
117 
118                 throw new MissingPropertyException(propertyName, CompositeBuilder.class);
119             }
120         });
121     }
122 
123     private void doRegisterVariables(@Nonnull BuilderCustomizer customizer) {
124         for (Map.Entry<String, Object> entry : customizer.getVariables().entrySet()) {
125             setVariable(entry.getKey(), entry.getValue());
126         }
127     }
128 
129     private void doRegisterFactories(@Nonnull BuilderCustomizer customizer) {
130         for (Map.Entry<String, Factory> entry : customizer.getFactories().entrySet()) {
131             registerFactory(entry.getKey(), entry.getValue());
132         }
133     }
134 
135     private void doRegisterMethods(@Nonnull BuilderCustomizer customizer) {
136         for (Map.Entry<String, Closure> e : customizer.getMethods().entrySet()) {
137             registerExplicitMethod(e.getKey(), e.getValue());
138         }
139     }
140 
141     private void doRegisterProps(@Nonnull BuilderCustomizer customizer) {
142         for (Map.Entry<String, Closure[]> entry : customizer.getProps().entrySet()) {
143             Closure[] accessors = entry.getValue();
144             Closure getter = accessors[0];
145             Closure setter = accessors[1];
146             registerExplicitProperty(entry.getKey(), getter, setter);
147         }
148     }
149 
150     private void doRegisterAttributeDelegates(@Nonnull BuilderCustomizer customizer) {
151         for (Closure c : customizer.getAttributeDelegates()) {
152             addAttributeDelegate(c);
153         }
154     }
155 
156     private void doRegisterPreInstantiateDelegates(@Nonnull BuilderCustomizer customizer) {
157         for (Closure c : customizer.getPreInstantiateDelegates()) {
158             addPreInstantiateDelegate(c);
159         }
160     }
161 
162     private void doRegisterPostInstantiateDelegates(@Nonnull BuilderCustomizer customizer) {
163         for (Closure c : customizer.getPostInstantiateDelegates()) {
164             addPostInstantiateDelegate(c);
165         }
166     }
167 
168     private void doRegisterPostNodeCompletionDelegates(@Nonnull BuilderCustomizer customizer) {
169         for (Closure c : customizer.getPostNodeCompletionDelegates()) {
170             addPostNodeCompletionDelegate(c);
171         }
172     }
173 
174     private void doDisposalClosures(@Nonnull BuilderCustomizer customizer) {
175         for (Closure c : customizer.getDisposalClosures()) {
176             addDisposalClosure(c);
177         }
178     }
179 }