(no commit message)
[utils] / system / general / src / main / java / org / wamblee / system / adapters / SetterConfiguration.java
index ec99512f3fa99843c9cc79f552e770ec9539dc4f..57323a54b409531f53667e9d771c4f9d542a0060 100644 (file)
@@ -18,13 +18,19 @@ package org.wamblee.system.adapters;
 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;
 
 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;
@@ -40,172 +46,258 @@ import org.wamblee.system.core.SystemAssemblyException;
  */
 public class SetterConfiguration {
 
-    private Class _class;
-
-    private List<Pair<Method, ParameterValues>> _setters;
-
-    /**
-     * Constructs the setter configuration. By default all setters are added.
-     * 
-     * @param aClass
-     *            Class which is being configured.
-     */
-    public SetterConfiguration(Class aClass) {
-        _class = aClass;
-        _setters = getAllSetters();
-    }
-
-    /**
-     * Removes all setters.
-     * 
-     * @return Reference to the current object to allow call chaining.
-     */
-    public SetterConfiguration 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).
-     * @return Reference to the current object to allow call chaining.
-     */
-    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);
-            }
+       private Class _class;
+       private boolean _publicOnly;
 
-        });
-        if ( _setters.size() == setters.size()) { 
-            throw new IllegalArgumentException("No setter configured by the name of '" + aName + "'");
-        }
-        _setters = setters;
-        return this;
-    }
-
-    /**
-     * Creates the name of a setter based on the name of the setter without
-     * the "set" prefix. 
-     * @param aName Setter name. 
-     * @return Setter name. 
-     */
-    private String createSetterName(String aName) {
-        return "set" + aName.substring(0, 1).toUpperCase()
-                + aName.substring(1);
-    }
-
-    /**
-     * Adds a given setter name to the setters. 
-     * @param aName
-     * @return Reference to the current object to allow call chaining. 
-     */
-    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);
-                    }
-
-                });
-        if ( _setters.size() == oldlen) { 
-            throw new IllegalArgumentException("No setter found for '" + aName + "'");
-        }
-        return this;
-    }
-
-    /**
-     * Gets all setters for the current class. 
-     * @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] })));
-            }
-        }
-        return result;
-    }
-    
-    /**
-     * Gets the required interfaces based on the configured setteres.
-     * @return List of required interfaces. 
-     */
-    public List<RequiredInterface> getRequiredInterfaces() { 
-        List<RequiredInterface> result = new ArrayList<RequiredInterface>();
-        for (Pair<Method,ParameterValues> method: _setters) {
-            result.addAll(method.getSecond().getRequiredInterfaces());
-        }
-        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. 
-     */
-    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(); 
-            
-            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);
-            }
-        }
-    }
-    
-    /**
-     * 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();
+       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;
+               _publicOnly = true;
+               _setters = new HashMap<Method, ParameterValues>();
+       }
+
+       /**
+        * Makes sure that all available setters are used. 
+        */
+       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.
+        */
+       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();
+               return this;
+       }
+
+       /**
+        * Removes a setter from the set of methods.
+        * 
+        * @param aName
+        *            Name of the setter to remove.
+        * @return Reference to the current object to allow call chaining.
+        */
+       public SetterConfiguration remove(String aName) {
+               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
+        */
+       public SetterConfiguration remove(Method aMethod) { 
+           if ( !aMethod.getDeclaringClass().isAssignableFrom(_class) ) { 
+               throw new RuntimeException("Method " + aMethod + " not found in class " + _class + " or its superclasses");
+           }
+           for (Method method : _setters.keySet()) {
+            if (method.equals(aMethod)) {
+                _setters.remove(method);
+                return this;
             }
         }
-        throw new IllegalArgumentException("No setter method '" + name + "' 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. 
-     */
-    private String getSetterName(Method aMethod) {
-        String result = aMethod.getName().substring(3);
-        return result.substring(0,1).toLowerCase() +  result.substring(1);
-    }
-    
+        throw new IllegalArgumentException(
+                "Method '" + aMethod + "' was not configured. ");
+       }
+       
+       /**
+        * 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.
+        */
+       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 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 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 (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.
+        */
+       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;
+       }
+
+       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);
+       }
+
+       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.
+        */
+       public List<RequiredInterface> getRequiredInterfaces() {
+               List<RequiredInterface> result = new ArrayList<RequiredInterface>();
+               for (Method method : _setters.keySet()) {
+                       result.addAll(_setters.get(method).getRequiredInterfaces());
+               }
+               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.
+        */
+       public void inject(Scope aScope, Object aObject) {
+               if (!_class.isInstance(aObject)) {
+                       throw new IllegalArgumentException("Object '" + aObject
+                                       + "' is not an instance of " + _class.getName());
+               }
+               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);
+                       }
+               }
+       }
+
+       /**
+        * 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) {
+               for (Method method : _setters.keySet()) {
+                       if (method.getName().equals(aMethod)) {
+                               return _setters.get(method);
+                       }
+               }
+               throw new IllegalArgumentException("No setter method '" + aMethod
+                               + "' found");
+       }
+
+       public List<Method> getSetters() { 
+               return new ArrayList<Method>(_setters.keySet());
+       }
 }