From bf63e81664d81262daa3bc276ff2b70bb9e8dfcb Mon Sep 17 00:00:00 2001
From: erik <erik@77661180-640e-0410-b3a8-9f9b13e6d0e0>
Date: Mon, 9 Jun 2008 20:49:44 +0000
Subject: [PATCH]

---
 system/TODO.txt                               |   1 -
 .../system/adapters/SetterConfiguration.java  | 364 +++++++++-------
 .../system/core/DefaultRequiredInterface.java |   5 +
 .../adapters/SetterConfigurationTest.java     | 397 +++++++++++-------
 .../java/org/wamblee/system/adapters/X5.java  |   9 +
 .../core/DefaultInterfaceDescriptorTest.java  |  10 +
 6 files changed, 471 insertions(+), 315 deletions(-)

diff --git a/system/TODO.txt b/system/TODO.txt
index 14020c35..e1ba91c6 100644
--- a/system/TODO.txt
+++ b/system/TODO.txt
@@ -1,5 +1,4 @@
 SetterConfiguration:
-- setNonPublic(boolean aNonPublic): by default publicOnly = true
 - add select(Class aType) to select a specific setter as an alternative
   to the name based selection.
 - add values(Class aType) method for selecting a setter. 
diff --git a/system/general/src/main/java/org/wamblee/system/adapters/SetterConfiguration.java b/system/general/src/main/java/org/wamblee/system/adapters/SetterConfiguration.java
index 374edae6..581c5b59 100644
--- a/system/general/src/main/java/org/wamblee/system/adapters/SetterConfiguration.java
+++ b/system/general/src/main/java/org/wamblee/system/adapters/SetterConfiguration.java
@@ -18,7 +18,9 @@ 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.List;
 
 import org.wamblee.collections.CollectionFilter;
@@ -40,172 +42,216 @@ import org.wamblee.system.core.SystemAssemblyException;
  */
 public class SetterConfiguration {
 
-    private Class _class;
+	private Class _class;
+	private boolean _publicOnly;
 
-    private List<Pair<Method, ParameterValues>> _setters;
+	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();
-    }
+	/**
+	 * Constructs the setter configuration. By default all setters are added.
+	 * 
+	 * @param aClass
+	 *            Class which is being configured.
+	 */
+	public SetterConfiguration(Class aClass) {
+		_class = aClass;
+		_publicOnly = true;
+		_setters = getAllSetters(_class, _publicOnly);
+	}
 
-    /**
-     * Removes all setters.
-     * 
-     * @return Reference to the current object to allow call chaining.
-     */
-    public SetterConfiguration clear() {
-        _setters.clear();
-        return this;
-    }
+	/**
+	 * Called to set whether non-public setters are also used. By default only
+	 * public setters are used.
+	 * 
+	 * @param aIsNonPublic
+	 *            Non public flag.
+	 */
+	public void setNonPublic(boolean aIsNonPublic) {
+		_publicOnly = !aIsNonPublic;
+		_setters = getAllSetters(_class, _publicOnly);
+	}
 
