2 * Copyright 2005-2010 the original author or authors.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org.wamblee.system.adapters;
18 import java.lang.reflect.InvocationTargetException;
19 import java.lang.reflect.Method;
20 import java.lang.reflect.Modifier;
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.List;
26 import org.wamblee.collections.CollectionFilter;
27 import org.wamblee.conditions.Condition;
28 import org.wamblee.reflection.ReflectionUtils;
29 import org.wamblee.system.core.RequiredInterface;
30 import org.wamblee.system.core.Scope;
31 import org.wamblee.system.core.SystemAssemblyException;
34 * Represents the configuration for exposing the setters of a class as required
37 * @author Erik Brakkee
39 public class SetterConfiguration {
42 private boolean publicOnly;
44 private Map<Method, ParameterValues> setters;
47 * Constructs the setter configuration. By default no setters are added.
50 * Class which is being configured.
52 public SetterConfiguration(Class aClass) {
55 setters = new HashMap<Method, ParameterValues>();
59 * Makes sure that all available setters are used.
62 public SetterConfiguration initAllSetters() {
65 for (Method method : getAllSetters(clazz, publicOnly)) {
66 setters.put(method, createParameterValues(method));
73 * Called to set whether non-public setters are also used. By default only
74 * public setters are used. The currently selected setters remain chosen.
80 public SetterConfiguration setNonPublic(boolean aIsNonPublic) {
81 publicOnly = !aIsNonPublic;
87 * Removes all setters.
89 * @return Reference to the current object to allow call chaining.
91 public SetterConfiguration clear() {
98 * Removes a setter from the set of methods.
101 * Name of the setter to remove.
103 * @return Reference to the current object to allow call chaining.
106 public SetterConfiguration remove(String aName) {
107 for (Method method : setters.keySet()) {
108 if (method.getName().equals(aName)) {
109 setters.remove(method);
115 throw new IllegalArgumentException(
116 "No method configured by the name of '" + aName + "'");
120 * Removes the method from the set of methods.
128 public SetterConfiguration remove(Method aMethod) {
129 if (!aMethod.getDeclaringClass().isAssignableFrom(clazz)) {
130 throw new RuntimeException("Method " + aMethod +
131 " not found in class " + clazz + " or its superclasses");
134 for (Method method : setters.keySet()) {
135 if (method.equals(aMethod)) {
136 setters.remove(method);
142 throw new IllegalArgumentException("Method '" + aMethod +
143 "' was not configured. ");
147 * Adds a given setter name to the setters.
150 * Name of a setter method.
152 * @return Reference to the current object to allow call chaining.
155 public SetterConfiguration add(final String aName) {
156 List<Method> methods = new ArrayList<Method>();
157 CollectionFilter.filter(getAllSetters(clazz, publicOnly), methods,
158 new Condition<Method>() {
160 public boolean matches(Method aObject) {
161 return aObject.getName().equals(aName);
165 if (methods.size() == 0) {
166 throw new IllegalArgumentException("Method '" + aName +
167 "' not found in " + clazz.getName());
170 // TODO is it possible to get more than one setter here in case the
171 // subclass overrides
172 // the baseclass method?
173 setters.put(methods.get(0), createParameterValues(methods.get(0)));
179 * Adds a given setter identified by the type it accepts to the list of
183 * Type to look for. Note that this must be the exact type as
184 * autoboxing and autounboxing is not used.
186 * @return Reference to the current object to allow call chaining.
188 * @throws IllegalArgumentException
189 * In case no setter is found or multiple setters are found.
191 public SetterConfiguration addSetter(final Class aType) {
192 List<Method> result = new ArrayList<Method>();
193 CollectionFilter.filter(getAllSetters(clazz, publicOnly), result,
194 new Condition<Method>() {
196 public boolean matches(Method aObject) {
197 Class type = aObject.getParameterTypes()[0];
199 return type.equals(aType);
203 if (result.size() == 0) {
204 throw new IllegalArgumentException("No setter found in class '" +
205 clazz.getName() + "' that has a setter with argument type '" +
206 aType.getName() + "'");
209 if (result.size() > 1) {
210 StringBuffer settersString = new StringBuffer();
212 for (Method method : result) {
213 settersString.append((method.getName() + " "));
216 throw new IllegalArgumentException(
217 "Multiple setters found in class '" + clazz.getName() +
218 " that accept type '" + aType.getName() + "': " +
222 Method method = result.get(0);
223 setters.put(method, createParameterValues(method));
229 * Gets all setters for the current class.
232 * @return List of all setters.
234 public static List<Method> getAllSetters(Class aClass, boolean aPublicOnly) {
235 List<Method> result = new ArrayList<Method>();
237 for (Method method : getAllMethods(aClass)) {
238 if (!aPublicOnly || Modifier.isPublic(method.getModifiers())) {
239 if (method.getName().startsWith("set") &&
240 (method.getParameterTypes().length == 1)) {
241 method.setAccessible(true);
250 private static ParameterValues createParameterValues(Method aMethod) {
251 Class[] paramTypes = aMethod.getParameterTypes();
252 String[] paramNames = new String[paramTypes.length];
254 for (int i = 0; i < paramTypes.length; i++) {
255 paramNames[i] = aMethod.getName() + "." + i;
258 return new ParameterValues(paramNames, paramTypes);
261 private static final List<Method> getAllMethods(Class aClass) {
262 return ReflectionUtils.getAllMethods(aClass);
266 * Gets the required interfaces based on the configured setteres.
268 * @return List of required interfaces.
270 public List<RequiredInterface> getRequiredInterfaces() {
271 List<RequiredInterface> result = new ArrayList<RequiredInterface>();
273 for (Method method : setters.keySet()) {
274 result.addAll(setters.get(method).getRequiredInterfaces());
281 * Invokes all configured setters with the appropriate values.
284 * Scope within which invocation takes place.
286 * Object on which the invocation takes place.
289 public void inject(Scope aScope, Object aObject) {
290 if (!clazz.isInstance(aObject)) {
291 throw new IllegalArgumentException("Object '" + aObject +
292 "' is not an instance of " + clazz.getName());
295 for (Method method : setters.keySet()) {
296 ParameterValues values = setters.get(method);
299 method.invoke(aObject, values.values(aScope));
300 } catch (IllegalAccessException e) {
301 throw new SystemAssemblyException("Problem invoking " + method +
302 " with " + values, e);
303 } catch (InvocationTargetException e) {
304 throw new SystemAssemblyException("Problem invoking " + method +
305 " with " + values, e);
311 * Returns the parameter values for allowing detailed configuration of how
312 * parameter values are set.
315 * Setter name without the "set" prefix with the first character
316 * converted to lower case.
318 * @return Parameter values.
321 public ParameterValues values(String aMethod) {
322 for (Method method : setters.keySet()) {
323 if (method.getName().equals(aMethod)) {
324 return setters.get(method);
328 throw new IllegalArgumentException("No setter method '" + aMethod +
332 public List<Method> getSetters() {
333 return new ArrayList<Method>(setters.keySet());