diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/SimpleConstructorNamespaceHandler.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/SimpleConstructorNamespaceHandler.java
new file mode 100644
index 0000000000..81d8632bf5
--- /dev/null
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/SimpleConstructorNamespaceHandler.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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.springframework.beans.factory.xml;
+
+import java.util.Collection;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
+import org.springframework.core.Conventions;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * Simple NamespaceHandler
implementation that maps custom
+ * attributes directly through to bean properties. An important point to note is
+ * that this NamespaceHandler
does not have a corresponding schema
+ * since there is no way to know in advance all possible attribute names.
+ *
+ *
+ * An example of the usage of this NamespaceHandler
is shown below:
+ *
+ *
+ * <bean id="author" class="..TestBean" c:name="Enescu" c:work-ref="compositions"/>
+ *
+ *
+ * Here the 'c:name
' corresponds directly to the 'name
+ * ' argument declared on the constructor of class 'TestBean
'. The
+ * 'c:work-ref
' attributes corresponds to the 'work
'
+ * argument and, rather than being the concrete value, it contains the name of
+ * the bean that will be considered as a parameter.
+ *
+ * Note: This implementation supports only named parameters - there is no
+ * support for indexes or types. Further more, the names are used as hints by
+ * the container which, by default, does type introspection.
+ *
+ * @see SimplePropertyNamespaceHandler
+ * @author Costin Leau
+ */
+public class SimpleConstructorNamespaceHandler implements NamespaceHandler {
+
+ private static final String REF_SUFFIX = "-ref";
+ private static final String DELIMITER_PREFIX = "_";
+
+ public void init() {
+ }
+
+ public BeanDefinition parse(Element element, ParserContext parserContext) {
+ parserContext.getReaderContext().error(
+ "Class [" + getClass().getName() + "] does not support custom elements.", element);
+ return null;
+ }
+
+ public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
+ if (node instanceof Attr) {
+ Attr attr = (Attr) node;
+ String argName = StringUtils.trimWhitespace(parserContext.getDelegate().getLocalName(attr));
+ String argValue = StringUtils.trimWhitespace(attr.getValue());
+
+ ConstructorArgumentValues cvs = definition.getBeanDefinition().getConstructorArgumentValues();
+ boolean ref = false;
+
+ // handle -ref arguments
+ if (argName.endsWith(REF_SUFFIX)) {
+ ref = true;
+ argName = argName.substring(0, argName.length() - REF_SUFFIX.length());
+ }
+
+ ValueHolder valueHolder = new ValueHolder(ref ? new RuntimeBeanReference(argValue) : argValue);
+ valueHolder.setSource(parserContext.getReaderContext().extractSource(attr));
+
+ // handle "escaped"/"_" arguments
+ if (argName.startsWith(DELIMITER_PREFIX)) {
+ String arg = argName.substring(1).trim();
+
+ // fast default check
+ if (!StringUtils.hasText(arg)) {
+ cvs.addGenericArgumentValue(valueHolder);
+ }
+ // assume an index otherwise
+ else {
+ int index = -1;
+ try {
+ index = Integer.parseInt(arg);
+ } catch (NumberFormatException ex) {
+ parserContext.getReaderContext().error(
+ "Constructor argument '" + argName + "' specifies an invalid integer", attr);
+ }
+ if (index < 0) {
+ parserContext.getReaderContext().error(
+ "Constructor argument '" + argName + "' specifies a negative index", attr);
+ }
+
+ if (cvs.hasIndexedArgumentValue(index)){
+ parserContext.getReaderContext().error(
+ "Constructor argument '" + argName + "' with index "+ index+" already defined using ." +
+ " Only one approach may be used per argument.", attr);
+ }
+
+ cvs.addIndexedArgumentValue(index, valueHolder);
+ }
+ }
+ // no escaping -> ctr name
+ else {
+ String name = Conventions.attributeNameToPropertyName(argName);
+ if (containsArgWithName(name, cvs)){
+ parserContext.getReaderContext().error(
+ "Constructor argument '" + argName + "' already defined using ." +
+ " Only one approach may be used per argument.", attr);
+ }
+ valueHolder.setName(Conventions.attributeNameToPropertyName(argName));
+ cvs.addGenericArgumentValue(valueHolder);
+ }
+ }
+ return definition;
+ }
+
+ private boolean containsArgWithName(String name, ConstructorArgumentValues cvs) {
+ if (!checkName(name, cvs.getGenericArgumentValues())) {
+ return checkName(name, cvs.getIndexedArgumentValues().values());
+ }
+
+ return true;
+ }
+
+ private boolean checkName(String name, Collection values) {
+ for (ValueHolder holder : values) {
+ if (name.equals(holder.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/org.springframework.beans/src/main/resources/META-INF/spring.handlers b/org.springframework.beans/src/main/resources/META-INF/spring.handlers
index 8fda43f0c7..80f1923094 100644
--- a/org.springframework.beans/src/main/resources/META-INF/spring.handlers
+++ b/org.springframework.beans/src/main/resources/META-INF/spring.handlers
@@ -1,2 +1,3 @@
+http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
diff --git a/org.springframework.beans/src/test/java/org/springframework/beans/factory/xml/SimpleConstructorNamespaceHandlerTests.java b/org.springframework.beans/src/test/java/org/springframework/beans/factory/xml/SimpleConstructorNamespaceHandlerTests.java
new file mode 100644
index 0000000000..86ddda5642
--- /dev/null
+++ b/org.springframework.beans/src/test/java/org/springframework/beans/factory/xml/SimpleConstructorNamespaceHandlerTests.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2002-2007 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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.springframework.beans.factory.xml;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
+import org.springframework.core.io.ClassPathResource;
+
+import test.beans.DummyBean;
+import test.beans.TestBean;
+
+/**
+ * @author Costin Leau
+ */
+public class SimpleConstructorNamespaceHandlerTests {
+
+ @Test
+ public void simpleValue() throws Exception {
+ XmlBeanFactory beanFactory = createFactory("simpleConstructorNamespaceHandlerTests.xml");
+ String name = "simple";
+ // beanFactory.getBean("simple1", DummyBean.class);
+ DummyBean nameValue = beanFactory.getBean(name, DummyBean.class);
+ assertEquals("simple", nameValue.getValue());
+ }
+
+ @Test
+ public void simpleRef() throws Exception {
+ XmlBeanFactory beanFactory = createFactory("simpleConstructorNamespaceHandlerTests.xml");
+ String name = "simple-ref";
+ // beanFactory.getBean("name-value1", TestBean.class);
+ DummyBean nameValue = beanFactory.getBean(name, DummyBean.class);
+ assertEquals(beanFactory.getBean("name"), nameValue.getValue());
+ }
+
+ @Test
+ public void nameValue() throws Exception {
+ XmlBeanFactory beanFactory = createFactory("simpleConstructorNamespaceHandlerTests.xml");
+ String name = "name-value";
+ // beanFactory.getBean("name-value1", TestBean.class);
+ TestBean nameValue = beanFactory.getBean(name, TestBean.class);
+ assertEquals(name, nameValue.getName());
+ assertEquals(10, nameValue.getAge());
+ }
+
+ @Test
+ public void nameRef() throws Exception {
+ XmlBeanFactory beanFactory = createFactory("simpleConstructorNamespaceHandlerTests.xml");
+ TestBean nameValue = beanFactory.getBean("name-value", TestBean.class);
+ DummyBean nameRef = beanFactory.getBean("name-ref", DummyBean.class);
+
+ assertEquals("some-name", nameRef.getName());
+ assertEquals(nameValue, nameRef.getSpouse());
+ }
+
+ @Test
+ public void typeIndexedValue() throws Exception {
+ XmlBeanFactory beanFactory = createFactory("simpleConstructorNamespaceHandlerTests.xml");
+ DummyBean typeRef = beanFactory.getBean("indexed-value", DummyBean.class);
+
+ assertEquals("at", typeRef.getName());
+ assertEquals("austria", typeRef.getValue());
+ assertEquals(10, typeRef.getAge());
+ }
+
+ @Test
+ public void typeIndexedRef() throws Exception {
+ XmlBeanFactory beanFactory = createFactory("simpleConstructorNamespaceHandlerTests.xml");
+ DummyBean typeRef = beanFactory.getBean("indexed-ref", DummyBean.class);
+
+ assertEquals("some-name", typeRef.getName());
+ assertEquals(beanFactory.getBean("name-value"), typeRef.getSpouse());
+ }
+
+ @Test(expected = BeanDefinitionStoreException.class)
+ public void ambiguousConstructor() throws Exception {
+ new XmlBeanFactory(new ClassPathResource("simpleConstructorNamespaceHandlerTestsWithErrors.xml", getClass()));
+ }
+
+ @Test
+ public void constructorWithNameEndingInRef() throws Exception {
+ XmlBeanFactory beanFactory = createFactory("simpleConstructorNamespaceHandlerTests.xml");
+ DummyBean derivedBean = beanFactory.getBean("beanWithRefConstructorArg", DummyBean.class);
+ assertEquals(10, derivedBean.getAge());
+ assertEquals("silly name", derivedBean.getName());
+ }
+
+ private XmlBeanFactory createFactory(String resourceName) {
+ XmlBeanFactory fact = new XmlBeanFactory(new ClassPathResource(resourceName, getClass()));
+ fact.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer());
+ return fact;
+ }
+}
\ No newline at end of file
diff --git a/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/simpleConstructorNamespaceHandlerTests.xml b/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/simpleConstructorNamespaceHandlerTests.xml
new file mode 100644
index 0000000000..f245c964dd
--- /dev/null
+++ b/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/simpleConstructorNamespaceHandlerTests.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file