Browse Source

Improved Jaxb2Marshaller.supports()

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@2989 50f2f4bb-b051-0410-bef5-90022cba6387
pull/1/head
Arjen Poutsma 16 years ago
parent
commit
08092f34a6
  1. 93
      org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java
  2. 76
      org.springframework.oxm/src/test/java/org/springframework/oxm/jaxb/Jaxb2MarshallerTests.java
  3. 61
      org.springframework.oxm/src/test/java/org/springframework/oxm/jaxb/Primitives.java
  4. 129
      org.springframework.oxm/src/test/java/org/springframework/oxm/jaxb/StandardClasses.java

93
org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-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.
@ -16,20 +16,26 @@ @@ -16,20 +16,26 @@
package org.springframework.oxm.jaxb;
import java.awt.Image;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.lang.reflect.Type;
import java.lang.reflect.ParameterizedType;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.xml.XMLConstants;
@ -43,10 +49,12 @@ import javax.xml.bind.Unmarshaller; @@ -43,10 +49,12 @@ import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.ValidationException;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.attachment.AttachmentMarshaller;
import javax.xml.bind.attachment.AttachmentUnmarshaller;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamReader;
@ -68,13 +76,13 @@ import org.springframework.beans.factory.BeanClassLoaderAware; @@ -68,13 +76,13 @@ import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.oxm.GenericMarshaller;
import org.springframework.oxm.GenericUnmarshaller;
import org.springframework.oxm.MarshallingFailureException;
import org.springframework.oxm.UncategorizedMappingException;
import org.springframework.oxm.UnmarshallingFailureException;
import org.springframework.oxm.ValidationFailureException;
import org.springframework.oxm.XmlMappingException;
import org.springframework.oxm.GenericMarshaller;
import org.springframework.oxm.GenericUnmarshaller;
import org.springframework.oxm.mime.MimeContainer;
import org.springframework.oxm.mime.MimeMarshaller;
import org.springframework.oxm.mime.MimeUnmarshaller;
@ -120,7 +128,7 @@ public class Jaxb2Marshaller @@ -120,7 +128,7 @@ public class Jaxb2Marshaller
private String contextPath;
private Class[] classesToBeBound;
private Class<?>[] classesToBeBound;
private Map<String, ?> jaxbContextProperties;
@ -134,7 +142,7 @@ public class Jaxb2Marshaller @@ -134,7 +142,7 @@ public class Jaxb2Marshaller
private ValidationEventHandler validationEventHandler;
private XmlAdapter[] adapters;
private XmlAdapter<?, ?>[] adapters;
private Resource[] schemaResources;
@ -177,7 +185,7 @@ public class Jaxb2Marshaller @@ -177,7 +185,7 @@ public class Jaxb2Marshaller
/**
* Returns the list of Java classes to be recognized by a newly created JAXBContext.
*/
public Class[] getClassesToBeBound() {
public Class<?>[] getClassesToBeBound() {
return classesToBeBound;
}
@ -185,7 +193,7 @@ public class Jaxb2Marshaller @@ -185,7 +193,7 @@ public class Jaxb2Marshaller
* Set the list of Java classes to be recognized by a newly created JAXBContext.
* Setting this property or {@link #setContextPath "contextPath"} is required.
*/
public void setClassesToBeBound(Class[] classesToBeBound) {
public void setClassesToBeBound(Class<?>[] classesToBeBound) {
this.classesToBeBound = classesToBeBound;
}
@ -247,7 +255,7 @@ public class Jaxb2Marshaller @@ -247,7 +255,7 @@ public class Jaxb2Marshaller
* Specify the <code>XmlAdapter</code>s to be registered with the JAXB <code>Marshaller</code>
* and <code>Unmarshaller</code>
*/
public void setAdapters(XmlAdapter[] adapters) {
public void setAdapters(XmlAdapter<?, ?>[] adapters) {
this.adapters = adapters;
}
@ -394,13 +402,21 @@ public class Jaxb2Marshaller @@ -394,13 +402,21 @@ public class Jaxb2Marshaller
if (genericType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) genericType;
if (JAXBElement.class.equals(parameterizedType.getRawType()) &&
parameterizedType.getActualTypeArguments().length == 1 &&
parameterizedType.getActualTypeArguments()[0] instanceof Class) {
Class typeArgument = (Class) parameterizedType.getActualTypeArguments()[0];
return supportsInternal(typeArgument, false);
parameterizedType.getActualTypeArguments().length == 1) {
Type typeArgument = parameterizedType.getActualTypeArguments()[0];
if (typeArgument instanceof Class) {
Class<?> classArgument = (Class<?>) typeArgument;
if (isPrimitiveWrapper(classArgument) || isStandardClass(classArgument)) {
return true;
}
return supportsInternal(classArgument, false);
} else if (typeArgument instanceof GenericArrayType) {
GenericArrayType arrayType = (GenericArrayType) typeArgument;
return arrayType.getGenericComponentType().equals(Byte.TYPE);
}
}
} else if (genericType instanceof Class) {
Class clazz = (Class) genericType;
Class<?> clazz = (Class<?>) genericType;
return supportsInternal(clazz, true);
}
return false;
@ -410,9 +426,6 @@ public class Jaxb2Marshaller @@ -410,9 +426,6 @@ public class Jaxb2Marshaller
if (checkForXmlRootElement && AnnotationUtils.findAnnotation(clazz, XmlRootElement.class) == null) {
return false;
}
if (AnnotationUtils.findAnnotation(clazz, XmlType.class) == null) {
return false;
}
if (StringUtils.hasLength(getContextPath())) {
String packageName = ClassUtils.getPackageName(clazz);
String[] contextPaths = StringUtils.tokenizeToStringArray(getContextPath(), ":");
@ -429,6 +442,44 @@ public class Jaxb2Marshaller @@ -429,6 +442,44 @@ public class Jaxb2Marshaller
return false;
}
/**
* Checks whether the given type is a primitive wrapper type.
*
* @see section 8.5.1 of the JAXB2 spec
*/
private boolean isPrimitiveWrapper(Class<?> clazz) {
return Boolean.class.equals(clazz) ||
Byte.class.equals(clazz) ||
Short.class.equals(clazz) ||
Integer.class.equals(clazz) ||
Long.class.equals(clazz) ||
Float.class.equals(clazz) ||
Double.class.equals(clazz);
}
/**
* Checks whether the given type is a standard class.
* @see section 8.5.2 of the JAXB2 spec
*/
private boolean isStandardClass(Class<?> clazz) {
return String.class.equals(clazz) ||
BigInteger.class.isAssignableFrom(clazz) ||
BigDecimal.class.isAssignableFrom(clazz) ||
Calendar.class.isAssignableFrom(clazz) ||
Date.class.isAssignableFrom(clazz) ||
QName.class.isAssignableFrom(clazz) ||
URI.class.equals(clazz) ||
XMLGregorianCalendar.class.isAssignableFrom(clazz) ||
Duration.class.isAssignableFrom(clazz) ||
Image.class.equals(clazz) ||
DataHandler.class.equals(clazz) ||
// Source and subclasses should be supported according to the JAXB2 spec, but aren't in the RI
// Source.class.isAssignableFrom(clazz) ||
UUID.class.equals(clazz);
}
// Marshalling
public void marshal(Object graph, Result result) throws XmlMappingException {
@ -504,7 +555,7 @@ public class Jaxb2Marshaller @@ -504,7 +555,7 @@ public class Jaxb2Marshaller
marshaller.setEventHandler(this.validationEventHandler);
}
if (this.adapters != null) {
for (XmlAdapter adapter : this.adapters) {
for (XmlAdapter<?, ?> adapter : this.adapters) {
marshaller.setAdapter(adapter);
}
}
@ -589,7 +640,7 @@ public class Jaxb2Marshaller @@ -589,7 +640,7 @@ public class Jaxb2Marshaller
unmarshaller.setEventHandler(this.validationEventHandler);
}
if (this.adapters != null) {
for (XmlAdapter adapter : this.adapters) {
for (XmlAdapter<?, ?> adapter : this.adapters) {
unmarshaller.setAdapter(adapter);
}
}

76
org.springframework.oxm/src/test/java/org/springframework/oxm/jaxb/Jaxb2MarshallerTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-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.
@ -16,20 +16,24 @@ @@ -16,20 +16,24 @@
package org.springframework.oxm.jaxb;
import java.io.ByteArrayOutputStream;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Collections;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.transform.Result;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.JAXBElement;
import static org.custommonkey.xmlunit.XMLAssert.assertFalse;
import static org.custommonkey.xmlunit.XMLAssert.*;
import static org.custommonkey.xmlunit.XMLAssert.fail;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
@ -39,17 +43,16 @@ import org.xml.sax.Locator; @@ -39,17 +43,16 @@ import org.xml.sax.Locator;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.GenericTypeResolver;
import org.springframework.oxm.AbstractMarshallerTests;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.UncategorizedMappingException;
import org.springframework.oxm.XmlMappingException;
import org.springframework.oxm.GenericMarshaller;
import org.springframework.oxm.jaxb.test.FlightType;
import org.springframework.oxm.jaxb.test.Flights;
import org.springframework.oxm.jaxb.test.ObjectFactory;
import org.springframework.oxm.mime.MimeContainer;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.ReflectionUtils;
public class Jaxb2MarshallerTests extends AbstractMarshallerTests {
@ -148,7 +151,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests { @@ -148,7 +151,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests {
@Test
public void supportsContextPath() throws Exception {
testSupports(marshaller);
testSupports();
}
@ -157,14 +160,15 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests { @@ -157,14 +160,15 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests {
marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(new Class[]{Flights.class, FlightType.class});
marshaller.afterPropertiesSet();
testSupports(marshaller);
testSupports();
}
private void testSupports(Jaxb2Marshaller marshaller) throws Exception {
private void testSupports() throws Exception {
assertTrue("Jaxb2Marshaller does not support Flights class", marshaller.supports(Flights.class));
assertTrue("Jaxb2Marshaller does not support Flights generic type", marshaller.supports((Type)Flights.class));
assertFalse("Jaxb2Marshaller supports FlightType class", marshaller.supports(FlightType.class));
assertFalse("Jaxb2Marshaller supports FlightType type", marshaller.supports((Type)FlightType.class));
Method method = ObjectFactory.class.getDeclaredMethod("createFlight", FlightType.class);
assertTrue("Jaxb2Marshaller does not support JAXBElement<FlightsType>",
@ -181,6 +185,55 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests { @@ -181,6 +185,55 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests {
method = getClass().getDeclaredMethod("createDummyType");
assertFalse("Jaxb2Marshaller supports JAXBElement not in context path",
marshaller.supports(method.getGenericReturnType()));
testSupportsPrimitives();
testSupportsStandardClasses();
}
private void testSupportsPrimitives() {
final Primitives primitives = new Primitives();
ReflectionUtils.doWithMethods(Primitives.class, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Type returnType = method.getGenericReturnType();
assertTrue("Jaxb2Marshaller does not support JAXBElement<" + method.getName().substring(9) + ">",
marshaller.supports(returnType));
try {
// make sure the marshalling does not result in errors
Object returnValue = method.invoke(primitives);
marshaller.marshal(returnValue, new StreamResult(new ByteArrayOutputStream()));
}
catch (InvocationTargetException e) {
fail(e.getMessage());
}
}
}, new ReflectionUtils.MethodFilter() {
public boolean matches(Method method) {
return method.getName().startsWith("primitive");
}
});
}
private void testSupportsStandardClasses() throws Exception {
final StandardClasses standardClasses = new StandardClasses();
ReflectionUtils.doWithMethods(StandardClasses.class, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Type returnType = method.getGenericReturnType();
assertTrue("Jaxb2Marshaller does not support JAXBElement<" + method.getName().substring(13) + ">",
marshaller.supports(returnType));
try {
// make sure the marshalling does not result in errors
Object returnValue = method.invoke(standardClasses);
marshaller.marshal(returnValue, new StreamResult(new ByteArrayOutputStream()));
}
catch (InvocationTargetException e) {
fail(e.getMessage());
}
}
}, new ReflectionUtils.MethodFilter() {
public boolean matches(Method method) {
return method.getName().startsWith("standardClass");
}
});
}
@Test
@ -220,11 +273,12 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests { @@ -220,11 +273,12 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests {
private String s = "Hello";
}
public JAXBElement<DummyRootElement> createDummyRootElement() {
private JAXBElement<DummyRootElement> createDummyRootElement() {
return null;
}
public JAXBElement<DummyType> createDummyType() {
private JAXBElement<DummyType> createDummyType() {
return null;
}
}

61
org.springframework.oxm/src/test/java/org/springframework/oxm/jaxb/Primitives.java

@ -0,0 +1,61 @@ @@ -0,0 +1,61 @@
/*
* Copyright 2002-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.oxm.jaxb;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
/**
* Used by {@link org.springframework.oxm.jaxb.Jaxb2MarshallerTests}.
*
* @author Arjen Poutsma
*/
public class Primitives {
private static final QName NAME = new QName("http://springframework.org/oxm-test", "primitives");
// following methods are used to test support for primitives
public JAXBElement<Boolean> primitiveBoolean() {
return new JAXBElement<Boolean>(NAME, Boolean.class, true);
}
public JAXBElement<Byte> primitiveByte() {
return new JAXBElement<Byte>(NAME, Byte.class, (byte)42);
}
public JAXBElement<Short> primitiveShort() {
return new JAXBElement<Short>(NAME, Short.class, (short)42);
}
public JAXBElement<Integer> primitiveInteger() {
return new JAXBElement<Integer>(NAME, Integer.class, 42);
}
public JAXBElement<Long> primitiveLong() {
return new JAXBElement<Long>(NAME, Long.class, 42L);
}
public JAXBElement<Double> primitiveDouble() {
return new JAXBElement<Double>(NAME, Double.class, 42D);
}
public JAXBElement<byte[]> primitiveByteArray() {
return new JAXBElement<byte[]>(NAME, byte[].class, new byte[]{42});
}
}

129
org.springframework.oxm/src/test/java/org/springframework/oxm/jaxb/StandardClasses.java

@ -0,0 +1,129 @@ @@ -0,0 +1,129 @@
/*
* Copyright 2002-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.oxm.jaxb;
import java.awt.Image;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.UUID;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.URLDataSource;
import javax.imageio.ImageIO;
import javax.xml.bind.JAXBElement;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
/**
* Used by {@link org.springframework.oxm.jaxb.Jaxb2MarshallerTests}.
*
* @author Arjen Poutsma
*/
public class StandardClasses {
private static final QName NAME = new QName("http://springframework.org/oxm-test", "standard-classes");
private DatatypeFactory factory;
public StandardClasses() throws DatatypeConfigurationException {
factory = DatatypeFactory.newInstance();
}
/*
java.net.URI
javax.xml.datatype.XMLGregorianCalendarxs:anySimpleType
javax.xml.datatype.Duration
java.lang.Object
java.awt.Image
javax.activation.DataHandler
javax.xml.transform.Source
java.util.UUID
*/
public JAXBElement<String> standardClassString() {
return new JAXBElement<String>(NAME, String.class, "42");
}
public JAXBElement<BigInteger> standardClassBigInteger() {
return new JAXBElement<BigInteger>(NAME, BigInteger.class, new BigInteger("42"));
}
public JAXBElement<BigDecimal> standardClassBigDecimal() {
return new JAXBElement<BigDecimal>(NAME, BigDecimal.class, new BigDecimal("42.0"));
}
public JAXBElement<Calendar> standardClassCalendar() {
return new JAXBElement<Calendar>(NAME, Calendar.class, Calendar.getInstance());
}
public JAXBElement<GregorianCalendar> standardClassGregorianCalendar() {
return new JAXBElement<GregorianCalendar>(NAME, GregorianCalendar.class, (GregorianCalendar)Calendar.getInstance());
}
public JAXBElement<Date> standardClassDate() {
return new JAXBElement<Date>(NAME, Date.class, new Date());
}
public JAXBElement<QName> standardClassQName() {
return new JAXBElement<QName>(NAME, QName.class, NAME);
}
public JAXBElement<URI> standardClassURI() {
return new JAXBElement<URI>(NAME, URI.class, URI.create("http://springframework.org"));
}
public JAXBElement<XMLGregorianCalendar> standardClassXMLGregorianCalendar() throws DatatypeConfigurationException {
XMLGregorianCalendar calendar =
factory.newXMLGregorianCalendar((GregorianCalendar) Calendar.getInstance());
return new JAXBElement<XMLGregorianCalendar>(NAME, XMLGregorianCalendar.class, calendar);
}
public JAXBElement<Duration> standardClassDuration() {
Duration duration = factory.newDuration(42000);
return new JAXBElement<Duration>(NAME, Duration.class, duration);
}
public JAXBElement<Image> standardClassImage() throws IOException {
Image image = ImageIO.read(getClass().getResourceAsStream("spring-ws.png"));
return new JAXBElement<Image>(NAME, Image.class, image);
}
public JAXBElement<DataHandler> standardClassDataHandler() {
DataSource dataSource = new URLDataSource(getClass().getResource("spring-ws.png"));
DataHandler dataHandler = new DataHandler(dataSource);
return new JAXBElement<DataHandler>(NAME, DataHandler.class, dataHandler);
}
/* The following should work according to the spec, but doesn't on the JAXB2 implementation including in JDK 1.6.0_17
public JAXBElement<Source> standardClassSource() {
StringReader reader = new StringReader("<foo/>");
return new JAXBElement<Source>(NAME, Source.class, new StreamSource(reader));
}
*/
public JAXBElement<UUID> standardClassUUID() {
return new JAXBElement<UUID>(NAME, UUID.class, UUID.randomUUID());
}
}
Loading…
Cancel
Save