-    /**
-     * 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);
-            }
-
-        });
-        if ( _setters.size() == setters.size()) { 
-            throw new IllegalArgumentException("No setter configured by the name of '" + aName + "'");
-        }
-        _setters = setters;
-        return this;
-    }
+	/**
+	 * Removes all setters.
+	 * 
+	 * @return Reference to the current object to allow call chaining.
+	 */
+	public SetterConfiguration clear() {
+		_setters.clear();
+		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);
-    }
+	/**
+	 * 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);
+					}
 
-    /**
-     * 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 + "' in " + _class.getName());
-        }
-        return this;
-    }
+				});
+		if (_setters.size() == setters.size()) {
+			throw new IllegalArgumentException(
+					"No setter configured by the name of '" + aName + "'");
+		}
+		_setters = setters;
+		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();
-            }
-        }
-        throw new IllegalArgumentException("No setter method '" + name + "' found");
-    }
+	/**
+	 * 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);
+	}
 
-    /**
-     * 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);
+	/**
+	 * 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(_class, false), _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());
+		}
+		return this;
+	}
+
+	/**
+	 * Gets all setters for the current class.
+	 * 
+	 * @return List of all setters.
+	 */
+	private static List<Pair<Method, ParameterValues>> getAllSetters(
+			Class aClass, boolean aPublicOnly) {
+		List<Pair<Method, ParameterValues>> result = new ArrayList<Pair<Method, ParameterValues>>();
+		for (Method method : getAllMethods(aClass)) {
+			if (!aPublicOnly || Modifier.isPublic(method.getModifiers())) {
+				if (method.getName().startsWith("set")
+						&& method.getParameterTypes().length == 1) {
+					method.setAccessible(true);
+					String name = getSetterName(method);
+					result
+							.add(new Pair<Method, ParameterValues>(method,
+									new ParameterValues(new String[] { name },
+											new Class[] { method
+													.getParameterTypes()[0] })));
+				}
+			}
+		}
+		return result;
+	}
+
+	private static final List<Method> getAllMethods(Class aClass) {
+    	List<Method> result = new ArrayList<Method>();
+    	result.addAll(Arrays.asList(aClass.getDeclaredMethods()));
+    	Class superClass = aClass.getSuperclass();
+    	if ( superClass != null ) { 
+    		result.addAll(getAllMethods(superClass));
+    	}
+    	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();
+			}
+		}
+		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 static String getSetterName(Method aMethod) {
+		String result = aMethod.getName().substring(3);
+		return result.substring(0, 1).toLowerCase() + result.substring(1);
+	}
+
 }
diff --git a/system/general/src/main/java/org/wamblee/system/core/DefaultRequiredInterface.java b/system/general/src/main/java/org/wamblee/system/core/DefaultRequiredInterface.java
index 01575db2..2983ca6f 100644
--- a/system/general/src/main/java/org/wamblee/system/core/DefaultRequiredInterface.java
+++ b/system/general/src/main/java/org/wamblee/system/core/DefaultRequiredInterface.java
@@ -17,6 +17,9 @@ package org.wamblee.system.core;
 
 import java.util.Arrays;
 
