From 03f3bf09d63c1ee9cfbce54bb20696d06fed49b0 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Mon, 2 Mar 2009 11:19:26 +0000 Subject: [PATCH] SPR-5449: XStreamMarshaller.setImplicitCollection(Map) is insufficient --- .../oxm/xstream/XStreamMarshaller.java | 74 ++++++------------- .../oxm/xstream/AnnotatedFlight.java | 34 --------- .../springframework/oxm/xstream/Flights.java | 27 +++++++ .../oxm/xstream/XStreamMarshallerTests.java | 69 ++++++++++------- .../oxm/xstream/XStreamUnmarshallerTests.java | 6 +- 5 files changed, 97 insertions(+), 113 deletions(-) delete mode 100644 org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/AnnotatedFlight.java create mode 100644 org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/Flights.java diff --git a/org.springframework.oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java b/org.springframework.oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java index ac2a439f423..81f5b928136 100644 --- a/org.springframework.oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java +++ b/org.springframework.oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java @@ -103,12 +103,11 @@ public class XStreamMarshaller extends AbstractMarshaller { private Class[] supportedClasses; - /** * Returns the XStream instance used by this marshaller. */ public final XStream getXStream() { - return this.xstream; + return xstream; } /** @@ -118,7 +117,7 @@ public class XStreamMarshaller extends AbstractMarshaller { * @see XStream#NO_REFERENCES */ public void setMode(int mode) { - this.xstream.setMode(mode); + this.getXStream().setMode(mode); } /** @@ -130,10 +129,10 @@ public class XStreamMarshaller extends AbstractMarshaller { public void setConverters(ConverterMatcher[] converters) { for (int i = 0; i < converters.length; i++) { if (converters[i] instanceof Converter) { - this.xstream.registerConverter((Converter) converters[i], i); + this.getXStream().registerConverter((Converter) converters[i], i); } else if (converters[i] instanceof SingleValueConverter) { - this.xstream.registerConverter((SingleValueConverter) converters[i], i); + this.getXStream().registerConverter((SingleValueConverter) converters[i], i); } else { throw new IllegalArgumentException("Invalid ConverterMatcher [" + converters[i] + "]"); @@ -146,26 +145,17 @@ public class XStreamMarshaller extends AbstractMarshaller { */ public void setAliases(Map aliases) { for (Map.Entry entry : aliases.entrySet()) { - this.xstream.alias(entry.getKey(), entry.getValue()); + this.getXStream().alias(entry.getKey(), entry.getValue()); } } - /** - * Add an alias for the given type. - * @param name alias to be used for the type - * @param type the type to be aliased - */ - public void addAlias(String name, Class type) { - this.xstream.alias(name, type); - } - /** * Set types to use XML attributes for. * @see XStream#useAttributeFor(Class) */ public void setUseAttributeForTypes(Class[] types) { for (Class type : types) { - this.xstream.useAttributeFor(type); + this.getXStream().useAttributeFor(type); } } @@ -179,10 +169,10 @@ public class XStreamMarshaller extends AbstractMarshaller { public void setUseAttributeFor(Map attributes) { for (Map.Entry entry : attributes.entrySet()) { if (entry.getKey() instanceof String && entry.getValue() instanceof Class) { - this.xstream.useAttributeFor((String) entry.getKey(), (Class) entry.getValue()); + this.getXStream().useAttributeFor((String) entry.getKey(), (Class) entry.getValue()); } else if (entry.getKey() instanceof Class && entry.getValue() instanceof String) { - this.xstream.useAttributeFor((Class) entry.getKey(), (String) entry.getValue()); + this.getXStream().useAttributeFor((Class) entry.getKey(), (String) entry.getValue()); } else { throw new IllegalArgumentException("Invalid attribute key and value pair. " + @@ -192,55 +182,40 @@ public class XStreamMarshaller extends AbstractMarshaller { } /** - * Set a implicit colletion/type map, consisting of implicit collection String keys - * mapped to Class values. - * @see XStream#addImplicitCollection(Class, String) + * Specify implicit collection fields, as a Map consisting of Class instances + * mapped to comma separated collection field names. + *@see XStream#addImplicitCollection(Class, String) */ - public void setImplicitCollection(Map implicitCollection) { - for (Map.Entry entry : implicitCollection.entrySet()) { - this.xstream.addImplicitCollection(entry.getValue(), entry.getKey()); + public void setImplicitCollections(Map, String> implicitCollections) { + for (Map.Entry, String> entry : implicitCollections.entrySet()) { + String[] collectionFields = StringUtils.commaDelimitedListToStringArray(entry.getValue()); + for (String collectionField : collectionFields) { + this.getXStream().addImplicitCollection(entry.getKey(), collectionField); + } } } - /** - * Add an implicit Collection for the given type. - * @see XStream#addImplicitCollection(Class, String) - */ - public void addImplicitCollection(String name, Class type) { - this.xstream.addImplicitCollection(type, name); - } - /** * Specify omitted fields, as a Map consisting of Class instances * mapped to comma separated field names. * @see XStream#omitField(Class, String) */ - public void setOmittedFields(Map omittedFields) { - for (Map.Entry entry : omittedFields.entrySet()) { + public void setOmittedFields(Map, String> omittedFields) { + for (Map.Entry, String> entry : omittedFields.entrySet()) { String[] fields = StringUtils.commaDelimitedListToStringArray(entry.getValue()); for (String field : fields) { - this.xstream.omitField(entry.getKey(), field); + this.getXStream().omitField(entry.getKey(), field); } } } - /** - * Add an omitted field for the given type. - * @param type the type to be containing the field - * @param fieldName field to omitt - * @see XStream#omitField(Class, String) - */ - public void addOmittedField(Class type, String fieldName) { - this.xstream.omitField(type, fieldName); - } - /** * Set the classes for which mappings will be read from class-level JDK 1.5+ annotation metadata. * @see com.thoughtworks.xstream.XStream#processAnnotations(Class) */ public void setAnnotatedClass(Class annotatedClass) { Assert.notNull(annotatedClass, "'annotatedClass' must not be null"); - this.xstream.processAnnotations(annotatedClass); + this.getXStream().processAnnotations(annotatedClass); } /** @@ -249,7 +224,7 @@ public class XStreamMarshaller extends AbstractMarshaller { */ public void setAnnotatedClasses(Class[] annotatedClasses) { Assert.notEmpty(annotatedClasses, "'annotatedClasses' must not be empty"); - this.xstream.processAnnotations(annotatedClasses); + this.getXStream().processAnnotations(annotatedClasses); } /** @@ -355,7 +330,7 @@ public class XStreamMarshaller extends AbstractMarshaller { */ private void marshal(Object graph, HierarchicalStreamWriter streamWriter) { try { - this.xstream.marshal(graph, streamWriter); + this.getXStream().marshal(graph, streamWriter); } catch (Exception ex) { throw convertXStreamException(ex, true); @@ -421,7 +396,7 @@ public class XStreamMarshaller extends AbstractMarshaller { private Object unmarshal(HierarchicalStreamReader streamReader) { try { - return this.xstream.unmarshal(streamReader); + return this.getXStream().unmarshal(streamReader); } catch (Exception ex) { throw convertXStreamException(ex, false); @@ -454,5 +429,4 @@ public class XStreamMarshaller extends AbstractMarshaller { return new UncategorizedMappingException("Unknown XStream exception", ex); } } - } diff --git a/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/AnnotatedFlight.java b/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/AnnotatedFlight.java deleted file mode 100644 index df9c4cd4681..00000000000 --- a/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/AnnotatedFlight.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2002-2009 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.xstream; - -import com.thoughtworks.xstream.annotations.XStreamAlias; - -@XStreamAlias("flight") -public class AnnotatedFlight { - - @XStreamAlias("number") - private long flightNumber; - - public long getFlightNumber() { - return flightNumber; - } - - public void setFlightNumber(long number) { - this.flightNumber = number; - } -} \ No newline at end of file diff --git a/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/Flights.java b/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/Flights.java new file mode 100644 index 00000000000..a8d28c2c609 --- /dev/null +++ b/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/Flights.java @@ -0,0 +1,27 @@ +package org.springframework.oxm.xstream; + +import java.util.ArrayList; +import java.util.List; + +public class Flights { + + private List flights = new ArrayList(); + + private List strings = new ArrayList(); + + public List getFlights() { + return flights; + } + + public void setFlights(List flights) { + this.flights = flights; + } + + public List getStrings() { + return strings; + } + + public void setStrings(List strings) { + this.strings = strings; + } +} diff --git a/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java b/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java index 2175b1d2d50..96ce6a022c4 100644 --- a/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java +++ b/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java @@ -40,7 +40,7 @@ import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.extended.EncodedByteArrayConverter; import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver; import static org.custommonkey.xmlunit.XMLAssert.*; -import org.easymock.MockControl; +import static org.easymock.EasyMock.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -49,6 +49,7 @@ import org.junit.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Text; +import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.springframework.util.xml.StaxUtils; @@ -62,15 +63,15 @@ public class XStreamMarshallerTests { private XStreamMarshaller marshaller; - private AnnotatedFlight flight; + private Flight flight; @Before public void createMarshaller() throws Exception { marshaller = new XStreamMarshaller(); Map aliases = new HashMap(); - aliases.put("flight", AnnotatedFlight.class); + aliases.put("flight", Flight.class); marshaller.setAliases(aliases); - flight = new AnnotatedFlight(); + flight = new Flight(); flight.setFlightNumber(42L); } @@ -139,21 +140,19 @@ public class XStreamMarshallerTests { @Test public void marshalSaxResult() throws Exception { - MockControl handlerControl = MockControl.createStrictControl(ContentHandler.class); - handlerControl.setDefaultMatcher(MockControl.ALWAYS_MATCHER); - ContentHandler handlerMock = (ContentHandler) handlerControl.getMock(); + ContentHandler handlerMock = createStrictMock(ContentHandler.class); handlerMock.startDocument(); - handlerMock.startElement("", "flight", "flight", null); - handlerMock.startElement("", "number", "number", null); - handlerMock.characters(new char[]{'4', '2'}, 0, 2); - handlerMock.endElement("", "number", "number"); + handlerMock.startElement(eq(""), eq("flight"), eq("flight"), isA(Attributes.class)); + handlerMock.startElement(eq(""), eq("flightNumber"), eq("flightNumber"), isA(Attributes.class)); + handlerMock.characters(isA(char[].class), eq(0), eq(2)); + handlerMock.endElement("", "flightNumber", "flightNumber"); handlerMock.endElement("", "flight", "flight"); handlerMock.endDocument(); - handlerControl.replay(); + replay(handlerMock); SAXResult result = new SAXResult(handlerMock); marshaller.marshal(flight, result); - handlerControl.verify(); + verify(handlerMock); } @Test @@ -208,28 +207,46 @@ public class XStreamMarshallerTests { @Test public void useAttributesForClassStringMap() throws Exception { - marshaller.setUseAttributeFor(Collections.singletonMap(AnnotatedFlight.class, "flightNumber")); + marshaller.setUseAttributeFor(Collections.singletonMap(Flight.class, "flightNumber")); Writer writer = new StringWriter(); marshaller.marshal(flight, new StreamResult(writer)); String expected = ""; assertXMLEqual("Marshaller does not use attributes", expected, writer.toString()); } + @Test - public void omitField() throws Exception { - marshaller.addOmittedField(AnnotatedFlight.class, "flightNumber"); + @SuppressWarnings("unchecked") + public void omitFields() throws Exception { + Map omittedFieldsMap = Collections.singletonMap(Flight.class, "flightNumber"); + marshaller.setOmittedFields(omittedFieldsMap); Writer writer = new StringWriter(); marshaller.marshal(flight, new StreamResult(writer)); assertXpathNotExists("/flight/flightNumber", writer.toString()); } @Test - public void omitFields() throws Exception { - Map omittedFieldsMap = Collections.singletonMap(AnnotatedFlight.class, "flightNumber"); - marshaller.setOmittedFields(omittedFieldsMap); + @SuppressWarnings("unchecked") + public void implicitCollections() throws Exception { + Flights flights = new Flights(); + flights.getFlights().add(flight); + flights.getStrings().add("42"); + + Map aliases = new HashMap(); + aliases.put("flight", Flight.class); + aliases.put("flights", Flights.class); + marshaller.setAliases(aliases); + + Map implicitCollections = Collections.singletonMap(Flights.class, "flights,strings"); + marshaller.setImplicitCollections(implicitCollections); + Writer writer = new StringWriter(); - marshaller.marshal(flight, new StreamResult(writer)); - assertXpathNotExists("/flight/flightNumber", writer.toString()); + marshaller.marshal(flights, new StreamResult(writer)); + String result = writer.toString(); + assertXpathNotExists("/flights/flights", result); + assertXpathExists("/flights/flight", result); + assertXpathNotExists("/flights/strings", result); + assertXpathExists("/flights/string", result); } @Test @@ -239,18 +256,18 @@ public class XStreamMarshallerTests { marshaller.marshal(flight, new StreamResult(writer)); assertEquals("Invalid result", "{\"flight\":{\"flightNumber\":42}}", writer.toString()); Object o = marshaller.unmarshal(new StreamSource(new StringReader(writer.toString()))); - assertTrue("Unmarshalled object is not Flights", o instanceof AnnotatedFlight); - AnnotatedFlight unflight = (AnnotatedFlight) o; + assertTrue("Unmarshalled object is not Flights", o instanceof Flight); + Flight unflight = (Flight) o; assertNotNull("Flight is null", unflight); assertEquals("Number is invalid", 42L, unflight.getFlightNumber()); } @Test - public void testMarshalStreamResultWriter() throws Exception { - marshaller.setAnnotatedClass(AnnotatedFlight.class); + public void testAnnotatedMarshalStreamResultWriter() throws Exception { + marshaller.setAnnotatedClass(Flight.class); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); - AnnotatedFlight flight = new AnnotatedFlight(); + Flight flight = new Flight(); flight.setFlightNumber(42); marshaller.marshal(flight, result); String expected = "42"; diff --git a/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/XStreamUnmarshallerTests.java b/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/XStreamUnmarshallerTests.java index 4c9f9f0cbc9..8c9bcd51d70 100644 --- a/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/XStreamUnmarshallerTests.java +++ b/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/XStreamUnmarshallerTests.java @@ -49,13 +49,13 @@ public class XStreamUnmarshallerTests { public void creteUnmarshaller() throws Exception { unmarshaller = new XStreamMarshaller(); Map aliases = new HashMap(); - aliases.put("flight", AnnotatedFlight.class); + aliases.put("flight", Flight.class); unmarshaller.setAliases(aliases); } private void testFlight(Object o) { - assertTrue("Unmarshalled object is not Flights", o instanceof AnnotatedFlight); - AnnotatedFlight flight = (AnnotatedFlight) o; + assertTrue("Unmarshalled object is not Flights", o instanceof Flight); + Flight flight = (Flight) o; assertNotNull("Flight is null", flight); assertEquals("Number is invalid", 42L, flight.getFlightNumber()); }