Removed DOCUMENT ME comments that were generated and applied source code
[utils] / system / general / src / main / java / org / wamblee / system / adapters / SetterConfiguration.java
1 /*
2  * Copyright 2008 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.wamblee.system.adapters;
17
18 import org.wamblee.collections.CollectionFilter;
19
20 import org.wamblee.conditions.Condition;
21 import org.wamblee.conditions.FixedCondition;
22
23 import org.wamblee.general.Pair;
24
25 import org.wamblee.reflection.ReflectionUtils;
26
27 import org.wamblee.system.core.DefaultProvidedInterface;
28 import org.wamblee.system.core.DefaultRequiredInterface;
29 import org.wamblee.system.core.ProvidedInterface;
30 import org.wamblee.system.core.RequiredInterface;
31 import org.wamblee.system.core.Scope;
32 import org.wamblee.system.core.SystemAssemblyException;
33
34 import java.awt.CompositeContext;
35
36 import java.lang.reflect.InvocationTargetException;
37 import java.lang.reflect.Method;
38 import java.lang.reflect.Modifier;
39
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.HashMap;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.Set;
46
47 /**
48  * Represents the configuration for exposing the setters of a class as required
49  * interfaces.
50  * 
51  * @author Erik Brakkee
52  */
53 public class SetterConfiguration {
54     private Class _class;
55
56     private boolean publicOnly;
57
58     private Map<Method, ParameterValues> setters;
59
60     /**
61      * Constructs the setter configuration. By default no setters are added.
62      * 
63      * @param aClass
64      *            Class which is being configured.
65      */
66     public SetterConfiguration(Class aClass) {
67         _class = aClass;
68         publicOnly = true;
69         setters = new HashMap<Method, ParameterValues>();
70     }
71
72     /**
73      * Makes sure that all available setters are used.
74      * 
75      */
76     public SetterConfiguration initAllSetters() {
77         setters.clear();
78
79         for (Method method : getAllSetters(_class, publicOnly)) {
80             setters.put(method, createParameterValues(method));
81         }
82
83         return this;
84     }
85
86     /**
87      * Called to set whether non-public setters are also used. By default only
88      * public setters are used. The currently selected setters remain chosen.
89      * 
90      * @param aIsNonPublic
91      *            Non public flag.
92      * 
93      */
94     public SetterConfiguration setNonPublic(boolean aIsNonPublic) {
95         publicOnly = !aIsNonPublic;
96
97         return this;
98     }
99
100     /**
101      * Removes all setters.
102      * 
103      * @return Reference to the current object to allow call chaining.
104      */
105     public SetterConfiguration clear() {
106         setters.clear();
107
108         return this;
109     }
110
111     /**
112      * Removes a setter from the set of methods.
113      * 
114      * @param aName
115      *            Name of the setter to remove.
116      * 
117      * @return Reference to the current object to allow call chaining.
118      * 
119      */
120     public SetterConfiguration remove(String aName) {
121         for (Method method : setters.keySet()) {
122             if (method.getName().equals(aName)) {
123                 setters.remove(method);
124
125                 return this;
126             }
127         }
128
129         throw new IllegalArgumentException(
130             "No method configured by the name of '" + aName + "'");
131     }
132
133     /**
134      * Removes the method from the set of methods.
135      * 
136      * @param aMethod
137      *            Method to remove.
138      * 
139      * @return
140      * 
141      */
142     public SetterConfiguration remove(Method aMethod) {
143         if (!aMethod.getDeclaringClass().isAssignableFrom(_class)) {
144             throw new RuntimeException("Method " + aMethod +
145                 " not found in class " + _class + " or its superclasses");
146         }
147
148         for (Method method : setters.keySet()) {
149             if (method.equals(aMethod)) {
150                 setters.remove(method);
151
152                 return this;
153             }
154         }
155
156         throw new IllegalArgumentException("Method '" + aMethod +
157             "' was not configured. ");
158     }
159
160     /**
161      * Adds a given setter name to the setters.
162      * 
163      * @param aName
164      *            Name of a setter method.
165      * 
166      * @return Reference to the current object to allow call chaining.
167      * 
168      */
169     public SetterConfiguration add(final String aName) {
170         int oldlen = setters.size();
171         List<Method> methods = new ArrayList<Method>();
172         CollectionFilter.filter(getAllSetters(_class, publicOnly), methods,
173             new Condition<Method>() {
174                 @Override
175                 public boolean matches(Method aObject) {
176                     return aObject.getName().equals(aName);
177                 }
178             });
179
180         if (methods.size() == 0) {
181             throw new IllegalArgumentException("Method '" + aName +
182                 "' not found in " + _class.getName());
183         }
184
185         // TODO is it possible to get more than one setter here in case the
186         // subclass overrides
187         // the baseclass method?
188         setters.put(methods.get(0), createParameterValues(methods.get(0)));
189
190         return this;
191     }
192
193     /**
194      * Adds a given setter identified by the type it accepts to the list of
195      * setters.N
196      * 
197      * @param aType
198      *            Type to look for. Note that this must be the exact type as
199      *            autoboxing and autounboxing is not used.
200      * 
201      * @return Reference to the current object to allow call chaining.
202      * 
203      * @throws IllegalArgumentException
204      *             In case no setter is found or multiple setters are found.
205      */
206     public SetterConfiguration addSetter(final Class aType) {
207         List<Method> result = new ArrayList<Method>();
208         CollectionFilter.filter(getAllSetters(_class, publicOnly), result,
209             new Condition<Method>() {
210                 @Override
211                 public boolean matches(Method aObject) {
212                     Class type = aObject.getParameterTypes()[0];
213
214                     return type.equals(aType);
215                 }
216             });
217
218         if (result.size() == 0) {
219             throw new IllegalArgumentException("No setter found in class '" +
220                 _class.getName() + "' that has a setter with argument type '" +
221                 aType.getName() + "'");
222         }
223
224         if (result.size() > 1) {
225             String setters = "";
226
227             for (Method method : result) {
228                 setters += (method.getName() + " ");
229             }
230
231             throw new IllegalArgumentException(
232                 "Multiple setters found in class '" + _class.getName() +
233                     " that accept type '" + aType.getName() + "': " + setters);
234         }
235
236         Method method = result.get(0);
237         setters.put(method, createParameterValues(method));
238
239         return this;
240     }
241
242     /**
243      * Gets all setters for the current class.
244      * 
245      * 
246      * @return List of all setters.
247      */
248     public static List<Method> getAllSetters(Class aClass, boolean aPublicOnly) {
249         List<Method> result = new ArrayList<Method>();
250
251         for (Method method : getAllMethods(aClass)) {
252             if (!aPublicOnly || Modifier.isPublic(method.getModifiers())) {
253                 if (method.getName().startsWith("set") &&
254                     (method.getParameterTypes().length == 1)) {
255                     method.setAccessible(true);
256                     result.add(method);
257                 }
258             }
259         }
260
261         return result;
262     }
263
264     private static ParameterValues createParameterValues(Method aMethod) {
265         Class[] paramTypes = aMethod.getParameterTypes();
266         String[] paramNames = new String[paramTypes.length];
267
268         for (int i = 0; i < paramTypes.length; i++) {
269             paramNames[i] = aMethod.getName() + "." + i;
270         }
271
272         return new ParameterValues(paramNames, paramTypes);
273     }
274
275     private static final List<Method> getAllMethods(Class aClass) {
276         return ReflectionUtils.getAllMethods(aClass);
277     }
278
279     /**
280      * Gets the required interfaces based on the configured setteres.
281      * 
282      * @return List of required interfaces.
283      */
284     public List<RequiredInterface> getRequiredInterfaces() {
285         List<RequiredInterface> result = new ArrayList<RequiredInterface>();
286
287         for (Method method : setters.keySet()) {
288             result.addAll(setters.get(method).getRequiredInterfaces());
289         }
290
291         return result;
292     }
293
294     /**
295      * Invokes all configured setters with the appropriate values.
296      * 
297      * @param aScope
298      *            Scope within which invocation takes place.
299      * @param aObject
300      *            Object on which the invocation takes place.
301      * 
302      */
303     public void inject(Scope aScope, Object aObject) {
304         if (!_class.isInstance(aObject)) {
305             throw new IllegalArgumentException("Object '" + aObject +
306                 "' is not an instance of " + _class.getName());
307         }
308
309         for (Method method : setters.keySet()) {
310             ParameterValues values = setters.get(method);
311
312             try {
313                 method.invoke(aObject, values.values(aScope));
314             } catch (IllegalAccessException e) {
315                 throw new SystemAssemblyException("Problem invoking " + method +
316                     " with " + values, e);
317             } catch (InvocationTargetException e) {
318                 throw new SystemAssemblyException("Problem invoking " + method +
319                     " with " + values, e);
320             }
321         }
322     }
323
324     /**
325      * Returns the parameter values for allowing detailed configuration of how
326      * parameter values are set.
327      * 
328      * @param aMethod
329      *            Setter name without the "set" prefix with the first character
330      *            converted to lower case.
331      * 
332      * @return Parameter values.
333      * 
334      */
335     public ParameterValues values(String aMethod) {
336         for (Method method : setters.keySet()) {
337             if (method.getName().equals(aMethod)) {
338                 return setters.get(method);
339             }
340         }
341
342         throw new IllegalArgumentException("No setter method '" + aMethod +
343             "' found");
344     }
345
346     public List<Method> getSetters() {
347         return new ArrayList<Method>(setters.keySet());
348     }
349 }