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 org.wamblee.collections.CollectionFilter;
20 import org.wamblee.conditions.Condition;
21 import org.wamblee.conditions.FixedCondition;
23 import org.wamblee.general.Pair;
25 import org.wamblee.reflection.ReflectionUtils;
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;
34 import java.awt.CompositeContext;
36 import java.lang.reflect.InvocationTargetException;
37 import java.lang.reflect.Method;
38 import java.lang.reflect.Modifier;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.HashMap;
43 import java.util.List;
48 * Represents the configuration for exposing the setters of a class as required
51 * @author Erik Brakkee
53 public class SetterConfiguration {
56 private boolean publicOnly;
58 private Map<Method, ParameterValues> setters;
61 * Constructs the setter configuration. By default no setters are added.
64 * Class which is being configured.
66 public SetterConfiguration(Class aClass) {
69 setters = new HashMap<Method, ParameterValues>();
73 * Makes sure that all available setters are used.
76 public SetterConfiguration initAllSetters() {
79 for (Method method : getAllSetters(clazz, publicOnly)) {
80 setters.put(method, createParameterValues(method));
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.
94 public SetterConfiguration setNonPublic(boolean aIsNonPublic) {
95 publicOnly = !aIsNonPublic;
101 * Removes all setters.
103 * @return Reference to the current object to allow call chaining.
105 public SetterConfiguration clear() {
112 * Removes a setter from the set of methods.
115 * Name of the setter to remove.
117 * @return Reference to the current object to allow call chaining.
120 public SetterConfiguration remove(String aName) {
121 for (Method method : setters.keySet()) {
122 if (method.getName().equals(aName)) {
123 setters.remove(method);
129 throw new IllegalArgumentException(
130 "No method configured by the name of '" + aName + "'");
134 * Removes the method from the set of methods.
142 public SetterConfiguration remove(Method aMethod) {
143 if (!aMethod.getDeclaringClass().isAssignableFrom(clazz)) {
144 throw new RuntimeException("Method " + aMethod +
145 " not found in class " + clazz + " or its superclasses");
148 for (Method method : setters.keySet()) {
149 if (method.equals(aMethod)) {
150 setters.remove(method);
156 throw new IllegalArgumentException("Method '" + aMethod +
157 "' was not configured. ");
161 * Adds a given setter name to the setters.
164 * Name of a setter method.
166 * @return Reference to the current object to allow call chaining.
169 public SetterConfiguration add(final String aName) {
170 List<Method> methods = new ArrayList<Method>();
171 CollectionFilter.filter(getAllSetters(clazz, publicOnly), methods,
172 new Condition<Method>() {
174 public boolean matches(Method aObject) {
175 return aObject.getName().equals(aName);
179 if (methods.size() == 0) {
180 throw new IllegalArgumentException("Method '" + aName +
181 "' not found in " + clazz.getName());
184 // TODO is it possible to get more than one setter here in case the
185 // subclass overrides
186 // the baseclass method?
187 setters.put(methods.get(0), createParameterValues(methods.get(0)));
193 * Adds a given setter identified by the type it accepts to the list of
197 * Type to look for. Note that this must be the exact type as
198 * autoboxing and autounboxing is not used.
200 * @return Reference to the current object to allow call chaining.
202 * @throws IllegalArgumentException
203 * In case no setter is found or multiple setters are found.
205 public SetterConfiguration addSetter(final Class aType) {
206 List<Method> result = new ArrayList<Method>();
207 CollectionFilter.filter(getAllSetters(clazz, publicOnly), result,
208 new Condition<Method>() {
210 public boolean matches(Method aObject) {
211 Class type = aObject.getParameterTypes()[0];
213 return type.equals(aType);
217 if (result.size() == 0) {
218 throw new IllegalArgumentException("No setter found in class '" +
219 clazz.getName() + "' that has a setter with argument type '" +
220 aType.getName() + "'");
223 if (result.size() > 1) {
224 StringBuffer setters = new StringBuffer();
226 for (Method method : result) {
227 setters.append((method.getName() + " "));
230 throw new IllegalArgumentException(
231 "Multiple setters found in class '" + clazz.getName() +
232 " that accept type '" + aType.getName() + "': " + setters);
235 Method method = result.get(0);
236 setters.put(method, createParameterValues(method));
242 * Gets all setters for the current class.
245 * @return List of all setters.
247 public static List<Method> getAllSetters(Class aClass, boolean aPublicOnly) {
248 List<Method> result = new ArrayList<Method>();
250 for (Method method : getAllMethods(aClass)) {
251 if (!aPublicOnly || Modifier.isPublic(method.getModifiers())) {
252 if (method.getName().startsWith("set") &&
253 (method.getParameterTypes().length == 1)) {
254 method.setAccessible(true);
263 private static ParameterValues createParameterValues(Method aMethod) {
264 Class[] paramTypes = aMethod.getParameterTypes();
265 String[] paramNames = new String[paramTypes.length];
267 for (int i = 0; i < paramTypes.length; i++) {
268 paramNames[i] = aMethod.getName() + "." + i;
271 return new ParameterValues(paramNames, paramTypes);
274 private static final List<Method> getAllMethods(Class aClass) {
275 return ReflectionUtils.getAllMethods(aClass);
279 * Gets the required interfaces based on the configured setteres.
281 * @return List of required interfaces.
283 public List<RequiredInterface> getRequiredInterfaces() {
284 List<RequiredInterface> result = new ArrayList<RequiredInterface>();
286 for (Method method : setters.keySet()) {
287 result.addAll(setters.get(method).getRequiredInterfaces());
294 * Invokes all configured setters with the appropriate values.
297 * Scope within which invocation takes place.
299 * Object on which the invocation takes place.
302 public void inject(Scope aScope, Object aObject) {
303 if (!clazz.isInstance(aObject)) {
304 throw new IllegalArgumentException("Object '" + aObject +
305 "' is not an instance of " + clazz.getName());
308 for (Method method : setters.keySet()) {
309 ParameterValues values = setters.get(method);
312 method.invoke(aObject, values.values(aScope));
313 } catch (IllegalAccessException e) {
314 throw new SystemAssemblyException("Problem invoking " + method +
315 " with " + values, e);
316 } catch (InvocationTargetException e) {
317 throw new SystemAssemblyException("Problem invoking " + method +
318 " with " + values, e);
324 * Returns the parameter values for allowing detailed configuration of how
325 * parameter values are set.
328 * Setter name without the "set" prefix with the first character
329 * converted to lower case.
331 * @return Parameter values.
334 public ParameterValues values(String aMethod) {
335 for (Method method : setters.keySet()) {
336 if (method.getName().equals(aMethod)) {
337 return setters.get(method);
341 throw new IllegalArgumentException("No setter method '" + aMethod +
345 public List<Method> getSetters() {
346 return new ArrayList<Method>(setters.keySet());