/*
* Copyright 2008 the original author or authors.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/
package org.wamblee.system.adapters;
-import java.awt.CompositeContext;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-
import org.wamblee.collections.CollectionFilter;
+
import org.wamblee.conditions.Condition;
import org.wamblee.conditions.FixedCondition;
+
import org.wamblee.general.Pair;
+
+import org.wamblee.reflection.ReflectionUtils;
+
import org.wamblee.system.core.DefaultProvidedInterface;
import org.wamblee.system.core.DefaultRequiredInterface;
import org.wamblee.system.core.ProvidedInterface;
import org.wamblee.system.core.Scope;
import org.wamblee.system.core.SystemAssemblyException;
+import java.awt.CompositeContext;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
/**
- * Represents the configuration for exposing the setters of a class as required
- * interfaces.
- *
+ * Represents the configuration for exposing the setters of a class as
+ * required interfaces.
+ *
* @author Erik Brakkee
*/
public class SetterConfiguration {
-
+ /**
+ * DOCUMENT ME!
+ */
private Class _class;
- private List<Pair<Method, ParameterValues>> _setters;
+ /**
+ * DOCUMENT ME!
+ */
+ private boolean publicOnly;
/**
- * Constructs the setter configuration. By default all setters are added.
- *
- * @param aClass
- * Class which is being configured.
+ * DOCUMENT ME!
*/
+ private Map<Method, ParameterValues> setters;
+
+/**
+ * Constructs the setter configuration. By default no setters are added.
+ *
+ * @param aClass
+ * Class which is being configured.
+ */
public SetterConfiguration(Class aClass) {
- _class = aClass;
- _setters = getAllSetters();
+ _class = aClass;
+ publicOnly = true;
+ setters = new HashMap<Method, ParameterValues>();
+ }
+
+ /**
+ * Makes sure that all available setters are used.
+ *
+ * @return DOCUMENT ME!
+ */
+ public SetterConfiguration initAllSetters() {
+ setters.clear();
+
+ for (Method method : getAllSetters(_class, publicOnly)) {
+ setters.put(method, createParameterValues(method));
+ }
+
+ return this;
+ }
+
+ /**
+ * Called to set whether non-public setters are also used. By
+ * default only public setters are used. The currently selected setters
+ * remain chosen.
+ *
+ * @param aIsNonPublic Non public flag.
+ *
+ * @return DOCUMENT ME!
+ */
+ public SetterConfiguration setNonPublic(boolean aIsNonPublic) {
+ publicOnly = !aIsNonPublic;
+
+ return this;
}
/**
* Removes all setters.
- *
+ *
* @return Reference to the current object to allow call chaining.
*/
public SetterConfiguration clear() {
- _setters.clear();
+ setters.clear();
+
return this;
}
/**
* Removes a setter from the set of methods.
- *
- * @param aName
- * Name of the setter to remove (without the "set" prefix).
+ *
+ * @param aName Name of the setter to remove.
+ *
* @return Reference to the current object to allow call chaining.
+ *
+ * @throws IllegalArgumentException DOCUMENT ME!
*/
public SetterConfiguration remove(String aName) {
- final String name = createSetterName(aName);
- List<Pair<Method,ParameterValues>> setters =
- new ArrayList<Pair<Method,ParameterValues>>();
- CollectionFilter.filter(_setters, setters, new Condition<Pair<Method,ParameterValues>>() {
- @Override
- public boolean matches(Pair<Method,ParameterValues> aObject) {
- return !aObject.getFirst().getName().equals(name);
+ for (Method method : setters.keySet()) {
+ if (method.getName().equals(aName)) {
+ setters.remove(method);
+
+ return this;
}
+ }
+
+ throw new IllegalArgumentException(
+ "No method configured by the name of '" + aName + "'");
+ }
+
+ /**
+ * Removes the method from the set of methods.
+ *
+ * @param aMethod Method to remove.
+ *
+ * @return
+ *
+ * @throws RuntimeException DOCUMENT ME!
+ * @throws IllegalArgumentException DOCUMENT ME!
+ */
+ public SetterConfiguration remove(Method aMethod) {
+ if (!aMethod.getDeclaringClass().isAssignableFrom(_class)) {
+ throw new RuntimeException("Method " + aMethod
+ + " not found in class " + _class + " or its superclasses");
+ }
- });
- if ( _setters.size() == setters.size()) {
- throw new IllegalArgumentException("No setter configured by the name of '" + aName + "'");
+ for (Method method : setters.keySet()) {
+ if (method.equals(aMethod)) {
+ setters.remove(method);
+
+ return this;
+ }
}
- _setters = setters;
- return this;
+
+ throw new IllegalArgumentException("Method '" + aMethod
+ + "' was not configured. ");
}
/**
- * Creates the name of a setter based on the name of the setter without
- * the "set" prefix.
- * @param aName Setter name.
- * @return Setter name.
+ * Adds a given setter name to the setters.
+ *
+ * @param aName Name of a setter method.
+ *
+ * @return Reference to the current object to allow call chaining.
+ *
+ * @throws IllegalArgumentException DOCUMENT ME!
*/
- private String createSetterName(String aName) {
- return "set" + aName.substring(0, 1).toUpperCase()
- + aName.substring(1);
+ public SetterConfiguration add(final String aName) {
+ int oldlen = setters.size();
+ List<Method> methods = new ArrayList<Method>();
+ CollectionFilter.filter(getAllSetters(_class, publicOnly), methods,
+ new Condition<Method>() {
+ @Override
+ public boolean matches(Method aObject) {
+ return aObject.getName().equals(aName);
+ }
+ });
+
+ if (methods.size() == 0) {
+ throw new IllegalArgumentException("Method '" + aName
+ + "' not found in " + _class.getName());
+ }
+
+ // TODO is it possible to get more than one setter here in case the subclass overrides
+ // the baseclass method?
+ setters.put(methods.get(0), createParameterValues(methods.get(0)));
+
+ return this;
}
/**
- * Adds a given setter name to the setters.
- * @param aName
- * @return Reference to the current object to allow call chaining.
+ * Adds a given setter identified by the type it accepts to the
+ * list of setters.N
+ *
+ * @param aType Type to look for. Note that this must be the exact type as
+ * autoboxing and autounboxing is not used.
+ *
+ * @return Reference to the current object to allow call chaining.
+ *
+ * @throws IllegalArgumentException In case no setter is found or multiple
+ * setters are found.
*/
- public SetterConfiguration add(String aName) {
- final String name = createSetterName(aName);
- int oldlen = _setters.size();
- CollectionFilter.filter(getAllSetters(), _setters,
- new Condition<Pair<Method,ParameterValues>>() {
- @Override
- public boolean matches(Pair<Method,ParameterValues> aObject) {
- return aObject.getFirst().getName().equals(name);
- }
+ public SetterConfiguration addSetter(final Class aType) {
+ List<Method> result = new ArrayList<Method>();
+ CollectionFilter.filter(getAllSetters(_class, publicOnly), result,
+ new Condition<Method>() {
+ @Override
+ public boolean matches(Method aObject) {
+ Class type = aObject.getParameterTypes()[0];
+
+ return type.equals(aType);
+ }
+ });
- });
- if ( _setters.size() == oldlen) {
- throw new IllegalArgumentException("No setter found for '" + aName + "'");
+ if (result.size() == 0) {
+ throw new IllegalArgumentException("No setter found in class '"
+ + _class.getName() + "' that has a setter with argument type '"
+ + aType.getName() + "'");
}
+
+ if (result.size() > 1) {
+ String setters = "";
+
+ for (Method method : result) {
+ setters += (method.getName() + " ");
+ }
+
+ throw new IllegalArgumentException(
+ "Multiple setters found in class '" + _class.getName()
+ + " that accept type '" + aType.getName() + "': " + setters);
+ }
+
+ Method method = result.get(0);
+ setters.put(method, createParameterValues(method));
+
return this;
}
/**
- * Gets all setters for the current class.
- * @return List of all setters.
+ * Gets all setters for the current class.
+ *
+ * @param aClass DOCUMENT ME!
+ * @param aPublicOnly DOCUMENT ME!
+ *
+ * @return List of all setters.
*/
- private List<Pair<Method, ParameterValues>> getAllSetters() {
- List<Pair<Method,ParameterValues>> result =
- new ArrayList<Pair<Method, ParameterValues>>();
- for (Method method : _class.getMethods()) {
- if (method.getName().startsWith("set")
- && method.getParameterTypes().length == 1) {
- String name = getSetterName(method);
- result.add(new Pair<Method,ParameterValues>(method,
- new ParameterValues(
- new String[] { name }, new Class[] { method.getParameterTypes()[0] })));
+ public static List<Method> getAllSetters(Class aClass, boolean aPublicOnly) {
+ List<Method> result = new ArrayList<Method>();
+
+ for (Method method : getAllMethods(aClass)) {
+ if (!aPublicOnly || Modifier.isPublic(method.getModifiers())) {
+ if (method.getName().startsWith("set")
+ && (method.getParameterTypes().length == 1)) {
+ method.setAccessible(true);
+ result.add(method);
+ }
}
}
+
return result;
}
-
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param aMethod DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ private static ParameterValues createParameterValues(Method aMethod) {
+ Class[] paramTypes = aMethod.getParameterTypes();
+ String[] paramNames = new String[paramTypes.length];
+
+ for (int i = 0; i < paramTypes.length; i++) {
+ paramNames[i] = aMethod.getName() + "." + i;
+ }
+
+ return new ParameterValues(paramNames, paramTypes);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param aClass DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ private static final List<Method> getAllMethods(Class aClass) {
+ return ReflectionUtils.getAllMethods(aClass);
+ }
+
/**
* Gets the required interfaces based on the configured setteres.
- * @return List of required interfaces.
+ *
+ * @return List of required interfaces.
*/
- public List<RequiredInterface> getRequiredInterfaces() {
+ public List<RequiredInterface> getRequiredInterfaces() {
List<RequiredInterface> result = new ArrayList<RequiredInterface>();
- for (Pair<Method,ParameterValues> method: _setters) {
- result.addAll(method.getSecond().getRequiredInterfaces());
+
+ for (Method method : setters.keySet()) {
+ result.addAll(setters.get(method).getRequiredInterfaces());
}
- return result;
+
+ return result;
}
-
-
+
/**
- * Invokes all configured setters with the appropriate values.
- * @param aScope Scope within which invocation takes place.
- * @param aObject Object on which the invocation takes place.
+ * Invokes all configured setters with the appropriate values.
+ *
+ * @param aScope Scope within which invocation takes place.
+ * @param aObject Object on which the invocation takes place.
+ *
+ * @throws IllegalArgumentException DOCUMENT ME!
+ * @throws SystemAssemblyException DOCUMENT ME!
*/
- public void invoke(Scope aScope, Object aObject) {
- if ( !_class.isInstance(aObject)) {
- throw new IllegalArgumentException("Object '" + aObject + "' is not an instance of "
- + _class.getName());
+ public void inject(Scope aScope, Object aObject) {
+ if (!_class.isInstance(aObject)) {
+ throw new IllegalArgumentException("Object '" + aObject
+ + "' is not an instance of " + _class.getName());
}
- for (Pair<Method,ParameterValues> setter: _setters) {
- Method method = setter.getFirst();
- ParameterValues values = setter.getSecond();
-
+
+ for (Method method : setters.keySet()) {
+ ParameterValues values = setters.get(method);
+
try {
method.invoke(aObject, values.values(aScope));
} catch (IllegalAccessException e) {
- throw new SystemAssemblyException("Problem invoking " + method + " with " + values, e);
- } catch (InvocationTargetException e) {
- throw new SystemAssemblyException("Problem invoking " + method + " with " + values, e);
+ throw new SystemAssemblyException("Problem invoking " + method
+ + " with " + values, e);
+ } catch (InvocationTargetException e) {
+ throw new SystemAssemblyException("Problem invoking " + method
+ + " with " + values, e);
}
}
}
-
- /**
- * Returns the parameter values for allowing detailed configuration of how
- * parameter values are set.
- * @param aSetter Setter name without the "set" prefix with the first
- * character converted to lower case.
- * @return Parameter values.
- */
- public ParameterValues values(String aMethod) {
- String name = createSetterName(aMethod);
- for (Pair<Method,ParameterValues> method: _setters) {
- if ( method.getFirst().getName().equals(name) ) {
- return method.getSecond();
+
+ /**
+ * Returns the parameter values for allowing detailed configuration
+ * of how parameter values are set.
+ *
+ * @param aMethod Setter name without the "set" prefix with the first
+ * character converted to lower case.
+ *
+ * @return Parameter values.
+ *
+ * @throws IllegalArgumentException DOCUMENT ME!
+ */
+ public ParameterValues values(String aMethod) {
+ for (Method method : setters.keySet()) {
+ if (method.getName().equals(aMethod)) {
+ return setters.get(method);
}
}
- throw new IllegalArgumentException("No setter method '" + name + "' found");
+
+ throw new IllegalArgumentException("No setter method '" + aMethod
+ + "' found");
}
/**
- * Gets the setter name for a given setter method. This is the name of the
- * setter without the "set" prefix and with the first character converted to
- * lowercase.
- * @param aMethod Method.
- * @return Setter name.
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
*/
- private String getSetterName(Method aMethod) {
- String result = aMethod.getName().substring(3);
- return result.substring(0,1).toLowerCase() + result.substring(1);
+ public List<Method> getSetters() {
+ return new ArrayList<Method>(setters.keySet());
}
-
}