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