+import org.hibernate.annotations.common.reflection.ReflectionUtil;
+import org.wamblee.reflection.ReflectionUtils;
+
 public class DefaultRequiredInterface implements RequiredInterface {
 
 	private String _name;
@@ -74,6 +77,8 @@ public class DefaultRequiredInterface implements RequiredInterface {
 	private boolean serviceProvided(Class aRequired, Class[] aProvided) { 
 		for (Class provided: aProvided) {
 			try {
+				provided = ReflectionUtils.wrapIfNeeded(provided);
+				aRequired = ReflectionUtils.wrapIfNeeded(aRequired);
 				provided.asSubclass(aRequired);
 				return true; 
 			} catch (ClassCastException e) {
diff --git a/system/general/src/test/java/org/wamblee/system/adapters/SetterConfigurationTest.java b/system/general/src/test/java/org/wamblee/system/adapters/SetterConfigurationTest.java
index ba01f191..2b7bd918 100644
--- a/system/general/src/test/java/org/wamblee/system/adapters/SetterConfigurationTest.java
+++ b/system/general/src/test/java/org/wamblee/system/adapters/SetterConfigurationTest.java
@@ -12,10 +12,12 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- */ 
+ */
 package org.wamblee.system.adapters;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 import org.wamblee.system.core.DefaultProvidedInterface;
@@ -26,158 +28,243 @@ import org.wamblee.test.AssertionUtils;
 
 public class SetterConfigurationTest extends AdapterTestCase {
 
-    public void testOneSetter() { 
-        SetterConfiguration config = new SetterConfiguration(X5.class);
-        List<RequiredInterface> required = config.getRequiredInterfaces();
-        assertEquals(1, required.size());
-        assertEquals("value", required.get(0).getName());
-        
-        ProvidedInterface provided = new DefaultProvidedInterface("janse", String.class);
-        required.get(0).setProvider(provided);
-        provided.publish("hello", _scope);
-        
-        X5 obj = new X5(); 
-        assertNull(obj.getValue());
-        config.inject(_scope, obj);
-        assertEquals("hello", obj.getValue());
-    }
-    
-    public void testMultipleSetters() { 
-        SetterConfiguration config = new SetterConfiguration(X6.class);
-        List<RequiredInterface> required = config.getRequiredInterfaces();
-        Collections.sort(required, new RequiredInterfaceComparator());
-        assertEquals(2, required.size());
-        assertEquals("host", required.get(0).getName());
-        assertEquals("port", required.get(1).getName());
-     
-        ProvidedInterface provided0 = new DefaultProvidedInterface("janse", String.class);
-        required.get(0).setProvider(provided0);
-        provided0.publish("hello", _scope);
-        
-        ProvidedInterface provided1 = new DefaultProvidedInterface("port", Integer.class);
-        required.get(1).setProvider(provided1);
-        provided1.publish(10, _scope);
-        
-        X6 obj = new X6(); 
-        assertNull(obj.getHost());
-        assertNull(obj.getPort());
-        
-        config.inject(_scope, obj);
-        assertEquals("hello", obj.getHost());
-        assertEquals(10, obj.getPort().intValue());
-    }
-    
-    public void testInvokeWrongType() { 
-        final SetterConfiguration config = new SetterConfiguration(X5.class);
-        List<RequiredInterface> required = config.getRequiredInterfaces();
-        assertEquals(1, required.size());
-        assertEquals("value", required.get(0).getName());
-        
-        ProvidedInterface provided = new DefaultProvidedInterface("janse", String.class);
-        required.get(0).setProvider(provided);
-        provided.publish("hello", _scope);
-       
-        final X6 obj = new X6();
-        AssertionUtils.assertException(new AssertionUtils.ErroneousCode() { 
-            @Override
-            public void run() throws Exception {
-                config.inject(_scope, obj);            
-            }
-        }, IllegalArgumentException.class);  
-    }
-    
-    public void testSetExplicitValue() { 
-        SetterConfiguration config = new SetterConfiguration(X5.class);
-        config.values("value").setValue(0, "bladibla");        
-        
-        List<RequiredInterface> required = config.getRequiredInterfaces();
-        assertEquals(0, required.size());
-        
-        X5 obj = new X5(); 
-        assertNull(obj.getValue());
-        config.inject(_scope, obj);
-        assertEquals("bladibla", obj.getValue());
-    }
-    
-    public void testClear() { 
-        SetterConfiguration config = new SetterConfiguration(X6.class);
-        config.clear();
-        List<RequiredInterface> required = config.getRequiredInterfaces();
-        Collections.sort(required, new RequiredInterfaceComparator());
-        assertEquals(0, required.size());
-        
-        X6 obj = new X6(); 
-        assertNull(obj.getHost());
-        assertNull(obj.getPort());
-        
-        config.inject(_scope, obj);
-      
-        assertNull(obj.getHost());
-        assertNull(obj.getPort());
-    }
-    
-    public void testAdd() { 
-        SetterConfiguration config = new SetterConfiguration(X6.class);
-        config.clear().add("host");
-        List<RequiredInterface> required = config.getRequiredInterfaces();
-        Collections.sort(required, new RequiredInterfaceComparator());
-        assertEquals(1, required.size());
-        assertEquals("host", required.get(0).getName());
-     
-        ProvidedInterface provided0 = new DefaultProvidedInterface("janse", String.class);
-        required.get(0).setProvider(provided0);
-        provided0.publish("hello", _scope);
-        
-        X6 obj = new X6(); 
-        assertNull(obj.getHost());
-        assertNull(obj.getPort());
-        
-        config.inject(_scope, obj);
-        assertEquals("hello", obj.getHost());
-        assertNull(obj.getPort());
-    }
-    
-    public void testAddNonExisting() { 
-        final SetterConfiguration config = new SetterConfiguration(X6.class);
-        config.clear();
-        
-        AssertionUtils.assertException(new AssertionUtils.ErroneousCode() { 
-            @Override
-            public void run() throws Exception {
-                config.add("bladibla");   
-            }
-        }, IllegalArgumentException.class);
-    }
-    
-    public void testRemove() { 
-        SetterConfiguration config = new SetterConfiguration(X6.class);
-        config.remove("port");
-        List<RequiredInterface> required = config.getRequiredInterfaces();
-        Collections.sort(required, new RequiredInterfaceComparator());
-        assertEquals(1, required.size());
-        assertEquals("host", required.get(0).getName());
-     
-        ProvidedInterface provided0 = new DefaultProvidedInterface("janse", String.class);
-        required.get(0).setProvider(provided0);
-        provided0.publish("hello", _scope);
-        
-        X6 obj = new X6(); 
-        assertNull(obj.getHost());
-        assertNull(obj.getPort());
-        
-        config.inject(_scope, obj);
-        assertEquals("hello", obj.getHost());
-        assertNull(obj.getPort());
-    }
-    
-    public void testRemoveNonExisting() { 
-        final SetterConfiguration config = new SetterConfiguration(X6.class);
-        
-        AssertionUtils.assertException(new AssertionUtils.ErroneousCode() { 
-            @Override
-            public void run() throws Exception {
-                config.remove("bladibla");   
-            }
-        }, IllegalArgumentException.class);
-    }
+	public void testOneSetter() {
+		SetterConfiguration config = new SetterConfiguration(X5.class);
+		List<RequiredInterface> required = config.getRequiredInterfaces();
+		assertEquals(1, required.size());
+		assertEquals("value", required.get(0).getName());
+
+		ProvidedInterface provided = new DefaultProvidedInterface("janse",
+				String.class);
+		required.get(0).setProvider(provided);
+		provided.publish("hello", _scope);
+
+		X5 obj = new X5();
+		assertNull(obj.getValue());
+		config.inject(_scope, obj);
+		assertEquals("hello", obj.getValue());
+	}
+
+	public void testPrivateSetter() {
+		SetterConfiguration config = new SetterConfiguration(X5.class);
+		config.setNonPublic(true);
+		List<RequiredInterface> required = new ArrayList<RequiredInterface>(
+				config.getRequiredInterfaces());
+		Collections.sort(required, new Comparator<RequiredInterface>() {
+			@Override
+			public int compare(RequiredInterface aO1, RequiredInterface aO2) {
+				return aO1.getName().compareTo(aO2.getName());
+			}
+		});
+		assertEquals(2, required.size());
+		assertEquals("value", required.get(0).getName());
+		assertEquals("xyz", required.get(1).getName());
+
+		ProvidedInterface providedString = new DefaultProvidedInterface(
+				"janse", String.class);
+		assertTrue(required.get(0).implementedBy(providedString));
+		required.get(0).setProvider(providedString);
+		providedString.publish("hello", _scope);
+
+		ProvidedInterface providedInt = new DefaultProvidedInterface("xxx",
+				Integer.class);
+		assertTrue(required.get(1).implementedBy(providedInt));
+		required.get(1).setProvider(providedInt);
+		providedInt.publish(100, _scope);
+
+		X5 obj = new X5();
+		assertNull(obj.getValue());
+		assertNull(obj.getXyz());
+		config.inject(_scope, obj);
+		assertEquals("hello", obj.getValue());
+		assertEquals(100, obj.getXyz().intValue());
+	}
+
+	public void testInheritance() {
+		SetterConfiguration config = new SetterConfiguration(X9.class);
+		config.setNonPublic(true);
+		List<RequiredInterface> required = new ArrayList<RequiredInterface>(
+				config.getRequiredInterfaces());
+		Collections.sort(required, new Comparator<RequiredInterface>() {
+			@Override
+			public int compare(RequiredInterface aO1, RequiredInterface aO2) {
+				return aO1.getName().compareTo(aO2.getName());
+			}
+		});
+		assertEquals(3, required.size());
+		assertEquals("flag", required.get(0).getName());
+		assertEquals("value", required.get(1).getName());
+		assertEquals("xyz", required.get(2).getName());
+
+		ProvidedInterface providedBoolean = new DefaultProvidedInterface(
+				"janse", Boolean.class);
+		assertTrue(required.get(0).implementedBy(providedBoolean));
+		required.get(0).setProvider(providedBoolean);
+		providedBoolean.publish(true, _scope);
+
+		ProvidedInterface providedString = new DefaultProvidedInterface(
+				"janse", String.class);
+		assertTrue(required.get(1).implementedBy(providedString));
+		required.get(1).setProvider(providedString);
+		providedString.publish("hello", _scope);
+
+		ProvidedInterface providedInt = new DefaultProvidedInterface("xxx",
+				Integer.class);
+		assertTrue(required.get(2).implementedBy(providedInt));
+		required.get(2).setProvider(providedInt);
+		providedInt.publish(100, _scope);
+
+		X9 obj = new X9();
+		assertNull(obj.getValue());
+		assertNull(obj.getXyz());
+		assertFalse(obj.isFlag());
+		config.inject(_scope, obj);
+		assertEquals("hello", obj.getValue());
+		assertEquals(100, obj.getXyz().intValue());
+		assertTrue(obj.isFlag());
+	}
+
+	public void testMultipleSetters() {
+		SetterConfiguration config = new SetterConfiguration(X6.class);
+		List<RequiredInterface> required = config.getRequiredInterfaces();
+		Collections.sort(required, new RequiredInterfaceComparator());
+		assertEquals(2, required.size());
+		assertEquals("host", required.get(0).getName());
+		assertEquals("port", required.get(1).getName());
+
+		ProvidedInterface provided0 = new DefaultProvidedInterface("janse",
+				String.class);
+		required.get(0).setProvider(provided0);
+		provided0.publish("hello", _scope);
+
+		ProvidedInterface provided1 = new DefaultProvidedInterface("port",
+				Integer.class);
+		required.get(1).setProvider(provided1);
+		provided1.publish(10, _scope);
+
+		X6 obj = new X6();
+		assertNull(obj.getHost());
+		assertNull(obj.getPort());
+
+		config.inject(_scope, obj);
+		assertEquals("hello", obj.getHost());
+		assertEquals(10, obj.getPort().intValue());
+	}
+
+	public void testInvokeWrongType() {
+		final SetterConfiguration config = new SetterConfiguration(X5.class);
+		List<RequiredInterface> required = config.getRequiredInterfaces();
+		assertEquals(1, required.size());
+		assertEquals("value", required.get(0).getName());
+
+		ProvidedInterface provided = new DefaultProvidedInterface("janse",
+				String.class);
+		required.get(0).setProvider(provided);
+		provided.publish("hello", _scope);
+
+		final X6 obj = new X6();
+		AssertionUtils.assertException(new AssertionUtils.ErroneousCode() {
+			@Override
+			public void run() throws Exception {
+				config.inject(_scope, obj);
+			}
+		}, IllegalArgumentException.class);
+	}
+
+	public void testSetExplicitValue() {
+		SetterConfiguration config = new SetterConfiguration(X5.class);
+		config.values("value").setValue(0, "bladibla");
+
+		List<RequiredInterface> required = config.getRequiredInterfaces();
+		assertEquals(0, required.size());
+
+		X5 obj = new X5();
+		assertNull(obj.getValue());
+		config.inject(_scope, obj);
+		assertEquals("bladibla", obj.getValue());
+	}
+
+	public void testClear() {
+		SetterConfiguration config = new SetterConfiguration(X6.class);
+		config.clear();
+		List<RequiredInterface> required = config.getRequiredInterfaces();
+		Collections.sort(required, new RequiredInterfaceComparator());
+		assertEquals(0, required.size());
+
+		X6 obj = new X6();
+		assertNull(obj.getHost());
+		assertNull(obj.getPort());
+
+		config.inject(_scope, obj);
+
+		assertNull(obj.getHost());
+		assertNull(obj.getPort());
+	}
+
+	public void testAdd() {
+		SetterConfiguration config = new SetterConfiguration(X6.class);
+		config.clear().add("host");
+		List<RequiredInterface> required = config.getRequiredInterfaces();
+		Collections.sort(required, new RequiredInterfaceComparator());
+		assertEquals(1, required.size());
+		assertEquals("host", required.get(0).getName());
+
+		ProvidedInterface provided0 = new DefaultProvidedInterface("janse",
+				String.class);
+		required.get(0).setProvider(provided0);
+		provided0.publish("hello", _scope);
+
+		X6 obj = new X6();
+		assertNull(obj.getHost());
+		assertNull(obj.getPort());
+
+		config.inject(_scope, obj);
+		assertEquals("hello", obj.getHost());
+		assertNull(obj.getPort());
+	}
+
+	public void testAddNonExisting() {
+		final SetterConfiguration config = new SetterConfiguration(X6.class);
+		config.clear();
+
+		AssertionUtils.assertException(new AssertionUtils.ErroneousCode() {
+			@Override
+			public void run() throws Exception {
+				config.add("bladibla");
+			}
+		}, IllegalArgumentException.class);
+	}
+
+	public void testRemove() {
+		SetterConfiguration config = new SetterConfiguration(X6.class);
+		config.remove("port");
+		List<RequiredInterface> required = config.getRequiredInterfaces();
+		Collections.sort(required, new RequiredInterfaceComparator());
+		assertEquals(1, required.size());
+		assertEquals("host", required.get(0).getName());
+
+		ProvidedInterface provided0 = new DefaultProvidedInterface("janse",
+				String.class);
+		required.get(0).setProvider(provided0);
+		provided0.publish("hello", _scope);
+
+		X6 obj = new X6();
+		assertNull(obj.getHost());
+		assertNull(obj.getPort());
+
+		config.inject(_scope, obj);
+		assertEquals("hello", obj.getHost());
+		assertNull(obj.getPort());
+	}
+
+	public void testRemoveNonExisting() {
+		final SetterConfiguration config = new SetterConfiguration(X6.class);
+
+		AssertionUtils.assertException(new AssertionUtils.ErroneousCode() {
+			@Override
+			public void run() throws Exception {
+				config.remove("bladibla");
+			}
+		}, IllegalArgumentException.class);
+	}
 }
diff --git a/system/general/src/test/java/org/wamblee/system/adapters/X5.java b/system/general/src/test/java/org/wamblee/system/adapters/X5.java
index de3e0fb3..c0a82449 100644
--- a/system/general/src/test/java/org/wamblee/system/adapters/X5.java
+++ b/system/general/src/test/java/org/wamblee/system/adapters/X5.java
@@ -18,6 +18,7 @@ package org.wamblee.system.adapters;
 public class X5 {
     
     private String _value; 
+    private Integer _xyz; 
     
 	public X5() {
 	
@@ -35,4 +36,12 @@ public class X5 {
 	public void doSomething() { 
 	    // Empty.
 	}
+	
+	private void setXyz(int aXyz) { 
+		_xyz = aXyz;
+	}
+	
+	public Integer getXyz() {
+		return _xyz;
+	}
 }
\ No newline at end of file
diff --git a/system/general/src/test/java/org/wamblee/system/core/DefaultInterfaceDescriptorTest.java b/system/general/src/test/java/org/wamblee/system/core/DefaultInterfaceDescriptorTest.java
index c455395b..5bfca072 100644
--- a/system/general/src/test/java/org/wamblee/system/core/DefaultInterfaceDescriptorTest.java
+++ b/system/general/src/test/java/org/wamblee/system/core/DefaultInterfaceDescriptorTest.java
@@ -82,5 +82,15 @@ public class DefaultInterfaceDescriptorTest extends TestCase {
 		assertTrue(required.implementedBy(provided));
 	}
 	
+	public void testPrimitiveAndWrapperType() { 
+		RequiredInterface req1 = new DefaultRequiredInterface("req1", int.class);
+		RequiredInterface req2 = new DefaultRequiredInterface("req1", Integer.class);
+		ProvidedInterface prov1 = new DefaultProvidedInterface("prov1", int.class);
+		ProvidedInterface prov2 = new DefaultProvidedInterface("prov2", Integer.class);
+		assertTrue(req1.implementedBy(prov1));
+		assertTrue(req2.implementedBy(prov1));
+		assertTrue(req1.implementedBy(prov2));
+		assertTrue(req2.implementedBy(prov2));
+	}
 	
 }
-- 
2.31.1