From d27f4ee556a4982144f9354793c629b31399fd52 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 6 Feb 2020 14:11:58 +0100 Subject: [PATCH] Fixes #4550 XmlConfiguration named parameters Fixed #4550 named parameters with a moderate refactor. The named parameter matching was duplicated, only considering number of args and not applied to call arguments. This refactor puts all the behaviour in common methods and reorders the arguments to match parameters. Signed-off-by: Greg Wilkins --- .../eclipse/jetty/xml/XmlConfiguration.java | 331 +++++++-------- .../jetty/xml/AnnotatedTestConfiguration.java | 44 ++ .../jetty/xml/XmlConfigurationTest.java | 397 ++++++++++++------ 3 files changed, 467 insertions(+), 305 deletions(-) diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java index fde042b1b30..f5ca7e9fa9f 100644 --- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java +++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java @@ -24,6 +24,7 @@ import java.io.StringReader; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -37,11 +38,11 @@ import java.nio.file.Paths; import java.security.AccessController; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -51,7 +52,6 @@ import java.util.Queue; import java.util.ServiceLoader; import java.util.Set; -import org.eclipse.jetty.util.ArrayUtil; import org.eclipse.jetty.util.LazyList; import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.MultiException; @@ -412,39 +412,13 @@ public class XmlConfiguration int index = 0; if (obj == null && oClass != null) { - index = _root.size(); - Map namedArgMap = new HashMap<>(); - - List arguments = new LinkedList<>(); - for (int i = 0; i < _root.size(); i++) - { - Object o = _root.get(i); - if (o instanceof String) - continue; - - XmlParser.Node node = (XmlParser.Node)o; - if (node.getTag().equals("Arg")) - { - String namedAttribute = node.getAttribute("name"); - Object value = value(null, (XmlParser.Node)o); - if (namedAttribute != null) - namedArgMap.put(namedAttribute, value); - arguments.add(value); - } - else - { - index = i; - break; - } - } - try { - obj = construct(oClass, arguments.toArray(), namedArgMap); + obj = construct(oClass, new NamedArgs(null, XmlConfiguration.getNodes(_root, "Arg"))); } catch (NoSuchMethodException x) { - throw new IllegalStateException(String.format("No constructor %s(%s,%s) in %s", oClass, arguments, namedArgMap, _configuration)); + throw new IllegalStateException(String.format("No maatching constructor %s in %s", oClass, _configuration)); } } if (id != null) @@ -916,7 +890,6 @@ public class XmlConfiguration String id = aoeNode.getString("Id"); String name = aoeNode.getString("Name"); String clazz = aoeNode.getString("Class"); - List args = aoeNode.getList("Arg"); Class oClass; if (clazz != null) @@ -937,7 +910,7 @@ public class XmlConfiguration try { - Object nobj = call(oClass, name, obj, args.toArray(new Object[0])); + Object nobj = call(oClass, name, obj, new NamedArgs(obj, aoeNode.getNodes("Arg"))); if (id != null) _configuration.getIdMap().put(id, nobj); configure(nobj, node, aoeNode.getNext()); @@ -949,7 +922,7 @@ public class XmlConfiguration } } - private Object call(Class oClass, String methodName, Object obj, Object[] arg) throws InvocationTargetException, NoSuchMethodException + private Object call(Class oClass, String methodName, Object obj, NamedArgs args) throws InvocationTargetException, NoSuchMethodException { Objects.requireNonNull(oClass, "Class cannot be null"); Objects.requireNonNull(methodName, "Method name cannot be null"); @@ -961,7 +934,8 @@ public class XmlConfiguration { if (!method.getName().equals(methodName)) continue; - if (method.getParameterCount() != arg.length) + Object[] arguments = args.applyTo(method); + if (arguments == null) continue; if (Modifier.isStatic(method.getModifiers()) != (obj == null)) continue; @@ -970,34 +944,7 @@ public class XmlConfiguration try { - return invokeMethod(method, obj, arg); - } - catch (IllegalAccessException | IllegalArgumentException e) - { - LOG.ignore(e); - } - } - - // Lets look for a method with varargs arguments - Object[] argsWithVarargs = null; - for (Method method : oClass.getMethods()) - { - if (!method.getName().equals(methodName)) - continue; - if (method.getParameterCount() != arg.length + 1) - continue; - if (!method.getParameterTypes()[arg.length].isArray()) - continue; - if (Modifier.isStatic(method.getModifiers()) != (obj == null)) - continue; - if ((obj == null) && method.getDeclaringClass() != oClass) - continue; - - if (argsWithVarargs == null) - argsWithVarargs = ArrayUtil.addToArray(arg, new Object[0], Object.class); - try - { - return invokeMethod(method, obj, argsWithVarargs); + return invokeMethod(method, obj, arguments); } catch (IllegalAccessException | IllegalArgumentException e) { @@ -1020,33 +967,16 @@ public class XmlConfiguration AttrOrElementNode aoeNode = new AttrOrElementNode(obj, node, "Id", "Class", "Arg"); String id = aoeNode.getString("Id"); String clazz = aoeNode.getString("Class"); - List argNodes = aoeNode.getNodes("Arg"); if (LOG.isDebugEnabled()) LOG.debug("XML new " + clazz); Class oClass = Loader.loadClass(clazz); - // Find the elements - Map namedArgMap = new HashMap<>(); - List arguments = new LinkedList<>(); - for (XmlParser.Node child : argNodes) - { - String namedAttribute = child.getAttribute("name"); - Object value = value(obj, child); - if (namedAttribute != null) - { - // named arguments - namedArgMap.put(namedAttribute, value); - } - // raw arguments - arguments.add(value); - } - Object nobj; try { - nobj = construct(oClass, arguments.toArray(), namedArgMap); + nobj = construct(oClass, new NamedArgs(obj, aoeNode.getNodes("Arg"))); } catch (NoSuchMethodException e) { @@ -1061,80 +991,19 @@ public class XmlConfiguration return nobj; } - private Object construct(Class klass, Object[] arguments, Map namedArgMap) throws InvocationTargetException, NoSuchMethodException + private Object construct(Class klass, NamedArgs args) throws InvocationTargetException, NoSuchMethodException { Objects.requireNonNull(klass, "Class cannot be null"); - Objects.requireNonNull(namedArgMap, "Named Argument Map cannot be null"); + Objects.requireNonNull(args, "Named list cannot be null"); + constructors: for (Constructor constructor : klass.getConstructors()) { - if (arguments == null) - { - // null arguments in .newInstance() is allowed - if (constructor.getParameterCount() != 0) - continue; - } - else if (constructor.getParameterCount() != arguments.length) - { - continue; - } - try { - if (arguments == null || arguments.length == 0) - { - if (LOG.isDebugEnabled()) - LOG.debug("Invoking constructor, no arguments"); - return invokeConstructor(constructor); - } - - if (namedArgMap.isEmpty()) - { - if (LOG.isDebugEnabled()) - LOG.debug("Invoking constructor, no XML parameter mapping"); + Object[] arguments = args.applyTo(constructor); + if (arguments != null) return invokeConstructor(constructor, arguments); - } - - Annotation[][] parameterAnnotations = constructor.getParameterAnnotations(); - if (parameterAnnotations == null || parameterAnnotations.length == 0) - { - if (LOG.isDebugEnabled()) - LOG.debug("Invoking constructor, no parameter annotations"); - return invokeConstructor(constructor, arguments); - } - - int count = 0; - Object[] swizzled = new Object[arguments.length]; - for (Annotation[] annotations : parameterAnnotations) - { - for (Annotation annotation : annotations) - { - if (annotation instanceof Name) - { - Name param = (Name)annotation; - if (namedArgMap.containsKey(param.value())) - { - if (LOG.isDebugEnabled()) - LOG.debug("Mapping named parameter {} in position {}", param.value(), count); - swizzled[count] = namedArgMap.get(param.value()); - } - else - { - if (LOG.isDebugEnabled()) - LOG.debug("Mapping argument {} in position {}", arguments[count], count); - swizzled[count] = arguments[count]; - } - ++count; - } - else - { - if (LOG.isDebugEnabled()) - LOG.debug("Skipping parameter annotated with {}", annotation); - } - } - } - - return invokeConstructor(constructor, swizzled); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException e) { @@ -1777,40 +1646,146 @@ public class XmlConfiguration public List getNodes(String elementName) { - String attrName = StringUtil.asciiToLowerCase(elementName); - final List values = new ArrayList<>(); - - String attr = _node.getAttribute(attrName); - if (attr != null) - { - for (String a : StringUtil.csvSplit(null, attr, 0, attr.length())) - { - // create a fake node - XmlParser.Node n = new XmlParser.Node(null, elementName, null); - n.add(a); - values.add(n); - } - } - - for (int i = 0; i < _next; i++) - { - Object o = _node.get(i); - if (!(o instanceof XmlParser.Node)) - continue; - XmlParser.Node n = (XmlParser.Node)o; - - if (elementName.equals(n.getTag())) - { - if (attr != null) - throw new IllegalStateException("Cannot have attr '" + attrName + "' and element '" + elementName + "'"); - - values.add(n); - } - } - - return values; + return XmlConfiguration.getNodes(_node, elementName); } } + + private class NamedArgs + { + final List _arguments; + final List _names; + + NamedArgs(Object obj, List args) throws Exception + { + _arguments = new ArrayList<>(); + _names = new ArrayList<>(); + for (XmlParser.Node child : args) + { + _arguments.add(value(obj, child)); + _names.add(child.getAttribute("name")); + } + } + + NamedArgs(List arguments, List names) + { + _arguments = arguments; + _names = names; + } + + Object[] applyTo(Executable executable) + { + Object[] args = matchArgsToParameters(executable); + if (args == null) + { + // Could this be an empty varargs match? + int count = executable.getParameterCount(); + if (count > 0 && executable.getParameterTypes()[count - 1].isArray()) + args = asEmptyVarArgs(executable.getParameterTypes()[count - 1]).matchArgsToParameters(executable); + } + return args; + } + + NamedArgs asEmptyVarArgs(Class varArgType) + { + List arguments = new ArrayList<>(_arguments); + arguments.add(Array.newInstance(varArgType.getComponentType(), 0)); + List names = new ArrayList<>(_names); + names.add(null); + return new NamedArgs(arguments, names); + } + + Object[] matchArgsToParameters(Executable executable) + { + int count = executable.getParameterCount(); + + // No match of wrong number of parameters + if (count != _arguments.size()) + return null; + + // Handle no parameter case + if (count == 0) + return new Object[0]; + + // If no arg names are specified, keep the arg order + if (_names.stream().noneMatch(Objects::nonNull)) + return _arguments.toArray(new Object[0]); + + // If we don't have any parameters with names, then no match + Annotation[][] parameterAnnotations = executable.getParameterAnnotations(); + if (parameterAnnotations == null || parameterAnnotations.length == 0) + return null; + + // Find the position of all named parameters from the executable + Map position = new HashMap<>(); + int p = 0; + for (Annotation[] paramAnnotation : parameterAnnotations) + { + Integer pos = p++; + Arrays.stream(paramAnnotation) + .filter(Name.class::isInstance) + .map(Name.class::cast) + .findFirst().ifPresent(n -> position.put(n.value(), pos)); + } + + List arguments = new ArrayList<>(_arguments); + List names = new ArrayList<>(_names); + // Map the actual arguments to the names + for (p = 0; p < count; p++) + { + String name = names.get(p); + if (name != null) + { + Integer pos = position.get(name); + if (pos == null) + return null; + if (pos != p) + { + // adjust position of parameter + arguments.add(pos, arguments.remove(p)); + names.add(pos, names.remove(p)); + p = Math.min(p, pos); + } + } + } + return arguments.toArray(new Object[0]); + } + } + } + + private static List getNodes(XmlParser.Node node, String elementName) + { + String attrName = StringUtil.asciiToLowerCase(elementName); + final List values = new ArrayList<>(); + + String attr = node.getAttribute(attrName); + if (attr != null) + { + for (String a : StringUtil.csvSplit(null, attr, 0, attr.length())) + { + // create a fake node + XmlParser.Node n = new XmlParser.Node(null, elementName, null); + n.add(a); + values.add(n); + } + } + + for (int i = 0; i < node.size(); i++) + { + Object o = node.get(i); + if (!(o instanceof XmlParser.Node)) + continue; + XmlParser.Node n = (XmlParser.Node)o; + + if (elementName.equals(n.getTag())) + { + if (attr != null) + throw new IllegalStateException("Cannot have attr '" + attrName + "' and element '" + elementName + "'"); + + values.add(n); + } + } + + return values; } /** diff --git a/jetty-xml/src/test/java/org/eclipse/jetty/xml/AnnotatedTestConfiguration.java b/jetty-xml/src/test/java/org/eclipse/jetty/xml/AnnotatedTestConfiguration.java index bb50abcc987..d114fc3b78a 100644 --- a/jetty-xml/src/test/java/org/eclipse/jetty/xml/AnnotatedTestConfiguration.java +++ b/jetty-xml/src/test/java/org/eclipse/jetty/xml/AnnotatedTestConfiguration.java @@ -41,6 +41,18 @@ public class AnnotatedTestConfiguration { } + public AnnotatedTestConfiguration(Integer test) + { + // exists to make constructor matching harder + throw new UnsupportedOperationException("Should not be called"); + } + + public AnnotatedTestConfiguration(Integer one, Integer two, Integer three) + { + // exists to make constructor matching harder + throw new UnsupportedOperationException("Should not be called"); + } + public AnnotatedTestConfiguration(@Name("first") String first, @Name("second") String second, @Name("third") String third) { this.first = first; @@ -48,6 +60,38 @@ public class AnnotatedTestConfiguration this.third = third; } + public AnnotatedTestConfiguration(Long one, Long two, Long three) + { + // exists to make constructor matching harder + throw new UnsupportedOperationException("Should not be called"); + } + + public void setAll(Integer one, Integer two, Integer three) + { + // exists to make method matching harder + throw new UnsupportedOperationException("Should not be called"); + } + + public void setAll(@Name("first") String first, @Name("second") String second, @Name("third") String third) + { + this.first = first; + this.second = second; + this.third = third; + } + + public void setAll(long one, long two, long three) + { + // exists to make method matching harder + throw new UnsupportedOperationException("Should not be called"); + } + + public void setVarArgs(String first, String... theRest) + { + this.first = first; + this.second = theRest.length > 0 ? theRest[0] : null; + this.third = theRest.length > 1 ? theRest[1] : null; + } + public String getFirst() { return first; diff --git a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java index 99b859ecada..e730d35af1d 100644 --- a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java +++ b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java @@ -35,6 +35,7 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; @@ -45,6 +46,11 @@ import org.eclipse.jetty.util.resource.PathResource; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; import org.xml.sax.SAXException; import static java.nio.charset.StandardCharsets.UTF_8; @@ -67,9 +73,17 @@ public class XmlConfigurationTest { public WorkDir workDir; - protected String[] _configure = new String[]{ - "org/eclipse/jetty/xml/configureWithAttr.xml", "org/eclipse/jetty/xml/configureWithElements.xml" - }; + public static class ScenarioProvider implements ArgumentsProvider + { + @Override + public Stream provideArguments(ExtensionContext context) + { + return Stream.of( + "org/eclipse/jetty/xml/configureWithAttr.xml", + "org/eclipse/jetty/xml/configureWithElements.xml" + ).map(Arguments::of); + } + } private static final String STRING_ARRAY_XML = "String1String2"; private static final String INT_ARRAY_XML = "12"; @@ -82,170 +96,166 @@ public class XmlConfigurationTest configuration.configure(); } - @Test - public void testPassedObject() throws Exception + @ParameterizedTest + @ArgumentsSource(ScenarioProvider.class) + public void testPassedObject(String configure) throws Exception { - for (String configure : _configure) - { - Map properties = new HashMap<>(); - properties.put("whatever", "xxx"); - TestConfiguration.VALUE = 77; - URL url = XmlConfigurationTest.class.getClassLoader().getResource(configure); - XmlConfiguration configuration = new XmlConfiguration(url); - TestConfiguration tc = new TestConfiguration("tc"); - configuration.getProperties().putAll(properties); - configuration.configure(tc); + Map properties = new HashMap<>(); + properties.put("whatever", "xxx"); + TestConfiguration.VALUE = 77; + URL url = XmlConfigurationTest.class.getClassLoader().getResource(configure); + XmlConfiguration configuration = new XmlConfiguration(url); + TestConfiguration tc = new TestConfiguration("tc"); + configuration.getProperties().putAll(properties); + configuration.configure(tc); - assertEquals("SetValue", tc.testObject, "Set String"); - assertEquals(2, tc.testInt, "Set Type"); + assertEquals("SetValue", tc.testObject, "Set String"); + assertEquals(2, tc.testInt, "Set Type"); - assertEquals(18080, tc.propValue); + assertEquals(18080, tc.propValue); - assertEquals("PutValue", tc.get("Test"), "Put"); - assertEquals("2", tc.get("TestDft"), "Put dft"); - assertEquals(2, tc.get("TestInt"), "Put type"); + assertEquals("PutValue", tc.get("Test"), "Put"); + assertEquals("2", tc.get("TestDft"), "Put dft"); + assertEquals(2, tc.get("TestInt"), "Put type"); - assertEquals("PutValue", tc.get("Trim"), "Trim"); - assertNull(tc.get("Null"), "Null"); - assertNull(tc.get("NullTrim"), "NullTrim"); + assertEquals("PutValue", tc.get("Trim"), "Trim"); + assertNull(tc.get("Null"), "Null"); + assertNull(tc.get("NullTrim"), "NullTrim"); - assertEquals(1.2345, tc.get("ObjectTrim"), "ObjectTrim"); - assertEquals("-1String", tc.get("Objects"), "Objects"); - assertEquals("-1String", tc.get("ObjectsTrim"), "ObjectsTrim"); - assertEquals("\n PutValue\n ", tc.get("String"), "String"); - assertEquals("", tc.get("NullString"), "NullString"); - assertEquals("\n ", tc.get("WhiteSpace"), "WhiteSpace"); - assertEquals("\n 1.2345\n ", tc.get("ObjectString"), "ObjectString"); - assertEquals("-1String", tc.get("ObjectsString"), "ObjectsString"); - assertEquals("-1\n String", tc.get("ObjectsWhiteString"), "ObjectsWhiteString"); + assertEquals(1.2345, tc.get("ObjectTrim"), "ObjectTrim"); + assertEquals("-1String", tc.get("Objects"), "Objects"); + assertEquals("-1String", tc.get("ObjectsTrim"), "ObjectsTrim"); + assertEquals("\n PutValue\n ", tc.get("String"), "String"); + assertEquals("", tc.get("NullString"), "NullString"); + assertEquals("\n ", tc.get("WhiteSpace"), "WhiteSpace"); + assertEquals("\n 1.2345\n ", tc.get("ObjectString"), "ObjectString"); + assertEquals("-1String", tc.get("ObjectsString"), "ObjectsString"); + assertEquals("-1\n String", tc.get("ObjectsWhiteString"), "ObjectsWhiteString"); - assertEquals(System.getProperty("user.dir") + "/stuff", tc.get("SystemProperty"), "SystemProperty"); - assertEquals(System.getenv("HOME"), tc.get("Env"), "Env"); + assertEquals(System.getProperty("user.dir") + "/stuff", tc.get("SystemProperty"), "SystemProperty"); + assertEquals(System.getenv("HOME"), tc.get("Env"), "Env"); - assertEquals("xxx", tc.get("Property"), "Property"); + assertEquals("xxx", tc.get("Property"), "Property"); - assertEquals("Yes", tc.get("Called"), "Called"); + assertEquals("Yes", tc.get("Called"), "Called"); - assertTrue(TestConfiguration.called); + assertTrue(TestConfiguration.called); - assertEquals("Blah", tc.oa[0], "oa[0]"); - assertEquals("1.2.3.4:5678", tc.oa[1], "oa[1]"); - assertEquals(1.2345, tc.oa[2], "oa[2]"); - assertNull(tc.oa[3], "oa[3]"); + assertEquals("Blah", tc.oa[0], "oa[0]"); + assertEquals("1.2.3.4:5678", tc.oa[1], "oa[1]"); + assertEquals(1.2345, tc.oa[2], "oa[2]"); + assertNull(tc.oa[3], "oa[3]"); - assertEquals(1, tc.ia[0], "ia[0]"); - assertEquals(2, tc.ia[1], "ia[1]"); - assertEquals(3, tc.ia[2], "ia[2]"); - assertEquals(0, tc.ia[3], "ia[3]"); + assertEquals(1, tc.ia[0], "ia[0]"); + assertEquals(2, tc.ia[1], "ia[1]"); + assertEquals(3, tc.ia[2], "ia[2]"); + assertEquals(0, tc.ia[3], "ia[3]"); - TestConfiguration tc2 = tc.nested; - assertNotNull(tc2); - assertEquals(true, tc2.get("Arg"), "Called(bool)"); + TestConfiguration tc2 = tc.nested; + assertNotNull(tc2); + assertEquals(true, tc2.get("Arg"), "Called(bool)"); - assertNull(tc.get("Arg"), "nested config"); - assertEquals(true, tc2.get("Arg"), "nested config"); + assertNull(tc.get("Arg"), "nested config"); + assertEquals(true, tc2.get("Arg"), "nested config"); - assertEquals("Call1", tc2.testObject, "nested config"); - assertEquals(4, tc2.testInt, "nested config"); - assertEquals("http://www.eclipse.com/", tc2.url.toString(), "nested call"); + assertEquals("Call1", tc2.testObject, "nested config"); + assertEquals(4, tc2.testInt, "nested config"); + assertEquals("http://www.eclipse.com/", tc2.url.toString(), "nested call"); - assertEquals(tc.testField1, 77, "static to field"); - assertEquals(tc.testField2, 2, "field to field"); - assertEquals(TestConfiguration.VALUE, 42, "literal to static"); + assertEquals(tc.testField1, 77, "static to field"); + assertEquals(tc.testField2, 2, "field to field"); + assertEquals(TestConfiguration.VALUE, 42, "literal to static"); - assertEquals(((Map)configuration.getIdMap().get("map")).get("key0"), "value0"); - assertEquals(((Map)configuration.getIdMap().get("map")).get("key1"), "value1"); - } + assertEquals(((Map)configuration.getIdMap().get("map")).get("key0"), "value0"); + assertEquals(((Map)configuration.getIdMap().get("map")).get("key1"), "value1"); } - @Test - public void testNewObject() throws Exception + @ParameterizedTest + @ArgumentsSource(ScenarioProvider.class) + public void testNewObject(String configure) throws Exception { - for (String configure : _configure) + TestConfiguration.VALUE = 71; + Map properties = new HashMap<>(); + properties.put("whatever", "xxx"); + + URL url = XmlConfigurationTest.class.getClassLoader().getResource(configure); + final AtomicInteger count = new AtomicInteger(0); + XmlConfiguration configuration = new XmlConfiguration(url) { - TestConfiguration.VALUE = 71; - Map properties = new HashMap<>(); - properties.put("whatever", "xxx"); - - URL url = XmlConfigurationTest.class.getClassLoader().getResource(configure); - final AtomicInteger count = new AtomicInteger(0); - XmlConfiguration configuration = new XmlConfiguration(url) + @Override + public void initializeDefaults(Object object) { - @Override - public void initializeDefaults(Object object) + if (object instanceof TestConfiguration) { - if (object instanceof TestConfiguration) - { - count.incrementAndGet(); - ((TestConfiguration)object).setNested(null); - ((TestConfiguration)object).setTestString("NEW DEFAULT"); - } + count.incrementAndGet(); + ((TestConfiguration)object).setNested(null); + ((TestConfiguration)object).setTestString("NEW DEFAULT"); } - }; - configuration.getProperties().putAll(properties); - TestConfiguration tc = (TestConfiguration)configuration.configure(); + } + }; + configuration.getProperties().putAll(properties); + TestConfiguration tc = (TestConfiguration)configuration.configure(); - assertEquals(3, count.get()); + assertEquals(3, count.get()); - assertEquals("NEW DEFAULT", tc.getTestString()); - assertEquals("nested", tc.getNested().getTestString()); - assertEquals("NEW DEFAULT", tc.getNested().getNested().getTestString()); + assertEquals("NEW DEFAULT", tc.getTestString()); + assertEquals("nested", tc.getNested().getTestString()); + assertEquals("NEW DEFAULT", tc.getNested().getNested().getTestString()); - assertEquals("SetValue", tc.testObject, "Set String"); - assertEquals(2, tc.testInt, "Set Type"); + assertEquals("SetValue", tc.testObject, "Set String"); + assertEquals(2, tc.testInt, "Set Type"); - assertEquals(18080, tc.propValue); + assertEquals(18080, tc.propValue); - assertEquals("PutValue", tc.get("Test"), "Put"); - assertEquals("2", tc.get("TestDft"), "Put dft"); - assertEquals(2, tc.get("TestInt"), "Put type"); + assertEquals("PutValue", tc.get("Test"), "Put"); + assertEquals("2", tc.get("TestDft"), "Put dft"); + assertEquals(2, tc.get("TestInt"), "Put type"); - assertEquals("PutValue", tc.get("Trim"), "Trim"); - assertNull(tc.get("Null"), "Null"); - assertNull(tc.get("NullTrim"), "NullTrim"); + assertEquals("PutValue", tc.get("Trim"), "Trim"); + assertNull(tc.get("Null"), "Null"); + assertNull(tc.get("NullTrim"), "NullTrim"); - assertEquals(1.2345, tc.get("ObjectTrim"), "ObjectTrim"); - assertEquals("-1String", tc.get("Objects"), "Objects"); - assertEquals("-1String", tc.get("ObjectsTrim"), "ObjectsTrim"); - assertEquals("\n PutValue\n ", tc.get("String"), "String"); - assertEquals("", tc.get("NullString"), "NullString"); - assertEquals("\n ", tc.get("WhiteSpace"), "WhiteSpace"); - assertEquals("\n 1.2345\n ", tc.get("ObjectString"), "ObjectString"); - assertEquals("-1String", tc.get("ObjectsString"), "ObjectsString"); - assertEquals("-1\n String", tc.get("ObjectsWhiteString"), "ObjectsWhiteString"); + assertEquals(1.2345, tc.get("ObjectTrim"), "ObjectTrim"); + assertEquals("-1String", tc.get("Objects"), "Objects"); + assertEquals("-1String", tc.get("ObjectsTrim"), "ObjectsTrim"); + assertEquals("\n PutValue\n ", tc.get("String"), "String"); + assertEquals("", tc.get("NullString"), "NullString"); + assertEquals("\n ", tc.get("WhiteSpace"), "WhiteSpace"); + assertEquals("\n 1.2345\n ", tc.get("ObjectString"), "ObjectString"); + assertEquals("-1String", tc.get("ObjectsString"), "ObjectsString"); + assertEquals("-1\n String", tc.get("ObjectsWhiteString"), "ObjectsWhiteString"); - assertEquals(System.getProperty("user.dir") + "/stuff", tc.get("SystemProperty"), "SystemProperty"); - assertEquals("xxx", tc.get("Property"), "Property"); + assertEquals(System.getProperty("user.dir") + "/stuff", tc.get("SystemProperty"), "SystemProperty"); + assertEquals("xxx", tc.get("Property"), "Property"); - assertEquals("Yes", tc.get("Called"), "Called"); + assertEquals("Yes", tc.get("Called"), "Called"); - assertTrue(TestConfiguration.called); + assertTrue(TestConfiguration.called); - assertEquals("Blah", tc.oa[0], "oa[0]"); - assertEquals("1.2.3.4:5678", tc.oa[1], "oa[1]"); - assertEquals(1.2345, tc.oa[2], "oa[2]"); - assertNull(tc.oa[3], "oa[3]"); + assertEquals("Blah", tc.oa[0], "oa[0]"); + assertEquals("1.2.3.4:5678", tc.oa[1], "oa[1]"); + assertEquals(1.2345, tc.oa[2], "oa[2]"); + assertNull(tc.oa[3], "oa[3]"); - assertEquals(1, tc.ia[0], "ia[0]"); - assertEquals(2, tc.ia[1], "ia[1]"); - assertEquals(3, tc.ia[2], "ia[2]"); - assertEquals(0, tc.ia[3], "ia[3]"); + assertEquals(1, tc.ia[0], "ia[0]"); + assertEquals(2, tc.ia[1], "ia[1]"); + assertEquals(3, tc.ia[2], "ia[2]"); + assertEquals(0, tc.ia[3], "ia[3]"); - TestConfiguration tc2 = tc.nested; - assertNotNull(tc2); - assertEquals(true, tc2.get("Arg"), "Called(bool)"); + TestConfiguration tc2 = tc.nested; + assertNotNull(tc2); + assertEquals(true, tc2.get("Arg"), "Called(bool)"); - assertNull(tc.get("Arg"), "nested config"); - assertEquals(true, tc2.get("Arg"), "nested config"); + assertNull(tc.get("Arg"), "nested config"); + assertEquals(true, tc2.get("Arg"), "nested config"); - assertEquals("Call1", tc2.testObject, "nested config"); - assertEquals(4, tc2.testInt, "nested config"); - assertEquals("http://www.eclipse.com/", tc2.url.toString(), "nested call"); + assertEquals("Call1", tc2.testObject, "nested config"); + assertEquals(4, tc2.testInt, "nested config"); + assertEquals("http://www.eclipse.com/", tc2.url.toString(), "nested call"); - assertEquals(71, tc.testField1, "static to field"); - assertEquals(2, tc.testField2, "field to field"); - assertEquals(42, TestConfiguration.VALUE, "literal to static"); - } + assertEquals(71, tc.testField1, "static to field"); + assertEquals(2, tc.testField2, "field to field"); + assertEquals(42, TestConfiguration.VALUE, "literal to static"); } public XmlConfiguration asXmlConfiguration(String rawXml) throws IOException, SAXException @@ -648,6 +658,139 @@ public class XmlConfigurationTest assertEquals("arg3", atc.getNested().getThird(), "nested third parameter not wired correctly"); } + @Test + public void testCallNamedInjection() throws Exception + { + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + " arg1 " + + " arg2 " + + " arg3 " + + " " + + ""); + + AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure(); + + assertEquals("arg1", atc.getFirst(), "first parameter not wired correctly"); + assertEquals("arg2", atc.getSecond(), "second parameter not wired correctly"); + assertEquals("arg3", atc.getThird(), "third parameter not wired correctly"); + } + + @Test + public void testCallNamedInjectionOrdered() throws Exception + { + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + " arg1 " + + " arg2 " + + " arg3 " + + " " + + ""); + + AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure(); + + assertEquals("arg1", atc.getFirst(), "first parameter not wired correctly"); + assertEquals("arg2", atc.getSecond(), "second parameter not wired correctly"); + assertEquals("arg3", atc.getThird(), "third parameter not wired correctly"); + } + + @Test + public void testCallNamedInjectionUnOrdered() throws Exception + { + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + " arg1 " + + " arg3 " + + " arg2 " + + " " + + ""); + + AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure(); + + assertEquals("arg1", atc.getFirst(), "first parameter not wired correctly"); + assertEquals("arg2", atc.getSecond(), "second parameter not wired correctly"); + assertEquals("arg3", atc.getThird(), "third parameter not wired correctly"); + } + + @Test + public void testCallNamedInjectionOrderedMixed() throws Exception + { + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + " arg1 " + + " arg2 " + + " arg3 " + + " " + + ""); + + AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure(); + + assertEquals("arg1", atc.getFirst(), "first parameter not wired correctly"); + assertEquals("arg2", atc.getSecond(), "second parameter not wired correctly"); + assertEquals("arg3", atc.getThird(), "third parameter not wired correctly"); + } + + @Test + public void testCallNamedInjectionUnorderedMixed() throws Exception + { + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + " arg3 " + + " arg2 " + + " arg1 " + + " " + + ""); + + AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure(); + + assertEquals("arg1", atc.getFirst(), "first parameter not wired correctly"); + assertEquals("arg2", atc.getSecond(), "second parameter not wired correctly"); + assertEquals("arg3", atc.getThird(), "third parameter not wired correctly"); + } + + @Test + public void testCallVarArgs() throws Exception + { + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + " one " + + " twothree " + + " " + + ""); + + AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure(); + + assertEquals("one", atc.getFirst(), "first parameter not wired correctly"); + assertEquals("two", atc.getSecond(), "second parameter not wired correctly"); + assertEquals("three", atc.getThird(), "third parameter not wired correctly"); + } + + @Test + public void testCallMissingVarArgs() throws Exception + { + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " arg1 " + + " arg2 " + + " arg3 " + + " " + + " one" + + " " + + ""); + + AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure(); + + assertEquals("one", atc.getFirst(), "first parameter not wired correctly"); + assertNull(atc.getSecond()); + assertNull(atc.getThird()); + } + @Test public void testArgumentsGetIgnoredMissingDTD() throws Exception {