(no commit message)
[utils] / system / general / src / main / java / org / wamblee / system / adapters / SetterConfiguration.java
index 374edae6cb12a3afb767070985dbc444f54bcd7a..18fde542b0050ede0ea92acbdde55093ccfc8cda 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2008 the original author or authors.
+ * Copyright 2005-2010 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.
  */
 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.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.wamblee.collections.CollectionFilter;
 import org.wamblee.conditions.Condition;
-import org.wamblee.conditions.FixedCondition;
-import org.wamblee.general.Pair;
-import org.wamblee.system.core.DefaultProvidedInterface;
-import org.wamblee.system.core.DefaultRequiredInterface;
-import org.wamblee.system.core.ProvidedInterface;
+import org.wamblee.reflection.ReflectionUtils;
 import org.wamblee.system.core.RequiredInterface;
 import org.wamblee.system.core.Scope;
 import org.wamblee.system.core.SystemAssemblyException;
@@ -39,20 +37,50 @@ import org.wamblee.system.core.SystemAssemblyException;
  * @author Erik Brakkee
  */
 public class SetterConfiguration {
+    private Class clazz;
 
-    private Class _class;
+    private boolean publicOnly;
 
-    private List<Pair<Method, ParameterValues>> _setters;
+    private Map<Method, ParameterValues> setters;
 
     /**
-     * Constructs the setter configuration. By default all setters are added.
+     * 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();
+        clazz = 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(clazz, 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;
     }
 
     /**
@@ -61,7 +89,8 @@ public class SetterConfiguration {
      * @return Reference to the current object to allow call chaining.
      */
     public SetterConfiguration clear() {
-        _setters.clear();
+        setters.clear();
+
         return this;
     }
 
@@ -69,143 +98,238 @@ public class SetterConfiguration {
      * Removes a setter from the set of methods.
      * 
      * @param aName
-     *            Name of the setter to remove (without the "set" prefix).
+     *            Name of the setter to remove.
+     * 
      * @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);
-            }
+        for (Method method : setters.keySet()) {
+            if (method.getName().equals(aName)) {
+                setters.remove(method);
 
-        });
-        if ( _setters.size() == setters.size()) { 
-            throw new IllegalArgumentException("No setter configured by the name of '" + aName + "'");
+                return this;
+            }
         }
-        _setters = setters;
-        return this;
+
+        throw new IllegalArgumentException(
+            "No method configured by the name of '" + aName + "'");
     }
 
     /**
-     * Creates the name of a setter based on the name of the setter without
-     * the "set" prefix. 
-     * @param aName Setter name. 
-     * @return Setter name. 
+     * Removes the method from the set of methods.
+     * 
+     * @param aMethod
+     *            Method to remove.
+     * 
+     * @return
+     * 
      */
-    private String createSetterName(String aName) {
-        return "set" + aName.substring(0, 1).toUpperCase()
-                + aName.substring(1);
+    public SetterConfiguration remove(Method aMethod) {
+        if (!aMethod.getDeclaringClass().isAssignableFrom(clazz)) {
+            throw new RuntimeException("Method " + aMethod +
+                " not found in class " + clazz + " or its superclasses");
+        }
+
+        for (Method method : setters.keySet()) {
+            if (method.equals(aMethod)) {
+                setters.remove(method);
+
+                return this;
+            }
+        }
+
+        throw new IllegalArgumentException("Method '" + aMethod +
+            "' was not configured. ");
     }
 
     /**
-     * Adds a given setter name to the setters. 
+     * Adds a given setter name to the setters.
+     * 
      * @param aName
-     * @return Reference to the current object to allow call chaining. 
+     *            Name of a setter method.
+     * 
+     * @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 + "' in " + _class.getName());
+    public SetterConfiguration add(final String aName) {
+        List<Method> methods = new ArrayList<Method>();
+        CollectionFilter.filter(getAllSetters(clazz, 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 " + clazz.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;
     }
 
     /**
-     * Gets all setters for the current class. 
-     * @return List of all setters. 
+     * 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.
      */
-    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 SetterConfiguration addSetter(final Class aType) {
+        List<Method> result = new ArrayList<Method>();
+        CollectionFilter.filter(getAllSetters(clazz, 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 '" +
+                clazz.getName() + "' that has a setter with argument type '" +
+                aType.getName() + "'");
+        }
+
+        if (result.size() > 1) {
+            StringBuffer settersString = new StringBuffer();
+
+            for (Method method : result) {
+                settersString.append((method.getName() + " "));
+            }
+
+            throw new IllegalArgumentException(
+                "Multiple setters found in class '" + clazz.getName() +
+                    " that accept type '" + aType.getName() + "': " +
+                    settersString);
+        }
+
+        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. 
+     * 
+     * @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.
+     * 
      */
     public void inject(Scope aScope, Object aObject) {
-        if ( !_class.isInstance(aObject)) { 
-            throw new IllegalArgumentException("Object '" + aObject + "' is not an instance of " 
-                    + _class.getName());
+        if (!clazz.isInstance(aObject)) {
+            throw new IllegalArgumentException("Object '" + aObject +
+                "' is not an instance of " + clazz.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. 
+     * 
+     * @param aMethod
+     *            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();
+    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. 
-     */
-    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());
     }
-    
 }