SPR-7470
+ add c: namespace git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@3784 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
parent
6e9e1f0946
commit
f578693015
|
|
@ -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 @@
|
||||||
|
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/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
|
||||||
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
|
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
|
||||||
|
|
|
||||||
|
|
@ -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 @@
|
||||||
|
<?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