(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.List;
25
26 import org.wamblee.collections.CollectionFilter;
27 import org.wamblee.conditions.Condition;
28 import org.wamblee.conditions.FixedCondition;
29 import org.wamblee.general.Pair;
30 import org.wamblee.system.core.DefaultProvidedInterface;
31 import org.wamblee.system.core.DefaultRequiredInterface;
32 import org.wamblee.system.core.ProvidedInterface;
33 import org.wamblee.system.core.RequiredInterface;
34 import org.wamblee.system.core.Scope;
35 import org.wamblee.system.core.SystemAssemblyException;
36
37 /**
38  * Represents the configuration for exposing the setters of a class as required
39  * interfaces.
40  * 
41  * @author Erik Brakkee
42  */
43 public class SetterConfiguration {
44
45         private Class _class;
46         private boolean _publicOnly;
47
48         private List<Pair<Method, ParameterValues>> _setters;
49
50         /**
51          * Constructs the setter configuration. By default all setters are added.
52          * 
53          * @param aClass
54          *            Class which is being configured.
55          */
56         public SetterConfiguration(Class aClass) {
57                 _class = aClass;
58                 _publicOnly = true;
59                 _setters = getAllSetters(_class, _publicOnly);
60         }
61
62         /**
63          * Called to set whether non-public setters are also used. By default only
64          * public setters are used.
65          * 
66          * @param aIsNonPublic
67          *            Non public flag.
68          */
69         public void setNonPublic(boolean aIsNonPublic) {
70                 _publicOnly = !aIsNonPublic;
71                 _setters = getAllSetters(_class, _publicOnly);
72         }
73
74         /**
75          * Removes all setters.
76          * 
77          * @return Reference to the current object to allow call chaining.
78          */
79         public SetterConfiguration clear() {
80                 _setters.clear();
81                 return this;
82         }
83
84         /**
85          * Removes a setter from the set of methods.
86          * 
87          * @param aName
88          *            Name of the setter to remove (without the "set" prefix).
89          * @return Reference to the current object to allow call chaining.
90          */
91         public SetterConfiguration remove(String aName) {
92                 final String name = createSetterName(aName);
93                 List<Pair<Method, ParameterValues>> setters = new ArrayList<Pair<Method, ParameterValues>>();
94                 CollectionFilter.filter(_setters, setters,
95                                 new Condition<Pair<Method, ParameterValues>>() {
96                                         @Override
97                                         public boolean matches(Pair<Method, ParameterValues> aObject) {
98                                                 return !aObject.getFirst().getName().equals(name);
99                                         }
100
101                                 });
102                 if (_setters.size() == setters.size()) {
103                         throw new IllegalArgumentException(
104                                         "No setter configured by the name of '" + aName + "'");
105                 }
106                 _setters = setters;
107                 return this;
108         }
109
110         /**
111          * Creates the name of a setter based on the name of the setter without the
112          * "set" prefix.
113          * 
114          * @param aName
115          *            Setter name.
116          * @return Setter name.
117          */
118         private String createSetterName(String aName) {
119                 return "set" + aName.substring(0, 1).toUpperCase() + aName.substring(1);
120         }
121
122         /**
123          * Adds a given setter name to the setters.
124          * 
125          * @param aName
126          * @return Reference to the current object to allow call chaining.
127          */
128         public SetterConfiguration add(String aName) {
129                 final String name = createSetterName(aName);
130                 int oldlen = _setters.size();
131                 CollectionFilter.filter(getAllSetters(_class, false), _setters,
132                                 new Condition<Pair<Method, ParameterValues>>() {
133                                         @Override
134                                         public boolean matches(Pair<Method, ParameterValues> aObject) {
135                                                 return aObject.getFirst().getName().equals(name);
136                                         }
137
138                                 });
139                 if (_setters.size() == oldlen) {
140                         throw new IllegalArgumentException("No setter found for '" + aName
141                                         + "' in " + _class.getName());
142                 }
143                 return this;
144         }
145
146         /**
147          * Gets all setters for the current class.
148          * 
149          * @return List of all setters.
150          */
151         private static List<Pair<Method, ParameterValues>> getAllSetters(
152                         Class aClass, boolean aPublicOnly) {
153                 List<Pair<Method, ParameterValues>> result = new ArrayList<Pair<Method, ParameterValues>>();
154                 for (Method method : getAllMethods(aClass)) {
155                         if (!aPublicOnly || Modifier.isPublic(method.getModifiers())) {
156                                 if (method.getName().startsWith("set")
157                                                 && method.getParameterTypes().length == 1) {
158                                         method.setAccessible(true);
159                                         String name = getSetterName(method);
160                                         result
161                                                         .add(new Pair<Method, ParameterValues>(method,
162                                                                         new ParameterValues(new String[] { name },
163                                                                                         new Class[] { method
164                                                                                                         .getParameterTypes()[0] })));
165                                 }
166                         }
167                 }
168                 return result;
169         }
170
171         private static final List<Method> getAllMethods(Class aClass) {
172         List<Method> result = new ArrayList<Method>();
173         result.addAll(Arrays.asList(aClass.getDeclaredMethods()));
174         Class superClass = aClass.getSuperclass();
175         if ( superClass != null ) { 
176                 result.addAll(getAllMethods(superClass));
177         }
178         return result; 
179     }
180
181         /**
182          * Gets the required interfaces based on the configured setteres.
183          * 
184          * @return List of required interfaces.
185          */
186         public List<RequiredInterface> getRequiredInterfaces() {
187                 List<RequiredInterface> result = new ArrayList<RequiredInterface>();
188                 for (Pair<Method, ParameterValues> method : _setters) {
189                         result.addAll(method.getSecond().getRequiredInterfaces());
190                 }
191                 return result;
192         }
193
194         /**
195          * Invokes all configured setters with the appropriate values.
196          * 
197          * @param aScope
198          *            Scope within which invocation takes place.
199          * @param aObject
200          *            Object on which the invocation takes place.
201          */
202         public void inject(Scope aScope, Object aObject) {
203                 if (!_class.isInstance(aObject)) {
204                         throw new IllegalArgumentException("Object '" + aObject
205                                         + "' is not an instance of " + _class.getName());
206                 }
207                 for (Pair<Method, ParameterValues> setter : _setters) {
208                         Method method = setter.getFirst();
209                         ParameterValues values = setter.getSecond();
210
211                         try {
212                                 method.invoke(aObject, values.values(aScope));
213                         } catch (IllegalAccessException e) {
214                                 throw new SystemAssemblyException("Problem invoking " + method
215                                                 + " with " + values, e);
216                         } catch (InvocationTargetException e) {
217                                 throw new SystemAssemblyException("Problem invoking " + method
218                                                 + " with " + values, e);
219                         }
220                 }
221         }
222
223         /**
224          * Returns the parameter values for allowing detailed configuration of how
225          * parameter values are set.
226          * 
227          * @param aSetter
228          *            Setter name without the "set" prefix with the first character
229          *            converted to lower case.
230          * @return Parameter values.
231          */
232         public ParameterValues values(String aMethod) {
233                 String name = createSetterName(aMethod);
234                 for (Pair<Method, ParameterValues> method : _setters) {
235                         if (method.getFirst().getName().equals(name)) {
236                                 return method.getSecond();
237                         }
238                 }
239                 throw new IllegalArgumentException("No setter method '" + name
240                                 + "' found");
241         }
242
243         /**
244          * Gets the setter name for a given setter method. This is the name of the
245          * setter without the "set" prefix and with the first character converted to
246          * lowercase.
247          * 
248          * @param aMethod
249          *            Method.
250          * @return Setter name.
251          */
252         private static String getSetterName(Method aMethod) {
253                 String result = aMethod.getName().substring(3);
254                 return result.substring(0, 1).toLowerCase() + result.substring(1);
255         }
256
257 }