Costin Leau
14 years ago
4 changed files with 308 additions and 0 deletions
@ -0,0 +1,151 @@
@@ -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 <code>NamespaceHandler</code> implementation that maps custom |
||||
* attributes directly through to bean properties. An important point to note is |
||||
* that this <code>NamespaceHandler</code> does not have a corresponding schema |
||||
* since there is no way to know in advance all possible attribute names. |
||||
* |
||||
* <p> |
||||
* An example of the usage of this <code>NamespaceHandler</code> is shown below: |
||||
* |
||||
* <pre class="code"> |
||||
* <bean id="author" class="..TestBean" c:name="Enescu" c:work-ref="compositions"/> |
||||
* </pre> |
||||
* |
||||
* Here the '<code>c:name</code>' corresponds directly to the '<code>name</code> |
||||
* ' argument declared on the constructor of class '<code>TestBean</code>'. The |
||||
* '<code>c:work-ref</code>' attributes corresponds to the '<code>work</code>' |
||||
* argument and, rather than being the concrete value, it contains the name of |
||||
* the bean that will be considered as a parameter. |
||||
* |
||||
* <b>Note</b>: 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 <constructor-arg>." + |
||||
" 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 <constructor-arg>." + |
||||
" 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<ValueHolder> values) { |
||||
for (ValueHolder holder : values) { |
||||
if (name.equals(holder.getName())) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
} |
@ -1,2 +1,3 @@
@@ -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 |
||||
|
@ -0,0 +1,109 @@
@@ -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; |
||||
} |
||||
} |
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<beans xmlns="http://www.springframework.org/schema/beans" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xmlns:c="http://www.springframework.org/schema/c" |
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> |
||||
|
||||
<!-- |
||||
<bean id="simple1" class="test.beans.DummyBean"> |
||||
<constructor-arg value="name"/> |
||||
</bean> |
||||
--> |
||||
<bean id="simple" class="test.beans.DummyBean" c:_="simple"/> |
||||
|
||||
<!-- |
||||
<bean id="simple1-ref" class="test.beans.DummyBean"> |
||||
<constructor-arg ref="name"/> |
||||
</bean> |
||||
--> |
||||
<bean id="simple-ref" class="test.beans.DummyBean" c:_-ref="name"/> |
||||
|
||||
<bean id="name" class="java.lang.String"> |
||||
<constructor-arg type="java.lang.String" value="some-name" index="0"/> |
||||
</bean> |
||||
|
||||
<!-- |
||||
<bean id="name-value1" class="test.beans.TestBean"> |
||||
<constructor-arg name="name" value="foo"/> |
||||
<constructor-arg name="age" value="10"/> |
||||
</bean> |
||||
--> |
||||
<bean id="name-value" class="test.beans.TestBean" c:age="10" c:name="name-value"/> |
||||
|
||||
<!-- |
||||
<bean id="name-ref1" class="test.beans.DummyBean"> |
||||
<constructor-arg name="name" ref="name"/> |
||||
<constructor-arg name="spouse" ref="name-value"/> |
||||
</bean> |
||||
--> |
||||
<bean id="name-ref" class="test.beans.DummyBean" c:name-ref="name" c:spouse-ref="name-value"/> |
||||
|
||||
<bean id="indexed-value" class="test.beans.DummyBean" c:_1="austria" c:_0="at" c:_2="10"/> |
||||
|
||||
<bean id="indexed-ref" class="test.beans.DummyBean" c:_0-ref="name" c:_1-ref="name-value"/> |
||||
|
||||
<bean id="beanWithRefConstructorArg" class="test.beans.DummyBean" c:nameRef="silly name" c:ageRef="10"/> |
||||
|
||||
</beans> |
Loading…
Reference in new issue