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(_class, 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(_class)) {
144 throw new RuntimeException("Method " + aMethod +
145 " not found in class " + _class + " 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 int oldlen = setters.size();
171 List<Method> methods = new ArrayList<Method>();
172 CollectionFilter.filter(getAllSetters(_class, publicOnly), methods,
173 new Condition<Method>() {
175 public boolean matches(Method aObject) {
176 return aObject.getName().equals(aName);
180 if (methods.size() == 0) {
181 throw new IllegalArgumentException("Method '" + aName +
182 "' not found in " + _class.getName());
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)));
194 * Adds a given setter identified by the type it accepts to the list of
198 * Type to look for. Note that this must be the exact type as
199 * autoboxing and autounboxing is not used.
201 * @return Reference to the current object to allow call chaining.
203 * @throws IllegalArgumentException
204 * In case no setter is found or multiple setters are found.
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>() {
211 public boolean matches(Method aObject) {
212 Class type = aObject.getParameterTypes()[0];
214 return type.equals(aType);
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() + "'");
224 if (result.size() > 1) {
227 for (Method method : result) {
228 setters += (method.getName() + " ");
231 throw new IllegalArgumentException(
232 "Multiple setters found in class '" + _class.getName() +
233 " that accept type '" + aType.getName() + "': " + setters);
236 Method method = result.get(0);
237 setters.put(method, createParameterValues(method));
243 * Gets all setters for the current class.
246 * @return List of all setters.
248 public static List<Method> getAllSetters(Class aClass, boolean aPublicOnly) {
249 List<Method> result = new ArrayList<Method>();
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);
264 private static ParameterValues createParameterValues(Method aMethod) {
265 Class[] paramTypes = aMethod.getParameterTypes();
266 String[] paramNames = new String[paramTypes.length];
268 for (int i = 0; i < paramTypes.length; i++) {
269 paramNames[i] = aMethod.getName() + "." + i;
272 return new ParameterValues(paramNames, paramTypes);
275 private static final List<Method> getAllMethods(Class aClass) {
276 return ReflectionUtils.getAllMethods(aClass);
280 * Gets the required interfaces based on the configured setteres.
282 * @return List of required interfaces.
284 public List<RequiredInterface> getRequiredInterfaces() {
285 List<RequiredInterface> result = new ArrayList<RequiredInterface>();
287 for (Method method : setters.keySet()) {
288 result.addAll(setters.get(method).getRequiredInterfaces());
295 * Invokes all configured setters with the appropriate values.
298 * Scope within which invocation takes place.
300 * Object on which the invocation takes place.
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());
309 for (Method method : setters.keySet()) {
310 ParameterValues values = setters.get(method);
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);
325 * Returns the parameter values for allowing detailed configuration of how
326 * parameter values are set.
329 * Setter name without the "set" prefix with the first character
330 * converted to lower case.
332 * @return Parameter values.
335 public ParameterValues values(String aMethod) {
336 for (Method method : setters.keySet()) {
337 if (method.getName().equals(aMethod)) {
338 return setters.get(method);
342 throw new IllegalArgumentException("No setter method '" + aMethod +
346 public List<Method> getSetters() {
347 return new ArrayList<Method>(setters.keySet());