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