Re: [picocontainer-dev] ConfigParameterTestCase / ConfigParameter
Paul Hammant <[email protected]>
| Newsgroups | gmane.comp.java.picocontainer.devel |
|---|---|
| Message-ID | <[email protected]> |
Here it is again with reflection using converters, as per Jörgs
suggestions -
public class BasicComponentParameter implements Parameter,
Serializable {
private static interface Converter {
Object convert(String paramValue);
}
private static class ValueOfConverter implements Converter {
private Method m;
private ValueOfConverter(Class clazz) {
try {
m = clazz.getMethod("valueOf", String.class);
} catch (NoSuchMethodException e) {
}
}
public Object convert(String paramValue) {
try {
return m.invoke(null, paramValue);
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
return null;
}
}
private static class NewInstanceConverter implements Converter {
private Constructor c;
private NewInstanceConverter(Class clazz) {
try {
c = clazz.getConstructor(String.class);
} catch (NoSuchMethodException e) {
}
}
public Object convert(String paramValue) {
try {
return c.newInstance(paramValue);
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
} catch (InstantiationException e) {
}
return null;
}
}
private static final Map<Class, Converter> stringConverters =
new HashMap<Class, Converter>();
static {
stringConverters.put(Integer.class, new ValueOfConverter
(Integer.class));
stringConverters.put(Double.class, new ValueOfConverter
(Double.class));
stringConverters.put(Boolean.class, new ValueOfConverter
(Boolean.class));
stringConverters.put(Long.class, new ValueOfConverter
(Long.class));
stringConverters.put(Float.class, new ValueOfConverter
(Float.class));
stringConverters.put(Character.class, new ValueOfConverter
(Character.class));
stringConverters.put(Byte.class, new ValueOfConverter
(Byte.class));
stringConverters.put(Byte.class, new ValueOfConverter
(Short.class));
stringConverters.put(File.class, new NewInstanceConverter
(File.class));
}
Its all in core, with no deps on gems or XStream for this functionality.
TODO -
1) look at ConfigParameter again and potentially consolidate some of
the functionality
2) review ArgumentativePicoContaine, SystemPropertiesPicoContainer
and PropertiesPicoContainer for overlap/redunctancy
- Paul
---------------------------------------------------------------------
To unsubscribe from this list please visit:
http://xircles.codehaus.org/manage_email
improved.txt
(text/plain, 15.1 KB)
Index: src/test/org/picocontainer/containers/ArgumentativePicoContainerTestCase.java
===================================================================
--- src/test/org/picocontainer/containers/ArgumentativePicoContainerTestCase.java (revision 4004)
+++ src/test/org/picocontainer/containers/ArgumentativePicoContainerTestCase.java (working copy)
@@ -28,9 +28,9 @@
"foo=bar", "foo2=12", "foo3=true", "foo4="
});
assertEquals("bar",apc.getComponent("foo"));
- assertEquals(12,apc.getComponent("foo2"));
- assertEquals(true,apc.getComponent("foo3"));
- assertEquals(true,apc.getComponent("foo4"));
+ assertEquals("12",apc.getComponent("foo2"));
+ assertEquals("true",apc.getComponent("foo3"));
+ assertEquals("true",apc.getComponent("foo4"));
}
public void testAsParentContainer() {
@@ -38,7 +38,7 @@
"a=aaa", "b=bbb", "d=22"});
assertEquals("aaa",apc.getComponent("a"));
assertEquals("bbb",apc.getComponent("b"));
- assertEquals(22,apc.getComponent("d"));
+ assertEquals("22",apc.getComponent("d"));
DefaultPicoContainer dpc = new DefaultPicoContainer(apc);
dpc.addComponent(NeedsString.class);
@@ -57,41 +57,41 @@
"foo:bar", "foo2:12", "foo3:true"
});
assertEquals("bar",apc.getComponent("foo"));
- assertEquals(12,apc.getComponent("foo2"));
- assertEquals(true,apc.getComponent("foo3"));
+ assertEquals("12",apc.getComponent("foo2"));
+ assertEquals("true",apc.getComponent("foo3"));
}
public void testParsingWithWrongSeparator() {
ArgumentativePicoContainer apc = new ArgumentativePicoContainer(":", new String[] {
"foo=bar", "foo2=12", "foo3=true"
});
- assertEquals(true,apc.getComponent("foo=bar"));
- assertEquals(true,apc.getComponent("foo2=12"));
- assertEquals(true,apc.getComponent("foo3=true"));
+ assertEquals("true",apc.getComponent("foo=bar"));
+ assertEquals("true",apc.getComponent("foo2=12"));
+ assertEquals("true",apc.getComponent("foo3=true"));
}
public void testParsingOfPropertiesFile() throws IOException {
ArgumentativePicoContainer apc = new ArgumentativePicoContainer(":",
new StringReader("foo:bar\nfoo2:12\nfoo3:true\n"));
assertEquals("bar",apc.getComponent("foo"));
- assertEquals(12,apc.getComponent("foo2"));
- assertEquals(true,apc.getComponent("foo3"));
+ assertEquals("12",apc.getComponent("foo2"));
+ assertEquals("true",apc.getComponent("foo3"));
}
public void testParsingOfPropertiesFileAndArgs() throws IOException {
ArgumentativePicoContainer apc = new ArgumentativePicoContainer(":",
new StringReader("foo:bar\nfoo2:12\n"), new String[] {"foo3:true"});
assertEquals("bar",apc.getComponent("foo"));
- assertEquals(12,apc.getComponent("foo2"));
- assertEquals(true,apc.getComponent("foo3"));
+ assertEquals("12",apc.getComponent("foo2"));
+ assertEquals("true",apc.getComponent("foo3"));
}
public void testParsingOfPropertiesFileAndArgsWithClash() throws IOException {
ArgumentativePicoContainer apc = new ArgumentativePicoContainer(":",
new StringReader("foo:bar\nfoo2:99\n"), new String[] {"foo2:12","foo3:true"});
assertEquals("bar",apc.getComponent("foo"));
- assertEquals(12,apc.getComponent("foo2"));
- assertEquals(true,apc.getComponent("foo3"));
+ assertEquals("12",apc.getComponent("foo2"));
+ assertEquals("true",apc.getComponent("foo3"));
}
public void testbyTypeFailsEvenIfOneOfSameType() {
Index: src/java/org/picocontainer/parameters/BasicComponentParameter.java
===================================================================
--- src/java/org/picocontainer/parameters/BasicComponentParameter.java (revision 4004)
+++ src/java/org/picocontainer/parameters/BasicComponentParameter.java (working copy)
@@ -16,10 +16,15 @@
import org.picocontainer.PicoVisitor;
import org.picocontainer.injectors.AbstractInjector;
+import java.io.File;
import java.io.Serializable;
-import java.lang.reflect.Field;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -35,15 +40,72 @@
* @author Jörg Schaible
* @author Thomas Heller
*/
-public class BasicComponentParameter
- implements Parameter, Serializable
-{
+public class BasicComponentParameter implements Parameter, Serializable {
+ private static interface Converter {
+ Object convert(String paramValue);
+ }
+ private static class ValueOfConverter implements Converter {
+ private Method m;
+ private ValueOfConverter(Class clazz) {
+ try {
+ m = clazz.getMethod("valueOf", String.class);
+ } catch (NoSuchMethodException e) {
+ }
+ }
+
+ public Object convert(String paramValue) {
+ try {
+ return m.invoke(null, paramValue);
+ } catch (IllegalAccessException e) {
+ } catch (InvocationTargetException e) {
+ }
+ return null;
+
+ }
+ }
+ private static class NewInstanceConverter implements Converter {
+ private Constructor c;
+
+ private NewInstanceConverter(Class clazz) {
+ try {
+ c = clazz.getConstructor(String.class);
+ } catch (NoSuchMethodException e) {
+ }
+ }
+
+ public Object convert(String paramValue) {
+ try {
+ return c.newInstance(paramValue);
+ } catch (IllegalAccessException e) {
+ } catch (InvocationTargetException e) {
+ } catch (InstantiationException e) {
+ }
+ return null;
+ }
+ }
+
/** <code>BASIC_DEFAULT</code> is an instance of BasicComponentParameter using the default constructor. */
public static final BasicComponentParameter BASIC_DEFAULT = new BasicComponentParameter();
private Object componentKey;
+
+ private static final Map<Class, Converter> stringConverters = new HashMap<Class, Converter>();
+ static {
+ stringConverters.put(Integer.class, new ValueOfConverter(Integer.class));
+ stringConverters.put(Double.class, new ValueOfConverter(Double.class));
+ stringConverters.put(Boolean.class, new ValueOfConverter(Boolean.class));
+ stringConverters.put(Long.class, new ValueOfConverter(Long.class));
+ stringConverters.put(Float.class, new ValueOfConverter(Float.class));
+ stringConverters.put(Character.class, new ValueOfConverter(Character.class));
+ stringConverters.put(Byte.class, new ValueOfConverter(Byte.class));
+ stringConverters.put(Byte.class, new ValueOfConverter(Short.class));
+ stringConverters.put(File.class, new NewInstanceConverter(File.class));
+
+ }
+
+
/**
* Expect a parameter matching a component of a specific key.
*
@@ -80,7 +142,12 @@
final ComponentAdapter componentAdapter =
resolveAdapter(container, adapter, (Class<?>)expectedType, expectedParameterName, useNames);
if (componentAdapter != null) {
- return (T) container.getComponent(componentAdapter.getComponentKey());
+ Object o = container.getComponent(componentAdapter.getComponentKey());
+ if (o instanceof String && expectedType != String.class) {
+ Converter converter = stringConverters.get(expectedType);
+ return (T) converter.convert((String) o);
+ }
+ return (T) o;
}
return null;
}
@@ -112,32 +179,37 @@
ComponentAdapter adapter,
Class<T> expectedType,
ParameterName expectedParameterName, boolean useNames) {
+ Class type = expectedType;
+ if (type.isPrimitive()) {
+ String expectedTypeName = expectedType.getName();
+ if (expectedTypeName == "int") {
+ type = Integer.class;
+ } else if (expectedTypeName == "long") {
+ type = Long.class;
+ } else if (expectedTypeName == "float") {
+ type = Float.class;
+ } else if (expectedTypeName == "double") {
+ type = Double.class;
+ } else if (expectedTypeName == "boolean") {
+ type = Boolean.class;
+ } else if (expectedTypeName == "char") {
+ type = Character.class;
+ } else if (expectedTypeName == "short") {
+ type = Short.class;
+ } else if (expectedTypeName == "byte") {
+ type = Byte.class;
+ }
+ }
- final ComponentAdapter<T> result = getTargetAdapter(container, expectedType, expectedParameterName, adapter, useNames);
+ final ComponentAdapter<T> result = getTargetAdapter(container, type, expectedParameterName, adapter, useNames);
if (result == null) {
return null;
}
- if (!expectedType.isAssignableFrom(result.getComponentImplementation())) {
- // check for primitive value
- if (expectedType.isPrimitive()) {
- try {
- final Field field = result.getComponentImplementation().getField("TYPE");
- final Class type = (Class)field.get(result.getComponentInstance(null));
- if (expectedType.isAssignableFrom(type)) {
- return result;
- }
- } catch (NoSuchFieldException e) {
- //ignore
- } catch (IllegalArgumentException e) {
- //ignore
- } catch (IllegalAccessException e) {
- //ignore
- } catch (ClassCastException e) {
- //ignore
- }
+ if (!type.isAssignableFrom(result.getComponentImplementation())) {
+ if (!(result.getComponentImplementation() == String.class && stringConverters.containsKey(type))) {
+ return null;
}
- return null;
}
return result;
}
@@ -166,7 +238,7 @@
if (useNames) {
ComponentAdapter found = container.getComponentAdapter(expectedParameterName.getName());
if ((found != null)
- && expectedType.isAssignableFrom(found.getComponentImplementation())
+ && areCompatible(expectedType, found)
&& found != excludeAdapter) {
return (ComponentAdapter<T>) found;
}
@@ -196,4 +268,10 @@
}
}
}
+
+ private <T> boolean areCompatible(Class<T> expectedType, ComponentAdapter found) {
+ Class foundImpl = found.getComponentImplementation();
+ return expectedType.isAssignableFrom(foundImpl) ||
+ (foundImpl == String.class && stringConverters.containsKey(expectedType)) ;
+ }
}
Index: src/java/org/picocontainer/injectors/SingleMemberInjector.java
===================================================================
--- src/java/org/picocontainer/injectors/SingleMemberInjector.java (revision 4004)
+++ src/java/org/picocontainer/injectors/SingleMemberInjector.java (working copy)
@@ -46,10 +46,23 @@
*/
protected Class box(Class parameterType) {
if (parameterType.isPrimitive()) {
- if (parameterType == Integer.TYPE) {
+ String parameterTypeName = parameterType.getName();
+ if (parameterTypeName == "int") {
return Integer.class;
- } else if (parameterType == Boolean.TYPE) {
+ } else if (parameterTypeName == "boolean") {
return Boolean.class;
+ } else if (parameterTypeName == "long") {
+ return Long.class;
+ } else if (parameterTypeName == "float") {
+ return Float.class;
+ } else if (parameterTypeName == "double") {
+ return Double.class;
+ } else if (parameterTypeName == "char") {
+ return Character.class;
+ } else if (parameterTypeName == "byte") {
+ return Byte.class;
+ } else if (parameterTypeName == "short") {
+ return Short.class;
}
}
return parameterType;
Index: src/java/org/picocontainer/injectors/ConstructorInjector.java
===================================================================
--- src/java/org/picocontainer/injectors/ConstructorInjector.java (revision 4004)
+++ src/java/org/picocontainer/injectors/ConstructorInjector.java (working copy)
@@ -78,9 +78,11 @@
// remember: all constructors with less arguments than the given parameters are filtered out already
for (int j = 0; j < currentParameters.length; j++) {
- // check wether this constructor is statisfiable
- if (currentParameters[j].isResolvable(container, this, box(parameterTypes[j]),
- new SingleMemberInjectorParameterName(sortedMatchingConstructor,j), useNames())) {
+ // check whether this constructor is statisfiable
+ Class boxed = box(parameterTypes[j]);
+ boolean un = useNames();
+ if (currentParameters[j].isResolvable(container, this, boxed,
+ new SingleMemberInjectorParameterName(sortedMatchingConstructor, j), un)) {
continue;
}
unsatisfiableDependencyTypes.add(Arrays.asList(parameterTypes));
Index: src/java/org/picocontainer/containers/ArgumentativePicoContainer.java
===================================================================
--- src/java/org/picocontainer/containers/ArgumentativePicoContainer.java (revision 4004)
+++ src/java/org/picocontainer/containers/ArgumentativePicoContainer.java (working copy)
@@ -88,25 +88,12 @@
return new EmptyPicoContainer();
}
- private Object getValue(String s) {
- if (s.equals("true")) {
- return true;
- } else if (s.equals("false")) {
- return false;
- }
- try {
- return Integer.parseInt(s);
- } catch (NumberFormatException e) {
- }
- return s;
- }
-
private void processArgument(String argument, String separator) {
String[] kvs = argument.split(separator);
if (kvs.length == 2) {
- addConfig(kvs[0], getValue(kvs[1]));
+ addConfig(kvs[0], kvs[1]);
} else if (kvs.length == 1) {
- addConfig(kvs[0], true);
+ addConfig(kvs[0], "true");
} else if (kvs.length > 2) {
throw new PicoCompositionException(
"Argument name'"+separator+"'value pair '" + argument + "' has too many '"+separator+"' characters");