diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1420aca --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +* text=auto + +*.css text +*.diff text +*.htm* text +*.java text +*.js text +*.md text +*.patch text +*.txt text +*.xml text + +*.gif binary +*.jpg binary +*.png binary +*.psp binary +*.vsd binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0b870e1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.* +target +*.iml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..b4b4a4d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: java +jdk: + - oraclejdk8 + - oraclejdk7 + - openjdk6 +install: true +script: "mvn -V -B -e clean package" +cache: + directories: + - $HOME/.m2 diff --git a/BUILD.txt b/BUILD.txt index 7ccabf6..5547b4d 100644 --- a/BUILD.txt +++ b/BUILD.txt @@ -1,4 +1,5 @@ -For Java 5 or higher build with Maven 2.2 or 3 +For Java 6 or higher build with Maven 3.2.5 or higher +For Java 5 build with Maven 3.0.5 For Java 1.4 build with Maven 2.0.11 Before building: @@ -9,7 +10,8 @@ Before deploying: -copy settings-template.xml to ~/.m2/settings.xml adding your Codehaus DAV username and passwords. +copy settings-template.xml to ~/.m2/settings.xml adding your Sonatype OSSRH +username and passwords. To deploy (optionally adding sources and javadoc jars): mvn deploy diff --git a/LICENSE.txt b/LICENSE.txt index fa11e38..a9cd276 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,7 +1,7 @@ (BSD Style License) Copyright (c) 2003-2006, Joe Walnes -Copyright (c) 2006-2011, XStream Committers +Copyright (c) 2006-2015, XStream Committers All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md new file mode 100644 index 0000000..999ac08 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +[![Build Status](https://travis-ci.org/x-stream/xstream.svg?branch=v-1.4.x)](https://travis-ci.org/x-stream/xstream) +---- +# XStream +_Java to XML Serialization, and back again_ + +## Binaries +All binary artifacts are bundled in the -bin archive. It includes +the XStream jars and any other library used at build time, or +optional runtime extras. Xpp3 is recommend for use as it will +greatly improve the performance of XStream. + +## Documentation +Documentation can be found at [GitHub](http://x-stream.github.io). This +includes: +* Introduction and tutorial +* JavaDoc +* Change log +* Frequently asked questions + +## Source +The complete source for XStream is bundled in the -src archive. This includes: +* Main API [xstream/src/java] +* Unit tests [xstream/src/test] +* Maven build files [pom.xml] +* Hibernate module [xstream-hibernate] +* Web site [xstream-distribution] + diff --git a/README.txt b/README.txt index 0639a48..01b7797 100644 --- a/README.txt +++ b/README.txt @@ -8,14 +8,15 @@ --[ Binaries ]----------------------------------------------- -All binary artifacts are in the 'lib' directory. These include the -xstream jars and any other library used at build time, -or optional runtime extras. kXML2 is recommend for use as it will +All binary artifacts are bundled in the -bin archive. It includes +the XStream jars and any other library used at build time, or +optional runtime extras. Xpp3 is recommend for use as it will greatly improve the performance of XStream. --[ Documentation ]------------------------------------------ -Documentation can be found in docs/index.html. This includes: +Documentation can be found at http://x-stream.github.io. This +includes: * Introduction and tutorial * JavaDoc * Change log @@ -23,15 +24,17 @@ --[ Source ]------------------------------------------------- -The complete source for XStream is bundled. This includes: - * Main API [src/java] - * Unit tests [src/test] +The complete source for XStream is bundled in the -src archive. +This includes: + * Main API [xstream/src/java] + * Unit tests [xstream/src/test] * Maven build files [pom.xml] - * Dependencies [lib] + * Hibernate module [xstream-hibernate] + * Web site [xstream-distribution] ------------------------------------------------------------- --XStream Ccommitters +- XStream Committers - http://xstream.codehaus.org/ + http://x-stream.github.io/ diff --git a/pom.xml b/pom.xml index e77fd16..8224a37 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 - - org.codehaus - codehaus-parent - 3 - - com.thoughtworks.xstream xstream-parent pom - 1.4.8 + 1.4.9 XStream Parent + http://x-stream.github.io XStream is a serialization library from Java objects to XML and back. @@ -29,8 +24,16 @@ 2004 XStream - http://xstream.codehaus.org + http://x-stream.github.io + + + + xstream + XStream Committers + http://x-stream.github.io/team.html + + @@ -53,32 +56,18 @@ - jdk15-ge + + jdk16-ge - [1.5,) + [1.6,) - - - - org.apache.felix - maven-bundle-plugin - - - bundle-manifest - process-classes - - manifest - - - - - - - + xstream-jmh + xstream-distribution + jdk15 1.5 @@ -97,6 +86,7 @@ 1.3 1.3 + 1.0-beta-1 http://docs.oracle.com/javase/1.4.2/docs/api/ 1.8.0.10 @@ -113,6 +103,10 @@ + + org.apache.maven.plugins + maven-source-plugin + org.apache.maven.plugins maven-enforcer-plugin @@ -138,6 +132,57 @@ + osgi + + [1.6,) + + profiles/osgi + + + + + + org.apache.felix + maven-bundle-plugin + + + bundle-manifest + process-classes + + manifest + + + + + + ${bundle.export.package} + + + + + org.apache.maven.plugins + maven-jar-plugin + + + default-jar + + jar + + + + ${project.build.directory}/OSGi/MANIFEST.MF + + + + + + + + + ${project.groupId}.*;-noimport:=true + + + xstream-release [1.8,1.9) @@ -166,28 +211,70 @@ xstream xstream-hibernate xstream-benchmark - xstream-distribution BSD style - http://xstream.codehaus.org/license.html + http://x-stream.github.io/license.html repo + + github + https://github.com/x-stream/xstream/issues/ + + + Travis + https://travis-ci.org/x-stream/xstream + + + + XStream Users List + xstream-user+subscribe@googlegroups.com + xstream-user+unsubscribe@googlegroups.com + xstream-user@googlegroups.com + https://groups.google.com/forum/#!forum/xstream-user + + http://news.gmane.org/gmane.comp.java.xstream.user + http://markmail.org/search/list:org.codehaus.xstream.user + + + + XStream Notifications List + xstream-notifications+subscribe@googlegroups.com + xstream-notifications+unsubscribe@googlegroups.com + xstream-notifications@googlegroups.com + https://groups.google.com/forum/#!forum/xstream-notifications + + http://news.gmane.org/gmane.comp.java.xstream.scm + + + + Former (pre-2015-06) Development List + http://news.gmane.org/gmane.comp.java.xstream.dev + + http://markmail.org/search/list:org.codehaus.xstream.dev + + + + Former (pre-2015-06) Announcements List + http://markmail.org/search/list:org.codehaus.xstream.announce + + + com.thoughtworks.xstream xstream - 1.4.8 + 1.4.9 com.thoughtworks.xstream xstream - 1.4.8 + 1.4.9 tests test-jar test @@ -195,31 +282,43 @@ com.thoughtworks.xstream xstream - 1.4.8 + 1.4.9 javadoc provided com.thoughtworks.xstream xstream-hibernate - 1.4.8 + 1.4.9 com.thoughtworks.xstream xstream-hibernate - 1.4.8 + 1.4.9 javadoc provided com.thoughtworks.xstream - xstream-benchmark - 1.4.8 + xstream-jmh + 1.4.9 + + + com.thoughtworks.xstream + xstream-jmh + 1.4.9 + javadoc + provided com.thoughtworks.xstream xstream-benchmark - 1.4.8 + 1.4.9 + + + com.thoughtworks.xstream + xstream-benchmark + 1.4.9 javadoc provided @@ -387,6 +486,13 @@ + javax.activation + activation + ${version.javax.activation} + provided + + + org.hibernate hibernate-core ${version.org.hibernate.core} @@ -420,6 +526,18 @@ runtime + + org.openjdk.jmh + jmh-core + ${version.org.openjdk.jmh} + + + org.openjdk.jmh + jmh-generator-annprocess + ${version.org.openjdk.jmh} + provided + + junit @@ -427,7 +545,6 @@ ${version.junit} test - jmock jmock @@ -575,9 +692,10 @@ maven-release-plugin ${version.plugin.maven.release} + forked-path deploy true - + false -Pxstream-release @@ -739,25 +857,30 @@ - - - codehaus.org - Codehaus XStream Site - dav:https://dav.codehaus.org/xstream - + + ossrh-staging + http://oss.sonatype.org/service/local/staging/deploy/maven2 + + + ossrh-snapshots + http://oss.sonatype.org/content/repositories/snapshots + + - scm:svn:http://svn.codehaus.org/xstream/tags/XSTREAM_1_4_8 - scm:svn:https://svn.codehaus.org/xstream/tags/XSTREAM_1_4_8 - http://fisheye.codehaus.org/browse/xstream/tags/XSTREAM_1_4_8 + http://github.com/x-stream/xstream + scm:git:https://github.com/x-stream/xstream.git + scm:git:https://github.com/x-stream/xstream.git + v-1.4.x + UTF-8 + 1.5 1.5 [1.4,) @@ -794,6 +917,7 @@ 1.6.1 2.2.8 3.12.1.GA + 1.1.1 1.0.1 1.6 3.8.1 @@ -805,6 +929,7 @@ 1.1.3 2.0.5 20080701 + 1.11.1 1.6.1 2.0.8 1.2.0 diff --git a/settings-template.xml b/settings-template.xml index b67ae92..ffea412 100644 --- a/settings-template.xml +++ b/settings-template.xml @@ -1,6 +1,6 @@ @@ -310,9 +315,11 @@ **/Lambda** + **/extended/PathConverter* **/Lambda** + **/acceptance/Extended17TypesTest* **/extended/*17Test* @@ -381,6 +388,7 @@ **/basic/UUID* **/core/util/Types* **/extended/*15* + **/extended/PathConverter* **/io/xml/JDom2* @@ -393,6 +401,7 @@ **/io/xml/JDom2*Test* **/acceptance/Basic15TypesTest* **/acceptance/Concurrent15TypesTest* + **/acceptance/Extended17TypesTest* @@ -429,4 +438,8 @@ + + !com.thoughtworks.xstream.core.util,com.thoughtworks.xstream.*;-noimport:=true + + diff --git a/xstream/profiles/osgi b/xstream/profiles/osgi new file mode 100644 index 0000000..e69de29 diff --git a/xstream/src/java/com/thoughtworks/xstream/XStream.java b/xstream/src/java/com/thoughtworks/xstream/XStream.java index 40f83d2..a2b7feb 100644 --- a/xstream/src/java/com/thoughtworks/xstream/XStream.java +++ b/xstream/src/java/com/thoughtworks/xstream/XStream.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2003, 2004, 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 26. September 2003 by Joe Walnes */ package com.thoughtworks.xstream; @@ -343,7 +343,7 @@ * The instance will use the {@link XppDriver} as default and tries to determine the best * match for the {@link ReflectionProvider} on its own. *

- * + * * @throws InitializationException in case of an initialization problem */ public XStream() { @@ -370,7 +370,7 @@ * The instance will tries to determine the best match for the {@link ReflectionProvider} on * its own. *

- * + * * @param hierarchicalStreamDriver the driver instance * @throws InitializationException in case of an initialization problem */ @@ -600,8 +600,9 @@ mapper = new LocalConversionMapper(mapper); mapper = new ImmutableTypesMapper(mapper); if (JVM.is18()) { - mapper = buildMapperDynamically("com.thoughtworks.xstream.mapper.LambdaMapper", new Class[]{Mapper.class}, - new Object[]{mapper}); + mapper = + buildMapperDynamically("com.thoughtworks.xstream.mapper.LambdaMapper", new Class[]{Mapper.class}, + new Object[]{mapper}); } mapper = new SecurityMapper(mapper); if (JVM.is15()) { @@ -734,6 +735,12 @@ alias("awt-color", JVM.loadClassForName("java.awt.Color", false)); alias("awt-font", JVM.loadClassForName("java.awt.Font", false)); alias("awt-text-attribute", JVM.loadClassForName("java.awt.font.TextAttribute")); + + // only available in Java 5 when javax.activation:activation is available on CP + Class type = JVM.loadClassForName("javax.activation.ActivationDataFlavor"); + if (type != null) { + alias("activation-data-flavor", type); + } } if (JVM.isSQLAvailable()) { @@ -763,6 +770,11 @@ alias("string-builder", JVM.loadClassForName("java.lang.StringBuilder")); alias("uuid", JVM.loadClassForName("java.util.UUID")); } + + if (JVM.is17()) { + aliasType("path", JVM.loadClassForName("java.nio.file.Path")); + } + if (JVM.loadClassForName("java.lang.invoke.SerializedLambda") != null) { aliasDynamically("serialized-lambda", "java.lang.invoke.SerializedLambda"); } @@ -893,6 +905,14 @@ "com.thoughtworks.xstream.converters.basic.UUIDConverter", PRIORITY_NORMAL, null, null); } + if (JVM.loadClassForName("javax.activation.ActivationDataFlavor") != null) { + registerConverterDynamically("com.thoughtworks.xstream.converters.extended.ActivationDataFlavorConverter", + PRIORITY_NORMAL, null, null); + } + if (JVM.is17()) { + registerConverterDynamically("com.thoughtworks.xstream.converters.extended.PathConverter", + PRIORITY_NORMAL, null, null); + } if (JVM.is18()) { registerConverterDynamically("com.thoughtworks.xstream.converters.reflection.LambdaConverter", PRIORITY_NORMAL, new Class[]{Mapper.class, ReflectionProvider.class, ClassLoaderReference.class}, @@ -929,52 +949,56 @@ } // primitives are always immutable - addImmutableType(boolean.class); - addImmutableType(Boolean.class); - addImmutableType(byte.class); - addImmutableType(Byte.class); - addImmutableType(char.class); - addImmutableType(Character.class); - addImmutableType(double.class); - addImmutableType(Double.class); - addImmutableType(float.class); - addImmutableType(Float.class); - addImmutableType(int.class); - addImmutableType(Integer.class); - addImmutableType(long.class); - addImmutableType(Long.class); - addImmutableType(short.class); - addImmutableType(Short.class); + addImmutableType(boolean.class, false); + addImmutableType(Boolean.class, false); + addImmutableType(byte.class, false); + addImmutableType(Byte.class, false); + addImmutableType(char.class, false); + addImmutableType(Character.class, false); + addImmutableType(double.class, false); + addImmutableType(Double.class, false); + addImmutableType(float.class, false); + addImmutableType(Float.class, false); + addImmutableType(int.class, false); + addImmutableType(Integer.class, false); + addImmutableType(long.class, false); + addImmutableType(Long.class, false); + addImmutableType(short.class, false); + addImmutableType(Short.class, false); // additional types - addImmutableType(Mapper.Null.class); - addImmutableType(BigDecimal.class); - addImmutableType(BigInteger.class); - addImmutableType(String.class); - addImmutableType(URI.class); - addImmutableType(URL.class); - addImmutableType(File.class); - addImmutableType(Class.class); - - addImmutableType(Collections.EMPTY_LIST.getClass()); - addImmutableType(Collections.EMPTY_SET.getClass()); - addImmutableType(Collections.EMPTY_MAP.getClass()); + addImmutableType(Mapper.Null.class, false); + addImmutableType(BigDecimal.class, false); + addImmutableType(BigInteger.class, false); + addImmutableType(String.class, false); + addImmutableType(URL.class, false); + addImmutableType(File.class, false); + addImmutableType(Class.class, false); if (JVM.isAWTAvailable()) { - addImmutableTypeDynamically("java.awt.font.TextAttribute"); + addImmutableTypeDynamically("java.awt.font.TextAttribute", false); } if (JVM.is14()) { // late bound types - allows XStream to be compiled on earlier JDKs - addImmutableTypeDynamically("java.nio.charset.Charset"); - addImmutableTypeDynamically("java.util.Currency"); - } - } - - private void addImmutableTypeDynamically(String className) { + addImmutableTypeDynamically("java.nio.charset.Charset", true); + addImmutableTypeDynamically("java.util.Currency", true); + } + + if (JVM.is15()) { + addImmutableTypeDynamically("java.util.UUID", true); + } + + addImmutableType(URI.class, true); + addImmutableType(Collections.EMPTY_LIST.getClass(), true); + addImmutableType(Collections.EMPTY_SET.getClass(), true); + addImmutableType(Collections.EMPTY_MAP.getClass(), true); + } + + private void addImmutableTypeDynamically(String className, boolean isReferenceable) { Class type = JVM.loadClassForName(className); if (type != null) { - addImmutableType(type); + addImmutableType(type, isReferenceable); } } @@ -984,7 +1008,7 @@ /** * Serialize an object to a pretty-printed XML String. - * + * * @throws XStreamException if the object cannot be serialized */ public String toXML(Object obj) { @@ -1025,7 +1049,7 @@ /** * Serialize and object to a hierarchical data structure (such as XML). - * + * * @throws XStreamException if the object cannot be serialized */ public void marshal(Object obj, HierarchicalStreamWriter writer) { @@ -1045,7 +1069,7 @@ /** * Deserialize an object from an XML String. - * + * * @throws XStreamException if the object cannot be deserialized */ public Object fromXML(String xml) { @@ -1054,7 +1078,7 @@ /** * Deserialize an object from an XML Reader. - * + * * @throws XStreamException if the object cannot be deserialized */ public Object fromXML(Reader reader) { @@ -1063,7 +1087,7 @@ /** * Deserialize an object from an XML InputStream. - * + * * @throws XStreamException if the object cannot be deserialized */ public Object fromXML(InputStream input) { @@ -1171,7 +1195,7 @@ /** * Deserialize an object from a hierarchical data structure (such as XML). - * + * * @throws XStreamException if the object cannot be deserialized */ public Object unmarshal(HierarchicalStreamReader reader) { @@ -1216,7 +1240,7 @@ /** * Alias a Class to a shorter name to be used in XML elements. - * + * * @param name Short name * @param type Type to be aliased * @throws InitializationException if no {@link ClassAliasingMapper} is available @@ -1250,7 +1274,7 @@ /** * Alias a Class to a shorter name to be used in XML elements. - * + * * @param name Short name * @param type Type to be aliased * @param defaultImplementation Default implementation of type to use if no other specified. @@ -1264,7 +1288,7 @@ /** * Alias a package to a shorter name to be used in XML elements. - * + * * @param name Short name * @param pkgName package to be aliased * @throws InitializationException if no {@link DefaultImplementationsMapper} or no @@ -1282,7 +1306,7 @@ /** * Create an alias for a field name. - * + * * @param alias the alias itself * @param definedIn the type that declares the field * @param fieldName the name of the field @@ -1299,7 +1323,7 @@ /** * Create an alias for an attribute - * + * * @param alias the alias itself * @param attributeName the name of the attribute * @throws InitializationException if no {@link AttributeAliasingMapper} is available @@ -1335,7 +1359,7 @@ /** * Create an alias for an attribute. - * + * * @param definedIn the type where the attribute is defined * @param attributeName the name of the attribute * @param alias the alias itself @@ -1349,7 +1373,7 @@ /** * Use an attribute for a field or a specific type. - * + * * @param fieldName the name of the field * @param type the Class of the type to be rendered as XML attribute * @throws InitializationException if no {@link AttributeMapper} is available @@ -1366,7 +1390,7 @@ /** * Use an attribute for a field declared in a specific type. - * + * * @param fieldName the name of the field * @param definedIn the Class containing such field * @throws InitializationException if no {@link AttributeMapper} is available @@ -1383,7 +1407,7 @@ /** * Use an attribute for an arbitrary type. - * + * * @param type the Class of the type to be rendered as XML attribute * @throws InitializationException if no {@link AttributeMapper} is available * @since 1.2 @@ -1416,18 +1440,38 @@ } /** - * Add immutable types. The value of the instances of these types will always be written - * into the stream even if they appear multiple times. - * + * Add immutable types. The value of the instances of these types will always be written into the stream even if + * they appear multiple times. However, references are still supported at deserialization time. + * * @throws InitializationException if no {@link ImmutableTypesMapper} is available + * @deprecated As of 1.4.9 use {@link #addImmutableType(Class, boolean)} */ public void addImmutableType(Class type) { + addImmutableType(type, true); + } + + /** + * Add immutable types. The value of the instances of these types will always be written into the stream even if + * they appear multiple times. + *

+ * Note, while a reference-keeping marshaller will not write references for immutable types into the stream, a + * reference-keeping unmarshaller can still support such references in the stream for compatibility reasons at the + * expense of memory consumption. Therefore declare these types only as referenceable if your already persisted + * streams do contain such references. Otherwise you may waste a lot of memory during deserialization. + *

+ * + * @param isReferenceable true if support at deserialization time is required for compatibility at the + * cost of a higher memory footprint, false otherwise + * @throws InitializationException if no {@link ImmutableTypesMapper} is available + * @since 1.4.9 + */ + public void addImmutableType(final Class type, final boolean isReferenceable) { if (immutableTypesMapper == null) { throw new com.thoughtworks.xstream.InitializationException("No " + ImmutableTypesMapper.class.getName() + " available"); } - immutableTypesMapper.addImmutableType(type); + immutableTypesMapper.addImmutableType(type, isReferenceable); } public void registerConverter(Converter converter) { @@ -1453,7 +1497,7 @@ /** * Register a local {@link Converter} for a field. - * + * * @param definedIn the class type the field is defined in * @param fieldName the field name * @param converter the converter to use @@ -1470,7 +1514,7 @@ /** * Register a local {@link SingleValueConverter} for a field. - * + * * @param definedIn the class type the field is defined in * @param fieldName the field name * @param converter the converter to use @@ -1495,7 +1539,7 @@ /** * Retrieve the {@link ReflectionProvider} in use. - * + * * @return the mapper * @since 1.2.1 */ @@ -1551,7 +1595,7 @@ /** * Adds a default implicit collection which is used for any unmapped XML tag. - * + * * @param ownerType class owning the implicit collection * @param fieldName name of the field in the ownerType. This field must be a concrete * collection type or matching the default implementation type of the collection @@ -1563,7 +1607,7 @@ /** * Adds implicit collection which is used for all items of the given itemType. - * + * * @param ownerType class owning the implicit collection * @param fieldName name of the field in the ownerType. This field must be a concrete * collection type or matching the default implementation type of the collection @@ -1594,7 +1638,7 @@ /** * Adds an implicit array. - * + * * @param ownerType class owning the implicit array * @param fieldName name of the array field * @since 1.4 @@ -1634,7 +1678,7 @@ /** * Adds an implicit map. - * + * * @param ownerType class owning the implicit map * @param fieldName name of the field in the ownerType. This field must be a concrete * map type or matching the default implementation type of the map @@ -1649,7 +1693,7 @@ /** * Adds an implicit map. - * + * * @param ownerType class owning the implicit map * @param fieldName name of the field in the ownerType. This field must be a concrete * map type or matching the default implementation type of the map @@ -1673,7 +1717,7 @@ * Create a DataHolder that can be used to pass data to the converters. The DataHolder is * provided with a call to {@link #marshal(Object, HierarchicalStreamWriter, DataHolder)} or * {@link #unmarshal(HierarchicalStreamReader, Object, DataHolder)}. - * + * * @return a new {@link DataHolder} */ public DataHolder newDataHolder() { @@ -1776,7 +1820,7 @@ * be incomplete. *

*

Example

- * + * *
      *  ObjectOutputStream out = xstream.createObjectOutputStream(aWriter, "things");
      *   out.writeInt(123);
@@ -1784,7 +1828,7 @@
      *   out.writeObject(someObject)
      *   out.close();
      * 
- * + * * @param writer The writer to serialize the objects to. * @param rootNodeName The name of the root node enclosing the stream of objects. * @see #createObjectInputStream(com.thoughtworks.xstream.io.HierarchicalStreamReader) @@ -1908,7 +1952,7 @@ /** * Retrieve the ClassLoader XStream uses to load classes. - * + * * @since 1.1.1 */ public ClassLoader getClassLoader() { @@ -1945,7 +1989,7 @@ /** * Ignore all unknown elements. - * + * * @since 1.4.5 */ public void ignoreUnknownElements() { @@ -1954,7 +1998,7 @@ /** * Add pattern for unknown element names to ignore. - * + * * @param pattern the name pattern as regular expression * @since 1.4.5 */ @@ -1964,7 +2008,7 @@ /** * Add pattern for unknown element names to ignore. - * + * * @param pattern the name pattern as regular expression * @since 1.4.5 */ @@ -1979,7 +2023,7 @@ /** * Process the annotations of the given types and configure the XStream. - * + * * @param types the types with XStream annotations * @since 1.3 */ @@ -2025,7 +2069,7 @@ * Permissions are evaluated in the added sequence. An instance of {@link NoTypePermission} or * {@link AnyTypePermission} will implicitly wipe any existing permission. *

- * + * * @param permission the permission to add * @since 1.4.7 */ @@ -2037,7 +2081,7 @@ /** * Add security permission for explicit types by name. - * + * * @param names the type names to allow * @since 1.4.7 */ @@ -2047,7 +2091,7 @@ /** * Add security permission for explicit types. - * + * * @param types the types to allow * @since 1.4.7 */ @@ -2057,7 +2101,7 @@ /** * Add security permission for a type hierarchy. - * + * * @param type the base type to allow * @since 1.4.7 */ @@ -2067,7 +2111,7 @@ /** * Add security permission for types matching one of the specified regular expressions. - * + * * @param regexps the regular expressions to allow type names * @since 1.4.7 */ @@ -2077,7 +2121,7 @@ /** * Add security permission for types matching one of the specified regular expressions. - * + * * @param regexps the regular expressions to allow type names * @since 1.4.7 */ @@ -2095,7 +2139,7 @@ *
  • *: arbitrary number of non-control characters except separator, e.g. for types in a package like 'java.lang.*'
  • *
  • **: arbitrary number of non-control characters including separator, e.g. for types in a package and subpackages like 'java.lang.**'
  • * - * + * * @param patterns the patterns to allow type names * @since 1.4.7 */ @@ -2105,7 +2149,7 @@ /** * Add security permission denying another one. - * + * * @param permission the permission to deny * @since 1.4.7 */ @@ -2115,7 +2159,7 @@ /** * Add security permission forbidding explicit types by name. - * + * * @param names the type names to forbid * @since 1.4.7 */ @@ -2125,7 +2169,7 @@ /** * Add security permission forbidding explicit types. - * + * * @param types the types to forbid * @since 1.4.7 */ @@ -2135,7 +2179,7 @@ /** * Add security permission forbidding a type hierarchy. - * + * * @param type the base type to forbid * @since 1.4.7 */ @@ -2145,7 +2189,7 @@ /** * Add security permission forbidding types matching one of the specified regular expressions. - * + * * @param regexps the regular expressions to forbid type names * @since 1.4.7 */ @@ -2155,7 +2199,7 @@ /** * Add security permission forbidding types matching one of the specified regular expressions. - * + * * @param regexps the regular expressions to forbid type names * @since 1.4.7 */ @@ -2173,7 +2217,7 @@ *
  • *: arbitrary number of non-control characters except separator, e.g. for types in a package like 'java.lang.*'
  • *
  • **: arbitrary number of non-control characters including separator, e.g. for types in a package and subpackages like 'java.lang.**'
  • * - * + * * @param patterns the patterns to forbid names * @since 1.4.7 */ diff --git a/xstream/src/java/com/thoughtworks/xstream/XStreamException.java b/xstream/src/java/com/thoughtworks/xstream/XStreamException.java index 002a605..b6fdcdf 100644 --- a/xstream/src/java/com/thoughtworks/xstream/XStreamException.java +++ b/xstream/src/java/com/thoughtworks/xstream/XStreamException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 XStream Committers. + * Copyright (C) 2007, 2008, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -14,15 +14,13 @@ /** - * Base exception for all thrown exceptions with XStream. JDK 1.3 friendly cause handling. + * Base exception for all thrown exceptions with XStream. * * @author Joe Walnes * @author Jörg Schaible * @since 1.3 */ public class XStreamException extends BaseException { - - private Throwable cause; /** * Default constructor. @@ -62,12 +60,6 @@ * @since 1.3 */ public XStreamException(String message, Throwable cause) { - super(message + (cause == null ? "" : " : " + cause.getMessage())); - this.cause = cause; + super(message, cause); } - - public Throwable getCause() { - return cause; - } - } diff --git a/xstream/src/java/com/thoughtworks/xstream/XStreamer.java b/xstream/src/java/com/thoughtworks/xstream/XStreamer.java index 6d40890..ebf41a8 100644 --- a/xstream/src/java/com/thoughtworks/xstream/XStreamer.java +++ b/xstream/src/java/com/thoughtworks/xstream/XStreamer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2014 XStream Committers. + * Copyright (C) 2006, 2007, 2014, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,7 +21,6 @@ import javax.xml.datatype.DatatypeFactory; -import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.ConverterLookup; import com.thoughtworks.xstream.converters.ConverterMatcher; import com.thoughtworks.xstream.converters.ConverterRegistry; @@ -33,6 +32,7 @@ import com.thoughtworks.xstream.core.JVM; import com.thoughtworks.xstream.io.HierarchicalStreamDriver; import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.StreamException; import com.thoughtworks.xstream.io.naming.NameCoder; import com.thoughtworks.xstream.io.xml.XppDriver; import com.thoughtworks.xstream.mapper.Mapper; @@ -85,7 +85,7 @@ } catch (final ObjectStreamException e) { throw e; } catch (final IOException e) { - throw new ConversionException("Unexpected IO error from a StringWriter", e); + throw new StreamException("Unexpected IO error from a StringWriter", e); } return writer.toString(); } @@ -135,7 +135,7 @@ } catch (final ObjectStreamException e) { throw e; } catch (final IOException e) { - throw new ConversionException("Unexpected IO error from a StringReader", e); + throw new StreamException("Unexpected IO error from a StringReader", e); } } @@ -157,7 +157,7 @@ } catch (final ObjectStreamException e) { throw e; } catch (final IOException e) { - throw new ConversionException("Unexpected IO error from a StringReader", e); + throw new StreamException("Unexpected IO error from a StringReader", e); } } @@ -179,7 +179,7 @@ } catch (final ObjectStreamException e) { throw e; } catch (final IOException e) { - throw new ConversionException("Unexpected IO error from a StringReader", e); + throw new StreamException("Unexpected IO error from a StringReader", e); } } @@ -202,7 +202,7 @@ } catch (final ObjectStreamException e) { throw e; } catch (final IOException e) { - throw new ConversionException("Unexpected IO error from a StringReader", e); + throw new StreamException("Unexpected IO error from a StringReader", e); } } diff --git a/xstream/src/java/com/thoughtworks/xstream/annotations/Annotations.java b/xstream/src/java/com/thoughtworks/xstream/annotations/Annotations.java old file mode 100644 new file mode 100755 index 1e48132..1e48132 100755 diff --git a/xstream/src/java/com/thoughtworks/xstream/annotations/XStreamAlias.java b/xstream/src/java/com/thoughtworks/xstream/annotations/XStreamAlias.java old file mode 100644 new file mode 100755 index 88d3c4c..88d3c4c 100755 diff --git a/xstream/src/java/com/thoughtworks/xstream/annotations/XStreamContainedType.java b/xstream/src/java/com/thoughtworks/xstream/annotations/XStreamContainedType.java old file mode 100644 new file mode 100755 index 6f2bc9e..6f2bc9e 100755 diff --git a/xstream/src/java/com/thoughtworks/xstream/annotations/XStreamConverter.java b/xstream/src/java/com/thoughtworks/xstream/annotations/XStreamConverter.java index c0ef961..87a2c67 100644 --- a/xstream/src/java/com/thoughtworks/xstream/annotations/XStreamConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/annotations/XStreamConverter.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2012, 2013, 2014 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2012, 2013, 2014, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -106,4 +106,12 @@ double[] doubles() default {}; boolean[] booleans() default {}; + + /** + * Provide null types as arguments for the converter's constructor arguments. + * + * @return the types provided as null values + * @since 1.4.9 + */ + Class[] nulls() default {}; } diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/ConversionException.java b/xstream/src/java/com/thoughtworks/xstream/converters/ConversionException.java index cd920be..283d5bd 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/ConversionException.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/ConversionException.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2003, 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2011 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -10,12 +10,6 @@ * Created on 26. September 2003 by Joe Walnes */ package com.thoughtworks.xstream.converters; - -import com.thoughtworks.xstream.XStreamException; -import com.thoughtworks.xstream.core.util.OrderRetainingMap; - -import java.util.Iterator; -import java.util.Map; /** * Thrown by {@link Converter} implementations when they cannot convert an object @@ -29,20 +23,10 @@ * * @see ErrorWriter */ -public class ConversionException extends XStreamException implements ErrorWriter { - - private static final String SEPARATOR = "\n-------------------------------"; - private Map stuff = new OrderRetainingMap(); +public class ConversionException extends ErrorWritingException { public ConversionException(String msg, Throwable cause) { super(msg, cause); - if (msg != null) { - add("message", msg); - } - if (cause != null) { - add("cause-exception", cause.getClass().getName()); - add("cause-message", cause instanceof ConversionException ? ((ConversionException)cause).getShortMessage() : cause.getMessage()); - } } public ConversionException(String msg) { @@ -50,61 +34,6 @@ } public ConversionException(Throwable cause) { - this(cause.getMessage(), cause); - } - - public String get(String errorKey) { - return (String) stuff.get(errorKey); - } - - public void add(String name, String information) { - String key = name; - int i = 0; - while (stuff.containsKey(key)) { - String value = (String)stuff.get(key); - if (information.equals(value)) - return; - key = name + "[" + ++i +"]"; - } - stuff.put(key, information); - } - - public void set(String name, String information) { - String key = name; - int i = 0; - stuff.put(key, information); // keep order - while (stuff.containsKey(key)) { - if (i != 0) { - stuff.remove(key); - } - key = name + "[" + ++i +"]"; - } - } - - public Iterator keys() { - return stuff.keySet().iterator(); - } - - public String getMessage() { - StringBuffer result = new StringBuffer(); - if (super.getMessage() != null) { - result.append(super.getMessage()); - } - if (!result.toString().endsWith(SEPARATOR)) { - result.append("\n---- Debugging information ----"); - } - for (Iterator iterator = keys(); iterator.hasNext();) { - String k = (String) iterator.next(); - String v = get(k); - result.append('\n').append(k); - result.append(" ".substring(Math.min(20, k.length()))); - result.append(": ").append(v); - } - result.append(SEPARATOR); - return result.toString(); - } - - public String getShortMessage() { - return super.getMessage(); + super(cause); } } diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/ErrorWritingException.java b/xstream/src/java/com/thoughtworks/xstream/converters/ErrorWritingException.java new file mode 100644 index 0000000..e0eed51 --- /dev/null +++ b/xstream/src/java/com/thoughtworks/xstream/converters/ErrorWritingException.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2003, 2004, 2005 Joe Walnes. + * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2016 XStream Committers. + * All rights reserved. + * + * The software in this package is published under the terms of the BSD + * style license a copy of which has been included with this distribution in + * the LICENSE.txt file. + * + * Created on 24. February 2016 by Joerg Schaible, factored out of ConversionException. + */ +package com.thoughtworks.xstream.converters; + +import java.util.Iterator; +import java.util.Map; + +import com.thoughtworks.xstream.XStreamException; +import com.thoughtworks.xstream.core.util.OrderRetainingMap; + + +/** + * Abstract base class for exceptions supporting an ErrorWriter. It can be passed around to things accepting an + * ErrorWriter to add diagnostics. + * + * @author Jörg Schaible + * @see ErrorWriter + * @since 1.4.9 + */ +public abstract class ErrorWritingException extends XStreamException implements ErrorWriter { + + private static final String SEPARATOR = "\n-------------------------------"; + private final Map stuff = new OrderRetainingMap(); + + /** + * Constructs a ErrorWritingException. + * + * @param message the error message + * @since 1.4.9 + */ + public ErrorWritingException(String message) { + super(message); + addData(message, null); + } + + /** + * Constructs a ErrorWritingException. + * + * @param cause the causing exception + * @since 1.4.9 + */ + public ErrorWritingException(Throwable cause) { + super(cause); + addData(null, cause); + } + + /** + * Constructs a ErrorWritingException. + * + * @param message the error message + * @param cause the causing exception + * @since 1.4.9 + */ + public ErrorWritingException(String message, Throwable cause) { + super(message, cause); + addData(message, cause); + } + + private void addData(String msg, Throwable cause) { + if (msg != null) { + add("message", msg); + } + if (cause != null) { + add("cause-exception", cause.getClass().getName()); + add("cause-message", cause instanceof ErrorWritingException ? ((ErrorWritingException)cause).getShortMessage() : cause.getMessage()); + } + } + + public String get(String errorKey) { + return (String) stuff.get(errorKey); + } + + public void add(String name, String information) { + String key = name; + int i = 0; + while (stuff.containsKey(key)) { + String value = (String)stuff.get(key); + if (information.equals(value)) + return; + key = name + "[" + ++i +"]"; + } + stuff.put(key, information); + } + + public void set(String name, String information) { + String key = name; + int i = 0; + stuff.put(key, information); // keep order + while (stuff.containsKey(key)) { + if (i != 0) { + stuff.remove(key); + } + key = name + "[" + ++i +"]"; + } + } + + public Iterator keys() { + return stuff.keySet().iterator(); + } + + public String getMessage() { + StringBuffer result = new StringBuffer(); + if (super.getMessage() != null) { + result.append(super.getMessage()); + } + if (!result.toString().endsWith(SEPARATOR)) { + result.append("\n---- Debugging information ----"); + } + for (Iterator iterator = keys(); iterator.hasNext();) { + String k = (String) iterator.next(); + String v = get(k); + result.append('\n').append(k); + result.append(" ".substring(Math.min(20, k.length()))); + result.append(": ").append(v); + } + result.append(SEPARATOR); + return result.toString(); + } + + public String getShortMessage() { + return super.getMessage(); + } + +} diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/basic/DateConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/basic/DateConverter.java index ad111ed..5f0bffa 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/basic/DateConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/basic/DateConverter.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2003, 2004 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2012, 2013, 2014, 2015 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2012, 2013, 2014, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -227,7 +227,9 @@ } } // no dateFormats left to try - throw new ConversionException("Cannot parse date " + str); + ConversionException exception = new ConversionException("Cannot parse date"); + exception.add("date", str); + throw exception; } public String toString(Object obj) { diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/collections/AbstractCollectionConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/collections/AbstractCollectionConverter.java index 0909417..a7e46d1 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/collections/AbstractCollectionConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/collections/AbstractCollectionConverter.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2003, 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2013 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2013, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -13,8 +13,10 @@ import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.Converter; +import com.thoughtworks.xstream.converters.ErrorWritingException; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.converters.reflection.ObjectAccessException; import com.thoughtworks.xstream.core.util.HierarchicalStreams; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; @@ -72,13 +74,17 @@ } protected Object createCollection(Class type) { + ErrorWritingException ex = null; Class defaultType = mapper().defaultImplementationOf(type); try { return defaultType.newInstance(); } catch (InstantiationException e) { - throw new ConversionException("Cannot instantiate " + defaultType.getName(), e); + ex = new ConversionException("Cannot instantiate default collection", e); } catch (IllegalAccessException e) { - throw new ConversionException("Cannot instantiate " + defaultType.getName(), e); + ex = new ObjectAccessException("Cannot instantiate default collection", e); } + ex.add("collection-type", type.getName()); + ex.add("default-type", defaultType.getName()); + throw ex; } } diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/collections/TreeMapConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/collections/TreeMapConverter.java index a79f4f5..30a5c51 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/collections/TreeMapConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/collections/TreeMapConverter.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2010, 2011, 2013 XStream Committers. + * Copyright (C) 2006, 2007, 2010, 2011, 2013, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -11,9 +11,9 @@ */ package com.thoughtworks.xstream.converters.collections; -import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.converters.reflection.ObjectAccessException; import com.thoughtworks.xstream.core.JVM; import com.thoughtworks.xstream.core.util.Fields; import com.thoughtworks.xstream.core.util.HierarchicalStreams; @@ -128,7 +128,7 @@ result.putAll(sortedMap); // will use comparator for already sorted map } } catch (final IllegalAccessException e) { - throw new ConversionException("Cannot set comparator of TreeMap", e); + throw new ObjectAccessException("Cannot set comparator of TreeMap", e); } } } diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/collections/TreeSetConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/collections/TreeSetConverter.java index 7e9e630..d22815c 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/collections/TreeSetConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/collections/TreeSetConverter.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2010, 2011, 2013, 2014 XStream Committers. + * Copyright (C) 2006, 2007, 2010, 2011, 2013, 2014, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -11,9 +11,9 @@ */ package com.thoughtworks.xstream.converters.collections; -import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.converters.reflection.ObjectAccessException; import com.thoughtworks.xstream.core.JVM; import com.thoughtworks.xstream.core.util.Fields; import com.thoughtworks.xstream.core.util.PresortedSet; @@ -99,7 +99,7 @@ try { backingMap = sortedMapField.get(possibleResult); } catch (IllegalAccessException e) { - throw new ConversionException("Cannot get backing map of TreeSet", e); + throw new ObjectAccessException("Cannot get backing map of TreeSet", e); } if (backingMap instanceof TreeMap) { treeMap = (TreeMap)backingMap; diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/enums/EnumToStringConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/enums/EnumToStringConverter.java index 90fea84..6a4e048 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/enums/EnumToStringConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/enums/EnumToStringConverter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 XStream Committers. + * Copyright (C) 2013, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -13,9 +13,9 @@ import java.util.EnumMap; import java.util.EnumSet; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; +import com.thoughtworks.xstream.InitializationException; import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter; @@ -53,7 +53,7 @@ Map strings = new HashMap(values.size()); for (T value : values) { if (strings.put(value.toString(), value) != null) { - throw new IllegalArgumentException("Enum type " + throw new InitializationException("Enum type " + type.getName() + " does not have unique string representations for its values"); } @@ -63,7 +63,7 @@ private static void checkType(Class type) { if (!Enum.class.isAssignableFrom(type) && type != Enum.class) { - throw new IllegalArgumentException("Converter can only handle enum types"); + throw new InitializationException("Converter can only handle enum types"); } } @@ -94,11 +94,10 @@ } T result = strings.get(str); if (result == null) { - throw new ConversionException("Invalid string representation for enum type " - + enumType.getName() - + ": <" - + str - + ">"); + ConversionException exception = new ConversionException("Invalid string representation for enum type"); + exception.add("enum-type", enumType.getName()); + exception.add("enum-string", str); + throw exception; } return result; } diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/extended/ActivationDataFlavorConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/extended/ActivationDataFlavorConverter.java new file mode 100644 index 0000000..4e20cb3 --- /dev/null +++ b/xstream/src/java/com/thoughtworks/xstream/converters/extended/ActivationDataFlavorConverter.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2015 XStream Committers. + * All rights reserved. + * + * The software in this package is published under the terms of the BSD + * style license a copy of which has been included with this distribution in + * the LICENSE.txt file. + * + * Created on 21.06.2015 by Joerg Schaible + */ +package com.thoughtworks.xstream.converters.extended; + +import javax.activation.ActivationDataFlavor; + +import com.thoughtworks.xstream.converters.ConversionException; +import com.thoughtworks.xstream.converters.Converter; +import com.thoughtworks.xstream.converters.MarshallingContext; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; + + +/** + * Converts an {@link ActivationDataFlavor}. + * + * @author Jörg Schaible + * @since 1.4.9 + */ +public class ActivationDataFlavorConverter implements Converter { + + public boolean canConvert(final Class type) { + return type == ActivationDataFlavor.class; + } + + public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) { + final ActivationDataFlavor dataFlavor = (ActivationDataFlavor)source; + final String mimeType = dataFlavor.getMimeType(); + if (mimeType != null) { + writer.startNode("mimeType"); + writer.setValue(mimeType); + writer.endNode(); + } + final String name = dataFlavor.getHumanPresentableName(); + if (name != null) { + writer.startNode("humanRepresentableName"); + writer.setValue(name); + writer.endNode(); + } + final Class representationClass = dataFlavor.getRepresentationClass(); + if (representationClass != null) { + writer.startNode("representationClass"); + context.convertAnother(representationClass); + writer.endNode(); + } + } + + public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { + String mimeType = null; + String name = null; + Class type = null; + while (reader.hasMoreChildren()) { + reader.moveDown(); + + final String elementName = reader.getNodeName(); + if (elementName.equals("mimeType")) { + mimeType = reader.getValue(); + } else if (elementName.equals("humanRepresentableName")) { + name = reader.getValue(); + } else if (elementName.equals("representationClass")) { + type = (Class)context.convertAnother(null, Class.class); + } else { + final ConversionException exception = new ConversionException("Unknown child element"); + exception.add("element", reader.getNodeName()); + throw exception; + } + reader.moveUp(); + } + ActivationDataFlavor dataFlavor = null; + try { + if (type == null) { + dataFlavor = new ActivationDataFlavor(mimeType, name); + } else if (mimeType == null) { + dataFlavor = new ActivationDataFlavor(type, name); + } else { + dataFlavor = new ActivationDataFlavor(type, mimeType, name); + } + } catch (final IllegalArgumentException ex) { + throw new ConversionException(ex); + } catch (final NullPointerException ex) { + throw new ConversionException(ex); + } + return dataFlavor; + } +} diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/extended/ISO8601GregorianCalendarConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/extended/ISO8601GregorianCalendarConverter.java index 1d0da42..ddb0cd8 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/extended/ISO8601GregorianCalendarConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/extended/ISO8601GregorianCalendarConverter.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2011, 2013, 2014, 2015 XStream Committers. + * Copyright (C) 2006, 2007, 2011, 2013, 2014, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -109,7 +109,9 @@ // try with next formatter } } - throw new ConversionException("Cannot parse date " + str); + ConversionException exception = new ConversionException("Cannot parse date"); + exception.add("date", str); + throw exception; } public String toString(Object obj) { diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/extended/NamedMapConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/extended/NamedMapConverter.java index 24f6e0e..b6fda6e 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/extended/NamedMapConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/extended/NamedMapConverter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 XStream Committers. + * Copyright (C) 2013, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -247,10 +247,10 @@ SingleValueConverter keyConverter = null; SingleValueConverter valueConverter = null; if (keyAsAttribute) { - keyConverter = getSingleValueConverter(keyType); + keyConverter = getSingleValueConverter(keyType, "key"); } if (valueAsAttribute || valueName == null) { - valueConverter = getSingleValueConverter(valueType); + valueConverter = getSingleValueConverter(valueType, "value"); } for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) { Map.Entry entry = (Map.Entry)iterator.next(); @@ -287,10 +287,10 @@ SingleValueConverter keyConverter = null; SingleValueConverter valueConverter = null; if (keyAsAttribute) { - keyConverter = getSingleValueConverter(keyType); + keyConverter = getSingleValueConverter(keyType, "key"); } if (valueAsAttribute || valueName == null) { - valueConverter = getSingleValueConverter(valueType); + valueConverter = getSingleValueConverter(valueType, "value"); } while (reader.hasMoreChildren()) { @@ -336,7 +336,7 @@ } reader.moveUp(); } else if (!valueAsAttribute) { - value = reader.getValue(); + value = valueConverter.fromString(reader.getValue()); } target.put(key, value); @@ -347,7 +347,7 @@ } } - private SingleValueConverter getSingleValueConverter(Class type) { + private SingleValueConverter getSingleValueConverter(Class type, String part) { SingleValueConverter conv = UseAttributeForEnumMapper.isEnum(type) ? enumMapper .getConverterFromItemType(null, type, null) : mapper().getConverterFromItemType( null, type, null); @@ -356,7 +356,7 @@ if (converter instanceof SingleValueConverter) { conv = (SingleValueConverter)converter; } else { - throw new ConversionException("No SingleValueConverter for key available"); + throw new ConversionException("No SingleValueConverter for " + part + " available"); } } return conv; diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/extended/PathConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/extended/PathConverter.java new file mode 100644 index 0000000..aaa70a3 --- /dev/null +++ b/xstream/src/java/com/thoughtworks/xstream/converters/extended/PathConverter.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 XStream Committers. + * All rights reserved. + * + * The software in this package is published under the terms of the BSD + * style license a copy of which has been included with this distribution in + * the LICENSE.txt file. + * + * Created on 7. February 2016 by Aaron Johnson + */ +package com.thoughtworks.xstream.converters.extended; + +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.Paths; + +import com.thoughtworks.xstream.converters.ConversionException; +import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter; + + +/** + * Converts a {@link Path} to string. + * + * @author Aaron Johnson + * @author Jörg Schaible + */ +public class PathConverter extends AbstractSingleValueConverter { + + @Override + public boolean canConvert(final Class type) { + return Path.class.isAssignableFrom(type); + } + + @Override + public Object fromString(final String str) { + try { + final URI uri = new URI(str); + if (uri.getScheme() == null) { + return Paths.get(str); + } else { + return Paths.get(uri); + } + } catch (final URISyntaxException e) { + throw new ConversionException(e); + } + } + + @Override + public String toString(final Object obj) { + final Path path = (Path)obj; + if (path.getFileSystem() == FileSystems.getDefault()) { + return path.toString(); + } else { + return path.toUri().toString(); + } + } +} diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/extended/StackTraceElementFactory.java b/xstream/src/java/com/thoughtworks/xstream/converters/extended/StackTraceElementFactory.java index 7154c8a..c480653 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/extended/StackTraceElementFactory.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/extended/StackTraceElementFactory.java @@ -1,22 +1,22 @@ /* * Copyright (C) 2004 Joe Walnes. - * Copyright (C) 2006, 2007, 2014 XStream Committers. + * Copyright (C) 2006, 2007, 2014, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 30. May 2004 by Joe Walnes */ package com.thoughtworks.xstream.converters.extended; -import com.thoughtworks.xstream.converters.ConversionException; - import java.lang.reflect.Field; +import com.thoughtworks.xstream.core.util.Fields; + + /** - * Factory for creating StackTraceElements. * Factory for creating StackTraceElements. * * @author B. K. Oxley (binkley) @@ -25,24 +25,26 @@ */ public class StackTraceElementFactory { - public StackTraceElement nativeMethodElement(String declaringClass, String methodName) { + public StackTraceElement nativeMethodElement(final String declaringClass, final String methodName) { return create(declaringClass, methodName, "Native Method", -2); } - public StackTraceElement unknownSourceElement(String declaringClass, String methodName) { + public StackTraceElement unknownSourceElement(final String declaringClass, final String methodName) { return create(declaringClass, methodName, "Unknown Source", -1); } - public StackTraceElement element(String declaringClass, String methodName, String fileName) { + public StackTraceElement element(final String declaringClass, final String methodName, final String fileName) { return create(declaringClass, methodName, fileName, -1); } - public StackTraceElement element(String declaringClass, String methodName, String fileName, int lineNumber) { + public StackTraceElement element(final String declaringClass, final String methodName, final String fileName, + final int lineNumber) { return create(declaringClass, methodName, fileName, lineNumber); } - protected StackTraceElement create(String declaringClass, String methodName, String fileName, int lineNumber) { - StackTraceElement result = new Throwable().getStackTrace()[0]; + protected StackTraceElement create(final String declaringClass, final String methodName, final String fileName, + final int lineNumber) { + final StackTraceElement result = new Throwable().getStackTrace()[0]; setField(result, "declaringClass", declaringClass); setField(result, "methodName", methodName); setField(result, "fileName", fileName); @@ -50,14 +52,9 @@ return result; } - private void setField(StackTraceElement element, String fieldName, Object value) { - try { - final Field field = StackTraceElement.class.getDeclaredField(fieldName); - field.setAccessible(true); - field.set(element, value); - } catch (Exception e) { - throw new ConversionException(e); - } + private void setField(final StackTraceElement element, final String fieldName, final Object value) { + final Field field = Fields.find(StackTraceElement.class, fieldName); + Fields.write(field, element, value); } } diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/extended/ToAttributedValueConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/extended/ToAttributedValueConverter.java index df5f483..e4fa6c1 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/extended/ToAttributedValueConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/extended/ToAttributedValueConverter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2013 XStream Committers. + * Copyright (C) 2011, 2013, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -60,6 +60,23 @@ /** * Creates a new ToAttributedValueConverter instance. * + * All field elements will be attributes, the element itself will have no value. + * + * @param type the type that is handled by this converter instance + * @param mapper the mapper in use + * @param reflectionProvider the reflection provider in use + * @param lookup the converter lookup in use + * @since 1.4.9 + */ + public ToAttributedValueConverter( + final Class type, final Mapper mapper, final ReflectionProvider reflectionProvider, + final ConverterLookup lookup) { + this(type, mapper, reflectionProvider, lookup, null, null); + } + + /** + * Creates a new ToAttributedValueConverter instance. + * * @param type the type that is handled by this converter instance * @param mapper the mapper in use * @param reflectionProvider the reflection provider in use diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/extended/ToStringConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/extended/ToStringConverter.java index 852f264..cb177ee 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/extended/ToStringConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/extended/ToStringConverter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -12,6 +12,7 @@ import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter; +import com.thoughtworks.xstream.converters.reflection.ObjectAccessException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -26,12 +27,13 @@ * @author Paul Hammant */ public class ToStringConverter extends AbstractSingleValueConverter { + private static final Class[] STRING_PARAMETER = {String.class}; private final Class clazz; private final Constructor ctor; public ToStringConverter(Class clazz) throws NoSuchMethodException { this.clazz = clazz; - ctor = clazz.getConstructor(new Class[] {String.class}); + ctor = clazz.getConstructor(STRING_PARAMETER); } public boolean canConvert(Class type) { return type.equals(clazz); @@ -46,7 +48,7 @@ } catch (InstantiationException e) { throw new ConversionException("Unable to instantiate single String param constructor", e); } catch (IllegalAccessException e) { - throw new ConversionException("Unable to access single String param constructor", e); + throw new ObjectAccessException("Unable to access single String param constructor", e); } catch (InvocationTargetException e) { throw new ConversionException("Unable to target single String param constructor", e.getTargetException()); } diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/javabean/BeanProvider.java b/xstream/src/java/com/thoughtworks/xstream/converters/javabean/BeanProvider.java index 82fd9c1..2e7e3e3 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/javabean/BeanProvider.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/javabean/BeanProvider.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2010, 2011, 2013 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2010, 2011, 2013, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,8 @@ import java.util.Iterator; import java.util.List; +import com.thoughtworks.xstream.converters.ConversionException; +import com.thoughtworks.xstream.converters.ErrorWritingException; import com.thoughtworks.xstream.converters.reflection.ObjectAccessException; @@ -60,22 +62,26 @@ } public Object newInstance(Class type) { + ErrorWritingException ex = null; try { return type.newInstance(); } catch (InstantiationException e) { - throw new ObjectAccessException("Cannot construct " + type.getName(), e); + ex = new ConversionException("Cannot construct type", e); } catch (IllegalAccessException e) { - throw new ObjectAccessException("Cannot construct " + type.getName(), e); + ex = new ObjectAccessException("Cannot construct type", e); } catch (SecurityException e) { - throw new ObjectAccessException("Cannot construct " + type.getName(), e); + ex = new ObjectAccessException("Cannot construct type", e); } catch (ExceptionInInitializerError e) { - throw new ObjectAccessException("Cannot construct " + type.getName(), e); + ex = new ConversionException("Cannot construct type", e); } + ex.add("construction-type", type.getName()); + throw ex; } public void visitSerializableProperties(Object object, JavaBeanProvider.Visitor visitor) { PropertyDescriptor[] propertyDescriptors = getSerializableProperties(object); for (int i = 0; i < propertyDescriptors.length; i++ ) { + ErrorWritingException ex = null; PropertyDescriptor property = propertyDescriptors[i]; try { Method readMethod = property.getReadMethod(); @@ -86,43 +92,34 @@ visitor.visit(name, property.getPropertyType(), definedIn, value); } } catch (IllegalArgumentException e) { - throw new ObjectAccessException("Could not get property " - + object.getClass() - + "." - + property.getName(), e); + ex = new ConversionException("Cannot get property", e); } catch (IllegalAccessException e) { - throw new ObjectAccessException("Could not get property " - + object.getClass() - + "." - + property.getName(), e); + ex = new ObjectAccessException("Cannot access property", e); } catch (InvocationTargetException e) { - throw new ObjectAccessException("Could not get property " - + object.getClass() - + "." - + property.getName(), e); + ex = new ConversionException("Cannot get property", e.getTargetException()); + } + if (ex != null) { + ex.add("property", object.getClass() + "." + property.getName()); + throw ex; } } } public void writeProperty(Object object, String propertyName, Object value) { + ErrorWritingException ex = null; PropertyDescriptor property = getProperty(propertyName, object.getClass()); try { property.getWriteMethod().invoke(object, new Object[]{value}); } catch (IllegalArgumentException e) { - throw new ObjectAccessException("Could not set property " - + object.getClass() - + "." - + property.getName(), e); + ex = new ConversionException("Cannot set property", e); } catch (IllegalAccessException e) { - throw new ObjectAccessException("Could not set property " - + object.getClass() - + "." - + property.getName(), e); + ex = new ObjectAccessException("Cannot access property", e); } catch (InvocationTargetException e) { - throw new ObjectAccessException("Could not set property " - + object.getClass() - + "." - + property.getName(), e); + ex = new ConversionException("Cannot set property", e.getTargetException()); + } + if (ex != null) { + ex.add("property", object.getClass() + "." + property.getName()); + throw ex; } } diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/javabean/JavaBeanConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/javabean/JavaBeanConverter.java index 9109597..e0e5d6e 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/javabean/JavaBeanConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/javabean/JavaBeanConverter.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 12. April 2005 by Joe Walnes */ package com.thoughtworks.xstream.converters.javabean; @@ -20,14 +20,15 @@ import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.converters.reflection.MissingFieldException; import com.thoughtworks.xstream.core.util.FastField; +import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; -import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper; import com.thoughtworks.xstream.mapper.Mapper; + /** - * Can convert any bean with a public default constructor. The {@link BeanProvider} used as - * default is based on {@link java.beans.BeanInfo}. Indexed properties are currently not supported. + * Can convert any bean with a public default constructor. The {@link BeanProvider} used as default is based on + * {@link java.beans.BeanInfo}. Indexed properties are currently not supported. */ public class JavaBeanConverter implements Converter { @@ -82,7 +83,7 @@ } public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) { - final String classAttributeName = classAttributeIdentifier != null ? classAttributeIdentifier : mapper.aliasForSystemAttribute("class"); + final String classAttributeName = mapper.aliasForSystemAttribute("class"); beanProvider.visitSerializableProperties(source, new JavaBeanProvider.Visitor() { public boolean shouldVisit(String name, Class definedIn) { return mapper.shouldSerializeMember(definedIn, name); @@ -90,20 +91,29 @@ public void visit(String propertyName, Class fieldType, Class definedIn, Object newObj) { if (newObj != null) { - writeField(propertyName, fieldType, newObj, definedIn); + writeField(propertyName, fieldType, newObj); + } else { + writeNullField(propertyName); } } - private void writeField(String propertyName, Class fieldType, Object newObj, Class definedIn) { + private void writeField(String propertyName, Class fieldType, Object newObj) { Class actualType = newObj.getClass(); Class defaultType = mapper.defaultImplementationOf(fieldType); String serializedMember = mapper.serializedMember(source.getClass(), propertyName); - ExtendedHierarchicalStreamWriterHelper.startNode(writer, serializedMember, actualType); + ExtendedHierarchicalStreamWriterHelper.startNode(writer, serializedMember, actualType); if (!actualType.equals(defaultType) && classAttributeName != null) { writer.addAttribute(classAttributeName, mapper.serializedClass(actualType)); } context.convertAnother(newObj); + writer.endNode(); + } + + private void writeNullField(final String propertyName) { + final String serializedMember = mapper.serializedMember(source.getClass(), propertyName); + ExtendedHierarchicalStreamWriterHelper.startNode(writer, serializedMember, Mapper.Null.class); + writer.addAttribute(classAttributeName, mapper.serializedClass(Mapper.Null.class)); writer.endNode(); } }); diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/javabean/PropertyDictionary.java b/xstream/src/java/com/thoughtworks/xstream/converters/javabean/PropertyDictionary.java index c5c6b19..4d4eeae 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/javabean/PropertyDictionary.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/javabean/PropertyDictionary.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2011 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -107,8 +107,9 @@ try { beanInfo = Introspector.getBeanInfo(type, Object.class); } catch (IntrospectionException e) { - throw new ObjectAccessException( - "Cannot get BeanInfo of type " + type.getName(), e); + ObjectAccessException oaex = new ObjectAccessException("Cannot get BeanInfo of type", e); + oaex.add("bean-type", type.getName()); + throw oaex; } nameMap = new OrderRetainingMap(); PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/AbstractAttributedCharacterIteratorAttributeConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/AbstractAttributedCharacterIteratorAttributeConverter.java index ac65fab..67116d4 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/AbstractAttributedCharacterIteratorAttributeConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/AbstractAttributedCharacterIteratorAttributeConverter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2013 XStream Committers. + * Copyright (C) 2007, 2013, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -89,14 +89,19 @@ if (s.startsWith(className)) { return s.substring(className.length()+1, s.length()-1); } - throw new ConversionException("Cannot find name of attribute of type " + className, ex); + ConversionException exception = new ConversionException("Cannot find name of attribute", ex); + exception.add("attribute-type", className); + throw exception; } public Object fromString(final String str) { if (attributeMap.containsKey(str)) { return attributeMap.get(str); } - throw new ConversionException("Cannot find attribute of type " + type.getName() + " with name " + str); + ConversionException exception = new ConversionException("Cannot find attribute"); + exception.add("attribute-type", type.getName()); + exception.add("attribute-name", str); + throw exception; } private Object readResolve() { diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/AbstractReflectionConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/AbstractReflectionConverter.java index ffde863..04dfd76 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/AbstractReflectionConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/AbstractReflectionConverter.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2004, 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 02. March 2006 by Joerg Schaible */ package com.thoughtworks.xstream.converters.reflection; @@ -20,6 +20,7 @@ import com.thoughtworks.xstream.core.ReferencingMarshallingContext; import com.thoughtworks.xstream.core.util.ArrayIterator; import com.thoughtworks.xstream.core.util.FastField; +import com.thoughtworks.xstream.core.util.Fields; import com.thoughtworks.xstream.core.util.HierarchicalStreams; import com.thoughtworks.xstream.core.util.Primitives; import com.thoughtworks.xstream.core.util.SerializationMembers; @@ -94,6 +95,7 @@ final MarshallingContext context) { final List fields = new ArrayList(); final Map defaultFieldDefinition = new HashMap(); + final Class sourceType = source.getClass(); // Attributes might be preferred to child elements ... reflectionProvider.visitSerializableFields(source, new ReflectionProvider.Visitor() { @@ -106,7 +108,7 @@ if (!defaultFieldDefinition.containsKey(fieldName)) { Class lookupType = source.getClass(); // See XSTR-457 and OmitFieldsTest - if (definedIn != source.getClass() + if (definedIn != sourceType && !mapper.shouldSerializeMember(lookupType, fieldName)) { lookupType = definedIn; } @@ -120,18 +122,19 @@ final String attribute = mapper.aliasForAttribute(mapper.serializedMember( definedIn, fieldName)); if (value != null) { - if (writtenAttributes.contains(fieldName)) { // TODO: use attribute - throw new ConversionException("Cannot write field with name '" - + fieldName - + "' twice as attribute for object of type " - + source.getClass().getName()); + if (writtenAttributes.contains(fieldName)) { + ConversionException exception = + new ConversionException("Cannot write field as attribute for object, attribute name already in use"); + exception.add("field-name", fieldName); + exception.add("object-type", sourceType.getName()); + throw exception; } final String str = converter.toString(value); if (str != null) { writer.addAttribute(attribute, str); } } - writtenAttributes.add(fieldName); // TODO: use attribute + writtenAttributes.add(fieldName); } else { fields.add(new FieldInfo(fieldName, type, definedIn, value)); } @@ -140,12 +143,27 @@ new Object() { { + final Map hiddenMappers = new HashMap(); for (Iterator fieldIter = fields.iterator(); fieldIter.hasNext();) { FieldInfo info = (FieldInfo)fieldIter.next(); if (info.value != null) { + final Field defaultField = (Field)defaultFieldDefinition.get(info.fieldName); Mapper.ImplicitCollectionMapping mapping = mapper .getImplicitCollectionDefForFieldName( - source.getClass(), info.fieldName); + defaultField.getDeclaringClass() == info.definedIn ? sourceType : info.definedIn, + info.fieldName); + if (mapping != null) { + Set mappings = (Set)hiddenMappers.get(info.fieldName); + if (mappings == null) { + mappings = new HashSet(); + mappings.add(mapping); + hiddenMappers.put(info.fieldName, mappings); + } else { + if (!mappings.add(mapping)) { + mapping = null; + } + } + } if (mapping != null) { if (context instanceof ReferencingMarshallingContext) { if (info.value != Collections.EMPTY_LIST @@ -205,7 +223,7 @@ Class actualType = newObj != null ? newObj.getClass() : fieldType; ExtendedHierarchicalStreamWriterHelper.startNode(writer, aliasName != null ? aliasName - : mapper.serializedMember(source.getClass(), fieldName), actualType); + : mapper.serializedMember(sourceType, fieldName), actualType); if (newObj != null) { Class defaultType = mapper.defaultImplementationOf(fieldType); @@ -300,10 +318,10 @@ type = Primitives.box(type); } if (value != null && !type.isAssignableFrom(value.getClass())) { - throw new ConversionException("Cannot convert type " - + value.getClass().getName() - + " to type " - + type.getName()); + ConversionException exception = new ConversionException("Cannot convert type"); + exception.add("source-type", value.getClass().getName()); + exception.add("target-type", type.getName()); + throw exception; } seenFields.add(new FastField(classDefiningField, attrName)); reflectionProvider.writeField(result, attrName, value, classDefiningField); @@ -332,7 +350,7 @@ field = reflectionProvider.getFieldOrNull(fieldDeclaringClass, fieldName); if (field == null) { // it is not a field ... do we have a field alias? - Class itemType = mapper.getItemTypeForItemFieldName(resultType, fieldName); + Class itemType = mapper.getItemTypeForItemFieldName(fieldDeclaringClass, fieldName); if (itemType != null) { String classAttribute = HierarchicalStreams.readClassAttribute( reader, mapper); @@ -347,7 +365,7 @@ try { type = mapper.realClass(originalNodeName); implicitFieldName = mapper.getFieldNameForItemTypeAndName( - context.getRequiredType(), type, originalNodeName); + fieldDeclaringClass, type, originalNodeName); } catch (CannotResolveClassException e) { // type stays null ... } @@ -355,7 +373,7 @@ // either not a type or element is a type alias, but does not // belong to an implicit field handleUnknownField( - explicitDeclaringClass, fieldName, resultType, originalNodeName); + explicitDeclaringClass, fieldName, fieldDeclaringClass, originalNodeName); // element is unknown in declaring class, ignore it now type = null; @@ -400,7 +418,6 @@ if (field != null && (fieldAlreadyChecked || (shouldUnmarshalField(field) && mapper .shouldSerializeMember(field.getDeclaringClass(), fieldName)))) { - String classAttribute = HierarchicalStreams.readClassAttribute( reader, mapper); if (classAttribute != null) { @@ -446,7 +463,7 @@ if (implicitFieldName == null) { // look for implicit field implicitFieldName = mapper.getFieldNameForItemTypeAndName( - context.getRequiredType(), + fieldDeclaringClass, value != null ? value.getClass() : Mapper.Null.class, originalNodeName); } @@ -454,7 +471,8 @@ implicitCollectionsForCurrentObject = new HashMap(); } writeValueToImplicitCollection( - value, implicitCollectionsForCurrentObject, result, implicitFieldName); + value, implicitCollectionsForCurrentObject, result, new FieldLocation( + implicitFieldName, fieldDeclaringClass)); } reader.moveUp(); @@ -467,7 +485,12 @@ Object value = entry.getValue(); if (value instanceof ArraysList) { Object array = ((ArraysList)value).toPhysicalArray(); - reflectionProvider.writeField(result, (String)entry.getKey(), array, null); + final FieldLocation fieldLocation = (FieldLocation)entry.getKey(); + final Field field = reflectionProvider.getFieldOrNull(fieldLocation.definedIn, + fieldLocation.fieldName); + reflectionProvider.writeField(result, fieldLocation.fieldName, array, field != null + ? field.getDeclaringClass() + : null); } } } @@ -501,24 +524,24 @@ throw new UnknownFieldException(resultType.getName(), fieldName); } - private void writeValueToImplicitCollection(Object value, Map implicitCollections, Object result, String implicitFieldName) { - Collection collection = (Collection)implicitCollections.get(implicitFieldName); + private void writeValueToImplicitCollection(Object value, Map implicitCollections, Object result, final FieldLocation fieldLocation) { + Collection collection = (Collection)implicitCollections.get(fieldLocation); if (collection == null) { - Class physicalFieldType = reflectionProvider.getFieldType( - result, implicitFieldName, null); + final Field field = reflectionProvider.getFieldOrNull(fieldLocation.definedIn, fieldLocation.fieldName); + Class physicalFieldType = field != null + ? field.getType() + : reflectionProvider.getFieldType(result, fieldLocation.fieldName, null); if (physicalFieldType.isArray()) { collection = new ArraysList(physicalFieldType); } else { Class fieldType = mapper.defaultImplementationOf(physicalFieldType); if (!(Collection.class.isAssignableFrom(fieldType) || Map.class .isAssignableFrom(fieldType))) { - throw new ObjectAccessException( - "Field " - + implicitFieldName - + " of " - + result.getClass().getName() - + " is configured for an implicit Collection or Map, but field is of type " - + fieldType.getName()); + ObjectAccessException oaex = new ObjectAccessException( + "Field is configured for an implicit Collection or Map, but is of an incompatible type"); + oaex.add("field", result.getClass().getName() + "."+fieldLocation.fieldName); + oaex.add("field-type", fieldType.getName()); + throw oaex; } if (pureJavaReflectionProvider == null) { pureJavaReflectionProvider = new PureJavaReflectionProvider(); @@ -528,13 +551,15 @@ collection = (Collection)instance; } else { Mapper.ImplicitCollectionMapping implicitCollectionMapping = mapper - .getImplicitCollectionDefForFieldName(result.getClass(), implicitFieldName); + .getImplicitCollectionDefForFieldName(fieldLocation.definedIn, fieldLocation.fieldName); collection = new MappingList( (Map)instance, implicitCollectionMapping.getKeyFieldName()); } - reflectionProvider.writeField(result, implicitFieldName, instance, null); - } - implicitCollections.put(implicitFieldName, collection); + reflectionProvider.writeField(result, fieldLocation.fieldName, instance, field != null + ? field.getDeclaringClass() + : null); + } + implicitCollections.put(fieldLocation, collection); } collection.add(value); } @@ -584,16 +609,55 @@ } } - private static class FieldInfo { + private static class FieldLocation { final String fieldName; + final Class definedIn; + + FieldLocation(final String fieldName, final Class definedIn) { + this.fieldName = fieldName; + this.definedIn = definedIn; + } + + public int hashCode() { + final int prime = 7; + int result = 1; + result = prime * result + (definedIn == null ? 0 : definedIn.getName().hashCode()); + result = prime * result + (fieldName == null ? 0 : fieldName.hashCode()); + return result; + } + + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final FieldLocation other = (FieldLocation)obj; + if (definedIn != other.definedIn) { + return false; + } + if (fieldName == null) { + if (other.fieldName != null) { + return false; + } + } else if (!fieldName.equals(other.fieldName)) { + return false; + } + return true; + } + } + + private static class FieldInfo extends FieldLocation { final Class type; - final Class definedIn; final Object value; - FieldInfo(String fieldName, Class type, Class definedIn, Object value) { - this.fieldName = fieldName; + FieldInfo(final String fieldName, final Class type, final Class definedIn, final Object value) { + super(fieldName, definedIn); this.type = type; - this.definedIn = definedIn; this.value = value; } } @@ -646,30 +710,19 @@ fieldCache.put(itemType, field); } if (field != null) { - try { - Object key = field.get(object); - return map.put(key, object) == null; - } catch (IllegalArgumentException e) { - throw new ObjectAccessException("Could not get field " - + field.getClass() - + "." - + field.getName(), e); - } catch (IllegalAccessException e) { - throw new ObjectAccessException("Could not get field " - + field.getClass() - + "." - + field.getName(), e); - } + Object key = Fields.read(field, object); + return map.put(key, object) == null; } } else if (object instanceof Map.Entry) { final Map.Entry entry = (Map.Entry)object; return map.put(entry.getKey(), entry.getValue()) == null; } - throw new ConversionException("Element of type " - + object.getClass().getName() - + " is not defined as entry for map of type " - + map.getClass().getName()); + ConversionException exception = + new ConversionException("Element is not defined as entry for implicit map"); + exception.add("map-type", map.getClass().getName()); + exception.add("element-type", object.getClass().getName()); + throw exception; } public Object get(int index) { diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/CGLIBEnhancedConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/CGLIBEnhancedConverter.java index 6ba08cc..12c886d 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/CGLIBEnhancedConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/CGLIBEnhancedConverter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2010, 2011, 2013, 2014, 2015 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2010, 2011, 2013, 2014, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -125,8 +125,8 @@ } else { ConversionException exception = new ConversionException( "Cannot handle CGLIB enhanced proxies without factory that have multiple callbacks"); - exception.add("proxy superclass", type.getSuperclass().getName()); - exception.add("number of callbacks", String.valueOf(callbacks.length)); + exception.add("proxy-superclass", type.getSuperclass().getName()); + exception.add("number-of-callbacks", String.valueOf(callbacks.length)); throw exception; } writer.startNode("callbacks"); @@ -166,9 +166,9 @@ } catch (NoSuchFieldException e) { // OK, ignore } catch (IllegalAccessException e) { - throw new ObjectAccessException("Access to serialVersionUID of " - + type.getName() - + " not allowed", e); + ObjectAccessException exception = new ObjectAccessException("Cannot access field", e); + exception.add("field", type.getName() + ".serialVersionUID"); + throw exception; } if (hasInterceptor) { writer.startNode("instance"); @@ -202,12 +202,9 @@ Object callback = field.get(source); list.add(callback); } catch (IllegalAccessException e) { - throw new ObjectAccessException("Access to " - + type.getName() - + "." - + CALLBACK_MARKER - + i - + " not allowed", e); + ObjectAccessException exception = new ObjectAccessException("Cannot access field", e); + exception.add("field", type.getName() + "." + CALLBACK_MARKER + i); + throw exception; } } return (Callback[])list.toArray(new Callback[list.size()]); @@ -269,15 +266,15 @@ ? (Object[])null : createNullArguments(parameterTypes)); } catch (IllegalAccessException e) { - throw new ObjectAccessException("Access to " - + calledMethod - + " not allowed", e); + ObjectAccessException exception = new ObjectAccessException("Cannot access method", e); + exception.add("method", calledMethod.toString()); + throw exception; } catch (InvocationTargetException e) { // OK, ignore } catch (NoSuchMethodException e) { ConversionException exception = new ConversionException( "CGLIB enhanced proxies wit abstract nethod that has not been implemented"); - exception.add("proxy superclass", type.getSuperclass().getName()); + exception.add("proxy-superclass", type.getSuperclass().getName()); exception.add("method", method.toString()); throw exception; } @@ -336,7 +333,7 @@ if (iface == Callback.class) { ConversionException exception = new ConversionException( "Cannot handle CGLIB callback"); - exception.add("CGLIB callback type", callback.getClass().getName()); + exception.add("CGLIB-callback-type", callback.getClass().getName()); throw exception; } interfaces = iface.getInterfaces(); @@ -488,7 +485,7 @@ if (!callbackIndexMap.containsKey(method)) { ConversionException exception = new ConversionException( "CGLIB callback not detected in reverse engineering"); - exception.add("CGLIB callback", method.toString()); + exception.add("CGLIB-callback", method.toString()); throw exception; } return ((Integer)callbackIndexMap.get(method)).intValue(); diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ExternalizableConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ExternalizableConverter.java index cd8a585..05ab79c 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ExternalizableConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ExternalizableConverter.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2010, 2011, 2013, 2014, 2015 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2010, 2011, 2013, 2014, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -25,6 +25,7 @@ import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +import com.thoughtworks.xstream.io.StreamException; import com.thoughtworks.xstream.mapper.Mapper; import java.io.Externalizable; @@ -124,7 +125,7 @@ externalizable.writeExternal(objectOutput); objectOutput.popCallback(); } catch (IOException e) { - throw new ConversionException("Cannot serialize " + source.getClass().getName() + " using Externalization", e); + throw new StreamException("Cannot serialize " + source.getClass().getName() + " using Externalization", e); } } } @@ -168,17 +169,17 @@ objectInput.popCallback(); return serializationMembers.callReadResolve(externalizable); } catch (NoSuchMethodException e) { - throw new ConversionException("Cannot construct " + type.getClass() + ", missing default constructor", e); + throw new ConversionException("Missing default constructor of type", e); } catch (InvocationTargetException e) { - throw new ConversionException("Cannot construct " + type.getClass(), e); + throw new ConversionException("Cannot construct type", e); } catch (InstantiationException e) { - throw new ConversionException("Cannot construct " + type.getClass(), e); + throw new ConversionException("Cannot construct type", e); } catch (IllegalAccessException e) { - throw new ConversionException("Cannot construct " + type.getClass(), e); + throw new ObjectAccessException("Cannot construct type", e); } catch (IOException e) { - throw new ConversionException("Cannot externalize " + type.getClass(), e); + throw new StreamException("Cannot externalize " + type.getClass(), e); } catch (ClassNotFoundException e) { - throw new ConversionException("Cannot externalize " + type.getClass(), e); + throw new ConversionException("Cannot construct type", e); } } diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/FieldDictionary.java b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/FieldDictionary.java index a450e8f..77f01a4 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/FieldDictionary.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/FieldDictionary.java @@ -1,25 +1,21 @@ /* * Copyright (C) 2004, 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 XStream Committers. * All rights reserved. - * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * * Created on 14. May 2004 by Joe Walnes */ package com.thoughtworks.xstream.converters.reflection; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; -import java.util.List; +import java.util.LinkedList; import java.util.Map; -import java.util.Set; import com.thoughtworks.xstream.core.Caching; import com.thoughtworks.xstream.core.JVM; @@ -28,47 +24,46 @@ /** * A field dictionary instance caches information about classes fields. - * + * * @author Joe Walnes * @author Jörg Schaible * @author Guilherme Silveira */ public class FieldDictionary implements Caching { - private transient Map keyedByFieldNameCache; - private transient Map keyedByFieldKeyCache; + private static final DictionaryEntry OBJECT_DICTIONARY_ENTRY = new DictionaryEntry(Collections.EMPTY_MAP, + Collections.EMPTY_MAP); + + private transient Map dictionaryEntries; private final FieldKeySorter sorter; public FieldDictionary() { this(new ImmutableFieldKeySorter()); } - public FieldDictionary(FieldKeySorter sorter) { + public FieldDictionary(final FieldKeySorter sorter) { this.sorter = sorter; init(); } private void init() { - keyedByFieldNameCache = new HashMap(); - keyedByFieldKeyCache = new HashMap(); - keyedByFieldNameCache.put(Object.class, Collections.EMPTY_MAP); - keyedByFieldKeyCache.put(Object.class, Collections.EMPTY_MAP); + dictionaryEntries = new HashMap(); } /** * Returns an iterator for all fields for some class - * + * * @param cls the class you are interested on * @return an iterator for its fields * @deprecated As of 1.3, use {@link #fieldsFor(Class)} instead */ - public Iterator serializableFieldsFor(Class cls) { + public Iterator serializableFieldsFor(final Class cls) { return fieldsFor(cls); } /** * Returns an iterator for all fields for some class - * + * * @param cls the class you are interested on * @return an iterator for its fields */ @@ -77,19 +72,18 @@ } /** - * Returns an specific field of some class. If definedIn is null, it searches for the field - * named 'name' inside the class cls. If definedIn is different than null, tries to find the - * specified field name in the specified class cls which should be defined in class - * definedIn (either equals cls or a one of it's superclasses) - * + * Returns an specific field of some class. If definedIn is null, it searches for the field named 'name' inside the + * class cls. If definedIn is different than null, tries to find the specified field name in the specified class cls + * which should be defined in class definedIn (either equals cls or a one of it's superclasses) + * * @param cls the class where the field is to be searched * @param name the field name * @param definedIn the superclass (or the class itself) of cls where the field was defined * @return the field itself * @throws ObjectAccessException if no field can be found */ - public Field field(Class cls, String name, Class definedIn) { - Field field = fieldOrNull(cls, name, definedIn); + public Field field(final Class cls, final String name, final Class definedIn) { + final Field field = fieldOrNull(cls, name, definedIn); if (field == null) { throw new MissingFieldException(cls.getName(), name); } else { @@ -98,89 +92,98 @@ } /** - * Returns an specific field of some class. If definedIn is null, it searches for the field - * named 'name' inside the class cls. If definedIn is different than null, tries to find the - * specified field name in the specified class cls which should be defined in class - * definedIn (either equals cls or a one of it's superclasses) - * + * Returns an specific field of some class. If definedIn is null, it searches for the field named 'name' inside the + * class cls. If definedIn is different than null, tries to find the specified field name in the specified class cls + * which should be defined in class definedIn (either equals cls or a one of it's superclasses) + * * @param cls the class where the field is to be searched * @param name the field name * @param definedIn the superclass (or the class itself) of cls where the field was defined * @return the field itself or null * @since 1.4 */ - public Field fieldOrNull(Class cls, String name, Class definedIn) { - Map fields = buildMap(cls, definedIn != null); - Field field = (Field)fields.get(definedIn != null + public Field fieldOrNull(final Class cls, final String name, final Class definedIn) { + final Map fields = buildMap(cls, definedIn != null); + final Field field = (Field)fields.get(definedIn != null ? (Object)new FieldKey(name, definedIn, -1) : (Object)name); return field; } - private Map buildMap(final Class type, boolean tupleKeyed) { + private Map buildMap(final Class type, final boolean tupleKeyed) { + Class cls = type; - synchronized (this) { - if (!keyedByFieldNameCache.containsKey(type)) { - final List superClasses = new ArrayList(); - while (!Object.class.equals(cls) && cls != null) { - superClasses.add(0, cls); - cls = cls.getSuperclass(); + + DictionaryEntry lastDictionaryEntry = null; + final LinkedList superClasses = new LinkedList(); + while (lastDictionaryEntry == null) { + if (Object.class.equals(cls) || cls == null) { + lastDictionaryEntry = OBJECT_DICTIONARY_ENTRY; + } else { + lastDictionaryEntry = getDictionaryEntry(cls); + } + if (lastDictionaryEntry == null) { + superClasses.addFirst(cls); + cls = cls.getSuperclass(); + } + } + + for (final Iterator iter = superClasses.iterator(); iter.hasNext();) { + cls = (Class)iter.next(); + DictionaryEntry newDictionaryEntry = buildDictionaryEntryForClass(cls, lastDictionaryEntry); + synchronized (this) { + final DictionaryEntry concurrentEntry = getDictionaryEntry(cls); + if (concurrentEntry == null) { + dictionaryEntries.put(cls, newDictionaryEntry); + } else { + newDictionaryEntry = concurrentEntry; } - Map lastKeyedByFieldName = Collections.EMPTY_MAP; - Map lastKeyedByFieldKey = Collections.EMPTY_MAP; - for (final Iterator iter = superClasses.iterator(); iter.hasNext();) { - cls = (Class)iter.next(); - if (!keyedByFieldNameCache.containsKey(cls)) { - final Map keyedByFieldName = new HashMap(lastKeyedByFieldName); - final Map keyedByFieldKey = new OrderRetainingMap(lastKeyedByFieldKey); - Field[] fields = cls.getDeclaredFields(); - if (JVM.reverseFieldDefinition()) { - for (int i = fields.length >> 1; i-- > 0;) { - final int idx = fields.length - i - 1; - final Field field = fields[i]; - fields[i] = fields[idx]; - fields[idx] = field; - } - } - for (int i = 0; i < fields.length; i++ ) { - Field field = fields[i]; - if (!field.isAccessible()) { - field.setAccessible(true); - } - FieldKey fieldKey = new FieldKey( - field.getName(), field.getDeclaringClass(), i); - Field existent = (Field)keyedByFieldName.get(field.getName()); - if (existent == null - // do overwrite statics - || ((existent.getModifiers() & Modifier.STATIC) != 0) - // overwrite non-statics with non-statics only - || (existent != null && ((field.getModifiers() & Modifier.STATIC) == 0))) { - keyedByFieldName.put(field.getName(), field); - } - keyedByFieldKey.put(fieldKey, field); - } - final Map sortedFieldKeys = sorter.sort(cls, keyedByFieldKey); - keyedByFieldNameCache.put(cls, keyedByFieldName); - keyedByFieldKeyCache.put(cls, sortedFieldKeys); - lastKeyedByFieldName = keyedByFieldName; - lastKeyedByFieldKey = sortedFieldKeys; - } else { - lastKeyedByFieldName = (Map)keyedByFieldNameCache.get(cls); - lastKeyedByFieldKey = (Map)keyedByFieldKeyCache.get(cls); - } - } - return tupleKeyed ? lastKeyedByFieldKey : lastKeyedByFieldName; - } - } - return (Map)(tupleKeyed - ? keyedByFieldKeyCache.get(type) - : keyedByFieldNameCache.get(type)); + } + lastDictionaryEntry = newDictionaryEntry; + } + + return tupleKeyed ? lastDictionaryEntry.getKeyedByFieldKey() : lastDictionaryEntry.getKeyedByFieldName(); + + } + + private DictionaryEntry buildDictionaryEntryForClass(final Class cls, final DictionaryEntry lastDictionaryEntry) { + final Map keyedByFieldName = new HashMap(lastDictionaryEntry.getKeyedByFieldName()); + final Map keyedByFieldKey = new OrderRetainingMap(lastDictionaryEntry.getKeyedByFieldKey()); + final Field[] fields = cls.getDeclaredFields(); + if (JVM.reverseFieldDefinition()) { + for (int i = fields.length >> 1; i-- > 0;) { + final int idx = fields.length - i - 1; + final Field field = fields[i]; + fields[i] = fields[idx]; + fields[idx] = field; + } + } + for (int i = 0; i < fields.length; i++) { + final Field field = fields[i]; + if (!field.isAccessible()) { + field.setAccessible(true); + } + final FieldKey fieldKey = new FieldKey(field.getName(), field.getDeclaringClass(), i); + final Field existent = (Field)keyedByFieldName.get(field.getName()); + if (existent == null + // do overwrite statics + || (existent.getModifiers() & Modifier.STATIC) != 0 + // overwrite non-statics with non-statics only + || (existent != null && (field.getModifiers() & Modifier.STATIC) == 0)) { + keyedByFieldName.put(field.getName(), field); + } + keyedByFieldKey.put(fieldKey, field); + } + final Map sortedFieldKeys = sorter.sort(cls, keyedByFieldKey); + return new DictionaryEntry(keyedByFieldName, sortedFieldKeys); + } + + private synchronized DictionaryEntry getDictionaryEntry(final Class cls) { + return (DictionaryEntry)dictionaryEntries.get(cls); } public synchronized void flushCache() { - Set objectTypeSet = Collections.singleton(Object.class); - keyedByFieldNameCache.keySet().retainAll(objectTypeSet); - keyedByFieldKeyCache.keySet().retainAll(objectTypeSet); + dictionaryEntries.clear(); if (sorter instanceof Caching) { ((Caching)sorter).flushCache(); } @@ -190,4 +193,24 @@ init(); return this; } + + private static final class DictionaryEntry { + + private final Map keyedByFieldName; + private final Map keyedByFieldKey; + + public DictionaryEntry(final Map keyedByFieldName, final Map keyedByFieldKey) { + super(); + this.keyedByFieldName = keyedByFieldName; + this.keyedByFieldKey = keyedByFieldKey; + } + + public Map getKeyedByFieldName() { + return keyedByFieldName; + } + + public Map getKeyedByFieldKey() { + return keyedByFieldKey; + } + } } diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/MissingFieldException.java b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/MissingFieldException.java index 102fa92..1046d5c 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/MissingFieldException.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/MissingFieldException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 XStream Committers. + * Copyright (C) 2011, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -29,9 +29,10 @@ * @since 1.4.2 */ public MissingFieldException(final String className, final String fieldName) { - super("No field '" + fieldName + "' found in class '" + className + "'"); + super("Field not found in class."); this.className = className; this.fieldName = fieldName; + add("field", className + "." + fieldName); } /** diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ObjectAccessException.java b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ObjectAccessException.java index c3ac302..5ea05e8 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ObjectAccessException.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ObjectAccessException.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -11,9 +11,9 @@ */ package com.thoughtworks.xstream.converters.reflection; -import com.thoughtworks.xstream.XStreamException; +import com.thoughtworks.xstream.converters.ErrorWritingException; -public class ObjectAccessException extends XStreamException { +public class ObjectAccessException extends ErrorWritingException { public ObjectAccessException(String message) { super(message); } diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/PureJavaReflectionProvider.java b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/PureJavaReflectionProvider.java index 8158e37..d2beebd 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/PureJavaReflectionProvider.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/PureJavaReflectionProvider.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2009, 2011, 2013 XStream Committers. + * Copyright (C) 2006, 2007, 2009, 2011, 2013, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -12,6 +12,7 @@ package com.thoughtworks.xstream.converters.reflection; import com.thoughtworks.xstream.core.JVM; +import com.thoughtworks.xstream.core.util.Fields; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -57,6 +58,7 @@ } public Object newInstance(Class type) { + ObjectAccessException oaex = null; try { Constructor[] constructors = type.getDeclaredConstructors(); for (int i = 0; i < constructors.length; i++) { @@ -71,25 +73,27 @@ if (Serializable.class.isAssignableFrom(type)) { return instantiateUsingSerialization(type); } else { - throw new ObjectAccessException("Cannot construct " + type.getName() - + " as it does not have a no-args constructor"); + oaex = new ObjectAccessException("Cannot construct type as it does not have a no-args constructor"); } } catch (InstantiationException e) { - throw new ObjectAccessException("Cannot construct " + type.getName(), e); + oaex = new ObjectAccessException("Cannot construct type", e); } catch (IllegalAccessException e) { - throw new ObjectAccessException("Cannot construct " + type.getName(), e); + oaex = new ObjectAccessException("Cannot construct type", e); } catch (InvocationTargetException e) { if (e.getTargetException() instanceof RuntimeException) { throw (RuntimeException)e.getTargetException(); } else if (e.getTargetException() instanceof Error) { throw (Error)e.getTargetException(); } else { - throw new ObjectAccessException("Constructor for " + type.getName() + " threw an exception", e.getTargetException()); - } - } + oaex = new ObjectAccessException("Constructor for type threw an exception", e.getTargetException()); + } + } + oaex.add("construction-type", type.getName()); + throw oaex; } private Object instantiateUsingSerialization(final Class type) { + ObjectAccessException oaex = null; try { synchronized (serializedDataCache) { byte[] data = (byte[]) serializedDataCache.get(type); @@ -119,10 +123,12 @@ return in.readObject(); } } catch (IOException e) { - throw new ObjectAccessException("Cannot create " + type.getName() + " by JDK serialization", e); + oaex = new ObjectAccessException("Cannot create type by JDK serialization", e); } catch (ClassNotFoundException e) { - throw new ObjectAccessException("Cannot find class " + e.getMessage(), e); - } + oaex = new ObjectAccessException("Cannot find class", e); + } + oaex.add("construction-type", type.getName()); + throw oaex; } public void visitSerializableFields(Object object, ReflectionProvider.Visitor visitor) { @@ -132,27 +138,15 @@ continue; } validateFieldAccess(field); - try { - Object value = field.get(object); - visitor.visit(field.getName(), field.getType(), field.getDeclaringClass(), value); - } catch (IllegalArgumentException e) { - throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e); - } catch (IllegalAccessException e) { - throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e); - } + Object value = Fields.read(field, object); + visitor.visit(field.getName(), field.getType(), field.getDeclaringClass(), value); } } public void writeField(Object object, String fieldName, Object value, Class definedIn) { Field field = fieldDictionary.field(object.getClass(), fieldName, definedIn); validateFieldAccess(field); - try { - field.set(object, value); - } catch (IllegalArgumentException e) { - throw new ObjectAccessException("Could not set field " + object.getClass() + "." + field.getName(), e); - } catch (IllegalAccessException e) { - throw new ObjectAccessException("Could not set field " + object.getClass() + "." + field.getName(), e); - } + Fields.write(field, object, value); } public Class getFieldType(Object object, String fieldName, Class definedIn) { diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializableConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializableConverter.java index 2825980..6488069 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializableConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializableConverter.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2010, 2011, 2012, 2013, 2014, 2015 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2010, 2011, 2012, 2013, 2014, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -32,10 +32,12 @@ import com.thoughtworks.xstream.core.JVM; import com.thoughtworks.xstream.core.util.CustomObjectInputStream; import com.thoughtworks.xstream.core.util.CustomObjectOutputStream; +import com.thoughtworks.xstream.core.util.Fields; import com.thoughtworks.xstream.core.util.HierarchicalStreams; import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +import com.thoughtworks.xstream.io.StreamException; import com.thoughtworks.xstream.mapper.Mapper; /** @@ -151,8 +153,7 @@ ObjectStreamField field = objectStreamClass.getField(name); Object value = fields.get(name); if (field == null) { - throw new ObjectAccessException("Class " + value.getClass().getName() - + " may not write a field named '" + name + "'"); + throw new MissingFieldException(value.getClass().getName(), name); } if (value != null) { ExtendedHierarchicalStreamWriterHelper.startNode( @@ -280,7 +281,7 @@ } } } catch (IOException e) { - throw new ObjectAccessException("Could not call defaultWriteObject()", e); + throw new StreamException("Cannot write defaults", e); } } @@ -291,21 +292,8 @@ } private Object readField(ObjectStreamField field, Class type, Object instance) { - try { - Field javaField = type.getDeclaredField(field.getName()); - if (!javaField.isAccessible()) { - javaField.setAccessible(true); - } - return javaField.get(instance); - } catch (IllegalArgumentException e) { - throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e); - } catch (IllegalAccessException e) { - throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e); - } catch (NoSuchFieldException e) { - throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e); - } catch (SecurityException e) { - throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e); - } + Field javaField = Fields.find(type, field.getName()); + return Fields.read(javaField, instance); } protected List hierarchyFor(Class type) { @@ -426,7 +414,7 @@ try { validation.validateObject(); } catch (InvalidObjectException e) { - throw new ObjectAccessException("Cannot validate object : " + e.getMessage(), e); + throw new ObjectAccessException("Cannot validate object", e); } } }, priority); @@ -458,7 +446,7 @@ try { callback.defaultReadObject(); } catch (IOException e) { - throw new ObjectAccessException("Could not call defaultWriteObject()", e); + throw new StreamException("Cannot read defaults", e); } } } diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SortableFieldKeySorter.java b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SortableFieldKeySorter.java index f6bee5e..91333c8 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SortableFieldKeySorter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SortableFieldKeySorter.java @@ -1,41 +1,41 @@ /* - * Copyright (C) 2007, 2009, 2011 XStream Committers. + * Copyright (C) 2007, 2009, 2011, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 10. April 2007 by Guilherme Silveira */ package com.thoughtworks.xstream.converters.reflection; - -import com.thoughtworks.xstream.core.Caching; -import com.thoughtworks.xstream.core.util.OrderRetainingMap; -import com.thoughtworks.xstream.io.StreamException; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.Map; +import com.thoughtworks.xstream.converters.ConversionException; +import com.thoughtworks.xstream.core.Caching; +import com.thoughtworks.xstream.core.util.OrderRetainingMap; + /** - * The default implementation for sorting fields. Invoke registerFieldOrder in order to set the - * field order for an specific type. - * + * The default implementation for sorting fields. Invoke registerFieldOrder in order to set the field order for an + * specific type. + * * @author Guilherme Silveira * @since 1.2.2 */ public class SortableFieldKeySorter implements FieldKeySorter, Caching { + private final static FieldKey[] EMPTY_FIELD_KEY_ARRAY = {}; private final Map map = new HashMap(); - public Map sort(Class type, Map keyedByFieldKey) { + public Map sort(final Class type, final Map keyedByFieldKey) { if (map.containsKey(type)) { - Map result = new OrderRetainingMap(); - FieldKey[] fieldKeys = (FieldKey[])keyedByFieldKey.keySet().toArray( - new FieldKey[keyedByFieldKey.size()]); + final Map result = new OrderRetainingMap(); + final FieldKey[] fieldKeys = (FieldKey[])keyedByFieldKey.keySet().toArray(EMPTY_FIELD_KEY_ARRAY); Arrays.sort(fieldKeys, (Comparator)map.get(type)); for (int i = 0; i < fieldKeys.length; i++ ) { result.put(fieldKeys[i], keyedByFieldKey.get(fieldKeys[i])); @@ -47,28 +47,30 @@ } /** - * Registers the field order to use for a specific type. This will not affect any of the - * type's super or sub classes. If you skip a field which will be serialized, XStream will - * thrown an StreamException during the serialization process. - * + * Registers the field order to use for a specific type. This will not affect any of the type's super or sub + * classes. If you skip a field which will be serialized, XStream will thrown a {@link ConversionException} during + * the serialization process. + * * @param type the type * @param fields the field order */ - public void registerFieldOrder(Class type, String[] fields) { - map.put(type, new FieldComparator(fields)); + public void registerFieldOrder(final Class type, final String[] fields) { + map.put(type, new FieldComparator(type, fields)); } private class FieldComparator implements Comparator { private final String[] fieldOrder; + private final Class type; - public FieldComparator(String[] fields) { - this.fieldOrder = fields; + public FieldComparator(final Class type, final String[] fields) { + this.type = type; + fieldOrder = fields; } - public int compare(String first, String second) { + public int compare(final String first, final String second) { int firstPosition = -1, secondPosition = -1; - for (int i = 0; i < fieldOrder.length; i++ ) { + for (int i = 0; i < fieldOrder.length; i++) { if (fieldOrder[i].equals(first)) { firstPosition = i; } @@ -78,14 +80,16 @@ } if (firstPosition == -1 || secondPosition == -1) { // field not defined!!! - throw new StreamException( - "You have not given XStream a list of all fields to be serialized."); + final ConversionException exception = new ConversionException( + "Incomplete list of serialized fields for type"); + exception.add("sort-type", type.getName()); + throw exception; } return firstPosition - secondPosition; } - public int compare(Object firstObject, Object secondObject) { - FieldKey first = (FieldKey)firstObject, second = (FieldKey)secondObject; + public int compare(final Object firstObject, final Object secondObject) { + final FieldKey first = (FieldKey)firstObject, second = (FieldKey)secondObject; return compare(first.getFieldName(), second.getFieldName()); } diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SunLimitedUnsafeReflectionProvider.java b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SunLimitedUnsafeReflectionProvider.java index 4faef33..2c569ae 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SunLimitedUnsafeReflectionProvider.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SunLimitedUnsafeReflectionProvider.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2011, 2013, 2014 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2011, 2013, 2014, 2016 XStream Committers. * All rights reserved. * * Created on 08. January 2014 by Joerg Schaible, factored out from SunUnsafeReflectionProvider @@ -8,6 +8,9 @@ package com.thoughtworks.xstream.converters.reflection; import java.lang.reflect.Field; + +import com.thoughtworks.xstream.converters.ConversionException; +import com.thoughtworks.xstream.converters.ErrorWritingException; import sun.misc.Unsafe; @@ -70,17 +73,22 @@ public Object newInstance(Class type) { if (exception != null) { - throw new ObjectAccessException("Cannot construct " + type.getName(), exception); + ObjectAccessException ex = new ObjectAccessException("Cannot construct type", exception); + ex.add("construction-type", type.getName()); + throw ex; } + ErrorWritingException ex = null; try { return unsafe.allocateInstance(type); } catch (SecurityException e) { - throw new ObjectAccessException("Cannot construct " + type.getName(), e); + ex = new ObjectAccessException("Cannot construct type", e); } catch (InstantiationException e) { - throw new ObjectAccessException("Cannot construct " + type.getName(), e); + ex = new ConversionException("Cannot construct type", e); } catch (IllegalArgumentException e) { - throw new ObjectAccessException("Cannot construct " + type.getName(), e); + ex = new ObjectAccessException("Cannot construct type", e); } + ex.add("construction-type", type.getName()); + throw ex; } protected void validateFieldAccess(Field field) { diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SunUnsafeReflectionProvider.java b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SunUnsafeReflectionProvider.java index c97c0c8..4c13023 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SunUnsafeReflectionProvider.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SunUnsafeReflectionProvider.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2011, 2013, 2014 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2011, 2013, 2014, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -58,8 +58,9 @@ private void write(Field field, Object object, Object value) { if (exception != null) { - throw new ObjectAccessException("Could not set field " + object.getClass() + "." + field.getName(), - exception); + ObjectAccessException ex = new ObjectAccessException("Cannot set field", exception); + ex.add("field", object.getClass() + "." + field.getName()); + throw ex; } try { long offset = getFieldOffset(field); @@ -82,19 +83,19 @@ } else if (type.equals(Boolean.TYPE)) { unsafe.putBoolean(object, offset, ((Boolean)value).booleanValue()); } else { - throw new ObjectAccessException("Could not set field " - + object.getClass() - + "." - + field.getName() - + ": Unknown type " - + type); + ObjectAccessException ex = new ObjectAccessException("Cannot set field of unknown type", exception); + ex.add("field", object.getClass() + "." + field.getName()); + ex.add("unknown-type", type.getName()); + throw ex; } } else { unsafe.putObject(object, offset, value); } } catch (IllegalArgumentException e) { - throw new ObjectAccessException("Could not set field " + object.getClass() + "." + field.getName(), e); + ObjectAccessException ex = new ObjectAccessException("Cannot set field", e); + ex.add("field", object.getClass() + "." + field.getName()); + throw ex; } } diff --git a/xstream/src/java/com/thoughtworks/xstream/core/AbstractReferenceUnmarshaller.java b/xstream/src/java/com/thoughtworks/xstream/core/AbstractReferenceUnmarshaller.java index d8218fd..791ca18 100644 --- a/xstream/src/java/com/thoughtworks/xstream/core/AbstractReferenceUnmarshaller.java +++ b/xstream/src/java/com/thoughtworks/xstream/core/AbstractReferenceUnmarshaller.java @@ -1,11 +1,11 @@ /* - * Copyright (C) 2006, 2007, 2008, 2011 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 15. March 2007 by Joerg Schaible */ package com.thoughtworks.xstream.core; @@ -22,7 +22,7 @@ /** * Abstract base class for a TreeUnmarshaller, that resolves references. - * + * * @author Joe Walnes * @author Jörg Schaible * @author Mauro Talevi @@ -49,16 +49,21 @@ } } final Object result; - String attributeName = getMapper().aliasForSystemAttribute("reference"); - String reference = attributeName == null ? null : reader.getAttribute(attributeName); + final String attributeName = getMapper().aliasForSystemAttribute("reference"); + final String reference = attributeName == null ? null : reader.getAttribute(attributeName); + final boolean isReferenceable = getMapper().isReferenceable(type); if (reference != null) { - Object cache = values.get(getReferenceKey(reference)); + final Object cache = isReferenceable ? values.get(getReferenceKey(reference)) : null; if (cache == null) { final ConversionException ex = new ConversionException("Invalid reference"); ex.add("reference", reference); + ex.add("referenced-type", type.getName()); + ex.add("referenceable", Boolean.toString(isReferenceable)); throw ex; } result = cache == NULL ? null : cache; + } else if (!isReferenceable) { + result = super.convert(parent, type, converter); } else { Object currentReferenceKey = getCurrentReferenceKey(); parentStack.push(currentReferenceKey); diff --git a/xstream/src/java/com/thoughtworks/xstream/core/BaseException.java b/xstream/src/java/com/thoughtworks/xstream/core/BaseException.java index a1bdb51..de33aa6 100644 --- a/xstream/src/java/com/thoughtworks/xstream/core/BaseException.java +++ b/xstream/src/java/com/thoughtworks/xstream/core/BaseException.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -14,7 +14,6 @@ import com.thoughtworks.xstream.XStreamException; /** - * JDK1.3 friendly exception that retains cause. * @deprecated As of 1.3, use {@link XStreamException} instead */ public abstract class BaseException extends RuntimeException { @@ -23,5 +22,7 @@ super(message); } - public abstract Throwable getCause(); + protected BaseException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/xstream/src/java/com/thoughtworks/xstream/core/DefaultConverterLookup.java b/xstream/src/java/com/thoughtworks/xstream/core/DefaultConverterLookup.java index b4b955f..c89055a 100644 --- a/xstream/src/java/com/thoughtworks/xstream/core/DefaultConverterLookup.java +++ b/xstream/src/java/com/thoughtworks/xstream/core/DefaultConverterLookup.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2013 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2013, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -58,7 +58,9 @@ return converter; } } - throw new ConversionException("No converter specified for " + type); + ConversionException exception = new ConversionException("No converter specified"); + exception.add("type", type.getName()); + throw exception; } public void registerConverter(Converter converter, int priority) { diff --git a/xstream/src/java/com/thoughtworks/xstream/core/util/CustomObjectInputStream.java b/xstream/src/java/com/thoughtworks/xstream/core/util/CustomObjectInputStream.java index 5b47e5e..9ef7479 100644 --- a/xstream/src/java/com/thoughtworks/xstream/core/util/CustomObjectInputStream.java +++ b/xstream/src/java/com/thoughtworks/xstream/core/util/CustomObjectInputStream.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2010, 2011, 2013 XStream Committers. + * Copyright (C) 2006, 2007, 2010, 2011, 2013, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -20,9 +20,10 @@ import java.io.StreamCorruptedException; import java.util.Map; -import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.DataHolder; +import com.thoughtworks.xstream.converters.reflection.ObjectAccessException; import com.thoughtworks.xstream.core.ClassLoaderReference; +import com.thoughtworks.xstream.io.StreamException; public class CustomObjectInputStream extends ObjectInputStream { @@ -63,8 +64,10 @@ result.pushCallback(callback); } return result; + } catch (SecurityException e) { + throw new ObjectAccessException("Cannot create CustomObjectStream", e); } catch (IOException e) { - throw new ConversionException("Cannot create CustomObjectStream", e); + throw new StreamException("Cannot create CustomObjectStream", e); } } diff --git a/xstream/src/java/com/thoughtworks/xstream/core/util/CustomObjectOutputStream.java b/xstream/src/java/com/thoughtworks/xstream/core/util/CustomObjectOutputStream.java index 09a2cdf..a354005 100644 --- a/xstream/src/java/com/thoughtworks/xstream/core/util/CustomObjectOutputStream.java +++ b/xstream/src/java/com/thoughtworks/xstream/core/util/CustomObjectOutputStream.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -16,8 +16,9 @@ import java.io.ObjectOutputStream; import java.util.Map; -import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.DataHolder; +import com.thoughtworks.xstream.converters.reflection.ObjectAccessException; +import com.thoughtworks.xstream.io.StreamException; public class CustomObjectOutputStream extends ObjectOutputStream { @@ -36,8 +37,10 @@ result.pushCallback(callback); } return result; + } catch (SecurityException e) { + throw new ObjectAccessException("Cannot create CustomObjectStream", e); } catch (IOException e) { - throw new ConversionException("Cannot create CustomObjectStream", e); + throw new StreamException("Cannot create CustomObjectStream", e); } } diff --git a/xstream/src/java/com/thoughtworks/xstream/core/util/DependencyInjectionFactory.java b/xstream/src/java/com/thoughtworks/xstream/core/util/DependencyInjectionFactory.java index b7cb672..293d4b6 100644 --- a/xstream/src/java/com/thoughtworks/xstream/core/util/DependencyInjectionFactory.java +++ b/xstream/src/java/com/thoughtworks/xstream/core/util/DependencyInjectionFactory.java @@ -1,11 +1,11 @@ /* - * Copyright (c) 2007, 2009, 2010, 2011, 2012, 2013 XStream Committers. + * Copyright (c) 2007, 2009, 2010, 2011, 2012, 2013, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 30. March 2007 by Joerg Schaible */ package com.thoughtworks.xstream.core.util; @@ -23,7 +23,7 @@ /** * A dependency injection factory. - * + * * @author Jörg Schaible * @since 1.2.2 */ @@ -35,7 +35,7 @@ * matching the sequence of the dependencies' types match first. Otherwise all the types of the dependencies must * match one of the the parameters although no dependency is used twice. Use a {@link TypedNull} instance to inject * null as parameter. - * + * * @param type the type to create an instance of * @param dependencies the possible dependencies * @return the instantiated object @@ -46,14 +46,14 @@ public static Object newInstance(final Class type, final Object[] dependencies) { return newInstance(type, dependencies, null); } - + /** * Create an instance with dependency injection. The given dependencies are used to match the parameters of the * constructors of the type. Constructors with most parameters are examined first. A parameter type sequence * matching the sequence of the dependencies' types match first. Otherwise all the types of the dependencies must * match one of the the parameters although no dependency is used twice. Use a {@link TypedNull} instance to inject * null as parameter. - * + * * @param type the type to create an instance of * @param dependencies the possible dependencies * @param usedDependencies bit mask set by the method for all used dependencies (may be null) @@ -85,7 +85,7 @@ } final TypedValue[] typedDependencies = new TypedValue[dependencies.length]; - for (int i = 0; i < dependencies.length; i++ ) { + for (int i = 0; i < dependencies.length; i++) { Object dependency = dependencies[i]; Class depType = dependency.getClass(); if (depType.isPrimitive()) { @@ -100,7 +100,7 @@ Constructor possibleCtor = null; int arity = Integer.MAX_VALUE; - for (int i = 0; bestMatchingCtor == null && i < ctors.length; i++ ) { + for (int i = 0; bestMatchingCtor == null && i < ctors.length; i++) { final Constructor constructor = ctors[i]; final Class[] parameterTypes = constructor.getParameterTypes(); if (parameterTypes.length > dependencies.length) { @@ -118,7 +118,7 @@ arity = parameterTypes.length; } - for (int j = 0; j < parameterTypes.length; j++ ) { + for (int j = 0; j < parameterTypes.length; j++) { if (parameterTypes[j].isPrimitive()) { parameterTypes[j] = Primitives.box(parameterTypes[j]); } @@ -129,11 +129,11 @@ matchingDependencies.clear(); usedDeps = 0; for (int j = 0, k = 0; j < parameterTypes.length - && parameterTypes.length + k - j <= typedDependencies.length; k++ ) { + && parameterTypes.length + k - j <= typedDependencies.length; k++) { if (parameterTypes[j].isAssignableFrom(typedDependencies[k].type)) { matchingDependencies.add(typedDependencies[k].value); - usedDeps |= 1L << k; - if ( ++j == parameterTypes.length) { + usedDeps |= 1L << k; + if (++j == parameterTypes.length) { bestMatchingCtor = constructor; break; } @@ -151,7 +151,7 @@ usedDeps = 0; for (int j = 0; j < parameterTypes.length; j++ ) { int assignable = -1; - for (int k = 0; k < deps.length; k++ ) { + for (int k = 0; k < deps.length; k++) { if (deps[k] == null) { continue; } @@ -162,8 +162,8 @@ } else if (parameterTypes[j].isAssignableFrom(deps[k].type)) { // use most specific type if (assignable < 0 - || (deps[assignable].type != deps[k].type && deps[assignable].type - .isAssignableFrom(deps[k].type))) { + || deps[assignable].type != deps[k].type + && deps[assignable].type.isAssignableFrom(deps[k].type)) { assignable = k; } } @@ -178,7 +178,7 @@ break; } } - + if (possible) { // the smaller the value, the smaller the indices in the deps array if (possibleCtor != null && usedDeps >= possibleUsedDeps) { @@ -194,9 +194,10 @@ if (bestMatchingCtor == null) { if (possibleCtor == null) { usedDeps = 0; - throw new ObjectAccessException("Cannot construct " - + type.getName() - + ", none of the dependencies match any constructor's parameters"); + final ObjectAccessException ex = new ObjectAccessException( + "Cannot construct type, none of the arguments match any constructor's parameters"); + ex.add("construction-type", type.getName()); + throw ex; } else { bestMatchingCtor = possibleCtor; matchingDependencies.clear(); @@ -206,6 +207,7 @@ } } + Throwable th = null; try { final Object instance; if (bestMatchingCtor == null) { @@ -216,7 +218,7 @@ if (usedDependencies != null) { usedDependencies.clear(); int i = 0; - for(long l = 1; l < usedDeps; l <<= 1, ++i) { + for (long l = 1; l < usedDeps; l <<= 1, ++i) { if ((usedDeps & l) > 0) { usedDependencies.set(i); } @@ -224,16 +226,19 @@ } return instance; } catch (final InstantiationException e) { - throw new ObjectAccessException("Cannot construct " + type.getName(), e); + th = e; } catch (final IllegalAccessException e) { - throw new ObjectAccessException("Cannot construct " + type.getName(), e); + th = e; } catch (final InvocationTargetException e) { - throw new ObjectAccessException("Cannot construct " + type.getName(), e); + th = e.getCause(); } catch (final SecurityException e) { - throw new ObjectAccessException("Cannot construct " + type.getName(), e); + th = e; } catch (final ExceptionInInitializerError e) { - throw new ObjectAccessException("Cannot construct " + type.getName(), e); - } + th = e; + } + final ObjectAccessException ex = new ObjectAccessException("Cannot construct type", th); + ex.add("construction-type", type.getName()); + throw ex; } private static class TypedValue { @@ -246,8 +251,7 @@ this.value = value; } - public String toString() - { + public String toString() { return type.getName() + ":" + value; } } diff --git a/xstream/src/java/com/thoughtworks/xstream/core/util/Fields.java b/xstream/src/java/com/thoughtworks/xstream/core/util/Fields.java index 368353a..32d179c 100644 --- a/xstream/src/java/com/thoughtworks/xstream/core/util/Fields.java +++ b/xstream/src/java/com/thoughtworks/xstream/core/util/Fields.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2004 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2013 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2013, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 06. April 2004 by Joe Walnes */ package com.thoughtworks.xstream.core.util; @@ -16,18 +16,19 @@ import com.thoughtworks.xstream.converters.reflection.ObjectAccessException; + /** - * Slightly nicer way to find, get and set fields in classes. Wraps standard java.lang.reflect.Field calls but wraps - * wraps exception in XStreamExceptions. + * Slightly nicer way to find, get and set fields in classes. Wraps standard java.lang.reflect.Field calls and turns + * exceptions into XStreamExceptions. * * @author Joe Walnes * @author Jörg Schaible */ public class Fields { - public static Field locate(Class definedIn, Class fieldType, boolean isStatic) { + public static Field locate(final Class definedIn, final Class fieldType, final boolean isStatic) { Field field = null; try { - Field[] fields = definedIn.getDeclaredFields(); + final Field[] fields = definedIn.getDeclaredFields(); for(int i = 0; i < fields.length; ++i) { if (Modifier.isStatic(fields[i].getModifiers()) == isStatic) { if (fieldType.isAssignableFrom(fields[i].getType())) { @@ -38,45 +39,62 @@ if (field != null && !field.isAccessible()) { field.setAccessible(true); } - } catch (SecurityException e) { + } catch (final SecurityException e) { // active SecurityManager - } catch (NoClassDefFoundError e) { + } catch (final NoClassDefFoundError e) { // restricted type in GAE } return field; } - public static Field find(Class type, String name) { + public static Field find(final Class type, final String name) { try { - Field result = type.getDeclaredField(name); + final Field result = type.getDeclaredField(name); if (!result.isAccessible()) { result.setAccessible(true); } return result; - } catch (NoSuchFieldException e) { - throw new IllegalArgumentException("Could not access " + type.getName() + "." + name + " field: " + e.getMessage()); - } catch (NoClassDefFoundError e) { - throw new ObjectAccessException("Could not access " + type.getName() + "." + name + " field: " + e.getMessage()); + } catch (final SecurityException e) { + throw wrap("Cannot access field", type, name, e); + } catch (final NoSuchFieldException e) { + throw wrap("Cannot access field", type, name, e); + } catch (final NoClassDefFoundError e) { + throw wrap("Cannot access field", type, name, e); } } - public static void write(Field field, Object instance, Object value) { + public static void write(final Field field, final Object instance, final Object value) { try { field.set(instance, value); - } catch (IllegalAccessException e) { - throw new ObjectAccessException("Could not write " + field.getType().getName() + "." + field.getName() + " field", e); - } catch (NoClassDefFoundError e) { - throw new ObjectAccessException("Could not write " + field.getType().getName() + "." + field.getName() + " field", e); + } catch (final SecurityException e) { + throw wrap("Cannot write field", field.getType(), field.getName(), e); + } catch (final IllegalArgumentException e) { + throw wrap("Cannot write field", field.getType(), field.getName(), e); + } catch (final IllegalAccessException e) { + throw wrap("Cannot write field", field.getType(), field.getName(), e); + } catch (final NoClassDefFoundError e) { + throw wrap("Cannot write field", field.getType(), field.getName(), e); } } - public static Object read(Field field, Object instance) { + public static Object read(final Field field, final Object instance) { try { return field.get(instance); - } catch (IllegalAccessException e) { - throw new ObjectAccessException("Could not read " + field.getType().getName() + "." + field.getName() + " field", e); - } catch (NoClassDefFoundError e) { - throw new ObjectAccessException("Could not read " + field.getType().getName() + "." + field.getName() + " field", e); + } catch (final SecurityException e) { + throw wrap("Cannot read field", field.getType(), field.getName(), e); + } catch (final IllegalArgumentException e) { + throw wrap("Cannot read field", field.getType(), field.getName(), e); + } catch (final IllegalAccessException e) { + throw wrap("Cannot read field", field.getType(), field.getName(), e); + } catch (final NoClassDefFoundError e) { + throw wrap("Cannot read field", field.getType(), field.getName(), e); } } + + private static ObjectAccessException wrap(final String message, final Class type, final String name, + final Throwable ex) { + final ObjectAccessException exception = new ObjectAccessException(message, ex); + exception.add("field", type.getName() + "." + name); + return exception; + } } diff --git a/xstream/src/java/com/thoughtworks/xstream/core/util/SerializationMembers.java b/xstream/src/java/com/thoughtworks/xstream/core/util/SerializationMembers.java index 505df5d..b8fc632 100644 --- a/xstream/src/java/com/thoughtworks/xstream/core/util/SerializationMembers.java +++ b/xstream/src/java/com/thoughtworks/xstream/core/util/SerializationMembers.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2010, 2011, 2014, 2015 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2010, 2011, 2014, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -24,6 +24,7 @@ import java.util.Map; import com.thoughtworks.xstream.converters.ConversionException; +import com.thoughtworks.xstream.converters.ErrorWritingException; import com.thoughtworks.xstream.converters.reflection.ObjectAccessException; import com.thoughtworks.xstream.core.Caching; @@ -72,17 +73,16 @@ final Class resultType = result.getClass(); final Method readResolveMethod = getRRMethod(resultType, "readResolve"); if (readResolveMethod != null) { + ErrorWritingException ex = null; try { return readResolveMethod.invoke(result, EMPTY_ARGS); } catch (IllegalAccessException e) { - throw new ObjectAccessException("Could not call " - + resultType.getName() - + ".readResolve()", e); + ex = new ObjectAccessException("Cannot access method", e); } catch (InvocationTargetException e) { - throw new ObjectAccessException("Could not call " - + resultType.getName() - + ".readResolve()", e.getTargetException()); - } + ex = new ConversionException("Failed calling method", e.getTargetException()); + } + ex.add("method", resultType.getName() + ".readResolve()"); + throw ex; } else { return result; } @@ -96,17 +96,16 @@ final Class objectType = object.getClass(); final Method writeReplaceMethod = getRRMethod(objectType, "writeReplace"); if (writeReplaceMethod != null) { + ErrorWritingException ex = null; try { return writeReplaceMethod.invoke(object, EMPTY_ARGS); } catch (IllegalAccessException e) { - throw new ObjectAccessException("Could not call " - + objectType.getName() - + ".writeReplace()", e); + ex = new ObjectAccessException("Cannot access method", e); } catch (InvocationTargetException e) { - throw new ObjectAccessException("Could not call " - + objectType.getName() - + ".writeReplace()", e.getTargetException()); - } + ex = new ConversionException("Failed calling method", e.getTargetException()); + } + ex.add("method", objectType.getName() + ".writeReplace()"); + throw ex; } else { return object; } @@ -119,18 +118,19 @@ } public void callReadObject(final Class type, final Object object, final ObjectInputStream stream) { + ErrorWritingException ex = null; try { Method readObjectMethod = getMethod( type, "readObject", new Class[]{ObjectInputStream.class}, false); readObjectMethod.invoke(object, new Object[]{stream}); } catch (IllegalAccessException e) { - throw new ConversionException("Could not call " - + object.getClass().getName() - + ".readObject()", e); + ex = new ObjectAccessException("Cannot access method", e); } catch (InvocationTargetException e) { - throw new ConversionException("Could not call " - + object.getClass().getName() - + ".readObject()", e.getTargetException()); + ex = new ConversionException("Failed calling method", e.getTargetException()); + } + if (ex != null) { + ex.add("method", object.getClass().getName() + ".readObject()"); + throw ex; } } @@ -140,18 +140,19 @@ } public void callWriteObject(final Class type, final Object instance, final ObjectOutputStream stream) { + ErrorWritingException ex = null; try { Method readObjectMethod = getMethod( type, "writeObject", new Class[]{ObjectOutputStream.class}, false); readObjectMethod.invoke(instance, new Object[]{stream}); } catch (IllegalAccessException e) { - throw new ConversionException("Could not call " - + instance.getClass().getName() - + ".writeObject()", e); + ex = new ObjectAccessException("Cannot access method", e); } catch (InvocationTargetException e) { - throw new ConversionException("Could not call " - + instance.getClass().getName() - + ".writeObject()", e.getTargetException()); + ex = new ConversionException("Failed calling method", e.getTargetException()); + } + if (ex != null) { + ex.add("method", instance.getClass().getName() + ".writeObject()"); + throw ex; } } @@ -210,6 +211,7 @@ } Map result = (Map)fieldCache.get(type.getName()); if (result == null) { + ErrorWritingException ex = null; try { final Field field = type.getDeclaredField("serialPersistentFields"); if ((field.getModifiers() & PERSISTENT_FIELDS_MODIFIER) == PERSISTENT_FIELDS_MODIFIER) { @@ -224,9 +226,13 @@ } } catch (final NoSuchFieldException e) { } catch (final IllegalAccessException e) { - throw new ObjectAccessException("Cannot get " + type.getName() + ".serialPersistentFields.", e); + ex = new ObjectAccessException("Cannot get field", e); } catch (final ClassCastException e) { - throw new ObjectAccessException("Cannot get " + type.getName() + ".serialPersistentFields.", e); + ex = new ConversionException("Incompatible field type", e); + } + if (ex != null) { + ex.add("field", type.getName() + ".serialPersistentFields"); + throw ex; } if (result == null) { result = NO_FIELDS; diff --git a/xstream/src/java/com/thoughtworks/xstream/core/util/ThreadSafePropertyEditor.java b/xstream/src/java/com/thoughtworks/xstream/core/util/ThreadSafePropertyEditor.java index 34c9953..af811ef 100644 --- a/xstream/src/java/com/thoughtworks/xstream/core/util/ThreadSafePropertyEditor.java +++ b/xstream/src/java/com/thoughtworks/xstream/core/util/ThreadSafePropertyEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2008 XStream Committers. + * Copyright (c) 2007, 2008, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -10,6 +10,8 @@ */ package com.thoughtworks.xstream.core.util; +import com.thoughtworks.xstream.converters.ConversionException; +import com.thoughtworks.xstream.converters.ErrorWritingException; import com.thoughtworks.xstream.converters.reflection.ObjectAccessException; import java.beans.PropertyEditor; @@ -44,15 +46,16 @@ editorType = type; pool = new Pool(initialPoolSize, maxPoolSize, new Pool.Factory() { public Object newInstance() { + ErrorWritingException ex = null; try { return editorType.newInstance(); } catch (InstantiationException e) { - throw new ObjectAccessException("Could not call default constructor of " - + editorType.getName(), e); + ex = new ConversionException("Faild to call default constructor", e); } catch (IllegalAccessException e) { - throw new ObjectAccessException("Could not call default constructor of " - + editorType.getName(), e); + ex = new ObjectAccessException("Cannot call default constructor", e); } + ex.add("construction-type", editorType.getName()); + throw ex; } }); diff --git a/xstream/src/java/com/thoughtworks/xstream/io/xml/AbstractXppDriver.java b/xstream/src/java/com/thoughtworks/xstream/io/xml/AbstractXppDriver.java index efcd59a..37d2a1f 100644 --- a/xstream/src/java/com/thoughtworks/xstream/io/xml/AbstractXppDriver.java +++ b/xstream/src/java/com/thoughtworks/xstream/io/xml/AbstractXppDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2011 XStream Committers. + * Copyright (C) 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -52,8 +52,8 @@ public HierarchicalStreamReader createReader(Reader in) { try { return new XppReader(in, createParser(), getNameCoder()); - } catch (XmlPullParserException e) { - throw new StreamException("Cannot create XmlPullParser"); + } catch (final XmlPullParserException e) { + throw new StreamException("Cannot create XmlPullParser", e); } } diff --git a/xstream/src/java/com/thoughtworks/xstream/io/xml/BEAStaxDriver.java b/xstream/src/java/com/thoughtworks/xstream/io/xml/BEAStaxDriver.java index 3c67699..01efa8f 100644 --- a/xstream/src/java/com/thoughtworks/xstream/io/xml/BEAStaxDriver.java +++ b/xstream/src/java/com/thoughtworks/xstream/io/xml/BEAStaxDriver.java @@ -1,11 +1,11 @@ /* - * Copyright (C) 2009, 2011 XStream Committers. + * Copyright (C) 2009, 2011, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 29. April 2009 by Joerg Schaible */ package com.thoughtworks.xstream.io.xml; @@ -19,7 +19,7 @@ /** * A driver using the BEA StAX implementation. - * + * * @author Jörg Schaible * @since 1.4 */ @@ -62,7 +62,11 @@ } protected XMLInputFactory createInputFactory() { - return new MXParserFactory(); + final XMLInputFactory instance = new MXParserFactory(); + instance.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE); +// if (instance.isPropertySupported(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES)) +// throw new IllegalStateException("Should not support external entities now!"); + return instance; } protected XMLOutputFactory createOutputFactory() { diff --git a/xstream/src/java/com/thoughtworks/xstream/io/xml/Dom4JDriver.java b/xstream/src/java/com/thoughtworks/xstream/io/xml/Dom4JDriver.java index bce7cff..7018f42 100644 --- a/xstream/src/java/com/thoughtworks/xstream/io/xml/Dom4JDriver.java +++ b/xstream/src/java/com/thoughtworks/xstream/io/xml/Dom4JDriver.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2009, 2011 XStream Committers. + * Copyright (C) 2006, 2007, 2009, 2011, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -26,6 +26,7 @@ import org.dom4j.io.OutputFormat; import org.dom4j.io.SAXReader; import org.dom4j.io.XMLWriter; +import org.xml.sax.SAXException; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; @@ -89,8 +90,7 @@ public HierarchicalStreamReader createReader(Reader text) { try { - SAXReader reader = new SAXReader(); - Document document = reader.read(text); + final Document document = createReader().read(text); return new Dom4JReader(document, getNameCoder()); } catch (DocumentException e) { throw new StreamException(e); @@ -99,8 +99,7 @@ public HierarchicalStreamReader createReader(InputStream in) { try { - SAXReader reader = new SAXReader(); - Document document = reader.read(in); + final Document document = createReader().read(in); return new Dom4JReader(document, getNameCoder()); } catch (DocumentException e) { throw new StreamException(e); @@ -112,8 +111,7 @@ */ public HierarchicalStreamReader createReader(URL in) { try { - SAXReader reader = new SAXReader(); - Document document = reader.read(in); + final Document document = createReader().read(in); return new Dom4JReader(document, getNameCoder()); } catch (DocumentException e) { throw new StreamException(e); @@ -125,8 +123,7 @@ */ public HierarchicalStreamReader createReader(File in) { try { - SAXReader reader = new SAXReader(); - Document document = reader.read(in); + final Document document = createReader().read(in); return new Dom4JReader(document, getNameCoder()); } catch (DocumentException e) { throw new StreamException(e); @@ -148,4 +145,21 @@ final Writer writer = new OutputStreamWriter(out); return createWriter(writer); } + + /** + * Create and initialize the SAX reader. + * + * @return the SAX reader instance. + * @throws DocumentException if DOCTYPE processing cannot be disabled + * @since 1.4.9 + */ + protected SAXReader createReader() throws DocumentException { + final SAXReader reader = new SAXReader(); + try { + reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + } catch (SAXException e) { + throw new DocumentException("Cannot disable DOCTYPE processing", e); + } + return reader; + } } diff --git a/xstream/src/java/com/thoughtworks/xstream/io/xml/DomDriver.java b/xstream/src/java/com/thoughtworks/xstream/io/xml/DomDriver.java index 7b1709e..ae50fbe 100644 --- a/xstream/src/java/com/thoughtworks/xstream/io/xml/DomDriver.java +++ b/xstream/src/java/com/thoughtworks/xstream/io/xml/DomDriver.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2004, 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2011 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 07. March 2004 by Joe Walnes */ package com.thoughtworks.xstream.io.xml; @@ -19,6 +19,8 @@ import java.io.Reader; import java.io.UnsupportedEncodingException; import java.io.Writer; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.net.URL; import javax.xml.parsers.DocumentBuilder; @@ -30,6 +32,8 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; +import com.thoughtworks.xstream.converters.reflection.ObjectAccessException; +import com.thoughtworks.xstream.core.JVM; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.StreamException; @@ -39,7 +43,7 @@ public class DomDriver extends AbstractXmlDriver { private final String encoding; - private final DocumentBuilderFactory documentBuilderFactory; + private DocumentBuilderFactory documentBuilderFactory; /** * Construct a DomDriver. @@ -61,7 +65,6 @@ */ public DomDriver(String encoding, NameCoder nameCoder) { super(nameCoder); - documentBuilderFactory = DocumentBuilderFactory.newInstance(); this.encoding = encoding; } @@ -91,7 +94,14 @@ private HierarchicalStreamReader createReader(InputSource source) { try { - DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + if (documentBuilderFactory == null) { + synchronized (this) { + if (documentBuilderFactory == null) { + documentBuilderFactory = createDocumentBuilderFactory(); + } + } + } + final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); if (encoding != null) { source.setEncoding(encoding); } @@ -121,4 +131,34 @@ throw new StreamException(e); } } + + /** + * Create the DocumentBuilderFactory instance. + * + * @return the new instance + * @since 1.4.9 + */ + protected DocumentBuilderFactory createDocumentBuilderFactory() { + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + if (JVM.is15()) { + try { + Method method = DocumentBuilderFactory.class.getMethod("setFeature", + new Class[]{ String.class, boolean.class }); + method.invoke(factory, new Object[]{ "http://apache.org/xml/features/disallow-doctype-decl", Boolean.TRUE }); + } catch (NoSuchMethodException e) { + // Ignore + } catch (IllegalAccessException e) { + throw new ObjectAccessException("Cannot set feature of DocumentBuilderFactory.", e); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (JVM.is16() + || (cause instanceof ParserConfigurationException + && cause.getMessage().indexOf("disallow-doctype-decl") < 0)) { + throw new StreamException(cause); + } + } + } + factory.setExpandEntityReferences(false); + return factory; + } } diff --git a/xstream/src/java/com/thoughtworks/xstream/io/xml/JDom2Driver.java b/xstream/src/java/com/thoughtworks/xstream/io/xml/JDom2Driver.java index 876630e..86f4170 100644 --- a/xstream/src/java/com/thoughtworks/xstream/io/xml/JDom2Driver.java +++ b/xstream/src/java/com/thoughtworks/xstream/io/xml/JDom2Driver.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 XStream Committers. + * Copyright (C) 2013, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -47,8 +47,8 @@ public HierarchicalStreamReader createReader(Reader reader) { try { - SAXBuilder builder = new SAXBuilder(); - Document document = builder.build(reader); + final SAXBuilder builder = createBuilder(); + final Document document = builder.build(reader); return new JDom2Reader(document, getNameCoder()); } catch (IOException e) { throw new StreamException(e); @@ -59,8 +59,8 @@ public HierarchicalStreamReader createReader(InputStream in) { try { - SAXBuilder builder = new SAXBuilder(); - Document document = builder.build(in); + final SAXBuilder builder = createBuilder(); + final Document document = builder.build(in); return new JDom2Reader(document, getNameCoder()); } catch (IOException e) { throw new StreamException(e); @@ -71,8 +71,8 @@ public HierarchicalStreamReader createReader(URL in) { try { - SAXBuilder builder = new SAXBuilder(); - Document document = builder.build(in); + final SAXBuilder builder = createBuilder(); + final Document document = builder.build(in); return new JDom2Reader(document, getNameCoder()); } catch (IOException e) { throw new StreamException(e); @@ -83,8 +83,8 @@ public HierarchicalStreamReader createReader(File in) { try { - SAXBuilder builder = new SAXBuilder(); - Document document = builder.build(in); + final SAXBuilder builder = createBuilder(); + final Document document = builder.build(in); return new JDom2Reader(document, getNameCoder()); } catch (IOException e) { throw new StreamException(e); @@ -100,5 +100,17 @@ public HierarchicalStreamWriter createWriter(OutputStream out) { return new PrettyPrintWriter(new OutputStreamWriter(out)); } + + /** + * Create and initialize the SAX builder. + * + * @return the SAX builder instance. + * @since 1.4.9 + */ + protected SAXBuilder createBuilder() { + final SAXBuilder builder = new SAXBuilder(); + builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + return builder; + } } diff --git a/xstream/src/java/com/thoughtworks/xstream/io/xml/JDomDriver.java b/xstream/src/java/com/thoughtworks/xstream/io/xml/JDomDriver.java index 6bc3255..22a496b 100644 --- a/xstream/src/java/com/thoughtworks/xstream/io/xml/JDomDriver.java +++ b/xstream/src/java/com/thoughtworks/xstream/io/xml/JDomDriver.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2009, 2011 XStream Committers. + * Copyright (C) 2006, 2007, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -55,8 +55,8 @@ public HierarchicalStreamReader createReader(Reader reader) { try { - SAXBuilder builder = new SAXBuilder(); - Document document = builder.build(reader); + final SAXBuilder builder = createBuilder(); + final Document document = builder.build(reader); return new JDomReader(document, getNameCoder()); } catch (IOException e) { throw new StreamException(e); @@ -67,8 +67,8 @@ public HierarchicalStreamReader createReader(InputStream in) { try { - SAXBuilder builder = new SAXBuilder(); - Document document = builder.build(in); + final SAXBuilder builder = createBuilder(); + final Document document = builder.build(in); return new JDomReader(document, getNameCoder()); } catch (IOException e) { throw new StreamException(e); @@ -79,8 +79,8 @@ public HierarchicalStreamReader createReader(URL in) { try { - SAXBuilder builder = new SAXBuilder(); - Document document = builder.build(in); + final SAXBuilder builder = createBuilder(); + final Document document = builder.build(in); return new JDomReader(document, getNameCoder()); } catch (IOException e) { throw new StreamException(e); @@ -91,8 +91,8 @@ public HierarchicalStreamReader createReader(File in) { try { - SAXBuilder builder = new SAXBuilder(); - Document document = builder.build(in); + final SAXBuilder builder = createBuilder(); + final Document document = builder.build(in); return new JDomReader(document, getNameCoder()); } catch (IOException e) { throw new StreamException(e); @@ -109,5 +109,17 @@ return new PrettyPrintWriter(new OutputStreamWriter(out)); } + /** + * Create and initialize the SAX builder. + * + * @return the SAX builder instance. + * @since 1.4.9 + */ + protected SAXBuilder createBuilder() { + final SAXBuilder builder = new SAXBuilder(); + builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + return builder; + } + } diff --git a/xstream/src/java/com/thoughtworks/xstream/io/xml/QNameMap.java b/xstream/src/java/com/thoughtworks/xstream/io/xml/QNameMap.java index e047b13..06e7e96 100644 --- a/xstream/src/java/com/thoughtworks/xstream/io/xml/QNameMap.java +++ b/xstream/src/java/com/thoughtworks/xstream/io/xml/QNameMap.java @@ -22,7 +22,7 @@ * allowing class aliases and namespace aware mappings of QNames to class names. * * @author James Strachan - * @version $Revision: 1345 $ + * @version $Revision$ */ public class QNameMap { diff --git a/xstream/src/java/com/thoughtworks/xstream/io/xml/SjsxpDriver.java b/xstream/src/java/com/thoughtworks/xstream/io/xml/SjsxpDriver.java index 26d8009..a8d4d77 100644 --- a/xstream/src/java/com/thoughtworks/xstream/io/xml/SjsxpDriver.java +++ b/xstream/src/java/com/thoughtworks/xstream/io/xml/SjsxpDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2011, 2013 XStream Committers. + * Copyright (C) 2009, 2011, 2013, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -58,8 +58,10 @@ protected XMLInputFactory createInputFactory() { Exception exception = null; try { - return (XMLInputFactory)Class.forName("com.sun.xml.internal.stream.XMLInputFactoryImpl").newInstance(); - } catch (InstantiationException e) { + final XMLInputFactory instance = (XMLInputFactory)Class.forName("com.sun.xml.internal.stream.XMLInputFactoryImpl").newInstance(); + instance.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE); + return instance; + } catch (final InstantiationException e) { exception = e; } catch (IllegalAccessException e) { exception = e; diff --git a/xstream/src/java/com/thoughtworks/xstream/io/xml/StandardStaxDriver.java b/xstream/src/java/com/thoughtworks/xstream/io/xml/StandardStaxDriver.java index 1fe744d..272be6c 100644 --- a/xstream/src/java/com/thoughtworks/xstream/io/xml/StandardStaxDriver.java +++ b/xstream/src/java/com/thoughtworks/xstream/io/xml/StandardStaxDriver.java @@ -1,11 +1,11 @@ /* - * Copyright (C) 2013 XStream Committers. + * Copyright (C) 2013, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 27. July 2013 by Joerg Schaible */ package com.thoughtworks.xstream.io.xml; @@ -28,7 +28,7 @@ * implementations configured in lib/stax.properties or registered with the Service * API. *

    - * + * * @author Jörg Schaible * @since 1.4.5 */ @@ -75,7 +75,9 @@ try { Class staxInputFactory = JVM.getStaxInputFactory(); if (staxInputFactory != null) { - return (XMLInputFactory)staxInputFactory.newInstance(); + final XMLInputFactory instance = (XMLInputFactory)staxInputFactory.newInstance(); + instance.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE); + return instance; } else { throw new StreamException("Java runtime has no standard XMLInputFactory implementation.", exception); } diff --git a/xstream/src/java/com/thoughtworks/xstream/io/xml/StaxDriver.java b/xstream/src/java/com/thoughtworks/xstream/io/xml/StaxDriver.java index b4c0086..d1ee523 100644 --- a/xstream/src/java/com/thoughtworks/xstream/io/xml/StaxDriver.java +++ b/xstream/src/java/com/thoughtworks/xstream/io/xml/StaxDriver.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2009, 2011, 2013 XStream Committers. + * Copyright (C) 2006, 2007, 2009, 2011, 2013, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -40,7 +40,7 @@ * * @author James Strachan * @author Jörg Schaible - * @version $Revision: 2116 $ + * @version $Revision$ */ public class StaxDriver extends AbstractXmlDriver { @@ -238,7 +238,9 @@ * @since 1.4 */ protected XMLInputFactory createInputFactory() { - return XMLInputFactory.newInstance(); + final XMLInputFactory instance = XMLInputFactory.newInstance(); + instance.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE); + return instance; } /** diff --git a/xstream/src/java/com/thoughtworks/xstream/io/xml/StaxReader.java b/xstream/src/java/com/thoughtworks/xstream/io/xml/StaxReader.java index 08fe718..84e126f 100644 --- a/xstream/src/java/com/thoughtworks/xstream/io/xml/StaxReader.java +++ b/xstream/src/java/com/thoughtworks/xstream/io/xml/StaxReader.java @@ -24,7 +24,7 @@ * A reader using the StAX API. * * @author James Strachan - * @version $Revision: 1861 $ + * @version $Revision$ */ public class StaxReader extends AbstractPullReader { diff --git a/xstream/src/java/com/thoughtworks/xstream/io/xml/StaxWriter.java b/xstream/src/java/com/thoughtworks/xstream/io/xml/StaxWriter.java index 79410cc..6146717 100644 --- a/xstream/src/java/com/thoughtworks/xstream/io/xml/StaxWriter.java +++ b/xstream/src/java/com/thoughtworks/xstream/io/xml/StaxWriter.java @@ -23,7 +23,7 @@ * A stream writing that outputs to a StAX stream writer * * @author James Strachan - * @version $Revision: 1906 $ + * @version $Revision$ */ public class StaxWriter extends AbstractXmlWriter { diff --git a/xstream/src/java/com/thoughtworks/xstream/io/xml/WstxDriver.java b/xstream/src/java/com/thoughtworks/xstream/io/xml/WstxDriver.java index fd9b316..d01f319 100644 --- a/xstream/src/java/com/thoughtworks/xstream/io/xml/WstxDriver.java +++ b/xstream/src/java/com/thoughtworks/xstream/io/xml/WstxDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2011 XStream Committers. + * Copyright (C) 2009, 2011, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -62,7 +62,9 @@ } protected XMLInputFactory createInputFactory() { - return new WstxInputFactory(); + final XMLInputFactory instance = new WstxInputFactory(); + instance.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE); + return instance; } protected XMLOutputFactory createOutputFactory() { diff --git a/xstream/src/java/com/thoughtworks/xstream/io/xml/XomDriver.java b/xstream/src/java/com/thoughtworks/xstream/io/xml/XomDriver.java index 5700ea6..d6c041d 100644 --- a/xstream/src/java/com/thoughtworks/xstream/io/xml/XomDriver.java +++ b/xstream/src/java/com/thoughtworks/xstream/io/xml/XomDriver.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2009, 2011 XStream Committers. + * Copyright (C) 2006, 2007, 2009, 2011, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -35,9 +35,12 @@ private final Builder builder; public XomDriver() { - this(new Builder()); + this(new XmlFriendlyNameCoder()); } + /** + * @deprecated As of 1.4.9, use {@link #XomDriver()} and overload {@link #createBuilder()} instead + */ public XomDriver(Builder builder) { this(builder, new XmlFriendlyNameCoder()); } @@ -46,40 +49,63 @@ * @since 1.4 */ public XomDriver(NameCoder nameCoder) { - this(new Builder(), nameCoder); + super(nameCoder); + this.builder = null; } /** * @since 1.4 + * @deprecated As of 1.4.9, use {@link #XomDriver(NameCoder)} and overload {@link #createBuilder()} instead */ public XomDriver(Builder builder, NameCoder nameCoder) { - super(nameCoder); + super(nameCoder); this.builder = builder; } /** * @since 1.2 - * @deprecated As of 1.4, use {@link #XomDriver(Builder, NameCoder)} instead + * @deprecated As of 1.4, use {@link #XomDriver(NameCoder)} instead */ public XomDriver(XmlFriendlyReplacer replacer) { - this(new Builder(), replacer); + this((NameCoder)replacer); } /** * @since 1.2 - * @deprecated As of 1.4, use {@link #XomDriver(Builder, NameCoder)} instead + * @deprecated As of 1.4, use {@link #XomDriver(NameCoder)} and overload {@link #createBuilder()} instead */ public XomDriver(Builder builder, XmlFriendlyReplacer replacer) { - this((NameCoder)replacer); + this(builder, (NameCoder)replacer); } + /** + * @deprecated As of 1.4.9, overload {@link #createBuilder()} instead + */ protected Builder getBuilder() { return this.builder; } + /** + * Create the Builder instance. + * + * A XOM builder is a wrapper around a {@link org.xml.sax.XMLReader} + * instance which is not thread-safe by definition. Therefore each reader + * should use its own builder instance to avoid concurrency problems. + * + * Overload this method to configure the generated builder instances e.g. + * to activate validation. + * + * @return the new builder + * @since 1.4.9 + */ + protected Builder createBuilder() { + final Builder builder = getBuilder(); + return builder != null ? builder : new Builder(); + } + public HierarchicalStreamReader createReader(Reader text) { try { - Document document = builder.build(text); + final Document document = createBuilder().build(text); return new XomReader(document, getNameCoder()); } catch (ValidityException e) { throw new StreamException(e); @@ -92,7 +118,7 @@ public HierarchicalStreamReader createReader(InputStream in) { try { - Document document = builder.build(in); + final Document document = createBuilder().build(in); return new XomReader(document, getNameCoder()); } catch (ValidityException e) { throw new StreamException(e); @@ -105,7 +131,7 @@ public HierarchicalStreamReader createReader(URL in) { try { - Document document = builder.build(in.toExternalForm()); + final Document document = createBuilder().build(in.toExternalForm()); return new XomReader(document, getNameCoder()); } catch (ValidityException e) { throw new StreamException(e); @@ -118,7 +144,7 @@ public HierarchicalStreamReader createReader(File in) { try { - Document document = builder.build(in); + final Document document = createBuilder().build(in); return new XomReader(document, getNameCoder()); } catch (ValidityException e) { throw new StreamException(e); diff --git a/xstream/src/java/com/thoughtworks/xstream/mapper/AnnotationMapper.java b/xstream/src/java/com/thoughtworks/xstream/mapper/AnnotationMapper.java index 7d0e78e..e8145e9 100644 --- a/xstream/src/java/com/thoughtworks/xstream/mapper/AnnotationMapper.java +++ b/xstream/src/java/com/thoughtworks/xstream/mapper/AnnotationMapper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009, 2011, 2012, 2013 XStream Committers. + * Copyright (C) 2007, 2008, 2009, 2011, 2012, 2013, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -505,12 +505,13 @@ if (array != null) { int length = Array.getLength(array); for (int i = 0; i < length; i++ ) { - Object object = Array.get(array, i); - if (!parameter.contains(object)) { - parameter.add(object); - } - } - } + parameter.add(Array.get(array, i)); + } + } + } + for (final Class type : annotation.nulls()) { + final TypedNull nullType = new TypedNull(type); + parameter.add(nullType); } final Class converterType = annotation.value(); Map, Converter> converterMapping = converterCache.get(converterType); diff --git a/xstream/src/java/com/thoughtworks/xstream/mapper/ClassAliasingMapper.java b/xstream/src/java/com/thoughtworks/xstream/mapper/ClassAliasingMapper.java index c235db2..20cef9e 100644 --- a/xstream/src/java/com/thoughtworks/xstream/mapper/ClassAliasingMapper.java +++ b/xstream/src/java/com/thoughtworks/xstream/mapper/ClassAliasingMapper.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2011 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -79,10 +79,16 @@ return super.realClass(elementName); } + /** + * @deprecated As of 1.4.9 + */ public boolean itemTypeAsAttribute(Class clazz) { - return classToName.containsKey(clazz); + return classToName.containsKey(clazz.getName()); } + /** + * @deprecated As of 1.4.9 + */ public boolean aliasIsAttribute(String name) { return nameToType.containsKey(name); } diff --git a/xstream/src/java/com/thoughtworks/xstream/mapper/DefaultMapper.java b/xstream/src/java/com/thoughtworks/xstream/mapper/DefaultMapper.java index 4d029b8..c4a1d6d 100644 --- a/xstream/src/java/com/thoughtworks/xstream/mapper/DefaultMapper.java +++ b/xstream/src/java/com/thoughtworks/xstream/mapper/DefaultMapper.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2013 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2013, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 22. January 2005 by Joe Walnes */ package com.thoughtworks.xstream.mapper; @@ -18,8 +18,10 @@ /** - * Default mapper implementation with 'vanilla' functionality. To build up the functionality required, wrap this mapper - * with other mapper implementations. + * Default mapper implementation with 'vanilla' functionality. + *

    + * To build up the functionality required, wrap this mapper with other mapper implementations. + *

    * * @author Joe Walnes * @author Jörg Schaible @@ -38,7 +40,7 @@ /** * Construct a DefaultMapper. - * + * * @param classLoaderReference the reference to the classloader used by the XStream instance. * @since 1.4.5 */ @@ -48,7 +50,7 @@ /** * Construct a DefaultMapper. - * + * * @param classLoader the ClassLoader used by the XStream instance. * @deprecated As of 1.4.5 use {@link #DefaultMapper(ClassLoaderReference)} */ @@ -98,6 +100,10 @@ public boolean isImmutableValueType(Class type) { return false; + } + + public boolean isReferenceable(final Class type) { + return true; } public String getFieldNameForItemTypeAndName(Class definedIn, Class itemType, String itemFieldName) { diff --git a/xstream/src/java/com/thoughtworks/xstream/mapper/EnumMapper.java b/xstream/src/java/com/thoughtworks/xstream/mapper/EnumMapper.java index f46e4c2..d562126 100644 --- a/xstream/src/java/com/thoughtworks/xstream/mapper/EnumMapper.java +++ b/xstream/src/java/com/thoughtworks/xstream/mapper/EnumMapper.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2011 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 20. March 2005 by Joe Walnes */ package com.thoughtworks.xstream.mapper; @@ -22,11 +22,10 @@ /** - * Mapper that handles the special case of polymorphic enums in Java 1.5. This renames MyEnum$1 - * to MyEnum making it less bloaty in the XML and avoiding the need for an alias per enum value - * to be specified. Additionally every enum is treated automatically as immutable type and can - * be written as attribute. - * + * Mapper that handles the special case of polymorphic enums in Java 1.5. This renames MyEnum$1 to MyEnum making it less + * bloaty in the XML and avoiding the need for an alias per enum value to be specified. Additionally every enum is + * treated automatically as immutable and non-refrenceable type that can be written as attribute. + * * @author Joe Walnes * @author Jörg Schaible */ @@ -69,12 +68,21 @@ } @Override + public boolean isReferenceable(final Class type) { + if (type != null && Enum.class.isAssignableFrom(type)) { + return false; + } else { + return super.isReferenceable(type); + } + } + + @Override public SingleValueConverter getConverterFromItemType(String fieldName, Class type, - Class definedIn) { - SingleValueConverter converter = getLocalConverter(fieldName, type, definedIn); - return converter == null - ? super.getConverterFromItemType(fieldName, type, definedIn) - : converter; + Class definedIn) { + SingleValueConverter converter = getLocalConverter(fieldName, type, definedIn); + return converter == null + ? super.getConverterFromItemType(fieldName, type, definedIn) + : converter; } @Override @@ -88,8 +96,8 @@ private SingleValueConverter getLocalConverter(String fieldName, Class type, Class definedIn) { if (attributeMapper != null - && Enum.class.isAssignableFrom(type) - && attributeMapper.shouldLookForSingleValueConverter(fieldName, type, definedIn)) { + && Enum.class.isAssignableFrom(type) + && attributeMapper.shouldLookForSingleValueConverter(fieldName, type, definedIn)) { synchronized (enumConverterMap) { SingleValueConverter singleValueConverter = enumConverterMap.get(type); if (singleValueConverter == null) { diff --git a/xstream/src/java/com/thoughtworks/xstream/mapper/ImmutableTypesMapper.java b/xstream/src/java/com/thoughtworks/xstream/mapper/ImmutableTypesMapper.java index f6db320..b218807 100644 --- a/xstream/src/java/com/thoughtworks/xstream/mapper/ImmutableTypesMapper.java +++ b/xstream/src/java/com/thoughtworks/xstream/mapper/ImmutableTypesMapper.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2009 XStream Committers. + * Copyright (C) 2006, 2007, 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 22. January 2005 by Joe Walnes */ package com.thoughtworks.xstream.mapper; @@ -17,19 +17,44 @@ /** * Mapper that specifies which types are basic immutable types. Types that are marked as immutable will be written * multiple times in the serialization stream without using references. + *

    + * Note, that an already persisted stream might still contain references for immutable types. They can be dereferenced + * at deserialization time, unless the type is explicitly declared as unreferenceable. However, this is only possible at + * the expense of memory book-keeping all instances. + *

    * * @author Joe Walnes */ public class ImmutableTypesMapper extends MapperWrapper { + private final Set unreferenceableTypes = new HashSet(); private final Set immutableTypes = new HashSet(); public ImmutableTypesMapper(Mapper wrapped) { super(wrapped); } + /** + * @deprecated As of 1.4.9 use {@link #addImmutableType(Class, boolean)} + */ public void addImmutableType(Class type) { + addImmutableType(type, true); + } + + /** + * Declare a type as immutable. + * + * @param type the immutable type + * @param isReferenceable flag for possible references + * @since 1.4.9 + */ + public void addImmutableType(final Class type, final boolean isReferenceable) { immutableTypes.add(type); + if (!isReferenceable) { + unreferenceableTypes.add(type); + } else { + unreferenceableTypes.remove(type); + } } public boolean isImmutableValueType(Class type) { @@ -40,4 +65,11 @@ } } + public boolean isReferenceable(final Class type) { + if (unreferenceableTypes.contains(type)) { + return false; + } else { + return super.isReferenceable(type); + } + } } diff --git a/xstream/src/java/com/thoughtworks/xstream/mapper/ImplicitCollectionMapper.java b/xstream/src/java/com/thoughtworks/xstream/mapper/ImplicitCollectionMapper.java index 36eca76..2eb442b 100644 --- a/xstream/src/java/com/thoughtworks/xstream/mapper/ImplicitCollectionMapper.java +++ b/xstream/src/java/com/thoughtworks/xstream/mapper/ImplicitCollectionMapper.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2009, 2011, 2012, 2013, 2014 XStream Committers. + * Copyright (C) 2006, 2007, 2009, 2011, 2012, 2013, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 16. February 2005 by Joe Walnes */ package com.thoughtworks.xstream.mapper; @@ -15,6 +15,7 @@ import com.thoughtworks.xstream.core.util.Primitives; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -30,12 +31,27 @@ // { definedIn (Class) -> (ImplicitCollectionMapperForClass) } private final Map classNameToMapper = new HashMap(); - private ImplicitCollectionMapperForClass getMapper(Class definedIn) { + private ImplicitCollectionMapperForClass getMapper(final Class declaredFor, final String fieldName) { + Class definedIn = declaredFor; while (definedIn != null) { ImplicitCollectionMapperForClass mapper = (ImplicitCollectionMapperForClass)classNameToMapper .get(definedIn); if (mapper != null) { return mapper; + } else { + if (fieldName != null) { + try { + // do not continue search for a hidden field + final Field field = definedIn.getDeclaredField(fieldName); + if (field != null && !Modifier.isStatic(field.getModifiers())) { + return null; + } + } catch (final SecurityException e) { + throw new InitializationException("Access denied for field with implicit collection", e); + } catch (final NoSuchFieldException e) { + // OK, we can continue the search in the class hierarchy + } + } } definedIn = definedIn.getSuperclass(); } @@ -54,7 +70,7 @@ public String getFieldNameForItemTypeAndName(Class definedIn, Class itemType, String itemFieldName) { - ImplicitCollectionMapperForClass mapper = getMapper(definedIn); + ImplicitCollectionMapperForClass mapper = getMapper(definedIn, null); if (mapper != null) { return mapper.getFieldNameForItemTypeAndName(itemType, itemFieldName); } else { @@ -63,7 +79,7 @@ } public Class getItemTypeForItemFieldName(Class definedIn, String itemFieldName) { - ImplicitCollectionMapperForClass mapper = getMapper(definedIn); + ImplicitCollectionMapperForClass mapper = getMapper(definedIn, null); if (mapper != null) { return mapper.getItemTypeForItemFieldName(itemFieldName); } else { @@ -73,7 +89,7 @@ public ImplicitCollectionMapping getImplicitCollectionDefForFieldName(Class itemType, String fieldName) { - ImplicitCollectionMapperForClass mapper = getMapper(itemType); + ImplicitCollectionMapperForClass mapper = getMapper(itemType, fieldName); if (mapper != null) { return mapper.getImplicitCollectionDefForFieldName(fieldName); } else { @@ -91,16 +107,20 @@ public void add(Class definedIn, String fieldName, String itemFieldName, Class itemType, String keyFieldName) { Field field = null; - Class declaredIn = definedIn; - while (declaredIn != Object.class && definedIn != null) { - try { - field = declaredIn.getDeclaredField(fieldName); - break; - } catch (SecurityException e) { - throw new InitializationException( - "Access denied for field with implicit collection", e); - } catch (NoSuchFieldException e) { - declaredIn = declaredIn.getSuperclass(); + if (definedIn != null) { + Class declaredIn = definedIn; + while (declaredIn != Object.class) { + try { + field = declaredIn.getDeclaredField(fieldName); + if (!Modifier.isStatic(field.getModifiers())) { + break; + } + field = null; + } catch (final SecurityException e) { + throw new InitializationException("Access denied for field with implicit collection", e); + } catch (final NoSuchFieldException e) { + declaredIn = declaredIn.getSuperclass(); + } } } if (field == null) { @@ -177,7 +197,7 @@ if (unnamed != null) { return unnamed.getFieldName(); } else { - ImplicitCollectionMapperForClass mapper = ImplicitCollectionMapper.this.getMapper(definedIn.getSuperclass()); + ImplicitCollectionMapperForClass mapper = ImplicitCollectionMapper.this.getMapper(definedIn.getSuperclass(), null); return mapper != null ? mapper.getFieldNameForItemTypeAndName(itemType, itemFieldName) : null; } } @@ -187,7 +207,7 @@ if (def != null) { return def.getItemType(); } else { - ImplicitCollectionMapperForClass mapper = ImplicitCollectionMapper.this.getMapper(definedIn.getSuperclass()); + ImplicitCollectionMapperForClass mapper = ImplicitCollectionMapper.this.getMapper(definedIn.getSuperclass(), null); return mapper != null ? mapper.getItemTypeForItemFieldName(itemFieldName) : null; } } @@ -201,7 +221,7 @@ if (mapping != null) { return mapping; } else { - ImplicitCollectionMapperForClass mapper = ImplicitCollectionMapper.this.getMapper(definedIn.getSuperclass()); + ImplicitCollectionMapperForClass mapper = ImplicitCollectionMapper.this.getMapper(definedIn.getSuperclass(), null); return mapper != null ? mapper.getImplicitCollectionDefByItemFieldName(itemFieldName) : null; } } @@ -212,7 +232,7 @@ if (mapping != null) { return mapping; } else { - ImplicitCollectionMapperForClass mapper = ImplicitCollectionMapper.this.getMapper(definedIn.getSuperclass()); + ImplicitCollectionMapperForClass mapper = ImplicitCollectionMapper.this.getMapper(definedIn.getSuperclass(), null); return mapper != null ? mapper.getImplicitCollectionDefForFieldName(fieldName) : null; } } diff --git a/xstream/src/java/com/thoughtworks/xstream/mapper/Mapper.java b/xstream/src/java/com/thoughtworks/xstream/mapper/Mapper.java index 17bcaeb..3760ab2 100644 --- a/xstream/src/java/com/thoughtworks/xstream/mapper/Mapper.java +++ b/xstream/src/java/com/thoughtworks/xstream/mapper/Mapper.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2011 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 22. January 2005 by Joe Walnes */ package com.thoughtworks.xstream.mapper; @@ -41,16 +41,23 @@ String realMember(Class type, String serialized); /** - * Whether this type is a simple immutable value (int, boolean, String, URL, etc. - * Immutable types will be repeatedly written in the serialized stream, instead of using object references. + * Whether this type is a simple immutable value (int, boolean, String, URL, etc). Immutable types will be + * repeatedly written in the serialized stream, instead of using object references. */ boolean isImmutableValueType(Class type); + + /** + * Whether this type is referenceable in a stream. + * + * @since 1.4.9 + */ + boolean isReferenceable(Class type); Class defaultImplementationOf(Class type); /** * Get the alias for an attribute's name. - * + * * @param attribute the attribute * @return the alias * @since 1.2 @@ -59,7 +66,7 @@ /** * Get the attribute's name for an alias. - * + * * @param alias the alias * @return the attribute's name * @since 1.2 @@ -68,7 +75,7 @@ /** * Get the alias for a system attribute's name. - * + * * @param attribute the system attribute * @return the alias * @since 1.3.1 @@ -78,8 +85,8 @@ /** * Get the name of the field that acts as the default collection for an object, or return null if there is none. * - * @param definedIn owning type - * @param itemType item type + * @param definedIn owning type + * @param itemType item type * @param itemFieldName optional item element name */ String getFieldNameForItemTypeAndName(Class definedIn, Class itemType, String itemFieldName); @@ -123,7 +130,7 @@ /** * Returns a single value converter to be used in a specific field. - * + * * @param fieldName the field name * @param type the field type * @param definedIn the type which defines this field @@ -157,7 +164,7 @@ /** * Returns which converter to use for an specific attribute in a type. - * + * * @param definedIn the field's parent * @param attribute the attribute name * @deprecated As of 1.3.1, use {@link #getConverterFromAttribute(Class, String, Class)} @@ -166,7 +173,7 @@ /** * Returns which converter to use for an specific attribute in a type. - * + * * @param definedIn the field's parent * @param attribute the attribute name * @param type the type the converter should create diff --git a/xstream/src/java/com/thoughtworks/xstream/mapper/MapperWrapper.java b/xstream/src/java/com/thoughtworks/xstream/mapper/MapperWrapper.java index 93ced3d..71649f8 100644 --- a/xstream/src/java/com/thoughtworks/xstream/mapper/MapperWrapper.java +++ b/xstream/src/java/com/thoughtworks/xstream/mapper/MapperWrapper.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -40,6 +40,10 @@ public boolean isImmutableValueType(Class type) { return wrapped.isImmutableValueType(type); + } + + public boolean isReferenceable(Class type) { + return wrapped.isReferenceable(type); } public Class defaultImplementationOf(Class type) { diff --git a/xstream/src/java/com/thoughtworks/xstream/mapper/OuterClassMapper.java b/xstream/src/java/com/thoughtworks/xstream/mapper/OuterClassMapper.java index f98c514..6b136bc 100644 --- a/xstream/src/java/com/thoughtworks/xstream/mapper/OuterClassMapper.java +++ b/xstream/src/java/com/thoughtworks/xstream/mapper/OuterClassMapper.java @@ -1,24 +1,34 @@ /* * Copyright (C) 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2009 XStream Committers. + * Copyright (C) 2006, 2007, 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 31. January 2005 by Joe Walnes */ package com.thoughtworks.xstream.mapper; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import com.thoughtworks.xstream.core.Caching; + /** * Mapper that uses a more meaningful alias for the field in an inner class (this$0) that refers to the outer class. * * @author Joe Walnes */ -public class OuterClassMapper extends MapperWrapper { +public class OuterClassMapper extends MapperWrapper implements Caching { + private static final String[] EMPTY_NAMES = new String[0]; private final String alias; + private final Map innerFields; public OuterClassMapper(Mapper wrapped) { this(wrapped, "outer-class"); @@ -27,21 +37,61 @@ public OuterClassMapper(Mapper wrapped, String alias) { super(wrapped); this.alias = alias; + innerFields = Collections.synchronizedMap(new HashMap()); + innerFields.put(Object.class.getName(), EMPTY_NAMES); } public String serializedMember(Class type, String memberName) { - if (memberName.equals("this$0")) { - return alias; - } else { - return super.serializedMember(type, memberName); + if (memberName.startsWith("this$")) { + final String[] innerFieldNames = getInnerFieldNames(type); + for (int i = 0; i < innerFieldNames.length; ++i) { + if (innerFieldNames[i].equals(memberName)) { + return i == 0 ? alias : alias + '-' + i; + } + } } + return super.serializedMember(type, memberName); } public String realMember(Class type, String serialized) { - if (serialized.equals(alias)) { - return "this$0"; - } else { - return super.realMember(type, serialized); + if (serialized.startsWith(alias)) { + int idx = -1; + final int len = alias.length(); + if (len == serialized.length()) { + idx = 0; + } else if (serialized.length() > len + 1 && serialized.charAt(len) == '-') { + idx = Integer.valueOf(serialized.substring(len + 1)).intValue(); + } + if (idx >= 0) { + final String[] innerFieldNames = getInnerFieldNames(type); + if (idx < innerFieldNames.length) { + return innerFieldNames[idx]; + } + } } + return super.realMember(type, serialized); + } + + private String[] getInnerFieldNames(final Class type) { + String[] innerFieldNames = (String[])innerFields.get(type.getName()); + if (innerFieldNames == null) { + innerFieldNames = getInnerFieldNames(type.getSuperclass()); + Field[] declaredFields = type.getDeclaredFields(); + for (int i = 0; i < declaredFields.length; i++) { + final Field field = declaredFields[i]; + if (field.getName().startsWith("this$")) { + String[] temp = new String[innerFieldNames.length+1]; + System.arraycopy(innerFieldNames, 0, temp, 0, innerFieldNames.length); + innerFieldNames = temp; + innerFieldNames[innerFieldNames.length - 1] = field.getName(); + } + } + innerFields.put(type.getName(), innerFieldNames); + } + return innerFieldNames; + } + + public void flushCache() { + innerFields.keySet().retainAll(Collections.singletonList(Object.class.getName())); } } diff --git a/xstream/src/java/com/thoughtworks/xstream/persistence/FilePersistenceStrategy.java b/xstream/src/java/com/thoughtworks/xstream/persistence/FilePersistenceStrategy.java index ea973e8..1fe685e 100644 --- a/xstream/src/java/com/thoughtworks/xstream/persistence/FilePersistenceStrategy.java +++ b/xstream/src/java/com/thoughtworks/xstream/persistence/FilePersistenceStrategy.java @@ -1,32 +1,31 @@ /* - * Copyright (C) 2008 XStream Committers. + * Copyright (C) 2008, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 20. November 2008 by Joerg Schaible */ package com.thoughtworks.xstream.persistence; +import java.io.File; + import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.SingleValueConverter; -import com.thoughtworks.xstream.io.StreamException; import com.thoughtworks.xstream.io.xml.DomDriver; - -import java.io.File; /** - * PersistenceStrategy to assign keys with single value to objects persisted in files. The - * default naming strategy is based on the key's type and its {@link SingleValueConverter}. It - * escapes all characters that are normally illegal in the most common file systems. Such a - * character is escaped with percent escaping as it is done by URL encoding. The XStream used to - * marshal the values is also requested for the key's SingleValueConverter. A - * {@link StreamException} is thrown if no such converter is registered. - * + * PersistenceStrategy to assign keys with single value to objects persisted in files. The default naming strategy is + * based on the key's type and its {@link SingleValueConverter}. It escapes all characters that are normally illegal in + * the most common file systems. Such a character is escaped with percent escaping as it is done by URL encoding. The + * XStream used to marshal the values is also requested for the key's SingleValueConverter. A + * {@link ConversionException} is thrown if no such converter is registered. + * * @author Jörg Schaible * @author Guilherme Silveira * @since 1.3.1 @@ -36,9 +35,8 @@ private final String illegalChars; /** - * Create a new FilePersistenceStrategy. Use a standard XStream instance with a - * {@link DomDriver}. - * + * Create a new FilePersistenceStrategy. Use a standard XStream instance with a {@link DomDriver}. + * * @param baseDirectory the directory for the serialized values * @since 1.3.1 */ @@ -48,7 +46,7 @@ /** * Create a new FilePersistenceStrategy with a provided XStream instance. - * + * * @param baseDirectory the directory for the serialized values * @param xstream the XStream instance to use for (de)serialization * @since 1.3.1 @@ -58,19 +56,17 @@ } /** - * Create a new FilePersistenceStrategy with a provided XStream instance and the characters - * to encode. - * + * Create a new FilePersistenceStrategy with a provided XStream instance and the characters to encode. + * * @param baseDirectory the directory for the serialized values * @param xstream the XStream instance to use for (de)serialization * @param encoding encoding used to write the files - * @param illegalChars illegal characters for file names (should always include '%' as long - * as you do not overwrite the (un)escape methods) + * @param illegalChars illegal characters for file names (should always include '%' as long as you do not overwrite + * the (un)escape methods) * @since 1.3.1 */ public FilePersistenceStrategy( - final File baseDirectory, final XStream xstream, final String encoding, - final String illegalChars) { + final File baseDirectory, final XStream xstream, final String encoding, final String illegalChars) { super(baseDirectory, xstream, encoding); this.illegalChars = illegalChars; } @@ -81,7 +77,7 @@ /** * Given a filename, the unescape method returns the key which originated it. - * + * * @param name the filename * @return the original key */ @@ -90,19 +86,22 @@ if ("null@null".equals(key)) { return null; } - int idx = key.indexOf('@'); + final int idx = key.indexOf('@'); if (idx < 0) { - throw new StreamException("Not a valid key: " + key); + final ConversionException exception = new ConversionException("No valid key"); + exception.add("key", key); + throw exception; } - Class type = getMapper().realClass(key.substring(0, idx)); - Converter converter = getConverterLookup().lookupConverterForType(type); + final Class type = getMapper().realClass(key.substring(0, idx)); + final Converter converter = getConverterLookup().lookupConverterForType(type); if (converter instanceof SingleValueConverter) { final SingleValueConverter svConverter = (SingleValueConverter)converter; return svConverter.fromString(key.substring(idx + 1)); } else { - throw new StreamException("No SingleValueConverter for type " - + type.getName() - + " available"); + final ConversionException exception = new ConversionException( + "No SingleValueConverter available for key type"); + exception.add("key-type", type.getName()); + throw exception; } } @@ -110,7 +109,7 @@ final StringBuffer buffer = new StringBuffer(); for (int idx = name.indexOf('%'); idx >= 0; idx = name.indexOf('%')) { buffer.append(name.substring(0, idx)); - int c = Integer.parseInt(name.substring(idx + 1, idx + 3), 16); + final int c = Integer.parseInt(name.substring(idx + 1, idx + 3), 16); buffer.append((char)c); name = name.substring(idx + 3); } @@ -120,7 +119,7 @@ /** * Given a key, the escape method returns the filename which shall be used. - * + * * @param key the key * @return the desired and escaped filename */ @@ -128,18 +127,16 @@ if (key == null) { return "null@null.xml"; } - Class type = key.getClass(); - Converter converter = getConverterLookup().lookupConverterForType(type); + final Class type = key.getClass(); + final Converter converter = getConverterLookup().lookupConverterForType(type); if (converter instanceof SingleValueConverter) { final SingleValueConverter svConverter = (SingleValueConverter)converter; - return getMapper().serializedClass(type) - + '@' - + escape(svConverter.toString(key)) - + ".xml"; + return getMapper().serializedClass(type) + '@' + escape(svConverter.toString(key)) + ".xml"; } else { - throw new StreamException("No SingleValueConverter for type " - + type.getName() - + " available"); + final ConversionException exception = new ConversionException( + "No SingleValueConverter available for key type"); + exception.add("key-type", type.getName()); + throw exception; } } diff --git a/xstream/src/test/com/thoughtworks/acceptance/AbstractAcceptanceTest.java b/xstream/src/test/com/thoughtworks/acceptance/AbstractAcceptanceTest.java index 7fcf9f6..ee11141 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/AbstractAcceptanceTest.java +++ b/xstream/src/test/com/thoughtworks/acceptance/AbstractAcceptanceTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2003, 2004, 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2014 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -71,9 +71,8 @@ Class type = Class.forName(driver); return (HierarchicalStreamDriver) type.newInstance(); } - } - catch (Exception e) { - throw new RuntimeException("Could not load driver: " + driver); + } catch (final Exception e) { + throw new RuntimeException("Could not load driver: " + driver, e); } return new XppDriver(); } @@ -121,9 +120,10 @@ return resultRoot; - } catch (TransformerException e) { - throw new AssertionFailedError("Cannot normalize XML: " + e.getMessage()); - // .initCause(e); ... still JDK 1.3 + } catch (final TransformerException e) { + final AssertionFailedError error = new AssertionFailedError("Cannot normalize XML: " + e.getMessage()); + error.initCause(e); + throw error; } } diff --git a/xstream/src/test/com/thoughtworks/acceptance/AbstractReferenceTest.java b/xstream/src/test/com/thoughtworks/acceptance/AbstractReferenceTest.java index 514187c..07664a4 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/AbstractReferenceTest.java +++ b/xstream/src/test/com/thoughtworks/acceptance/AbstractReferenceTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2009, 2010, 2011, 2014 XStream Committers. + * Copyright (C) 2006, 2007, 2009, 2010, 2011, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -66,6 +66,8 @@ Thing t0 = (Thing)result.get(0); Thing t1 = (Thing)result.get(1); Thing t2 = (Thing)result.get(2); + + assertSame(t0, t1); t0.field = "bye"; @@ -472,4 +474,59 @@ String xml = xstream.toXML(emails); assertEquals(emails, xstream.fromXML(xml)); } + + public void testImmutableInstancesAreNotReferenced() { + xstream.addImmutableType(Thing.class, false); + + Thing sameThing = new Thing("hello"); + Thing anotherThing = new Thing("hello"); + + List list = new ArrayList(); + list.add(sameThing); + list.add(sameThing); + list.add(anotherThing); + + String xml = xstream.toXML(list); + List result = (List)xstream.fromXML(xml); + + Thing t0 = (Thing)result.get(0); + Thing t1 = (Thing)result.get(1); + Thing t2 = (Thing)result.get(2); + + assertEquals(t0, t1); + assertNotSame(t0, t1); + } + + public void testImmutableInstancesCanBeDereferenced() { + + Thing sameThing = new Thing("hello"); + Thing anotherThing = new Thing("hello"); + + List list = new ArrayList(); + list.add(sameThing); + list.add(sameThing); + list.add(anotherThing); + + String xml = xstream.toXML(list); + + xstream.addImmutableType(Thing.class, false); + + try { + xstream.fromXML(xml); + fail("Thrown " + ConversionException.class.getName() + " expected"); + } catch (final ConversionException e) { + assertEquals(Thing.class.getName(), e.get("referenced-type")); + } + + xstream.addImmutableType(Thing.class, true); + + List result = (List)xstream.fromXML(xml); + + Thing t0 = (Thing)result.get(0); + Thing t1 = (Thing)result.get(1); + Thing t2 = (Thing)result.get(2); + + assertEquals(t0, t1); + assertSame(t0, t1); + } } diff --git a/xstream/src/test/com/thoughtworks/acceptance/Extended17TypesTest.java b/xstream/src/test/com/thoughtworks/acceptance/Extended17TypesTest.java new file mode 100644 index 0000000..edc7b99 --- /dev/null +++ b/xstream/src/test/com/thoughtworks/acceptance/Extended17TypesTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 XStream Committers. + * All rights reserved. + * + * The software in this package is published under the terms of the BSD + * style license a copy of which has been included with this distribution in + * the LICENSE.txt file. + * + * Created on 7. February 2016 by Aaron Johnson + */ +package com.thoughtworks.acceptance; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; + +import com.thoughtworks.xstream.XStream; + + +/** + * @author Aaron Johnson + * @author Jörg Schaible + */ +public class Extended17TypesTest extends AbstractAcceptanceTest { + + @Override + protected void setupSecurity(final XStream xstream) { + super.setupSecurity(xstream); + xstream.allowTypeHierarchy(Path.class); + } + + public void testPathOfDefaultFileSystem() { + assertBothWays(Paths.get("../a/relative/path"), "../a/relative/path"); + assertBothWays(Paths.get("/an/absolute/path"), "/an/absolute/path"); + + final String absolutePathName = Paths.get("target").toAbsolutePath().toString(); + final URI uri = URI.create("file://" + absolutePathName); + assertBothWays(Paths.get(uri), "" + absolutePathName + ""); + } + + public void testPathOfNonDefaultFileSystem() throws IOException { + final Map env = new HashMap(); + env.put("create", "true"); + final URI uri = URI.create("jar:file://" + + Paths.get("target/lib/proxytoys-0.2.1.jar").toAbsolutePath().toString()); + + FileSystem zipfs = null; + try { + zipfs = FileSystems.newFileSystem(uri, env); + final String entry = "/com/thoughtworks/proxy/kit/SimpleReference.class"; + final Path path = zipfs.getPath(entry); + assertBothWays(path, "" + uri.toString() + "!" + entry + ""); + } finally { + if (zipfs != null) { + zipfs.close(); + } + } + } +} diff --git a/xstream/src/test/com/thoughtworks/acceptance/FinalFieldsTest.java b/xstream/src/test/com/thoughtworks/acceptance/FinalFieldsTest.java index ea8d806..d3ebcbf 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/FinalFieldsTest.java +++ b/xstream/src/test/com/thoughtworks/acceptance/FinalFieldsTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2013, 2014 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2013, 2014, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -45,7 +45,7 @@ } } catch (ObjectAccessException expectedException) { assertEquals("Invalid final field " + ThingWithFinalField.class.getName() + ".number", - expectedException.getMessage()); + expectedException.get("message")); } } } diff --git a/xstream/src/test/com/thoughtworks/acceptance/ImplicitArrayTest.java b/xstream/src/test/com/thoughtworks/acceptance/ImplicitArrayTest.java index b98c519..0573897 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/ImplicitArrayTest.java +++ b/xstream/src/test/com/thoughtworks/acceptance/ImplicitArrayTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2012, 2013 XStream Committers. + * Copyright (C) 2011, 2012, 2013, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -27,6 +27,8 @@ xstream.alias("farm", Farm.class); xstream.alias("animal", Animal.class); xstream.alias("MEGA-farm", MegaFarm.class); + xstream.alias("area", Area.class); + xstream.alias("country", Country.class); xstream.ignoreUnknownElements(); } @@ -483,6 +485,179 @@ assertBothWays(farm, expected); } + public static class Area extends Farm { + Animal[] animals; + } + + public void testWithHiddenArray() { + Area area = new Area(); + ((Farm)area).animals = new Animal[2]; + ((Farm)area).animals[0] = new Animal("Cow"); + ((Farm)area).animals[1] = new Animal("Sheep"); + area.animals = new Animal[2]; + area.animals[0] = new Animal("Falcon"); + area.animals[1] = new Animal("Sparrow"); + + String expected = "" + + "\n" + + " \n" + + " Cow\n" + + " \n" + + " \n" + + " Sheep\n" + + " \n" + + " \n" + + " Falcon\n" + + " \n" + + " \n" + + " Sparrow\n" + + " \n" + + ""; + + xstream.addImplicitArray(Farm.class, "animals"); + xstream.addImplicitArray(Area.class, "animals"); + assertBothWays(area, expected); + } + + public void testWithHiddenArrayAndDifferentAlias() { + Area area = new Area(); + ((Farm)area).animals = new Animal[2]; + ((Farm)area).animals[0] = new Animal("Cow"); + ((Farm)area).animals[1] = new Animal("Sheep"); + area.animals = new Animal[2]; + area.animals[0] = new Animal("Falcon"); + area.animals[1] = new Animal("Sparrow"); + + String expected = "" + + "\n" + + " \n" + + " Cow\n" + + " \n" + + " \n" + + " Sheep\n" + + " \n" + + " \n" + + " Falcon\n" + + " \n" + + " \n" + + " Sparrow\n" + + " \n" + + ""; + + xstream.addImplicitArray(Farm.class, "animals", "domesticated"); + xstream.addImplicitArray(Area.class, "animals", "wild"); + assertBothWays(area, expected); + } + + public void testDoesNotInheritFromHiddenArrayOfSuperclass() { + Area area = new Area(); + ((Farm)area).animals = new Animal[2]; + ((Farm)area).animals[0] = new Animal("Cow"); + ((Farm)area).animals[1] = new Animal("Sheep"); + area.animals = new Animal[2]; + area.animals[0] = new Animal("Falcon"); + area.animals[1] = new Animal("Sparrow"); + + String expected = "" + + "\n" + + " \n" + + " Cow\n" + + " \n" + + " \n" + + " Sheep\n" + + " \n" + + " \n" + + " \n" + + " Falcon\n" + + " \n" + + " \n" + + " Sparrow\n" + + " \n" + + " \n" + + ""; + + xstream.addImplicitArray(Farm.class, "animals"); + assertBothWays(area, expected); + } + + public void testDoesNotPropagateToHiddenArrayOfSuperclass() { + Area area = new Area(); + ((Farm)area).animals = new Animal[2]; + ((Farm)area).animals[0] = new Animal("Cow"); + ((Farm)area).animals[1] = new Animal("Sheep"); + area.animals = new Animal[2]; + area.animals[0] = new Animal("Falcon"); + area.animals[1] = new Animal("Sparrow"); + + String expected = "" + + "\n" + + " \n" + + " \n" + + " Cow\n" + + " \n" + + " \n" + + " Sheep\n" + + " \n" + + " \n" + + " \n" + + " Falcon\n" + + " \n" + + " \n" + + " Sparrow\n" + + " \n" + + ""; + + xstream.addImplicitArray(Area.class, "animals"); + assertBothWays(area, expected); + } + + public static class County extends Area { + } + + public static class Country extends County { + Animal[] animals; + } + + public void testWithDoubleHiddenArray() { + Country country = new Country(); + ((Farm)country).animals = new Animal[2]; + ((Farm)country).animals[0] = new Animal("Cow"); + ((Farm)country).animals[1] = new Animal("Sheep"); + ((Area)country).animals = new Animal[2]; + ((Area)country).animals[0] = new Animal("Falcon"); + ((Area)country).animals[1] = new Animal("Sparrow"); + country.animals = new Animal[2]; + country.animals[0] = new Animal("Wale"); + country.animals[1] = new Animal("Dolphin"); + + String expected = "" + + "\n" + + " \n" + + " Cow\n" + + " \n" + + " \n" + + " Sheep\n" + + " \n" + + " \n" + + " Falcon\n" + + " \n" + + " \n" + + " Sparrow\n" + + " \n" + + " \n" + + " Wale\n" + + " \n" + + " \n" + + " Dolphin\n" + + " \n" + + ""; + + xstream.addImplicitArray(Farm.class, "animals"); + xstream.addImplicitArray(Area.class, "animals"); + xstream.addImplicitArray(Country.class, "animals"); + assertBothWays(country, expected); + } + static class PrimitiveArray { int[] ints; }; diff --git a/xstream/src/test/com/thoughtworks/acceptance/ImplicitCollectionTest.java b/xstream/src/test/com/thoughtworks/acceptance/ImplicitCollectionTest.java index c489c13..502c486 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/ImplicitCollectionTest.java +++ b/xstream/src/test/com/thoughtworks/acceptance/ImplicitCollectionTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2012, 2013 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2012, 2013, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -57,6 +57,8 @@ xstream.alias("room", Room.class); xstream.alias("house", House.class); xstream.alias("person", Person.class); + xstream.alias("area", Area.class); + xstream.alias("country", Country.class); xstream.ignoreUnknownElements(); } @@ -549,4 +551,181 @@ xstream.addImplicitCollection(Farm.class, "animals", "beast", Animal.class); assertBothWays(farm, expected); } + + public static class Area extends Farm { + + List animals = new ArrayList(); + + public Area(int size) { + super(size); + } + + } + + public void testWithHiddenList() { + Area area = new Area(1000); + area.add(new Animal("Cow")); + area.add(new Animal("Sheep")); + area.animals.add(new Animal("Falcon")); + area.animals.add(new Animal("Sparrow")); + + String expected = "" + + "\n" + + " 1000\n" + + " \n" + + " Cow\n" + + " \n" + + " \n" + + " Sheep\n" + + " \n" + + " \n" + + " Falcon\n" + + " \n" + + " \n" + + " Sparrow\n" + + " \n" + + ""; + + xstream.addImplicitCollection(Farm.class, "animals"); + xstream.addImplicitCollection(Area.class, "animals"); + assertBothWays(area, expected); + } + + public void testWithHiddenListAndDifferentAlias() { + Area area = new Area(1000); + area.add(new Animal("Cow")); + area.add(new Animal("Sheep")); + area.animals.add(new Animal("Falcon")); + area.animals.add(new Animal("Sparrow")); + + String expected = "" + + "\n" + + " 1000\n" + + " \n" + + " Cow\n" + + " \n" + + " \n" + + " Sheep\n" + + " \n" + + " \n" + + " Falcon\n" + + " \n" + + " \n" + + " Sparrow\n" + + " \n" + + ""; + + xstream.addImplicitCollection(Farm.class, "animals", "domesticated", Animal.class); + xstream.addImplicitCollection(Area.class, "animals", "wild", Animal.class); + assertBothWays(area, expected); + } + + public void testDoesNotInheritFromHiddenListOfSuperclass() { + Area area = new Area(1000); + area.add(new Animal("Cow")); + area.add(new Animal("Sheep")); + area.animals.add(new Animal("Falcon")); + area.animals.add(new Animal("Sparrow")); + + String expected = "" + + "\n" + + " 1000\n" + + " \n" + + " Cow\n" + + " \n" + + " \n" + + " Sheep\n" + + " \n" + + " \n" + + " \n" + + " Falcon\n" + + " \n" + + " \n" + + " Sparrow\n" + + " \n" + + " \n" + + ""; + + xstream.addImplicitCollection(Farm.class, "animals"); + assertBothWays(area, expected); + } + + public void testDoesNotPropagateToHiddenListOfSuperclass() { + Area area = new Area(1000); + area.add(new Animal("Cow")); + area.add(new Animal("Sheep")); + area.animals.add(new Animal("Falcon")); + area.animals.add(new Animal("Sparrow")); + + String expected = "" + + "\n" + + " 1000\n" + + " \n" + + " \n" + + " Cow\n" + + " \n" + + " \n" + + " Sheep\n" + + " \n" + + " \n" + + " \n" + + " Falcon\n" + + " \n" + + " \n" + + " Sparrow\n" + + " \n" + + ""; + + xstream.addImplicitCollection(Area.class, "animals"); + assertBothWays(area, expected); + } + + public static class County extends Area { + + public County() { + super(10); + } + } + + public static class Country extends County { + List animals = new ArrayList(); + } + + public void testWithDoubleHiddenList() { + Country country = new Country(); + country.add(new Animal("Cow")); + country.add(new Animal("Sheep")); + ((Area)country).animals.add(new Animal("Falcon")); + ((Area)country).animals.add(new Animal("Sparrow")); + country.animals.add(new Animal("Wale")); + country.animals.add(new Animal("Dolphin")); + + String expected = "" + + "\n" + + " 10\n" + + " \n" + + " Cow\n" + + " \n" + + " \n" + + " Sheep\n" + + " \n" + + " \n" + + " Falcon\n" + + " \n" + + " \n" + + " Sparrow\n" + + " \n" + + " \n" + + " Wale\n" + + " \n" + + " \n" + + " Dolphin\n" + + " \n" + + ""; + + xstream.addImplicitCollection(Farm.class, "animals"); + xstream.addImplicitCollection(Area.class, "animals"); + xstream.addImplicitCollection(Country.class, "animals"); + assertBothWays(country, expected); + } } diff --git a/xstream/src/test/com/thoughtworks/acceptance/ImplicitMapTest.java b/xstream/src/test/com/thoughtworks/acceptance/ImplicitMapTest.java index 7a3d13d..3184c31 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/ImplicitMapTest.java +++ b/xstream/src/test/com/thoughtworks/acceptance/ImplicitMapTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2012, 2013 XStream Committers. + * Copyright (C) 2011, 2012, 2013, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -14,61 +14,37 @@ import com.thoughtworks.acceptance.objects.Product; import com.thoughtworks.acceptance.objects.SampleMaps; import com.thoughtworks.acceptance.objects.Software; -import com.thoughtworks.acceptance.objects.StandardObject; import com.thoughtworks.xstream.converters.collections.MapConverter; -import com.thoughtworks.xstream.core.util.OrderRetainingMap; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; public class ImplicitMapTest extends AbstractAcceptanceTest { - - public static class Farm extends StandardObject { - int size; - List animals = new ArrayList(); - - public Farm(int size) { - this.size = size; - } - - public void add(Animal animal) { - animals.add(animal); - } - } - - public static class Animal extends StandardObject implements Comparable { - String name; - - public Animal(String name) { - this.name = name; - } - - public int compareTo(Object o) { - return name.compareTo(((Animal)o).name); - } - } protected void setUp() throws Exception { super.setUp(); xstream.registerConverter(new MapConverter(xstream.getMapper()) { public boolean canConvert(Class type) { - return type == OrderRetainingMap.class; + return type == LinkedHashMap.class; } }); - xstream.addDefaultImplementation(OrderRetainingMap.class, Map.class); + xstream.addDefaultImplementation(LinkedHashMap.class, Map.class); xstream.alias("sample", SampleMaps.class); xstream.alias("software", Software.class); xstream.alias("hardware", Hardware.class); xstream.alias("product", Product.class); + xstream.alias("sample2", SampleMaps2.class); + xstream.alias("sample3", SampleMaps3.class); xstream.ignoreUnknownElements(); } public void testWithout() { SampleMaps sample = new SampleMaps(); - sample.good = new OrderRetainingMap(); + sample.good = new LinkedHashMap(); sample.good.put("Windows", new Software("Microsoft", "Windows")); sample.good.put("Linux", new Software("Red Hat", "Linux")); @@ -98,7 +74,7 @@ public void testWithMap() { SampleMaps sample = new SampleMaps(); - sample.good = new OrderRetainingMap(); + sample.good = new LinkedHashMap(); sample.good.put("Windows", new Software("Microsoft", "Windows")); sample.good.put("Linux", new Software("Red Hat", "Linux")); @@ -121,10 +97,10 @@ public static class MegaSampleMaps extends SampleMaps { String separator = "---"; - Map other = new OrderRetainingMap(); + Map other = new LinkedHashMap(); { - good = new OrderRetainingMap(); - bad = new OrderRetainingMap(); + good = new LinkedHashMap(); + bad = new LinkedHashMap(); } } @@ -371,7 +347,7 @@ public void testWithExplicitItemNameMatchingTheNameOfTheFieldWithTheMap() { SampleMaps sample = new SampleMaps(); - sample.bad = new OrderRetainingMap(); + sample.bad = new LinkedHashMap(); sample.bad.put("Windows", new Software("Microsoft", "Windows")); sample.bad.put("Linux", new Software("Red Hat", "Linux")); @@ -394,7 +370,7 @@ public void testWithImplicitNameMatchingTheNameOfTheFieldWithTheMap() { SampleMaps sample = new SampleMaps(); - sample.bad = new OrderRetainingMap(); + sample.bad = new LinkedHashMap(); sample.bad.put("Windows", new Software("Microsoft", "Windows")); sample.bad.put("Linux", new Software("Red Hat", "Linux")); @@ -418,7 +394,7 @@ public void testWithAliasedItemNameMatchingTheAliasedNameOfTheFieldWithTheMap() { SampleMaps sample = new SampleMaps(); - sample.bad = new OrderRetainingMap(); + sample.bad = new LinkedHashMap(); sample.bad.put("Windows", new Software("Microsoft", "Windows")); sample.bad.put("Linux", new Software("Red Hat", "Linux")); @@ -442,7 +418,7 @@ public void testWithNullElement() { SampleMaps sample = new SampleMaps(); - sample.good = new OrderRetainingMap(); + sample.good = new LinkedHashMap(); sample.good.put(null, null); sample.good.put("Linux", new Software("Red Hat", "Linux")); @@ -462,7 +438,7 @@ public void testWithAliasAndNullElement() { SampleMaps sample = new SampleMaps(); - sample.good = new OrderRetainingMap(); + sample.good = new LinkedHashMap(); sample.good.put(null, null); sample.good.put("Linux", new Software("Red Hat", "Linux")); @@ -479,10 +455,215 @@ xstream.addImplicitMap(SampleMaps.class, "good", "code", Software.class, "name"); assertBothWays(sample, expected); } + + public static class SampleMaps2 extends SampleMaps { + public Map good = new LinkedHashMap(); + } + + public void testWithHiddenMap() { + SampleMaps2 sample = new SampleMaps2(); + ((SampleMaps)sample).good = new LinkedHashMap(); + ((SampleMaps)sample).good.put("Windows", new Software("Microsoft", "Windows")); + ((SampleMaps)sample).good.put("Linux", new Software("Red Hat", "Linux")); + sample.good.put("Android", new Software("Google", "Android")); + sample.good.put("iOS", new Software("Apple", "iOS")); + sample.bad = null; + + String expected = "" + + "\n" + + " \n" + + " Microsoft\n" + + " Windows\n" + + " \n" + + " \n" + + " Red Hat\n" + + " Linux\n" + + " \n" + + " \n" + + " Google\n" + + " Android\n" + + " \n" + + " \n" + + " Apple\n" + + " iOS\n" + + " \n" + + ""; + + xstream.addImplicitMap(SampleMaps.class, "good", Software.class, "name"); + xstream.addImplicitMap(SampleMaps2.class, "good", Software.class, "name"); + assertBothWays(sample, expected); + } + + public void testWithHiddenMapAndDifferentAlias() { + SampleMaps2 sample = new SampleMaps2(); + ((SampleMaps)sample).good = new LinkedHashMap(); + ((SampleMaps)sample).good.put("Windows", new Software("Microsoft", "Windows")); + ((SampleMaps)sample).good.put("Linux", new Software("Red Hat", "Linux")); + sample.good.put("Android", new Software("Google", "Android")); + sample.good.put("iOS", new Software("Apple", "iOS")); + sample.bad = null; + + String expected = "" + + "\n" + + " \n" + + " Microsoft\n" + + " Windows\n" + + " \n" + + " \n" + + " Red Hat\n" + + " Linux\n" + + " \n" + + " \n" + + " Google\n" + + " Android\n" + + " \n" + + " \n" + + " Apple\n" + + " iOS\n" + + " \n" + + ""; + + xstream.addImplicitMap(SampleMaps.class, "good", "code", Software.class, "name"); + xstream.addImplicitMap(SampleMaps2.class, "good", "mobile", Software.class, "name"); + } + + public void testDoesNotInheritFromHiddenMapOfSuperclass() { + SampleMaps2 sample = new SampleMaps2(); + ((SampleMaps)sample).good = new LinkedHashMap(); + ((SampleMaps)sample).good.put("Windows", new Software("Microsoft", "Windows")); + ((SampleMaps)sample).good.put("Linux", new Software("Red Hat", "Linux")); + sample.good.put("Android", new Software("Google", "Android")); + sample.good.put("iOS", new Software("Apple", "iOS")); + sample.bad = null; + + String expected = "" + + "\n" + + " \n" + + " Microsoft\n" + + " Windows\n" + + " \n" + + " \n" + + " Red Hat\n" + + " Linux\n" + + " \n" + + " \n" + + " \n" + + " Android\n" + + " \n" + + " Google\n" + + " Android\n" + + " \n" + + " \n" + + " \n" + + " iOS\n" + + " \n" + + " Apple\n" + + " iOS\n" + + " \n" + + " \n" + + " \n" + + ""; + + xstream.addImplicitMap(SampleMaps.class, "good", Software.class, "name"); + assertBothWays(sample, expected); + } + + public void testDoesNotPropagateToHiddenMapOfSuperclass() { + SampleMaps2 sample = new SampleMaps2(); + ((SampleMaps)sample).good = new LinkedHashMap(); + ((SampleMaps)sample).good.put("Windows", new Software("Microsoft", "Windows")); + ((SampleMaps)sample).good.put("Linux", new Software("Red Hat", "Linux")); + sample.good.put("Android", new Software("Google", "Android")); + sample.good.put("iOS", new Software("Apple", "iOS")); + sample.bad = null; + + String expected = "" + + "\n" + + " \n" + + " \n" + + " Windows\n" + + " \n" + + " Microsoft\n" + + " Windows\n" + + " \n" + + " \n" + + " \n" + + " Linux\n" + + " \n" + + " Red Hat\n" + + " Linux\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " Google\n" + + " Android\n" + + " \n" + + " \n" + + " Apple\n" + + " iOS\n" + + " \n" + + ""; + + xstream.addImplicitMap(SampleMaps2.class, "good", Software.class, "name"); + assertBothWays(sample, expected); + } + + public static class IntermediateMaps extends SampleMaps2 { + } + + public static class SampleMaps3 extends IntermediateMaps { + Map good = new LinkedHashMap(); + } + + public void testWithDoubleHiddenList() { + SampleMaps3 sample = new SampleMaps3(); + ((SampleMaps)sample).good = new LinkedHashMap(); + ((SampleMaps)sample).good.put("Windows", new Software("Microsoft", "Windows")); + ((SampleMaps)sample).good.put("Linux", new Software("Red Hat", "Linux")); + ((SampleMaps2)sample).good.put("Android", new Software("Google", "Android")); + ((SampleMaps2)sample).good.put("iOS", new Software("Apple", "iOS")); + sample.good.put("Oracle", new Software("Oracle", "Oracle")); + sample.good.put("Hana", new Software("SAP", "Hana")); + sample.bad = null; + + String expected = "" + + "\n" + + " \n" + + " Microsoft\n" + + " Windows\n" + + " \n" + + " \n" + + " Red Hat\n" + + " Linux\n" + + " \n" + + " \n" + + " Google\n" + + " Android\n" + + " \n" + + " \n" + + " Apple\n" + + " iOS\n" + + " \n" + + " \n" + + " Oracle\n" + + " Oracle\n" + + " \n" + + " \n" + + " SAP\n" + + " Hana\n" + + " \n" + + ""; + + xstream.addImplicitMap(SampleMaps.class, "good", Software.class, "name"); + xstream.addImplicitMap(SampleMaps2.class, "good", Software.class, "name"); + xstream.addImplicitMap(SampleMaps3.class, "good", Software.class, "name"); + assertBothWays(sample, expected); + } public void testCollectsDifferentTypesWithFieldOfSameName() { SampleMaps sample = new SampleMaps(); - sample.good = new OrderRetainingMap(); + sample.good = new LinkedHashMap(); sample.good.put("iPhone", new Product("iPhone", "i", 399.99)); sample.good.put("Linux", new Software("Red Hat", "Linux")); sample.good.put("Intel", new Hardware("i386", "Intel")); @@ -511,9 +692,9 @@ public void testSeparatesItemsBasedOnItemName() { SampleMaps sample = new SampleMaps(); - sample.good = new OrderRetainingMap(); + sample.good = new LinkedHashMap(); sample.good.put("Chrome", new Software("Google", "Chrome")); - sample.bad = new OrderRetainingMap(); + sample.bad = new LinkedHashMap(); sample.bad.put("Linux", new Software("Red Hat", "Linux")); sample.bad.put("Windows", new Software("Microsoft", "Windows")); @@ -540,7 +721,7 @@ public void testWithoutKeyField() { SampleMaps sample = new SampleMaps(); - sample.good = new OrderRetainingMap(); + sample.good = new LinkedHashMap(); sample.good.put("Windows", new Software("Microsoft", "Windows")); sample.good.put("Linux", new Software("Red Hat", "Linux")); @@ -569,7 +750,7 @@ public void testCanUsePrimitiveAsKey() { SampleMaps sample = new SampleMaps(); - sample.good = new OrderRetainingMap(); + sample.good = new LinkedHashMap(); sample.good.put(new Double(399.99), new Product("iPhone", "i", 399.99)); String expected = "" + diff --git a/xstream/src/test/com/thoughtworks/acceptance/ImplicitTest.java b/xstream/src/test/com/thoughtworks/acceptance/ImplicitTest.java index fa04248..94b3439 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/ImplicitTest.java +++ b/xstream/src/test/com/thoughtworks/acceptance/ImplicitTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 XStream Committers. + * Copyright (C) 2013, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -11,6 +11,7 @@ package com.thoughtworks.acceptance; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -141,6 +142,10 @@ c.val = new Integer(6); implicits.cMap.put(c.val, c); assertBothWays(implicits, expected); + implicits.separator1 = implicits.separator2 = null; + assertBothWays(implicits, stripSeparator(expected)); + implicits.separator1 = implicits.separator2 = null; + assertBothWays(implicits, stripSeparator(expected)); } public void testAllImplicitTypesAtOnceWithExplicitElementNames() @@ -190,5 +195,118 @@ c.val = new Integer(6); implicits.cMap.put(c.val, c); assertBothWays(implicits, expected); + implicits.separator1 = implicits.separator2 = null; + assertBothWays(implicits, stripSeparator(expected)); + } + + private String stripSeparator(String s) { + return s.replaceAll(" *\n" + + " \n" + + " 1\n" + + " \n" + + " \n" + + " 2\n" + + " \n" + + " --1--\n" + + " \n" + + " 3\n" + + " \n" + + " \n" + + " 4\n" + + " \n" + + " --2--\n" + + " \n" + + " 5\n" + + " \n" + + " \n" + + " 6\n" + + " \n" + + " --X--\n" + + " a\n" + + " b\n" + + " c\n" + + " --H--\n" + + " \n" + + " 7\n" + + " \n" + + " \n" + + " 8\n" + + " \n" + + " \n" + + " 9\n" + + " \n" + + " \n" + + " 10\n" + + " \n" + + " \n" + + " 11\n" + + " \n" + + " \n" + + " 12\n" + + " \n" + + ""; + + AllHidingImplicitTypes implicits = new AllHidingImplicitTypes(); + ((AllImplicitTypes)implicits).aArray[0] = new AllImplicitTypes.A(); + ((AllImplicitTypes)implicits).aArray[0].val = 1; + ((AllImplicitTypes)implicits).aArray[1] = new AllImplicitTypes.A(); + ((AllImplicitTypes)implicits).aArray[1].val = 2; + implicits.aArray[0] = new AllImplicitTypes.A(); + implicits.aArray[0].val = 7; + implicits.aArray[1] = new AllImplicitTypes.A(); + implicits.aArray[1].val = 8; + ((AllImplicitTypes)implicits).bList.add(new AllImplicitTypes.B()); + ((AllImplicitTypes.B)((AllImplicitTypes)implicits).bList.get(0)).val = 3; + ((AllImplicitTypes)implicits).bList.add(new AllImplicitTypes.B()); + ((AllImplicitTypes.B)((AllImplicitTypes)implicits).bList.get(1)).val = 4; + implicits.bList.add(new AllImplicitTypes.B()); + ((AllImplicitTypes.B)implicits.bList.get(0)).val = 9; + implicits.bList.add(new AllImplicitTypes.B()); + ((AllImplicitTypes.B)implicits.bList.get(1)).val = 10; + AllImplicitTypes.C c = new AllImplicitTypes.C(); + c.val = new Integer(5); + ((AllImplicitTypes)implicits).cMap.put(c.val, c); + c = new AllImplicitTypes.C(); + c.val = new Integer(6); + ((AllImplicitTypes)implicits).cMap.put(c.val, c); + c = new AllImplicitTypes.C(); + c.val = new Integer(11); + implicits.cMap.put(c.val, c); + c = new AllImplicitTypes.C(); + c.val = new Integer(12); + implicits.cMap.put(c.val, c); + assertBothWays(implicits, expected); + implicits.separator1 = implicits.separator2 = ((AllHidingTypes)implicits).separator = implicits.separator = null; + assertBothWays(implicits, stripSeparator(expected)); } } diff --git a/xstream/src/test/com/thoughtworks/acceptance/InnerClassesTest.java b/xstream/src/test/com/thoughtworks/acceptance/InnerClassesTest.java index 1952400..f1114d8 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/InnerClassesTest.java +++ b/xstream/src/test/com/thoughtworks/acceptance/InnerClassesTest.java @@ -1,29 +1,31 @@ /* * Copyright (C) 2005, 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2014 XStream Committers. + * Copyright (C) 2006, 2007, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 31. January 2005 by Joe Walnes */ package com.thoughtworks.acceptance; + +import com.thoughtworks.xstream.core.JVM; public class InnerClassesTest extends AbstractAcceptanceTest { public void testSerializedInnerClassMaintainsReferenceToOuterClass() { xstream.allowTypes(new Class[]{Outer.class, Outer.Inner.class}); - Outer outer = new Outer("THE-OUTER-NAME", "THE-INNER-NAME"); - Outer.Inner inner = outer.getInner(); + final Outer outer = new Outer("THE-OUTER-NAME", "THE-INNER-NAME"); + final Outer.Inner inner = outer.getInner(); assertEquals("Hello from THE-INNER-NAME (inside THE-OUTER-NAME)", inner.getMessage()); - String xml = xstream.toXML(inner); + final String xml = xstream.toXML(inner); - String expectedXml = "" + final String expectedXml = "" + "\n" + " THE-INNER-NAME\n" + " \n" @@ -33,18 +35,100 @@ + ""; assertEquals(expectedXml, xml); - Outer.Inner newInner = (Outer.Inner) xstream.fromXML(xml); + final Outer.Inner newInner = (Outer.Inner)xstream.fromXML(xml); assertEquals("Hello from THE-INNER-NAME (inside THE-OUTER-NAME)", newInner.getMessage()); + } + + public static class OuterType { + private final String outerName = "Outer Name"; + public InnerType inner = new InnerType(); + private final InnerType.Dynamic1 dyn1 = inner.new Dynamic1(); + private final InnerType.Dynamic1.Dynamic2 dyn2 = dyn1.new Dynamic2(); + private final InnerType.Dynamic3 dyn3 = inner.new Dynamic3(dyn1); + + public class InnerType { + private final String innerName = "Inner Name"; + + public class Dynamic1 { + private final String name1 = "Name 1"; + + public class Dynamic2 { + private final String name2 = "Name 2"; + } + } + + public class Dynamic3 extends Dynamic1.Dynamic2 { + private final String name3 = "Name 3"; + final Dynamic1.Dynamic2 dyn4; + + public Dynamic3(final Dynamic1 outer) { + outer.super(); + class Dynamic4 extends Dynamic1.Dynamic2 { + private final String name4 = "Name 4"; + private final Dynamic5 dyn5 = new Dynamic5(); + class Dynamic5 { + private final String name5 = "Name 5"; + } + Dynamic4(Dynamic1 outer) { + outer.super(); + } + } + dyn4 = new Dynamic4(outer); + } + } + } + } + + public void testNestedDynamicTypes() { + final OuterType outer = new OuterType(); + + xstream.alias("inner", OuterType.InnerType.class); + xstream.alias("Dynamic4", outer.dyn3.dyn4.getClass()); + + String expectedXml = "" + + "\n" + + " Inner Name\n" + + " \n" + + " Outer Name\n" + + " \n" + + " \n" + + " Name 1\n" + + " \n" + + " \n" + + " \n" + + " Name 2\n" + + " \n" + + " \n" + + " \n" + + " Name 2\n" + + " \n" + + " Name 3\n" + + " \n" + + " Name 2\n" + + " \n" + + " Name 4\n" + + " \n" + + " Name 5\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + assertBothWays(outer.inner, expectedXml); } } class Outer { - private Inner inner; - private String outerName; + private final Inner inner; + private final String outerName; - public Outer(String outerName, String innerName) { + public Outer(final String outerName, final String innerName) { inner = new Inner(innerName); this.outerName = outerName; } @@ -54,9 +138,9 @@ } public class Inner { - private String innerName; + private final String innerName; - public Inner(String innerName) { + public Inner(final String innerName) { this.innerName = innerName; } @@ -65,5 +149,3 @@ } } } - - diff --git a/xstream/src/test/com/thoughtworks/acceptance/NamedLocalElementsTest.java b/xstream/src/test/com/thoughtworks/acceptance/NamedLocalElementsTest.java index 94b92c6..7716115 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/NamedLocalElementsTest.java +++ b/xstream/src/test/com/thoughtworks/acceptance/NamedLocalElementsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 XStream Committers. + * Copyright (C) 2013, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -11,9 +11,13 @@ package com.thoughtworks.acceptance; import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.TimeZone; import com.thoughtworks.acceptance.objects.Category; import com.thoughtworks.acceptance.objects.SampleMaps; @@ -384,20 +388,24 @@ public void testMapElementsUsingAttributeAndText() { xstream.registerLocalConverter( SampleMaps.class, "good", new NamedMapConverter( - xstream.getMapper(), "product", "name", String.class, null, String.class, + xstream.getMapper(), "product", "name", String.class, null, Date.class, true, false, xstream.getConverterLookup())); - - SampleMaps maps = new SampleMaps(); - maps.bad = null; - maps.good = new LinkedHashMap(); - maps.good.put("SiteMesh", "com.opensymphony"); - maps.good.put("XStream", "com.thoughtworks"); - - String expected = ("" - + "\n" - + " \n" - + " com.opensymphony\n" - + " com.thoughtworks\n" + + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.GERMANY); + cal.clear(); + cal.set(2016, Calendar.FEBRUARY, 8, 20, 11, 10); + SampleMaps maps = new SampleMaps(); + maps.bad = null; + maps.good = new LinkedHashMap(); + maps.good.put("SiteMesh", cal.getTime()); + cal.add(Calendar.DAY_OF_MONTH, 1); + maps.good.put("XStream", cal.getTime()); + + String expected = ("" + + "\n" + + " \n" + + " 2016-02-08 20:11:10.0 UTC\n" + + " 2016-02-09 20:11:10.0 UTC\n" + " \n" + "").replace('\'', '"'); diff --git a/xstream/src/test/com/thoughtworks/acceptance/SecurityManagerTest.java b/xstream/src/test/com/thoughtworks/acceptance/SecurityManagerTest.java index b0beced..6a66799 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/SecurityManagerTest.java +++ b/xstream/src/test/com/thoughtworks/acceptance/SecurityManagerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2009, 2010, 2013 XStream Committers. + * Copyright (C) 2006, 2007, 2009, 2010, 2013, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -184,6 +184,7 @@ sm.addPermission(source, new PropertyPermission("jdk.xml.maxGeneralEntitySizeLimit", "read")); sm.addPermission(source, new PropertyPermission("jdk.xml.maxParameterEntitySizeLimit", "read")); sm.addPermission(source, new PropertyPermission("jdk.xml.maxOccurLimit", "read")); + sm.addPermission(source, new PropertyPermission("jdk.xml.maxXMLNameLimit", "read")); sm.addPermission(source, new PropertyPermission("jdk.xml.totalEntitySizeLimit", "read")); sm.addPermission(source, new PropertyPermission("maxOccurLimit", "read")); sm.addPermission(source, new PropertyPermission("sun.boot.class.path", "read")); diff --git a/xstream/src/test/com/thoughtworks/acceptance/XStreamer.xsl b/xstream/src/test/com/thoughtworks/acceptance/XStreamer.xsl index b2200b4..467d8ad 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/XStreamer.xsl +++ b/xstream/src/test/com/thoughtworks/acceptance/XStreamer.xsl @@ -1,6 +1,6 @@ - + diff --git a/xstream/src/test/com/thoughtworks/acceptance/annotations/FieldConverterTest.java b/xstream/src/test/com/thoughtworks/acceptance/annotations/FieldConverterTest.java index 06d8c3b..1e99b7d 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/annotations/FieldConverterTest.java +++ b/xstream/src/test/com/thoughtworks/acceptance/annotations/FieldConverterTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -12,13 +12,16 @@ package com.thoughtworks.acceptance.annotations; import com.thoughtworks.acceptance.AbstractAcceptanceTest; +import com.thoughtworks.xstream.InitializationException; import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.XStreamException; import com.thoughtworks.xstream.annotations.XStreamAsAttribute; import com.thoughtworks.xstream.annotations.XStreamConverter; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.SingleValueConverter; import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.converters.enums.EnumToStringConverter; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; @@ -230,4 +233,24 @@ assertEquals(before + 1, after); } + @XStreamConverter(EnumToStringConverter.class) + public static class InvalidForConverter { + } + + public void testCausingExceptionIsNotSuppressed() { + try { + toXML(new InvalidForConverter()); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + Throwable th = e.getCause(); + for(;;) { + th = th.getCause(); + assertNotNull("No causing InitializationException.", th); + if (th instanceof InitializationException) { + assertTrue("No hint for enum types only", th.getMessage().indexOf(" enum ") >= 0); + break; + } + } + } + } } diff --git a/xstream/src/test/com/thoughtworks/acceptance/annotations/ParametrizedConverterTest.java b/xstream/src/test/com/thoughtworks/acceptance/annotations/ParametrizedConverterTest.java index 954a778..8388bab 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/annotations/ParametrizedConverterTest.java +++ b/xstream/src/test/com/thoughtworks/acceptance/annotations/ParametrizedConverterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2011, 2012, 2013 XStream Committers. + * Copyright (C) 2008, 2009, 2011, 2012, 2013, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -11,8 +11,11 @@ package com.thoughtworks.acceptance.annotations; import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import com.thoughtworks.acceptance.AbstractAcceptanceTest; @@ -24,6 +27,7 @@ import com.thoughtworks.xstream.annotations.XStreamInclude; import com.thoughtworks.xstream.converters.basic.BooleanConverter; import com.thoughtworks.xstream.converters.collections.MapConverter; +import com.thoughtworks.xstream.converters.extended.NamedCollectionConverter; import com.thoughtworks.xstream.converters.extended.NamedMapConverter; import com.thoughtworks.xstream.converters.extended.ToAttributedValueConverter; import com.thoughtworks.xstream.converters.extended.ToStringConverter; @@ -53,9 +57,13 @@ xstream.alias("decimal", Decimal.class); xstream.alias("type", Type.class); xstream.processAnnotations(MyMap.class); + xstream.processAnnotations(MyType.class); xstream.processAnnotations(DerivedType.class); + xstream.processAnnotations(DerivedType2.class); xstream.processAnnotations(SimpleBean.class); xstream.processAnnotations(ContainsMap.class); + xstream.processAnnotations(ContainsMap2.class); + xstream.processAnnotations(ContainsCollection.class); } public void testAnnotationForConvertersWithParameters() { @@ -141,6 +149,20 @@ } } + public void testConverterRequiringNull() { + final Type value = new DerivedType(new Decimal("1.5"), new Boolean(true), DerivedType.E.FOO); + String expected = "1.5".replace('\'', '"'); + assertBothWays(value, expected); + } + + @XStreamAlias("mytype") + @XStreamConverter(value=ToAttributedValueConverter.class, types={Type.class}, nulls={String.class}) + public static class MyType extends Type { + public MyType(Decimal decimal, Boolean bool) { + super(decimal, bool); + } + } + public void testConverterWithSecondTypeParameter() { final Type value = new DerivedType(new Decimal("1.5"), new Boolean(true), DerivedType.E.FOO); String expected = "1.5".replace('\'', '"'); @@ -155,6 +177,25 @@ private E e; public DerivedType(Decimal decimal, Boolean bool, E e) { + super(decimal, bool); + this.e = e; + } + } + + public void testConverterWithAllAttributes() { + final Type value = new DerivedType2(new Decimal("1.5"), new Boolean(true), DerivedType2.E.FOO); + String expected = "".replace('\'', '"'); + assertBothWays(value, expected); + } + + @XStreamAlias("dtype2") + @XStreamConverter(value=ToAttributedValueConverter.class) + public static class DerivedType2 extends Type { + public enum E { FOO, BAR }; + @XStreamAlias("enum") + private E e; + + public DerivedType2(Decimal decimal, Boolean bool, E e) { super(decimal, bool); this.e = e; } @@ -214,8 +255,62 @@ this.map = map; } } + + public void testAnnotatedNamedMapConverterWithMultipleSameArguments() { + xstream.addDefaultImplementation(LinkedHashMap.class, Map.class); + + final Map map = new LinkedHashMap(); + map.put("FOO", "foo"); + map.put("BAR", "bar"); + final ContainsMap2 value = new ContainsMap2(map); + String expected = ("" + + "\n" + + " \n" + + " FOO\n" + + " foo\n" + + " BAR\n" + + " bar\n" + + " \n" + + "").replace('\'', '"'); + assertBothWays(value, expected); + } + + @XStreamAlias("container-map") + public static class ContainsMap2 extends StandardObject { + @XStreamConverter(value = NamedMapConverter.class, strings = {"", "key", "value"}, types = { + LinkedHashMap.class, String.class, String.class}, booleans = {false, false}, useImplicitType = false) + private Map map; + + public ContainsMap2(Map map) { + this.map = map; + } + } @XStreamAlias("my-enums") public static class MyEnumMap extends LinkedHashMap { } + + public void testAnnotatedNamedCollectionConverter() { + List names = new ArrayList(Arrays.asList("joe", "joerg", "mauro")); + final ContainsCollection container = new ContainsCollection(names); + String expected = ("" + + "\n" + + " \n" + + " joe\n" + + " joerg\n" + + " mauro\n" + + " \n" + + "").replace('\'', '"'); + assertBothWays(container, expected); + } + + @XStreamAlias("CollCont") + public static class ContainsCollection extends StandardObject { + @XStreamConverter(value=NamedCollectionConverter.class, strings={"name"}, types={String.class}, useImplicitType = false) + private List names; + + public ContainsCollection(List names) { + this.names = names; + } + } } diff --git a/xstream/src/test/com/thoughtworks/acceptance/someobjects/Handler.java b/xstream/src/test/com/thoughtworks/acceptance/someobjects/Handler.java index a7654d6..030f594 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/someobjects/Handler.java +++ b/xstream/src/test/com/thoughtworks/acceptance/someobjects/Handler.java @@ -16,7 +16,7 @@ * * @author Jason van Zyl * - * @version $Id: Handler.java 2034 2013-03-12 22:15:00Z joehni $ + * @version $Id$ */ public class Handler { diff --git a/xstream/src/test/com/thoughtworks/acceptance/someobjects/HandlerManager.java b/xstream/src/test/com/thoughtworks/acceptance/someobjects/HandlerManager.java index 3896a74..aae0187 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/someobjects/HandlerManager.java +++ b/xstream/src/test/com/thoughtworks/acceptance/someobjects/HandlerManager.java @@ -18,7 +18,7 @@ * * @author Jason van Zyl * - * @version $Id: HandlerManager.java 1345 2007-12-11 01:50:12Z joehni $ + * @version $Id$ */ public class HandlerManager { diff --git a/xstream/src/test/com/thoughtworks/acceptance/someobjects/Protocol.java b/xstream/src/test/com/thoughtworks/acceptance/someobjects/Protocol.java index 1ece4f8..a6e0dd6 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/someobjects/Protocol.java +++ b/xstream/src/test/com/thoughtworks/acceptance/someobjects/Protocol.java @@ -16,7 +16,7 @@ * * @author Jason van Zyl * - * @version $Id: Protocol.java 2034 2013-03-12 22:15:00Z joehni $ + * @version $Id$ */ public class Protocol { diff --git a/xstream/src/test/com/thoughtworks/xstream/converters/ConversionExceptionTest.java b/xstream/src/test/com/thoughtworks/xstream/converters/ConversionExceptionTest.java index 2536b8a..256acf9 100644 --- a/xstream/src/test/com/thoughtworks/xstream/converters/ConversionExceptionTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/converters/ConversionExceptionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -43,6 +43,7 @@ StringTokenizer tokenizer = new StringTokenizer(ex.getMessage(), "\n\r"); tokenizer.nextToken(); tokenizer.nextToken(); + assertEquals("message : Message", tokenizer.nextToken()); assertEquals("1st : first", tokenizer.nextToken()); assertEquals("2nd : second", tokenizer.nextToken()); assertEquals("3rd : third", tokenizer.nextToken()); diff --git a/xstream/src/test/com/thoughtworks/xstream/converters/extended/ActivationDataFlavorConverterTest.java b/xstream/src/test/com/thoughtworks/xstream/converters/extended/ActivationDataFlavorConverterTest.java new file mode 100644 index 0000000..043c4c7 --- /dev/null +++ b/xstream/src/test/com/thoughtworks/xstream/converters/extended/ActivationDataFlavorConverterTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 XStream Committers. + * All rights reserved. + * + * The software in this package is published under the terms of the BSD + * style license a copy of which has been included with this distribution in + * the LICENSE.txt file. + * + * Created on 23.06.2015 by Joerg Schaible + */ +package com.thoughtworks.xstream.converters.extended; + +import java.awt.datatransfer.DataFlavor; + +import javax.activation.ActivationDataFlavor; + +import com.thoughtworks.acceptance.AbstractAcceptanceTest; +import com.thoughtworks.xstream.XStream; + + +public class ActivationDataFlavorConverterTest extends AbstractAcceptanceTest { + + protected void setupSecurity(XStream xstream) { + super.setupSecurity(xstream); + xstream.allowTypeHierarchy(DataFlavor.class); + } + + public void testMimeTypeOnly() { + final String expected = "" + + "\n" + + " application/x-junit\n" + + " java.io.InputStream\n" + + ""; + assertBothWays(new ActivationDataFlavor("application/x-junit", null), expected); + } + + public void testMimeTypeAndRepresentation() { + final String expected = "" + + "\n" + + " application/x-junit\n" + + " JUnit\n" + + " java.io.InputStream\n" + + ""; + assertBothWays(new ActivationDataFlavor("application/x-junit", "JUnit"), expected); + } + + public void testWithAllArguments() { + final String expected = "" + + "\n" + + " application/x-junit\n" + + " JUnit\n" + + " com.thoughtworks.xstream.converters.extended.ActivationDataFlavorConverterTest\n" + + ""; + assertBothWays(new ActivationDataFlavor(ActivationDataFlavorConverterTest.class, "application/x-junit", "JUnit"), expected); + } +} diff --git a/xstream/src/test/com/thoughtworks/xstream/converters/extended/ToAttributedValueConverterTest.java b/xstream/src/test/com/thoughtworks/xstream/converters/extended/ToAttributedValueConverterTest.java index c7703ab..354f23b 100644 --- a/xstream/src/test/com/thoughtworks/xstream/converters/extended/ToAttributedValueConverterTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/converters/extended/ToAttributedValueConverterTest.java @@ -47,7 +47,7 @@ /** * Tests {@link ToAttributedValueConverter}. * - * @author jos / last modified by $Author: joehni $ + * @author jos / last modified by $Author$ */ public class ToAttributedValueConverterTest extends TestCase { private HierarchicalStreamDriver driver; diff --git a/xstream/src/test/com/thoughtworks/xstream/converters/javabean/JavaBeanConverterTest.java b/xstream/src/test/com/thoughtworks/xstream/converters/javabean/JavaBeanConverterTest.java index 6f69c15..3dd7932 100644 --- a/xstream/src/test/com/thoughtworks/xstream/converters/javabean/JavaBeanConverterTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/converters/javabean/JavaBeanConverterTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2010, 2011, 2013 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2010, 2011, 2013, 2014, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -201,6 +201,42 @@ assertEquals(expected, result); } + public void testSerializesNullValue() { + World world = new World(); + world.setAString(null); + + XStream xstream = new XStream(); + xstream.registerConverter(new JavaBeanConverter(xstream.getMapper(), new BeanProvider( + new StringComparator())), XStream.PRIORITY_LOW); + xstream.alias("world", World.class); + + String expected = "" + + "\n" + + " true\n" + + " false\n" + + " 4\n" + + " 5\n" + + " a\n" + + " w\n" + + " 8.0\n" + + " 9.0\n" + + " 10\n" + + " 11\n" + + " 1\n" + + " 2\n" + + " 6\n" + + " 7\n" + + " \n" + + ""; + + String result = xstream.toXML(world); + + assertEquals(expected, result); + + World world2 = (World) xstream.fromXML(result); + assertEquals(null, world2.getAString()); + } + /** * Only normal and trans are serializable properties, the field modifiers do not matter */ diff --git a/xstream/src/test/com/thoughtworks/xstream/converters/reflection/FieldDictionaryTest.java b/xstream/src/test/com/thoughtworks/xstream/converters/reflection/FieldDictionaryTest.java index 3243389..a857d39 100644 --- a/xstream/src/test/com/thoughtworks/xstream/converters/reflection/FieldDictionaryTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/converters/reflection/FieldDictionaryTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -15,7 +15,14 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; +import java.util.List; + +import com.thoughtworks.xstream.core.JVM; public class FieldDictionaryTest extends TestCase { @@ -78,4 +85,115 @@ } return field.getName(); } + + private static class AssertNoDuplicateHashMap extends HashMap { + public Object put(final Object key, final Object value) { + assertFalse("Attempt to insert duplicate key: " + key, containsKey(key)); + return super.put(key, value); + } + } + + static class A { String a; } + static class B extends A { String b; } + static class C extends B { String c; } + static class D extends C { String d; } + static class E extends D { String e; } + static class F extends E { String f; } + static class G extends F { String g; } + static class H extends G { String h; } + static class I extends H { String i; } + static class J extends I { String j; } + static class BB extends B { String b; } + static class CC extends C { String c; } + static class DD extends D { String d; } + static class EE extends E { String e; } + static class FF extends F { String f; } + static class GG extends G { String g; } + static class HH extends H { String h; } + static class II extends I { String i; } + static class JJ extends J { String j; } + static class JJJ extends JJ { String j; } + + public void testSynchronizedAccessShouldEnsureEachClassAddedOnceToCache() throws Exception { + AssertNoDuplicateHashMap assertNoDuplicateHashMap = new AssertNoDuplicateHashMap(); + + Field field = FieldDictionary.class.getDeclaredField("dictionaryEntries"); + field.setAccessible(true); + field.set(fieldDictionary, assertNoDuplicateHashMap); + + final List exceptions = Collections.synchronizedList(new ArrayList()); + + final List types = + Arrays.asList(new Class[] { + A.class, B.class, C.class, E.class, F.class, G.class, H.class, I.class, J.class, + BB.class, CC.class, DD.class, EE.class, FF.class, GG.class, HH.class, II.class, JJ.class, + JJJ.class, FieldDictionaryTest.class + }); + final Thread[] threads = createThreads(types, exceptions); + + for (int i = 0; i < threads.length; ++i) { + synchronized (threads[i]) { + threads[i].start(); + threads[i].wait(); + } + } + + for (int i = 0; i < threads.length; ++i) { + synchronized (threads[i]) { + threads[i].notifyAll(); + } + } + + Thread.sleep(1500); + + for (int i = 0; i < threads.length; ++i) { + threads[i].interrupt(); + } + for (int i = 0; i < threads.length; ++i) { + synchronized (threads[i]) { + threads[i].join(); + } + } + + assertEquals("Assertions failed or exceptions thrown", Collections.EMPTY_LIST, exceptions); + } + + private Thread[] createThreads(final List types, final List exceptions) { + Collections.shuffle(types); + final Thread[] threads = new Thread[types.size()]; + for (int i = 0; i < types.size(); i++) { + final Class type = (Class)types.get(i); + threads[i] = new Thread() { + public void run() { + try { + synchronized (this) { + notifyAll(); + wait(); + } + final Iterator fieldIterator = fieldDictionary.fieldsFor(type); + int fieldCount = 0; + while (fieldIterator.hasNext()) { + Field field = (Field)fieldIterator.next(); + if (JVM.is15() || !Modifier.isStatic(field.getModifiers())) { + fieldCount++; + } + } + + if (type == FieldDictionaryTest.class) { + assertEquals("fieldCount not equal for type " + type.getName(), 2, fieldCount); + } else { + int count = 0; + for(Class cls = type; cls != null; count++, cls = cls.getSuperclass()); + assertEquals("fieldCount not equal for type " + type.getName(), count-1, fieldCount); + } + } catch (final Exception e) { + exceptions.add(e); + } catch (final Error e) { + exceptions.add(e); + } + } + }; + } + return threads; + } } diff --git a/xstream/src/test/com/thoughtworks/xstream/converters/reflection/SortableFieldKeySorterTest.java b/xstream/src/test/com/thoughtworks/xstream/converters/reflection/SortableFieldKeySorterTest.java index a69d226..914ef3d 100644 --- a/xstream/src/test/com/thoughtworks/xstream/converters/reflection/SortableFieldKeySorterTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/converters/reflection/SortableFieldKeySorterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 XStream Committers. + * Copyright (C) 2007, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -13,8 +13,8 @@ import java.lang.reflect.Field; import java.util.Map; +import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.core.util.OrderRetainingMap; -import com.thoughtworks.xstream.io.StreamException; import junit.framework.TestCase; @@ -49,8 +49,8 @@ try { sorter.sort(Base.class, buildMap(Base.class)); fail(); - } catch (StreamException ex) { - // ok + } catch (ConversionException ex) { + assertEquals(Base.class.getName(), ex.get("sort-type")); } } diff --git a/xstream/src/test/com/thoughtworks/xstream/core/ReferenceByXPathMarshallingStrategyTest.java b/xstream/src/test/com/thoughtworks/xstream/core/ReferenceByXPathMarshallingStrategyTest.java index 8e42bf1..3d19dad 100644 --- a/xstream/src/test/com/thoughtworks/xstream/core/ReferenceByXPathMarshallingStrategyTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/core/ReferenceByXPathMarshallingStrategyTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -14,9 +14,19 @@ import com.thoughtworks.acceptance.AbstractAcceptanceTest; import com.thoughtworks.acceptance.objects.StandardObject; import com.thoughtworks.xstream.XStream; - +import com.thoughtworks.xstream.converters.ConverterLookup; +import com.thoughtworks.xstream.core.util.ObjectIdDictionary; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +import com.thoughtworks.xstream.io.path.Path; +import com.thoughtworks.xstream.mapper.Mapper; + +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; import java.util.ArrayList; import java.util.List; +import java.util.Map; public class ReferenceByXPathMarshallingStrategyTest extends AbstractAcceptanceTest { @@ -88,4 +98,135 @@ assertBothWays(list, expected); } + + public class CountingXPathStrategy extends ReferenceByXPathMarshallingStrategy{ + + public CountingXPathStrategy() { + super(ReferenceByXPathMarshallingStrategy.ABSOLUTE); + } + + public ReferenceByXPathMarshaller requestedMarshaller; + public ReferenceByXPathUnmarshaller requestedUnmarshaller; + + protected TreeUnmarshaller createUnmarshallingContext(Object root, + HierarchicalStreamReader reader, + ConverterLookup converterLookup, + Mapper mapper) { + + assertNull("strategy can only make one unmarshaller", requestedUnmarshaller); + requestedUnmarshaller = (ReferenceByXPathUnmarshaller) super.createUnmarshallingContext(root, reader, converterLookup, mapper); + return requestedUnmarshaller; + } + + protected TreeMarshaller createMarshallingContext(HierarchicalStreamWriter writer, + ConverterLookup converterLookup, + Mapper mapper) { + + assertNull("strategy can only make one marshaller", requestedMarshaller); + requestedMarshaller = (ReferenceByXPathMarshaller) super.createMarshallingContext(writer, converterLookup, mapper); + return requestedMarshaller; + } + } + + public void testDoNotKeepXPathMapForImmutablesOnMarshall() throws MalformedURLException { + //configure XStream + CountingXPathStrategy marshallingStrategy = new CountingXPathStrategy(); + xstream.setMarshallingStrategy(marshallingStrategy); + + //setup document + List list = new ArrayList(); + URL url = new URL("http://jira.codehaus.org/browse"); + list.add(url); + list.add(url); + + //act + String serialized = xstream.toXML(list); + + //assert + ObjectIdDictionary trackedPathsOnMarshal = getReferences(marshallingStrategy.requestedMarshaller); + + assertTrue(trackedPathsOnMarshal.containsId(list)); + assertEquals(1, trackedPathsOnMarshal.size()); + } + + public void testDoNotKeepXPathMapForImmutablesOnUnmarshall() { + //configure XStream + CountingXPathStrategy marshallingStrategy = new CountingXPathStrategy(); + xstream.setMarshallingStrategy(marshallingStrategy); + + //setup document + String document = "" + + "" + + " http://jira.codehaus.org/browse" + + " http://jira.codehaus.org/browse" + + ""; + + //act + Object result = xstream.fromXML(document); + + //assert + Map trackedPathsOnUnmarshal = getReferences(marshallingStrategy.requestedUnmarshaller); + + assertTrue(trackedPathsOnUnmarshal.containsKey(new Path("/list"))); + assertEquals(1, trackedPathsOnUnmarshal.size()); + } + + public static class DomainType extends StandardObject{ + public String value; + + public DomainType(String value){ + this.value = value; + } + } + + public void testDoesKeepXPathMapForBackwardsCompatibleImmutablesOnUnmarshall() { + //configure XStream + CountingXPathStrategy marshallingStrategy = new CountingXPathStrategy(); + xstream.setMarshallingStrategy(marshallingStrategy); + xstream.addImmutableType(Thing.class, true); + + //setup document + String document = "" + + "" + + " " + + " JUnit" + + " " + + " " + + " JUnit" + + " " + + ""; + + //act + Object result = xstream.fromXML(document); + + //assert + Map trackedPathsOnUnmarshal = getReferences(marshallingStrategy.requestedUnmarshaller); + + assertTrue(trackedPathsOnUnmarshal.containsKey(new Path("/list"))); + assertTrue(trackedPathsOnUnmarshal.containsKey(new Path("/list/thing"))); + assertTrue(trackedPathsOnUnmarshal.containsKey(new Path("/list/thing[2]"))); + assertEquals(3, trackedPathsOnUnmarshal.size()); + } + + private Map getReferences(ReferenceByXPathUnmarshaller requestedUnmarshaller) { + try { + Field field = AbstractReferenceUnmarshaller.class.getDeclaredField("values"); + field.setAccessible(true); + return (Map) field.get(requestedUnmarshaller); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + private ObjectIdDictionary getReferences(ReferenceByXPathMarshaller requestedMarshaller) { + try { + Field field = AbstractReferenceMarshaller.class.getDeclaredField("references"); + field.setAccessible(true); + return (ObjectIdDictionary) field.get(requestedMarshaller); + } + catch(Exception e){ + throw new RuntimeException(e); + } + } } diff --git a/xstream/src/test/com/thoughtworks/xstream/core/util/DependencyInjectionFactoryTest.java b/xstream/src/test/com/thoughtworks/xstream/core/util/DependencyInjectionFactoryTest.java index de7c425..6afa791 100644 --- a/xstream/src/test/com/thoughtworks/xstream/core/util/DependencyInjectionFactoryTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/core/util/DependencyInjectionFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2009, 2010, 2011, 2012 XStream Committers. + * Copyright (C) 2007, 2009, 2010, 2011, 2012, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -10,6 +10,7 @@ */ package com.thoughtworks.xstream.core.util; +import com.thoughtworks.xstream.converters.ErrorWriter; import com.thoughtworks.xstream.converters.reflection.ObjectAccessException; import junit.framework.TestCase; @@ -24,8 +25,8 @@ ObjectAccessException.class, new Object[]{ "The message", this, new RuntimeException("JUnit")}, used); assertTrue(exception instanceof ObjectAccessException); - assertEquals("The message : JUnit", exception.getMessage()); - assertEquals("JUnit", ((ObjectAccessException)exception).getCause().getMessage()); + assertEquals("The message", ((ErrorWriter)exception).get("message")); + assertEquals("JUnit", ((ErrorWriter)exception).get("cause-message")); assertTrue(used.get(0)); assertFalse(used.get(1)); assertTrue(used.get(2)); @@ -45,7 +46,8 @@ ObjectAccessException.class, new Object[]{ new TypedNull(String.class), this, new RuntimeException("JUnit")}, used); assertTrue(exception instanceof ObjectAccessException); - assertEquals("null : JUnit", exception.getMessage()); + assertNull(((ErrorWriter)exception).get("message")); + assertEquals("JUnit", ((ErrorWriter)exception).get("cause-message")); assertTrue(used.get(0)); assertFalse(used.get(1)); assertTrue(used.get(2)); @@ -69,7 +71,8 @@ ObjectAccessException.class, new Object[]{ new RuntimeException("JUnit"), this, "The message"}, used); assertTrue(exception instanceof ObjectAccessException); - assertEquals("The message : JUnit", exception.getMessage()); + assertEquals("The message", ((ErrorWriter)exception).get("message")); + assertEquals("JUnit", ((ErrorWriter)exception).get("cause-message")); assertTrue(used.get(0)); assertFalse(used.get(1)); assertTrue(used.get(2)); @@ -82,7 +85,8 @@ new RuntimeException("JUnit"), new IllegalArgumentException("foo"), this, "The message"}, used); assertTrue(exception instanceof ObjectAccessException); - assertEquals("The message : foo", exception.getMessage()); + assertEquals("The message", ((ErrorWriter)exception).get("message")); + assertEquals("foo", ((ErrorWriter)exception).get("cause-message")); assertFalse(used.get(0)); assertTrue(used.get(1)); assertFalse(used.get(2)); @@ -96,7 +100,8 @@ new RuntimeException("JUnit"), "The message", "bar", new IllegalArgumentException("foo"), this}, used); assertTrue(exception instanceof ObjectAccessException); - assertEquals("The message : foo", exception.getMessage()); + assertEquals("The message", ((ErrorWriter)exception).get("message")); + assertEquals("foo", ((ErrorWriter)exception).get("cause-message")); assertFalse(used.get(0)); assertTrue(used.get(1)); assertFalse(used.get(2)); diff --git a/xstream/src/test/com/thoughtworks/xstream/io/binary/BinaryStreamTest.java b/xstream/src/test/com/thoughtworks/xstream/io/binary/BinaryStreamTest.java index ba347af..d34962b 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/binary/BinaryStreamTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/binary/BinaryStreamTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2011 XStream Committers. + * Copyright (C) 2006, 2007, 2011, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -11,6 +11,7 @@ */ package com.thoughtworks.xstream.io.binary; +import com.thoughtworks.xstream.XStreamException; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.copy.HierarchicalStreamCopier; @@ -76,4 +77,16 @@ } + public void testIsXXEVulnerableWithExternalGeneralEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalGeneralEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (message.indexOf("resolve entity") < 0) { + throw e; + } + } + } + } diff --git a/xstream/src/test/com/thoughtworks/xstream/io/copy/HierarchicalStreamCopierTest.java b/xstream/src/test/com/thoughtworks/xstream/io/copy/HierarchicalStreamCopierTest.java index e9a88ae..9f65b80 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/copy/HierarchicalStreamCopierTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/copy/HierarchicalStreamCopierTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2011 XStream Committers. + * Copyright (C) 2006, 2007, 2011, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -11,6 +11,7 @@ */ package com.thoughtworks.xstream.io.copy; +import com.thoughtworks.xstream.XStreamException; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.xml.AbstractXMLReaderTest; @@ -58,4 +59,16 @@ assertEquals(expected, buffer.toString()); } + public void testIsXXEVulnerableWithExternalGeneralEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalGeneralEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (message.indexOf("resolve entity") < 0) { + throw e; + } + } + } + } diff --git a/xstream/src/test/com/thoughtworks/xstream/io/xml/AbstractXMLReaderTest.java b/xstream/src/test/com/thoughtworks/xstream/io/xml/AbstractXMLReaderTest.java index 7f818ef..194e930 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/xml/AbstractXMLReaderTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/xml/AbstractXMLReaderTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2011, 2012, 2013 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2011, 2012, 2013, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -242,6 +242,31 @@ assertEquals(content, xmlReader.getValue()); } + public void testIsXXEVulnerableWithExternalGeneralEntity() throws Exception { + HierarchicalStreamReader xmlReader = createReader("" + + "\n" + +"\n" + +"\n" +// +"\n" +// +"\n" + +"]>&content;"); + assertEquals("", xmlReader.getValue()); + } + + public void testIsXXEVulnerableWithExternalParameterEntity() throws Exception { + HierarchicalStreamReader xmlReader = createReader("" + + "\n" + +"\n" + +"\n" +// +"\n" +// +"\n" + +"%content;\n" + +"]>test"); + assertEquals("test", xmlReader.getValue()); + } + // TODO: See XSTR-473 public void todoTestCanReadNullValueInString() throws Exception { HierarchicalStreamReader xmlReader = createReader(""); diff --git a/xstream/src/test/com/thoughtworks/xstream/io/xml/BEAStaxReaderTest.java b/xstream/src/test/com/thoughtworks/xstream/io/xml/BEAStaxReaderTest.java index cc2a10a..8a92e40 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/xml/BEAStaxReaderTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/xml/BEAStaxReaderTest.java @@ -1,27 +1,33 @@ /* - * Copyright (C) 2011 XStream Committers. + * Copyright (C) 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 30. September 2011 by Joerg Schaible */ package com.thoughtworks.xstream.io.xml; +import java.io.StringReader; + import com.thoughtworks.xstream.io.HierarchicalStreamDriver; import com.thoughtworks.xstream.io.HierarchicalStreamReader; -import java.io.StringReader; public class BEAStaxReaderTest extends AbstractXMLReaderTest { - - private HierarchicalStreamDriver driver = new BEAStaxDriver(); + + private final HierarchicalStreamDriver driver = new BEAStaxDriver(); // factory method - protected HierarchicalStreamReader createReader(String xml) throws Exception { + protected HierarchicalStreamReader createReader(final String xml) throws Exception { return driver.createReader(new StringReader(xml)); + } + + public void testIsXXEVulnerableWithExternalParameterEntity() throws Exception { + // Implementation ignores XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES set to false. + // super.testIsXXEVulnerableWithExternalParameterEntity(); } // inherits tests from superclass diff --git a/xstream/src/test/com/thoughtworks/xstream/io/xml/Dom4JReaderTest.java b/xstream/src/test/com/thoughtworks/xstream/io/xml/Dom4JReaderTest.java index a6a7ad7..5c69ee5 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/xml/Dom4JReaderTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/xml/Dom4JReaderTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -11,7 +11,11 @@ */ package com.thoughtworks.xstream.io.xml; +import com.thoughtworks.xstream.XStreamException; +import com.thoughtworks.xstream.core.JVM; import com.thoughtworks.xstream.io.HierarchicalStreamReader; + +import java.io.StringReader; import org.dom4j.Document; import org.dom4j.DocumentException; @@ -22,7 +26,7 @@ // factory method protected HierarchicalStreamReader createReader(String xml) throws Exception { - return new Dom4JReader(DocumentHelper.parseText(xml)); + return new Dom4JDriver().createReader(new StringReader(xml)); } public void testCanReadFromElementOfLargerDocument() throws DocumentException { @@ -42,4 +46,32 @@ assertEquals("tiny", xmlReader.getNodeName()); } + public void testIsXXEVulnerableWithExternalGeneralEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalGeneralEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (message.indexOf("DOCTYPE") < 0) { + if (JVM.is17() || (JVM.is16() && message.indexOf("Nested exception: null") < 0)) { + throw e; + } + } + } + } + + public void testIsXXEVulnerableWithExternalParameterEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalParameterEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (message.indexOf("DOCTYPE") < 0) { + if (JVM.is17() || (JVM.is16() && message.indexOf("Nested exception: null") < 0)) { + throw e; + } + } + } + } + } diff --git a/xstream/src/test/com/thoughtworks/xstream/io/xml/DomReaderTest.java b/xstream/src/test/com/thoughtworks/xstream/io/xml/DomReaderTest.java index ab94ebe..4ca3b89 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/xml/DomReaderTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/xml/DomReaderTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -11,6 +11,8 @@ */ package com.thoughtworks.xstream.io.xml; +import com.thoughtworks.xstream.XStreamException; +import com.thoughtworks.xstream.core.JVM; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import org.w3c.dom.Document; @@ -20,6 +22,7 @@ import javax.xml.parsers.DocumentBuilderFactory; import java.io.ByteArrayInputStream; +import java.io.StringReader; import java.util.HashMap; import java.util.Map; @@ -27,7 +30,7 @@ // factory method protected HierarchicalStreamReader createReader(String xml) throws Exception { - return new DomReader(buildDocument(xml)); + return new DomDriver().createReader(new StringReader(xml)); } private Document buildDocument(String xml) throws Exception { @@ -82,4 +85,44 @@ assertEquals(0, xmlReader.getAttributeCount()); } + public void testIsXXEVulnerableWithExternalGeneralEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalGeneralEntity(); + if (JVM.is16()) { + fail("Thrown " + XStreamException.class.getName() + " expected"); + } + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (message.indexOf("DOCTYPE") < 0) { + throw e; + } + } catch (final NullPointerException e) { + // NPE only with Sun Java 1.6 runtime + if (JVM.is17() || !JVM.is16()) { + throw e; + } + } + } + + public void testIsXXEVulnerableWithExternalParameterEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalParameterEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (message.indexOf("DOCTYPE") < 0) { + // XXE vulnerable with Sun Java 1.6 runtime + if (JVM.is16()) { + throw e; + } else { + System.err.println("DomReader is vulnerable with Java 5 and 1.4!"); + } + } + } catch (final NullPointerException e) { + // NPE only with Sun Java 1.6 runtime + if (JVM.is17() || !JVM.is16()) { + throw e; + } + } + } } diff --git a/xstream/src/test/com/thoughtworks/xstream/io/xml/JDom2ReaderTest.java b/xstream/src/test/com/thoughtworks/xstream/io/xml/JDom2ReaderTest.java index 8d763a4..c17630b 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/xml/JDom2ReaderTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/xml/JDom2ReaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 XStream Committers. + * Copyright (C) 2013, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -10,9 +10,12 @@ */ package com.thoughtworks.xstream.io.xml; +import com.thoughtworks.xstream.XStreamException; +import com.thoughtworks.xstream.core.JVM; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import org.jdom2.Document; +import org.jdom2.Element; import org.jdom2.input.SAXBuilder; import java.io.StringReader; @@ -21,8 +24,59 @@ // factory method protected HierarchicalStreamReader createReader(String xml) throws Exception { + return new JDom2Driver().createReader(new StringReader(xml)); + } + + public void testCanReadFromElementOfLargerDocument() throws Exception { + String xml ="" + + "" + + " " + + " " + + " " + + " " + + " " + + ""; Document document = new SAXBuilder().build(new StringReader(xml)); - return new JDom2Reader(document); + Element element = document.getRootElement().getChild("small"); + + HierarchicalStreamReader xmlReader = new JDom2Reader(element); + assertEquals("small", xmlReader.getNodeName()); + xmlReader.moveDown(); + assertEquals("tiny", xmlReader.getNodeName()); + } + + public void testIsXXEVulnerableWithExternalGeneralEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalGeneralEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (!message.contains("DOCTYPE")) { + throw e; + } + } catch (final NullPointerException e) { + // NPE only with Sun Java 1.6 runtime + if (JVM.is17()) { + throw e; + } + } + } + + public void testIsXXEVulnerableWithExternalParameterEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalParameterEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (!message.contains("DOCTYPE")) { + throw e; + } + } catch (final NullPointerException e) { + // NPE only with Sun Java 1.6 runtime + if (JVM.is17()) { + throw e; + } + } } // inherits tests from superclass diff --git a/xstream/src/test/com/thoughtworks/xstream/io/xml/JDomReaderTest.java b/xstream/src/test/com/thoughtworks/xstream/io/xml/JDomReaderTest.java index 506cb86..646669d 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/xml/JDomReaderTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/xml/JDomReaderTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -11,9 +11,12 @@ */ package com.thoughtworks.xstream.io.xml; +import com.thoughtworks.xstream.XStreamException; +import com.thoughtworks.xstream.core.JVM; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import org.jdom.Document; +import org.jdom.Element; import org.jdom.input.SAXBuilder; import java.io.StringReader; @@ -22,8 +25,59 @@ // factory method protected HierarchicalStreamReader createReader(String xml) throws Exception { + return new JDomDriver().createReader(new StringReader(xml)); + } + + public void testCanReadFromElementOfLargerDocument() throws Exception { + String xml ="" + + "" + + " " + + " " + + " " + + " " + + " " + + ""; Document document = new SAXBuilder().build(new StringReader(xml)); - return new JDomReader(document); + Element element = document.getRootElement().getChild("small"); + + HierarchicalStreamReader xmlReader = new JDomReader(element); + assertEquals("small", xmlReader.getNodeName()); + xmlReader.moveDown(); + assertEquals("tiny", xmlReader.getNodeName()); + } + + public void testIsXXEVulnerableWithExternalGeneralEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalGeneralEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (message.indexOf("DOCTYPE") < 0) { + throw e; + } + } catch (final NullPointerException e) { + // NPE only with Sun Java 1.6 runtime + if (JVM.is17()) { + throw e; + } + } + } + + public void testIsXXEVulnerableWithExternalParameterEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalParameterEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (message.indexOf("DOCTYPE") < 0) { + throw e; + } + } catch (final NullPointerException e) { + // NPE only with Sun Java 1.6 runtime + if (JVM.is17()) { + throw e; + } + } } // inherits tests from superclass diff --git a/xstream/src/test/com/thoughtworks/xstream/io/xml/KXml2ReaderTest.java b/xstream/src/test/com/thoughtworks/xstream/io/xml/KXml2ReaderTest.java index 8d7f8ae..0a65383 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/xml/KXml2ReaderTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/xml/KXml2ReaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 XStream Committers. + * Copyright (C) 2011, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -10,6 +10,7 @@ */ package com.thoughtworks.xstream.io.xml; +import com.thoughtworks.xstream.XStreamException; import com.thoughtworks.xstream.io.HierarchicalStreamDriver; import com.thoughtworks.xstream.io.HierarchicalStreamReader; @@ -28,5 +29,17 @@ return driver.createReader(new StringReader(xml)); } + public void testIsXXEVulnerableWithExternalGeneralEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalGeneralEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (message.indexOf("unresolved") < 0) { + throw e; + } + } + } + // inherits tests from superclass } diff --git a/xstream/src/test/com/thoughtworks/xstream/io/xml/SjsxpReaderTest.java b/xstream/src/test/com/thoughtworks/xstream/io/xml/SjsxpReaderTest.java index 46e0322..5361b85 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/xml/SjsxpReaderTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/xml/SjsxpReaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 XStream Committers. + * Copyright (C) 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -10,6 +10,8 @@ */ package com.thoughtworks.xstream.io.xml; +import com.thoughtworks.xstream.XStreamException; +import com.thoughtworks.xstream.core.JVM; import com.thoughtworks.xstream.io.HierarchicalStreamDriver; import com.thoughtworks.xstream.io.HierarchicalStreamReader; @@ -47,5 +49,12 @@ return driver.createReader(new StringReader(xml)); } + public void testIsXXEVulnerableWithExternalParameterEntity() throws Exception { + // Fails for Java 1.6 runtime + if (JVM.is17()) { + super.testIsXXEVulnerableWithExternalParameterEntity(); + } + } + // inherits tests from superclass } diff --git a/xstream/src/test/com/thoughtworks/xstream/io/xml/StandardStaxReaderTest.java b/xstream/src/test/com/thoughtworks/xstream/io/xml/StandardStaxReaderTest.java new file mode 100644 index 0000000..274881f --- /dev/null +++ b/xstream/src/test/com/thoughtworks/xstream/io/xml/StandardStaxReaderTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 XStream Committers. + * All rights reserved. + * + * The software in this package is published under the terms of the BSD + * style license a copy of which has been included with this distribution in + * the LICENSE.txt file. + * + * Created on 29. September 2015 by Joerg Schaible + */ +package com.thoughtworks.xstream.io.xml; + +import com.thoughtworks.xstream.core.JVM; +import com.thoughtworks.xstream.io.HierarchicalStreamDriver; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestResult; +import junit.framework.TestSuite; + +import java.io.StringReader; + +public class StandardStaxReaderTest extends AbstractXMLReaderTest { + + public static Test suite() { + if (JVM.is16()) { + return new TestSuite(StandardStaxReaderTest.class); + } else { + return new TestCase(StandardStaxReaderTest.class.getName() + ": not available") { + + public int countTestCases() { + return 1; + } + + public void run(TestResult result) { + } + }; + } + } + + private HierarchicalStreamDriver driver = new StandardStaxDriver(); + + // factory method + protected HierarchicalStreamReader createReader(String xml) throws Exception { + return driver.createReader(new StringReader(xml)); + } + + public void testIsXXEVulnerableWithExternalParameterEntity() throws Exception { + // fails for Sun JDK 1.6 runtime + if (JVM.is17() || !JVM.getStaxInputFactory().getName().equals("com.sun.xml.internal.stream.XMLInputFactoryImpl")) { + super.testIsXXEVulnerableWithExternalParameterEntity(); + } + } + + // inherits tests from superclass +} diff --git a/xstream/src/test/com/thoughtworks/xstream/io/xml/StaxReaderTest.java b/xstream/src/test/com/thoughtworks/xstream/io/xml/StaxReaderTest.java index 3fa489d..1bc7ab4 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/xml/StaxReaderTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/xml/StaxReaderTest.java @@ -1,24 +1,61 @@ /* * Copyright (C) 2004 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 29. September 2004 by James Strachan */ package com.thoughtworks.xstream.io.xml; +import java.io.StringReader; + +import com.thoughtworks.xstream.XStreamException; +import com.thoughtworks.xstream.core.JVM; import com.thoughtworks.xstream.io.HierarchicalStreamReader; -import java.io.StringReader; public class StaxReaderTest extends AbstractXMLReaderTest { - protected HierarchicalStreamReader createReader(String xml) throws Exception { - StaxDriver driver = new StaxDriver(); + protected HierarchicalStreamReader createReader(final String xml) throws Exception { + final StaxDriver driver = new StaxDriver(); return driver.createReader(new StringReader(xml)); + } + + public void testIsXXEVulnerableWithExternalGeneralEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalGeneralEntity(); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (message.indexOf("external entity") < 0) { + throw e; + } + } + } + + public void testIsXXEVulnerableWithExternalParameterEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalParameterEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (JVM.is14() && message == null) { + if (JVM.is16()) { + throw e; + } + System.err.println("BEAStaxReader was selected as default StAX driver for StaxReaderTest!"); + } else if (message.indexOf("external entity") < 0) { + if (JVM.is16() && message.indexOf("com.wutka.dtd.DTDParseException") >= 0) { + System.err.println("BEAStaxReader was selected as default StAX driver for StaxReaderTest!"); + } else if (message.replaceAll("[:space:]", "").endsWith("null")) { + System.err.println("BEAStaxReader was selected as default StAX driver for StaxReaderTest!"); + } else { + throw e; + } + } + } } // inherits tests from superclass diff --git a/xstream/src/test/com/thoughtworks/xstream/io/xml/WstxReaderTest.java b/xstream/src/test/com/thoughtworks/xstream/io/xml/WstxReaderTest.java index e486d51..eb42cd9 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/xml/WstxReaderTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/xml/WstxReaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 XStream Committers. + * Copyright (C) 2011, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -10,6 +10,7 @@ */ package com.thoughtworks.xstream.io.xml; +import com.thoughtworks.xstream.XStreamException; import com.thoughtworks.xstream.io.HierarchicalStreamDriver; import com.thoughtworks.xstream.io.HierarchicalStreamReader; @@ -24,5 +25,29 @@ return driver.createReader(new StringReader(xml)); } + public void testIsXXEVulnerableWithExternalGeneralEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalGeneralEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (message.indexOf("external entity") < 0) { + throw e; + } + } + } + + public void testIsXXEVulnerableWithExternalParameterEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalParameterEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (message.indexOf("external entity") < 0) { + throw e; + } + } + } + // inherits tests from superclass } diff --git a/xstream/src/test/com/thoughtworks/xstream/io/xml/XomReaderTest.java b/xstream/src/test/com/thoughtworks/xstream/io/xml/XomReaderTest.java index 491222f..dcf7e74 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/xml/XomReaderTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/xml/XomReaderTest.java @@ -1,29 +1,59 @@ /* * Copyright (C) 2004 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. - * + * * Created on 02. September 2004 by Joe Walnes */ package com.thoughtworks.xstream.io.xml; +import java.io.StringReader; + +import com.thoughtworks.xstream.XStreamException; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import nu.xom.Builder; import nu.xom.Document; +import nu.xom.Element; -import java.io.StringReader; public class XomReaderTest extends AbstractXMLReaderTest { // factory method - protected HierarchicalStreamReader createReader(String xml) throws Exception { - Document document = new Builder().build(new StringReader(xml)); - return new XomReader(document); + protected HierarchicalStreamReader createReader(final String xml) throws Exception { + return new XomDriver().createReader(new StringReader(xml)); + } + + public void testCanReadFromElementOfLargerDocument() throws Exception { + final String xml = "" + + "" + + " " + + " " + + " " + + " " + + " " + + ""; + final Document document = new Builder().build(new StringReader(xml)); + final Element element = document.getRootElement().getFirstChildElement("small"); + + final HierarchicalStreamReader xmlReader = new XomReader(element); + assertEquals("small", xmlReader.getNodeName()); + xmlReader.moveDown(); + assertEquals("tiny", xmlReader.getNodeName()); + } + + public void testIsXXEVulnerableWithExternalGeneralEntity() throws Exception { + // No possibility to suppress support for external entities in XOM? + // super.testIsXXEVulnerableWithExternalGeneralEntity(); + } + + public void testIsXXEVulnerableWithExternalParameterEntity() throws Exception { + // No possibility to suppress support for external entities in XOM? + // super.testIsXXEVulnerableWithExternalParameterEntity(); } // inherits tests from superclass diff --git a/xstream/src/test/com/thoughtworks/xstream/io/xml/Xpp3ReaderTest.java b/xstream/src/test/com/thoughtworks/xstream/io/xml/Xpp3ReaderTest.java index 1443663..d899a51 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/xml/Xpp3ReaderTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/xml/Xpp3ReaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 XStream Committers. + * Copyright (C) 2011, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -10,6 +10,7 @@ */ package com.thoughtworks.xstream.io.xml; +import com.thoughtworks.xstream.XStreamException; import com.thoughtworks.xstream.io.HierarchicalStreamDriver; import com.thoughtworks.xstream.io.HierarchicalStreamReader; @@ -24,5 +25,17 @@ return driver.createReader(new StringReader(xml)); } + public void testIsXXEVulnerableWithExternalGeneralEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalGeneralEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (message.indexOf("resolve entity") < 0) { + throw e; + } + } + } + // inherits tests from superclass } diff --git a/xstream/src/test/com/thoughtworks/xstream/io/xml/XppDomReaderTest.java b/xstream/src/test/com/thoughtworks/xstream/io/xml/XppDomReaderTest.java index 7ae8d92..ac2fcdc 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/xml/XppDomReaderTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/xml/XppDomReaderTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2009, 2011 XStream Committers. + * Copyright (C) 2006, 2007, 2009, 2011, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -11,6 +11,7 @@ */ package com.thoughtworks.xstream.io.xml; +import com.thoughtworks.xstream.XStreamException; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.xml.xppdom.XppDom; import com.thoughtworks.xstream.io.xml.xppdom.XppFactory; @@ -75,4 +76,16 @@ assertEquals(0, xmlReader.getAttributeCount()); } + public void testIsXXEVulnerableWithExternalGeneralEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalGeneralEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (message.indexOf("resolve entity") < 0) { + throw e; + } + } + } + } diff --git a/xstream/src/test/com/thoughtworks/xstream/io/xml/XppReaderTest.java b/xstream/src/test/com/thoughtworks/xstream/io/xml/XppReaderTest.java index 80eeb1f..7852882 100644 --- a/xstream/src/test/com/thoughtworks/xstream/io/xml/XppReaderTest.java +++ b/xstream/src/test/com/thoughtworks/xstream/io/xml/XppReaderTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2004 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015, 2016 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -11,6 +11,7 @@ */ package com.thoughtworks.xstream.io.xml; +import com.thoughtworks.xstream.XStreamException; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import java.io.StringReader; @@ -20,5 +21,17 @@ return new XppReader(new StringReader(xml)); } + public void testIsXXEVulnerableWithExternalGeneralEntity() throws Exception { + try { + super.testIsXXEVulnerableWithExternalGeneralEntity(); + fail("Thrown " + XStreamException.class.getName() + " expected"); + } catch (final XStreamException e) { + final String message = e.getCause().getMessage(); + if (message.indexOf("resolve entity") < 0) { + throw e; + } + } + } + // inherits tests from superclass } diff --git a/xstream-benchmark/pom.xml b/xstream-benchmark/pom.xml index d6fb34a..bb8ddfe 100644 --- a/xstream-benchmark/pom.xml +++ b/xstream-benchmark/pom.xml @@ -14,7 +14,7 @@ com.thoughtworks.xstream xstream-parent - 1.4.8 + 1.4.9 xstream-benchmark jar @@ -91,10 +91,6 @@ org.apache.maven.plugins - maven-source-plugin - - - org.apache.maven.plugins maven-surefire-plugin true @@ -133,4 +129,8 @@ junit + + + !com.thoughtworks.xstream.tools.benchmark.model,com.thoughtworks.xstream.tools.benchmark.*;-noimport:=true + diff --git a/xstream-benchmark/profiles/osgi b/xstream-benchmark/profiles/osgi new file mode 100644 index 0000000..e69de29 diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Harness.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Harness.java index dd1bd74..6458cd4 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Harness.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Harness.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2013 XStream Committers. + * Copyright (C) 2006, 2007, 2013, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -53,6 +53,7 @@ * * * @author Joe Walnes + * @deprecated As of 1.4.9 use JMH instead */ public class Harness { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Metric.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Metric.java index 8d68b91..6a0db02 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Metric.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Metric.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,7 @@ * @author Joe Walnes * @author Jörg Schaible * @see Harness + * @deprecated As of 1.4.9 use JMH instead */ public interface Metric { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Product.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Product.java index 308ec39..eb86aca 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Product.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Product.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -20,6 +20,7 @@ * * @author Joe Walnes * @see Harness + * @deprecated As of 1.4.9 use JMH instead */ public interface Product { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Reporter.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Reporter.java index 67775e3..b3aa546 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Reporter.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Reporter.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -31,6 +31,7 @@ * * @author Joe Walnes * @see Harness + * @deprecated As of 1.4.9 use JMH instead */ public interface Reporter { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Target.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Target.java index 1010f51..1996582 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Target.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/Target.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -20,6 +20,7 @@ * * @author Joe Walnes * @see Harness + * @deprecated As of 1.4.9 use JMH instead */ public interface Target { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/CharacterCountMetric.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/CharacterCountMetric.java index 297d654..bd9f540 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/CharacterCountMetric.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/CharacterCountMetric.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009, 2011 XStream Committers. + * Copyright (C) 2007, 2008, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,7 @@ * * @author Jörg Schaible * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class CharacterCountMetric implements Metric { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/DeserializationSpeedMetric.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/DeserializationSpeedMetric.java index 9817575..a48235e 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/DeserializationSpeedMetric.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/DeserializationSpeedMetric.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2009 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -25,6 +25,7 @@ * @author Jörg Schaible * @see com.thoughtworks.xstream.tools.benchmark.Harness * @see Metric + * @deprecated As of 1.4.9 use JMH instead */ public class DeserializationSpeedMetric implements Metric { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/SerializationSpeedMetric.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/SerializationSpeedMetric.java index 699e611..7949638 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/SerializationSpeedMetric.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/SerializationSpeedMetric.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -23,6 +23,7 @@ * @author Joe Walnes * @see com.thoughtworks.xstream.tools.benchmark.Harness * @see Metric + * @deprecated As of 1.4.9 use JMH instead */ public class SerializationSpeedMetric implements Metric { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/SizeMetric.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/SizeMetric.java index a31c9e9..0662e10 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/SizeMetric.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/metrics/SizeMetric.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2008 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -23,6 +23,7 @@ * @author Joe Walnes * @see com.thoughtworks.xstream.tools.benchmark.Harness * @see Metric + * @deprecated As of 1.4.9 use JMH instead */ public class SizeMetric implements Metric { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A100Fields.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A100Fields.java index 770ebfe..02cc530 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A100Fields.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A100Fields.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2009, 2011 XStream Committers. + * Copyright (C) 2007, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -14,6 +14,7 @@ * Class with 100 fields. * * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class A100Fields { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A100Parents.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A100Parents.java index a6439ac..5896365 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A100Parents.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A100Parents.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2009, 2011 XStream Committers. + * Copyright (C) 2007, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -14,6 +14,7 @@ * Class with 100 parents. * * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class A100Parents { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A50InnerClasses.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A50InnerClasses.java index 749e3ae..963f019 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A50InnerClasses.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A50InnerClasses.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2009, 2011 XStream Committers. + * Copyright (C) 2007, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -14,6 +14,7 @@ * Class with inner classes 50 levels deep. * * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class A50InnerClasses { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A50StaticInnerClasses.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A50StaticInnerClasses.java index ae296ce..bc621f2 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A50StaticInnerClasses.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/A50StaticInnerClasses.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2009, 2011 XStream Committers. + * Copyright (C) 2007, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -14,6 +14,7 @@ * Class with static inner classes 50 levels deep. * * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class A50StaticInnerClasses { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/Five.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/Five.java index 8daa634..4059ea0 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/Five.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/Five.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2011 XStream Committers. + * Copyright (C) 2008, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -15,6 +15,7 @@ * Class containing 5 basic types. * * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class Five extends One { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/FiveBean.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/FiveBean.java index 21216ab..021156d 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/FiveBean.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/FiveBean.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2011 XStream Committers. + * Copyright (C) 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -15,6 +15,7 @@ * JavaBean class containing 5 basic types. * * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class FiveBean extends OneBean { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/One.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/One.java index 8b40d05..2dfa3a7 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/One.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/One.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2011 XStream Committers. + * Copyright (C) 2008, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -15,6 +15,7 @@ * Class containing one basic type. * * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class One { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/OneBean.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/OneBean.java index c9ae520..77dbc98 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/OneBean.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/OneBean.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2011 XStream Committers. + * Copyright (C) 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -15,6 +15,7 @@ * JavaBean class containing one basic type. * * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class OneBean { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/SerializableFive.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/SerializableFive.java index f1d4c2f..53e3415 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/SerializableFive.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/SerializableFive.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2011 XStream Committers. + * Copyright (C) 2008, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -18,6 +18,7 @@ * Serializable class containing 5 basic types. * * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class SerializableFive extends SerializableOne { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/SerializableOne.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/SerializableOne.java index 104c301..7ef8acb 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/SerializableOne.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/model/SerializableOne.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2011 XStream Committers. + * Copyright (C) 2008, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -19,6 +19,7 @@ * Serializable class containing one basic types. * * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class SerializableOne implements Serializable { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/JavaObjectSerialization.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/JavaObjectSerialization.java index 6263761..1988e7c 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/JavaObjectSerialization.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/JavaObjectSerialization.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -26,6 +26,7 @@ * @see Product * @see ObjectOutputStream * @see ObjectInputStream + * @deprecated As of 1.4.9 use JMH instead */ public class JavaObjectSerialization implements Product { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamBEAStax.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamBEAStax.java index 373372e..16f674c 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamBEAStax.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamBEAStax.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 XStream Committers. + * Copyright (C) 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,7 @@ * @see com.thoughtworks.xstream.tools.benchmark.Product * @see com.thoughtworks.xstream.XStream * @see BEAStaxDriver + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamBEAStax extends XStreamDriver { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamBinary.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamBinary.java index e66f848..2753c2c 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamBinary.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamBinary.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -28,6 +28,7 @@ * @see XStream * @see BinaryStreamReader * @see BinaryStreamWriter + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamBinary implements Product { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamCompact.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamCompact.java index 7a99d0a..a1109a5 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamCompact.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamCompact.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -28,6 +28,7 @@ * @see Product * @see XStream * @see CompactWriter + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamCompact implements Product { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamDom.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamDom.java index d953e46..7c3388e 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamDom.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamDom.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2009 XStream Committers. + * Copyright (C) 2006, 2007, 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,7 @@ * @see com.thoughtworks.xstream.tools.benchmark.Product * @see com.thoughtworks.xstream.XStream * @see DomDriver + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamDom extends XStreamDriver { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamDom4J.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamDom4J.java index d1b360c..8dbbbc2 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamDom4J.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamDom4J.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 XStream Committers. + * Copyright (C) 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -25,6 +25,7 @@ * @see com.thoughtworks.xstream.tools.benchmark.Product * @see com.thoughtworks.xstream.XStream * @see Dom4JDriver + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamDom4J extends XStreamDriver { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamDriver.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamDriver.java index 43b500d..995c8dc 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamDriver.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2011 XStream Committers. + * Copyright (C) 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -30,6 +30,7 @@ * @author Joe Walnes * @author Jörg Schaible * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamDriver implements Product { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamJDom.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamJDom.java index b374d05..c1f771c 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamJDom.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamJDom.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 XStream Committers. + * Copyright (C) 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,7 @@ * @see com.thoughtworks.xstream.tools.benchmark.Product * @see com.thoughtworks.xstream.XStream * @see JDomDriver + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamJDom extends XStreamDriver { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamKXml2.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamKXml2.java index 5b688cb..5b7959b 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamKXml2.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamKXml2.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 XStream Committers. + * Copyright (C) 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,7 @@ * @see com.thoughtworks.xstream.tools.benchmark.Product * @see com.thoughtworks.xstream.XStream * @see KXml2Driver + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamKXml2 extends XStreamDriver { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamKXml2DOM.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamKXml2DOM.java index 2b102f8..4a71e7e 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamKXml2DOM.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamKXml2DOM.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 XStream Committers. + * Copyright (C) 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,7 @@ * @see com.thoughtworks.xstream.tools.benchmark.Product * @see com.thoughtworks.xstream.XStream * @see KXml2DomDriver + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamKXml2DOM extends XStreamDriver { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamSjsxp.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamSjsxp.java index 05676f1..17d76cf 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamSjsxp.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamSjsxp.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 XStream Committers. + * Copyright (C) 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,7 @@ * @see com.thoughtworks.xstream.tools.benchmark.Product * @see com.thoughtworks.xstream.XStream * @see SjsxpDriver + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamSjsxp extends XStreamDriver { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamStax.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamStax.java index 8077f67..cf4ea15 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamStax.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamStax.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2009 XStream Committers. + * Copyright (C) 2006, 2007, 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,7 @@ * @see com.thoughtworks.xstream.tools.benchmark.Product * @see com.thoughtworks.xstream.XStream * @see StaxDriver + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamStax extends XStreamDriver { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamWoodstox.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamWoodstox.java index a46df2d..6d743f3 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamWoodstox.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamWoodstox.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 XStream Committers. + * Copyright (C) 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,7 @@ * @see com.thoughtworks.xstream.tools.benchmark.Product * @see com.thoughtworks.xstream.XStream * @see WstxDriver + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamWoodstox extends XStreamDriver { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXom.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXom.java index 6309ad1..d121c63 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXom.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXom.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 XStream Committers. + * Copyright (C) 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,7 @@ * @see com.thoughtworks.xstream.tools.benchmark.Product * @see com.thoughtworks.xstream.XStream * @see XomDriver + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamXom extends XStreamDriver { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXpp.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXpp.java index 4ae26e2..fadd959 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXpp.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXpp.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007, 2009 XStream Committers. + * Copyright (C) 2006, 2007, 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -22,6 +22,7 @@ * @see com.thoughtworks.xstream.tools.benchmark.Product * @see com.thoughtworks.xstream.XStream * @see XppDriver + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamXpp extends XStreamDriver { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXpp3.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXpp3.java index 567968b..d9fbc08 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXpp3.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXpp3.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 XStream Committers. + * Copyright (C) 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,7 @@ * @see com.thoughtworks.xstream.tools.benchmark.Product * @see com.thoughtworks.xstream.XStream * @see Xpp3Driver + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamXpp3 extends XStreamDriver { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXpp3DOM.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXpp3DOM.java index 646f1fd..5cae364 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXpp3DOM.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/products/XStreamXpp3DOM.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 XStream Committers. + * Copyright (C) 2009, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,7 @@ * @see com.thoughtworks.xstream.tools.benchmark.Product * @see com.thoughtworks.xstream.XStream * @see Xpp3DomDriver + * @deprecated As of 1.4.9 use JMH instead */ public class XStreamXpp3DOM extends XStreamDriver { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/reporters/HtmlReporter.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/reporters/HtmlReporter.java index 437c4fa..9209bef 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/reporters/HtmlReporter.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/reporters/HtmlReporter.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -25,6 +25,9 @@ import java.util.Iterator; import java.util.Date; +/** + * @deprecated As of 1.4.9 use JMH instead + */ public class HtmlReporter implements Reporter { private final PrettyPrintWriter out; diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/reporters/MultiReporter.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/reporters/MultiReporter.java index 270157f..a4ebc02 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/reporters/MultiReporter.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/reporters/MultiReporter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 XStream Committers. + * Copyright (C) 2007, 2008, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -20,6 +20,7 @@ * * @author Jörg Schaible * @since 1.3 + * @deprecated As of 1.4.9 use JMH instead */ public class MultiReporter implements Reporter { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/reporters/TextReporter.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/reporters/TextReporter.java index cdb696a..6c6e446 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/reporters/TextReporter.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/reporters/TextReporter.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -25,6 +25,7 @@ * @author Joe Walnes * @see com.thoughtworks.xstream.tools.benchmark.Harness * @see Reporter + * @deprecated As of 1.4.9 use JMH instead */ public class TextReporter implements Reporter { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/BasicTarget.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/BasicTarget.java index d7f4c90..a119f57 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/BasicTarget.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/BasicTarget.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2011 XStream Committers. + * Copyright (C) 2008, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -22,6 +22,7 @@ * * @author Jörg Schaible * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class BasicTarget implements Target { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/ExtendedTarget.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/ExtendedTarget.java index 2c15837..62195da 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/ExtendedTarget.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/ExtendedTarget.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2011 XStream Committers. + * Copyright (C) 2008, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -26,6 +26,7 @@ * * @author Jörg Schaible * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class ExtendedTarget implements Target { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/JTreeTarget.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/JTreeTarget.java index 00ed6cf..ee965f7 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/JTreeTarget.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/JTreeTarget.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -22,6 +22,7 @@ * @see com.thoughtworks.xstream.tools.benchmark.Harness * @see Target * @see JTree + * @deprecated As of 1.4.9 use JMH instead */ public class JTreeTarget implements Target { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/JavaBeanTarget.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/JavaBeanTarget.java index dd7240c..fe868a6 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/JavaBeanTarget.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/JavaBeanTarget.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2011 XStream Committers. + * Copyright (C) 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -22,6 +22,7 @@ * * @author Jörg Schaible * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class JavaBeanTarget implements Target { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/ListTarget.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/ListTarget.java index 89a5e43..1e37107 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/ListTarget.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/ListTarget.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -22,6 +22,7 @@ * @author Joe Walnes * @see com.thoughtworks.xstream.tools.benchmark.Harness * @see Target + * @deprecated As of 1.4.9 use JMH instead */ public class ListTarget implements Target { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/Person.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/Person.java index c347453..f1e92f9 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/Person.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/Person.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -16,6 +16,7 @@ /** * @see UserDefinedClassTarget + * @deprecated As of 1.4.9 use JMH instead */ public class Person implements Serializable { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/ReflectionTarget.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/ReflectionTarget.java index 1970f87..f8d31be 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/ReflectionTarget.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/ReflectionTarget.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2011 XStream Committers. + * Copyright (C) 2008, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -22,6 +22,7 @@ * * @author Jörg Schaible * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class ReflectionTarget implements Target { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/SerializableTarget.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/SerializableTarget.java index 537ba81..bff83dc 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/SerializableTarget.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/SerializableTarget.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2011 XStream Committers. + * Copyright (C) 2008, 2009, 2011, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -22,6 +22,7 @@ * * @author Jörg Schaible * @since 1.4 + * @deprecated As of 1.4.9 use JMH instead */ public class SerializableTarget implements Target { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/StringTarget.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/StringTarget.java index 0f11266..8b08b3a 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/StringTarget.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/StringTarget.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -19,6 +19,7 @@ * @author Joe Walnes * @see com.thoughtworks.xstream.tools.benchmark.Harness * @see Target + * @deprecated As of 1.4.9 use JMH instead */ public class StringTarget implements Target { diff --git a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/UserDefinedClassTarget.java b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/UserDefinedClassTarget.java index f82604b..5bcf1ff 100644 --- a/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/UserDefinedClassTarget.java +++ b/xstream-benchmark/src/java/com/thoughtworks/xstream/tools/benchmark/targets/UserDefinedClassTarget.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Joe Walnes. - * Copyright (C) 2006, 2007 XStream Committers. + * Copyright (C) 2006, 2007, 2015 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,7 @@ * @author Joe Walnes * @see com.thoughtworks.xstream.tools.benchmark.Harness * @see Target + * @deprecated As of 1.4.9 use JMH instead */ public class UserDefinedClassTarget implements Target { diff --git a/xstream-distribution/pom.xml b/xstream-distribution/pom.xml index cd64e6b..c5274fb 100644 --- a/xstream-distribution/pom.xml +++ b/xstream-distribution/pom.xml @@ -1,7 +1,7 @@ + + Benchmarks + + + + +

    Introduction

    + +

    Benchmark results are always dependent on a very individual setup. Normally it is not useful to generalize such results + for every use case, but it can give you a hint. However, if you're really in the need of maximum performance, you should + probably create an own benchmark with your objects or even use a profiler to detect the real hot spots in your application.

    + +

    XStream uses the Java Microbenchmark Harness (JMH) + of the JDK Tools as benchmark framework starting with version 1.4.9. As result it contains a ZIP file + (xstream-jmh-<version>-app.zip) as new artifact containing anything required to run the benchmarks. Unpack + the file and use the scripts in the bin directory to execute the benchmarks. Use option -h to look at the + options provided by JMH. You may exchange the libraries in the lib directory with other versions of + XStream or the individual parsers or you may even add new JMH benchmarks to the default ones of XStream.

    + +

    All benchmark values below measure the average throughput in nanosecond per operation. JMH provides additional + measurement options, see online help. The maximum deviation for each benchmark is recorded in the reference files + of the distributed ZIP file. The benchmark is executed on Linux 4.1.12 Gentoo 64-bit system with an Intel Core i7 + CPU 920 of 2.67 GHz. Note again, that these values are no replacement for real profiler results and they may + vary from run to run (see reference files) due to this machine's background processes on a single CPU. However, it + can give you some idea of what you can expect using different parser technologies.

    + +

    Parser Benchmark

    + +

    The values represent the average throughput of 15 runs with a single thread. The benchmarks emphasis the parser + efficiency for different structures.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ParserTextArrayNested
    W3C DOM (Oracle JDK 1.8.0_66)10037380.79554234293.3511917332.056
    JDOM (1.1.3)6368317.6367910979.2233862796.027
    JDOM 2 (2.0.5)5767640.10510570210.6532980906.727
    DOM4J (1.6.1)7816280.08492998322.9522108075.646
    XOM (1.1)7950778.53338704485.3102471019.743
    StAX (BEA 1.2.0)3108517.6991310406.961669111.164
    StAX (Woodstox 3.2.7)1884858.5251240767.393650470.623
    StAX (Oracle JDK 1.8.0_66)7366387.2721334398.501688229.709
    XPP (Xpp3 min 1.1.4c)2109341.0761309607.2103301732.767
    XPP (kXML2 min 2.3.0)3391204.2661514514.6808105934.241
    Binary (XStream 1.4.9)1144243.7501062031.901496839.565
    Jettison (1.2)3002547.2201159238.555682182.733
    + +
    +
    Text
    +
    A single element with a text of 100.000 characters.
    +
    Array
    +
    A single element with 1.000 child elements.
    +
    Nested
    +
    Nested elements in 500 levels.
    +
    + +

    Converter Type Benchmark

    + +

    The values represent the average throughput of 16 runs with four threads using the Xpp3 parser for a structure + with 1.000 elements. The benchmarks demonstrate the different converter types that can be used for a standard Java + class.

    + + + + + + + + + + + + + + + + + + +
    Converter TypeThroughput
    Custom11276718.384
    Java Bean28878706.293
    Reflection40085786.696
    + +
    +
    Custom
    +
    A converter especially written for the Java type to convert.
    +
    Java Bean
    +
    Usage of the generic JavaBeanConverter, since the Java type respects the Java Bean contract.
    +
    Reflection
    +
    Usage of the generic converter based on reflection.
    +
    + +

    String Converter Benchmark

    + +

    The values represent the average throughput of 16 runs with four threads using the Xpp3 parser for a structure + with 10.000 string elements of various sizes and duplicates. The benchmarks demonstrate different implementations + and configurations of the StringConverter.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    StringConverter StrategyThroughput
    No Cache19626160.696
    Intern23060982.052
    ConcurrentMap (length limit)21796001.29
    ConcurrentMap (unlimited)21378299.003
    Sync'd WeakCache (length limit)21838410.801
    Sync'd WeakCache (unlimited)22011251.691
    + +
    +
    No Cache
    +
    An implementation that does not cache deserialized String values with the consequence that repeated values + will always allocate separate memory.
    +
    Intern
    +
    An implementation that uses String.intern() to cache the individual values. The memory pool used for the + values is dependent on the JDK version. Up to Java 7 this was the permanent generation space i.e. the memory has + to be shared with all loaded classes. It is up to the garbage collection when these string values are freed + again.
    +
    ConcurrentMap (length limit)
    +
    An implementation that uses a ConcurrentHashMap as cache for strings of limited length (38 characters). The + lifetime of the cache is equivalent with the lifetime of the XStream instance.
    +
    ConcurrentMap (unlimited)
    +
    An implementation that uses a ConcurrentHashMap as cache for all strings. The lifetime of the cache is + equivalent with the lifetime of the XStream instance.
    +
    Sync'd WeakCache (length limit)
    +
    An implementation that uses a WeakCache for strings of limited length (38 characters). This cache uses weak + references for its keys and values. An entry is therefore only kept as long as the deserialized object structure + is referencing it. This is XStream's default strategy.
    +
    Syn'd WeakCache (unlimited)
    +
    An implementation that uses a WeakCache for all strings. This cache uses weak references for its keys and + values. An entry is therefore ony kept as long as the deserialized object structure is referencing it.
    +
    + +

    Name Coder Benchmark

    + +

    The values represent the average throughput of 25 runs with four threads using the Xpp3 parser for a structure + with 250 nested elements using names invalid for XML elements. The benchmarks demonstrate different implementation + strategies for a NameCoder to create valid tag names in XML.

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameCoder ImplementationThroughput
    No Coding7299475.975
    Dollar Coding7767196.902
    Escaped Underscore Coding9894393.289
    Cached Escaped Underscore Coding7867671.962
    Xml Friendly Coding8635501.208
    + +
    +
    No Coding
    +
    An implementation that does not encode the names of XML elements. It relies on the fact that the object graph + does not contain elements with invalid XML names, because the name of the class types and members are either + conforming or have been aliased.
    +
    Dollar Coding
    +
    An implementation that uses String.replace to replace any dollar sign with '·' (middle dot), a valid + character normally not used for Java identifiers. In typical Java code there are no other invalid characters used + for Java identifiers, however, Java allows identifiers to contain a wide range of UTF-8 characters and the JVM + has even less restrictions.
    +
    Escaped Underscore Coding
    +
    An implementation that uses a StringBuilder to create the XML name by replacing any dollar sign with '_-' and + escapes every plain underscore with two ones. The implementation will therefore only use characters in the + standard ASCII range. It is not possible to use a simple minus sign as replacement because it is not a valid + first character for XML names. However, the comments about Java identifiers and JVM identifiers from the Dollar + Coding still apply.
    +
    Cached Escaped Underscore Coding
    +
    An implementation that implements a cache for the NameCoder that escapes the underscores.
    +
    Xml Friendly Coding
    +
    The default implementation of XStream using a StringBuilder and a cache, encoding any character that is + invalid for XML names. It implements also the underscore escaping for compatibility reasons with XML created by + earlier versions of XStream.
    +
    + + + \ No newline at end of file diff --git a/xstream-distribution/src/content/changes.html b/xstream-distribution/src/content/changes.html index a45992c..51f4f7d 100644 --- a/xstream-distribution/src/content/changes.html +++ b/xstream-distribution/src/content/changes.html @@ -1,7 +1,7 @@ + +

    1.4.9

    + +

    Released March 15, 2016.

    + +

    Major changes

    + +
      +
    • GHI:#25: Fix XXE + vulnerability: Fixed affected drivers were Dom4JDriver, DomDriver, JDomDriver, JDom2Driver, SjsxpDriver, + StandardStaxDriver and WstxDriver. Still vulnerable are BEAStaxDriver and XomDriver. Processing of (external) + entities has been disabled. See FAQ for more information.
    • +
    • Benchmark module has been deprecated in favor of + JMH (Java Microbenchmarking Harness).
    • +
    • GHI:#33 and GHPR:#38: Add converter for java.nio.file.Path (by Aaron Jonson).
    • +
    + +

    Minor changes

    + +
      +
    • GHI:#35: AnnotationMapper dropped silently constructor arguments for converters if they were equal.
    • +
    • Fix: Possible concurrency problem with XomDriver.
    • +
    • JIRA:XSTR-773, GHPR:#3: Minimize memory footprint by not keeping internal references to instances of + immutable types.
    • +
    • Drop automatic reference support at deserialization time for immutable types before version 1.4 (primitive + types and their boxed counterpart, java.lang.Class, java.lang.String, java.math.BigDecimal, + java.math.BigInteger, java.io.File, java.net.URL, and java.awt.font.TextAttribute).
    • +
    • Fix: Implicit collection declaration is erroneously inherited or propagated to hidden field of same name.
    • +
    • XStreamConverter annotation supports null values as arguments for converter instantiation.
    • +
    • GHI:#5: Support null values for JavaBean properties.
    • +
    • GHI:#36: Fix NamedMapConverter, does not use SingleValueConverter of value if value is text of entry element.
    • +
    • GHI:#13: Own converter for javax.activation.ActivationDataFlavor, because ExternalizableConverter cannot handle + a type that violates the Java specification.
    • +
    • GHPR:#18: Minimize synchronized block in FieldDictionary.
    • +
    • JIRA:XSTR-769: Synthetic fields with references to outer class use compiler dependent names.
    • +
    • JIRA:XSTR-771: UUID is an immutable type by default.
    • +
    • GHPR:#23: Constructor of AbstractXppDriver swallows causing exception.
    • +
    • GHI:#28: Fix functionality of ClassAliaslingMapper.itemTypeAsAttributes(Class).
    • +
    • GHI:#37: Historical Java 1.3 support suppresses causing exception of a thrown InvocationTargetException.
    • +
    • GHI:#41: Cannot instantiate ToAttributedValueConverter with null value for valueFieldName using + XStreamConverter annotation.
    • +
    • Fix example code in description of security framework.
    • +
    • Clean-up usage of exceptions.
    • +
    + +

    API changes

    + +
      +
    • Added c.t.x.XStream.addImmutableType(Class, boolean) and deprecated c.t.x.XStream.addImmutableType(Class).
    • +
    • Added c.t.x.mapper.Mapper.isReferenceable(Class).
    • +
    • Added c.t.x.mapper.ImmutableTypesMapper.addImmutableType(Class, boolean) and deprecated + c.t.x.mapper.ImmutableTypesMapper.addImmutableType(Class).
    • +
    • Added c.t.x.io.xml.Dom4JDriver.createReader().
    • +
    • Added c.t.x.io.xml.DomDriver.createDocumentBuilderFactory().
    • +
    • Added c.t.x.io.xml.JDomDriver.createBuilder().
    • +
    • Added c.t.x.io.xml.JDom2Driver.createBuilder().
    • +
    • Added c.t.x.io.xml.XomDriver.createBuilder().
    • +
    • Added constructor c.t.x.converter.extended.ToAttributedValueConverter.ToAttributedValueConverter(Class, + Mapper, ReflectionProvider, ConverterLookup).
    • +
    • Added abstract c.t.x.converter.ErrorWritingException as common base for c.t.x.converter.ConversionException + and c.t.x.converter.reflection.ObjectAccessException.
    • +
    • Deprecated c.t.x.io.xml.XomDriver(Builder), c.t.x.io.xml.XomDriver(Builder, NameCoder) and c.t.x.io.xml.XomDriver.getBuilder().
    • +
    • Deprecated c.t.x.mapper.ClassAliaslingMapper.itemTypeAsAttributes(Class) and + c.t.x.mapper.ClassAliaslingMapper.aliasIsAttribute(String). Methods never called, left-over from old refactoring.
    • +
    +

    1.4.8

    Released February 18, 2015.

    @@ -46,19 +113,19 @@
    • Detect Java 9 runtime.
    • -
    • XSTR-767: Deserialization of referenced lambda expressions fail.
    • -
    • XSTR-762: Private method readResolve() called on base classes.
    • -
    • XSTR-761: Support ignored serialPersistentField at deserialization time.
    • -
    • XSTR-755: ExternalizableConverter does not respect writeReplace and readResolve.
    • -
    • XSTR-757: Deserialized TreeSet does not honor remove(Object) return value contract.
    • -
    • XSTR-759: Support deserialization of W3C datetime format +
    • JIRA:XSTR-767: Deserialization of referenced lambda expressions fail.
    • +
    • JIRA:XSTR-762: Private method readResolve() called on base classes.
    • +
    • JIRA:XSTR-761: Support ignored serialPersistentField at deserialization time.
    • +
    • JIRA:XSTR-755: ExternalizableConverter does not respect writeReplace and readResolve.
    • +
    • JIRA:XSTR-757: Deserialized TreeSet does not honor remove(Object) return value contract.
    • +
    • JIRA:XSTR-759: Support deserialization of W3C datetime format in DateConverter with Java 7 runtime.
    • Fix: DateConverter ignores provided locale.
    • -
    • XSTR-768: ISO8601GregorianCalendarConverter may set invalid time zone for Joda-Time.
    • +
    • JIRA:XSTR-768: ISO8601GregorianCalendarConverter may set invalid time zone for Joda-Time.
    • Fix: WeakCache.entrySet().iterator().next.setValue(value) returns the reference instead of the old value.
    • Fix: SqlTimestampConverter throws IllegalArgumentException instead of ConversionException on fromString().
    • Fix: CGLIBEnhancedConverter does not initialize transient members of parent after deserialization.
    • -
    • XSTR-763: Set scope of org.json:json to test instead declaring the dependency as optional.
    • +
    • JIRA:XSTR-763: Set scope of org.json:json to test instead declaring the dependency as optional.

    API changes

    @@ -212,7 +279,7 @@
  • Current IBM JDK for Java 1.4.2 no longer has a reverse field ordering.
  • LongConverter supports now positive hex and octal numbers over Long.MAX_VALUE within 64 bit.
  • Fix: Sun14RefectionProvider ignores a provided FieldDictionary.
  • -
  • JIRA:XSTR-457: Do not write 'defined-in' attribute it not needed.
  • +
  • JIRA:XSTR-457: Do not write 'defined-in' attribute if not needed.
  • JettisonMappedXmlDriver provides better support to overwrite its create methods.
  • JIRA:XSTR-685: StAX based drivers (StaxDriver and JettisonMappedXmlDriver) are not closing internal input stream reading from file or URL.
  • diff --git a/xstream-distribution/src/content/converters.html b/xstream-distribution/src/content/converters.html index a7aab01..307df45 100644 --- a/xstream-distribution/src/content/converters.html +++ b/xstream-distribution/src/content/converters.html @@ -1,7 +1,7 @@ @@ -740,6 +748,31 @@ Only automatically registered if runtime has AWT support.
    Warning: The AWT toolkit is definitely initialized when a TextAttribute is deserialized. + normal + + + + +

    java.awt.font

    + + + Converter + Supported types + Example + Notes + Prio + + + ActivationDataFlavorConverter + javax.activation.ActivationDataFlavor + + <activation-data-flavor>
    +   <mimeType>application/x-junit</mimeType>
    +   <humanRepresentableName>JUnit</humanRepresentableName>
    +   <representationClass>java.io.InputStream</representationClass>
    + </activation-data-flavor> + + Available under Java 1.6 or greater. Only automatically registered if runtime has AWT support.
    normal diff --git a/xstream-distribution/src/content/download.html b/xstream-distribution/src/content/download.html index 0a52b3b..f4c53d5 100644 --- a/xstream-distribution/src/content/download.html +++ b/xstream-distribution/src/content/download.html @@ -1,7 +1,7 @@

    Previous Releases

    Previous releases of XStream are also available. However, use of the latest stable version is recommended.

    Optional Dependencies

      -
    • Supported XML parsers and packages: -
        -
      • XmlPull, the XML pull parser API and factory to detect available implementations.
      • -
      • Xpp3, an XML pull parser (recommended).
      • -
      • kXML2 or kXML2-min, an XML pull parser.
      • -
      • DOM4J, easy XML representation and manipulation framework.
      • -
      • JDOM, easy XML representation and manipulation (requires Java 1.2, superseded by JDOM2).
      • -
      • JDOM2, easy XML representation and manipulation, successor of JDOM (requires Java 5).
      • -
      • StaX, the reference implementation of the Streaming API for XML.
      • -
      • Woodstox, an alternate open source StaX implementation.
      • -
      • XOM, another alternative XML API.
      • -
      -
    • -
    • Other optional 3rd party dependencies: -
        -
      • Joda Time for optional ISO8601 date/time formats.
      • -
      • CGLIB for optional support of some proxies generated with the CGLIB Enhancer.
      • -
      • Jettison for serialization and deserialization support with JSON. Note, that newer versions 1.3.x are no longer compatible with XStream.
      • -
      • Jettison 1.0.1 for serialization and deserialization support with JSON in JDK 1.4. Note, that newer version 1.1 is not compatible with XStream.
      • -
      -
    • +
    • Supported XML parsers and packages: +
        +
      • XmlPull, the XML pull parser API and factory to detect available implementations.
      • +
      • Xpp3, an XML pull parser (recommended).
      • +
      • kXML2 or kXML2-min, an XML pull parser.
      • +
      • DOM4J, easy XML representation and manipulation framework.
      • +
      • JDOM, easy XML representation and manipulation (requires Java 1.2, superseded by JDOM2).
      • +
      • JDOM2, easy XML representation and manipulation, successor of JDOM (requires Java 5).
      • +
      • StaX, the reference implementation of the Streaming API for XML.
      • +
      • Woodstox, an alternate open source StaX implementation.
      • +
      • XOM, another alternative XML API.
      • +
      +
    • +
    • Other optional 3rd party dependencies: +
        +
      • Joda Time for optional ISO8601 date/time formats.
      • +
      • CGLIB for optional support of some proxies generated with the CGLIB Enhancer.
      • +
      • Jettison for serialization and deserialization support with JSON. Note, that newer versions 1.3.x are no longer compatible with XStream.
      • +
      • Jettison 1.0.1 for serialization and deserialization support with JSON in JDK 1.4. Note, that newer version 1.1 is not compatible with XStream.
      • +
      +

    Dependencies Hibernate Module

    @@ -108,5 +110,16 @@ +

    Dependencies JMH Module

    + + + diff --git a/xstream-distribution/src/content/faq.html b/xstream-distribution/src/content/faq.html index 4ede3ca..3545ea7 100644 --- a/xstream-distribution/src/content/faq.html +++ b/xstream-distribution/src/content/faq.html @@ -118,7 +118,7 @@

    Are there plans to provide enhanced mode support to other JVMs?

    -

    Yes. Let us know which JVM you would like supported.

    +

    Yes. Let us know which JVM you would like supported.

    When should I use XStream not in enhanced mode?

    @@ -131,7 +131,7 @@

    Which permissions does XStream need when running with an active SecurityManager?

    This depends on the mode XStream is running in. Refer to the - SecurityManagerTest + SecurityManagerTest for details. Actually XStream's converters try to check since version 1.4.6 any critical operation, before they claim to be able to handle a type. As consequence XStream can behave differently running under a SecurityManager. E.g. if the SecurityManager does not permit to create an instance for a derived class of ObjectOutputStream, the @@ -170,6 +170,15 @@

    Yes. This was announced with the last 1.2.x release and was done to support the type inheritance of XML schemas. However, XStream is delivered with the XStream12FieldKeySorter that can be used to sort the fields according XStream 1.2.2.

    + + +

    WebShere 8 can no longer use XStream 1.4.x?

    + +

    XStream has a long history to support types from recent JDKs without dropping backward compatibility. Therefore it contains class files targeting different JDKs. + However, WebShpere scans by default all JAR files in its classpath for annotations to support CDI independent of the presence of a beans.xml file in META-INF. This + scanning fails for class files targeting a higher JDK runtime as currently used by WebSphere. Please, consult your WebSphere documentation, how to turn off the + scanning for individual files by providing an amm.filter.proeprties + file.

    @@ -257,7 +266,7 @@ always with the boxed types and converts to primitives types on the fly. However, for method and field type signatures the difference is essential. Nevertheless it is possible to register derived versions of the converters that are able to respect the aliasing with some minor effort. Following lines are taken from the AliasTest in the - acceptence tests:

    + acceptance tests:

    XStream xstream = new XStream();
     Mapper mapper = new MapperWrapper(xstream.getMapper().lookupMapperOfType(ArrayMapper.class)) {
       public Class realClass(String elementName) {
    @@ -299,6 +308,24 @@
         

    Yes.

    +

    Can immutable types be referenced?

    +

    Adding a type as immutable implies that a referencing marshaller strategy will write each occurrence of the same + instance separately into the stream without referencing it, i.e. instance of those types are normally never referenced + at deserialization time. Such referenced can exist though for persisted streams when a type is added as immutable in + later versions. In such cases the type can be added as immutable, but still referenceable.

    + +

    Any immutable type could be dereferenced before version 1.4.9, but only at the cost of a large memory footprint. + Since version 1.4.9 this is only possible if the immutable type has been explicitly declared as referenceable too. + This should be done only if backward compatibility is required and a persisted stream may contain a reference of such + an instance at all. Any type that has been declared as immutable by XStream itself before version 1.4 will not be + referenceable now by default (all primitive types and their boxed counterparts, java.lang.Class, java.lang.String, + java.math.BigInteger, java.math.BigDecimal, java.io.File, java.net.URL, and java.awt.font.TextAttribute). All other + immutable types (java.util.Currency, java.util.UUID, java.net.URI, java.nio.charset.Charset and the empty collection + types) will currently still be referenceable at deserialization time for compatibility reasons. This support by + default will be dropped with the next major version of XStream. You can always overwrite the default by adding the same + type again as immutable with a different value for the referenceable flag.

    + +

    My lambda expression is serialized to null!

    Non-serializable lambda expressions to not contain any information at all to recreate the instance at a later time again. These instances are treated as temporary objects and as such XStream has no other possibility as to serialize null instead.

    @@ -532,6 +559,15 @@ path like "/doc/list/elem/field" XStream will then generate "/doc[1]/list[1]/elem[1]/field[1]". The two notations are transparent at deserialization time.

    + +

    Does XStream support entities?

    + +

    Entity support is completely dependent on the XML parser. XStream uses by default the Xpp3 parser that does not + support entities at all (like the kXML2 parser). Other parsers support entities, but they might have been turned + off to avoid XXE vulnerability. To enable the entities again, you have to + overload the individual method of the + HierarchicalStreamDriver + implementation that generated the parser factory.

    JSON specifics

    @@ -541,7 +577,7 @@

    As always, first for historical reasons! Main difference is that the JettisonMappedXmlDriver is a - thin wrapper around Jettison in combination with the + thin wrapper around Jettison in combination with the StaxDriver, while the JsonHierarchicalStreamDriver uses an own more flexible implementation, but can only be used to generate JSON, deserialization is not implemented.

    @@ -661,12 +697,56 @@
    • Prevent the usage of the reflection-based converters. Register an own converter with priority LOW that claims to handle any type and throw a ConversionException in the marshal and unmarshal methods.
    • -
    • Overload XStream.setupConverters() and register only converters for the types that are allowd in your object +
    • Overload XStream.setupConverters() and register only converters for the types that are allowed in your object graph.
    • Provide own implementations for ConverterLookup and ConverterRegistry constructing the XStream. Your implementation can then select the appropriate converter at lookup time on its own or prevent the registration of specific converters.
    • +
    • Since XStream 1.4.7 you have the possibility to setup rules for the class types + that are allowed to be deserialized.
    + + +

    Is XStream XXE vulnerable?

    + +

    XStream does not contain an own XML parser, therefore it depends on the parser selected with the + HierarchicalStreamDriver + if the current XStream instance is + XXE vulnerable at deserialization time. However, XStream tries to deactivate the processing of external + entities by default. Status for the different supported XML parsers:

    + + + + + + + + + + + + + + + +
    DriverVulnerableExplanation
    BEAStaxDriveryesSetting XMLInputFactory.setProperty( + XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false) is only respected for general entities, but not + for parameter entities.
    Dom4JDrivernoDriver turns off DOCTYPE processing to suppress handling of external + entities with SAX reader in use.
    DomDriverno, Java 6 or higher
    yes, Java 5
    Driver turns off DOCTYPE processing with + Java 7 runtime or higher to suppress handling of external entities with SAX reader in use. Suppress external + entities only for Java 6 or lower, fails for parameterized entities.
    JDomDrivernoDriver turns off DOCTYPE processing to suppress handling of external + entities with SAX reader in use.
    JDom2DrivernoDriver turns off DOCTYPE processing to suppress handling of external + entities with SAX reader in use.
    KXml2DomDrivernokXML2 parser does not support entities.
    KXml2DrivernokXML2 parser does not support entities.
    SjsxpDriverno, Java 7 or higher
    yes, Java 6
    Driver turns off support for external + entities for the internal StaX parser of the Sun JDK, but fails for parameter entities in a Java 6 runtime.
    StandardStaxDriver?Driver tries to turns off support for external entities for + the internal StaX parser of the Java runtime. Save for Oracle JDK 7 or higher.
    StaxDriver?Driver tries to turns off support for external entities for the + standard StaX parser. However, the finally used StAX implementation is defined externally (see JDK + documentation) and a test should be made on the target platform to ensure that the parser respects the setting.
    WstxDrivernoDriver turns off support for external entities for the Woodstox StAX + parser.
    XomDriveryesXOM uses an internal list to test for available SAX parsers on the + classpath and will explicitly enable external entities, even if the SAXBuilder instance is provided manually.
    + +

    Note: Only a HierarchicalStreamReader created with the HierarchicalStreamDriver is setup to + avoid the XXE vulnerability. If you create such driver instances on your own, it is your task to setup the XML + parser instance on your own.

    Comparison to other products

    diff --git a/xstream-distribution/src/content/how-to-contribute.html b/xstream-distribution/src/content/how-to-contribute.html index f98124a..a67e68a 100644 --- a/xstream-distribution/src/content/how-to-contribute.html +++ b/xstream-distribution/src/content/how-to-contribute.html @@ -1,7 +1,7 @@ - - Developers' Mailing List - - - -

    General discussion and support for anyone who wants to get involved in the development of XStream.

    - -

    Note, if you just have questions about using XStream, you should use the user's mailing list.

    - - - - - - - - - - -
    Post(Un-)Subscribe
    dev@xstream.codehaus.orgManage Email
    - -

    Due to massive abuse by spammers the subscriptions is centralized for email lists of all projects hosted on Codehaus. - After following the link above just register with your email address and you can manage all subscriptions at once.

    - -

    Archives

    - - - - - diff --git a/xstream-distribution/src/content/list-user.html b/xstream-distribution/src/content/list-user.html deleted file mode 100644 index 1a98c38..0000000 --- a/xstream-distribution/src/content/list-user.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - Users' Mailing List - - - -

    General discussion and support for anyone using or evaluating XStream.

    - - - - - - - - - - -
    Post(Un-)Subscribe
    user@xstream.codehaus.orgManage Email
    - -

    Due to massive abuse by spammers the subscriptions is centralized for email lists of all projects hosted on Codehaus. - After following the link above just register with your email address and you can manage all subscriptions at once.

    - -

    Archives

    - - - - - diff --git a/xstream-distribution/src/content/mailing-lists.html b/xstream-distribution/src/content/mailing-lists.html new file mode 100644 index 0000000..6212cf0 --- /dev/null +++ b/xstream-distribution/src/content/mailing-lists.html @@ -0,0 +1,44 @@ + + + + Mailing Lists + + + +

    List and Forum

    + +

    The mailing lists have been consolidated into one Google Group for + users and developers asking questions about XStream's + usage, making enhancements requests or proposing improvements for its implementation. Another read-only Google + Group is for all kind of notifications like + announcements, commit or build server status messages.

    + +

    Google Groups provide standard mailing list functionality as well as a user interface in form of a web-based forum.

    + +

    News

    + +

    Gmane offers a news server interface for ordinary mailing lists. The + service collects all mails from the mailing list and offers them in a news group. The same news group was already + used for the old user's mailing list of Codehaus.

    + + + +

    Archives

    + +

    The former mailing lists hosted at Codehaus are gone, but still available in public archives + at MarkMail.

    + + + diff --git a/xstream-distribution/src/content/manual.html b/xstream-distribution/src/content/manual.html deleted file mode 100644 index a10c23f..0000000 --- a/xstream-distribution/src/content/manual.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - Documentation - - - -

    Essentials

    - -
      -
    • Uses of XStream
    • -
    • Architecture Overview
    • -
    - - - - \ No newline at end of file diff --git a/xstream-distribution/src/content/news.html b/xstream-distribution/src/content/news.html index 9731b1d..6af3887 100644 --- a/xstream-distribution/src/content/news.html +++ b/xstream-distribution/src/content/news.html @@ -1,7 +1,7 @@ - - Parser Benchmarks - - - - -

    Benchmark results are always dependent on a very individual setup. Normally it is not useful to generalize such results - for every use case, but it can give you a hint. However, if you're really in the need of maximum performance, you should - probably create an own benchmark with your objects or even use a profiler to detect the real hot spots in your application.

    - -

    Benchmark Values

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ParserSingle Value ConvertersStandard ConvertersReflection ConverterSerializable Converter
    W3C DOM1823.01495.01437.01758.0
    JDOM2620.01796.01776.02030.0
    DOM4J1824.01811.02636.02177.0
    XOM571.0716.0995.0958.0
    StAX (BEA)430.0359.0531.0737.0
    StAX (Woodstox)357.0344.0535.0725.0
    StAX (SJSXP)332.0445.0491.0667.0
    XPP (Xpp3)351.0395.0544.0743.0
    XPP (kXML2)299.0353.0517.0761.0
    XppDom (Xpp3)351.0395.0544.0743.0
    XppDom (kXML2)299.0353.0517.0761.0
    - -

    Setup

    - -

    The values have been generated running the ParserBenchmark harness of the XStream benchmark module's test code.

    - -
    -
    Single Value Converters
    -
    A list with a set of 10 objects of different types (like String, int, File, Locale, Double, ...) that will - be all handled by a SingleValueConverter.
    -
    Standard Converters
    -
    A list with a set of 6 objects of different types (like Properties, Color, Class, Method, ...) that will - be all handled by a specialized converter processing nested XML elements.
    -
    Reflection Converter
    -
    A list with a set of 6 objects of different types (One, Five of the XStream benchmark package) that will be - all handled by the ReflectionConverter.
    -
    Serializable Converter
    -
    A list with a set of 6 objects of different types (SerializableOne, SerializableFive of the XStream - benchmark package) that will be all handled by the SerializableConverter.
    -
    JavaBean Converter
    -
    A list with a set of 6 objects of different types (OneBean, FiveBean of the XStream benchmark package) that - will be all handled by the JavaBeanConverter. This converter has been registered especially for this two types.
    -
    - -

    Environment

    - -

    The values above's unit is ms measured after 1000 unmarshalling operations with the object graphs described in - the setup using XStream 1.4. The benchmark was run on an AMD Athlon with 2.1GHz running a JVM of Sun JDK 1.6.0_13 - (32-bit) in Gentoo Linux. Note again, that these values are no replacement for real profiler results and they may - vary from run to run for ~100ms due to this machine's background processes on a single CPU. However, it can give - you some idea of what you can expect using different parser technologies.

    - - - \ No newline at end of file diff --git a/xstream-distribution/src/content/references.html b/xstream-distribution/src/content/references.html index d50ffc7..40c63ad 100644 --- a/xstream-distribution/src/content/references.html +++ b/xstream-distribution/src/content/references.html @@ -1,6 +1,6 @@ + 4.0.0 + + com.thoughtworks.xstream + xstream-parent + 1.4.9 + + xstream-jmh + jar + XStream JMH Benchmark + JMH Benchmark suite of XStream. + + + + jdk18-ge + + [1.8,) + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${version.plugin.maven.javadoc} + + ${javadoc.xdoclint} + false + ${version.java.source} + + ${link.javadoc.javase} + + + + + + + + + + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + org.apache.maven.plugins + maven-assembly-plugin + + + app + package + + attached + + + + ${basedir}/src/assembly-app.xml + + + + + + + + + + + com.thoughtworks.xstream + xstream + + + org.openjdk.jmh + jmh-core + + + org.openjdk.jmh + jmh-generator-annprocess + + + + xpp3 + xpp3_min + runtime + + + xmlpull + xmlpull + runtime + + + net.sf.kxml + kxml2-min + runtime + + + stax + stax + runtime + + + org.codehaus.woodstox + wstx-asl + runtime + + + dom4j + dom4j + runtime + + + org.jdom + jdom + runtime + + + org.jdom + jdom2 + runtime + + + xom + xom + runtime + + + org.codehaus.jettison + jettison + runtime + + + + 1.6 + 1.6 + + diff --git a/xstream-jmh/src/application/bin/xstream-jmh.cmd b/xstream-jmh/src/application/bin/xstream-jmh.cmd new file mode 100644 index 0000000..e36e071 --- /dev/null +++ b/xstream-jmh/src/application/bin/xstream-jmh.cmd @@ -0,0 +1,71 @@ +@echo off +@REM Copyright (C) 2015 XStream Committers. +@REM All rights reserved. +@REM +@REM The software in this package is published under the terms of the BSD +@REM style license a copy of which has been included with this distribution in +@REM the LICENSE.txt file. +@REM +@REM Created on 28. October 2015 by Joerg Schaible + +@REM Run XStream JMH +if "%XSTREAM_SCRIPT_ECHO%"=="on" echo on + +if "%OS%"=="Windows_NT" @setlocal +if "%OS%"=="WINNT" @setlocal + +@REM * Set title +@REM *********** +title ScalarisDMS + +@REM * Goto script root dir +@REM ********************** +cd /d %~dp0\.. + +@REM * Initialize environment +@REM ************************ +@REM JAVA_OPTS and APP_OPTS can be set from outside +set JAVA_BIN= +set APP_CP= + +@REM * Set Java executable +@REM ********************* +if not defined JAVA_EXE set JAVA_EXE=java.exe +if "%JAVA_BIN%" NEQ "" if exist %JAVA_BIN% goto SetClassPath +if defined JAVA_HOME if "%JAVA_HOME%" NEQ "" set JAVA_BIN=%JAVA_HOME%\bin\%JAVA_EXE% +if exist %JAVA_BIN% goto SetClassPath +if defined JDK_HOME if "%JDK_HOME%" NEQ "" set JAVA_BIN=%JDK_HOME%\jre\bin\%JAVA_EXE% +if exist %JAVA_BIN% goto SetClassPath +set JAVA_BIN=%JAVA_EXE% + +:SetClassPath +@REM * Set class path +@REM **************** +for %%i in (lib\*.jar) do call :APP_CP_append %%i +call :APP_CP_append "config" + +@REM * Set options +@REM ************* +set JAVA_OPTS=%JAVA_OPTS% -Xmx1024m + + +@REM * Main class +@REM ************ +set MAIN_CLASS=org.openjdk.jmh.Main + +@REM * Run application +@REM ***************** +%JAVA_BIN% %JAVA_OPTS% %APP_DEFINES% -cp %APP_CP% %MAIN_CLASS% %APP_OPTS% %* + + +if "%OS%"=="Windows_NT" @endlocal +goto :EOF + + +@REM *************** +@REM * Sub functions +@REM *************** + +:APP_CP_append + set APP_CP=%APP_CP%;%1 + goto :EOF diff --git a/xstream-jmh/src/application/bin/xstream-jmh.sh b/xstream-jmh/src/application/bin/xstream-jmh.sh new file mode 100644 index 0000000..818697f --- /dev/null +++ b/xstream-jmh/src/application/bin/xstream-jmh.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# Copyright (C) 2015 XStream Committers. +# All rights reserved. +# +# The software in this package is published under the terms of the BSD +# style license a copy of which has been included with this distribution in +# the LICENSE.txt file. +# +# Created on 28. October 2015 by Joerg Schaible + +# Run XStream JMH + +# * Goto script root dir +# ********************** +cd `dirname $0`/.. + +# * Initialize environment +# ************************ +# JAVA_OPTS and APP_OPTS can be set from outside +JAVA_BIN= +APP_CP= + +# * Set Java executable +# ********************* +if [ -z "$JAVA_EXE" ]; then + JAVA_EXE=java +fi +if [ -z "$JAVA_BIN" ] || [ ! -r $JAVA_BIN ]; then + JAVA_BIN=$JAVA_HOME/bin/$JAVA_EXE + if [ -z "$JAVA_HOME" ] || [ ! -r $JAVA_BIN ]; then + JAVA_BIN=$JDK_HOME/jre/bin/$JAVA_EXE + if [ -z "$JDK_HOME" ] || [ ! -r $JAVA_BIN ]; then + JAVA_BIN=$JAVA_EXE + fi + fi +fi + +# * Set class path +# **************** +for i in lib/*.jar; do + APP_CP=$APP_CP:$i +done + +# * Set options +# ************* +JAVA_OPTS="$JAVA_OPTS -Xmx1024m" + +# * Main class +# ************ +MAIN_CLASS=org.openjdk.jmh.Main + +# * Debug +# ******* +if [ "$XSTREAM_SCRIPT_ECHO" = "on" ]; then + echo JAVA_BIN=$JAVA_BIN + echo JAVA_OPTS=$JAVA_OPTS + echo APP_OPTS=$APP_OPTS + echo APP_CP=$APP_CP + echo MAIN_CLASS=$MAIN_CLASS +fi + +# * Run application +# ***************** +$JAVA_BIN $JAVA_OPTS -cp $APP_CP $MAIN_CLASS $APP_OPTS "$@" + diff --git a/xstream-jmh/src/assembly-app.xml b/xstream-jmh/src/assembly-app.xml new file mode 100644 index 0000000..ca7082f --- /dev/null +++ b/xstream-jmh/src/assembly-app.xml @@ -0,0 +1,43 @@ + + + app + + zip + + + + + src/application/bin + bin + 0755 + + + src/reference + reference + + + + + lib + false + runtime + + + diff --git a/xstream-jmh/src/java/com/thoughtworks/xstream/benchmark/jmh/ConverterTypeBenchmark.java b/xstream-jmh/src/java/com/thoughtworks/xstream/benchmark/jmh/ConverterTypeBenchmark.java new file mode 100644 index 0000000..9fce668 --- /dev/null +++ b/xstream-jmh/src/java/com/thoughtworks/xstream/benchmark/jmh/ConverterTypeBenchmark.java @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2015 XStream Committers. + * All rights reserved. + * + * The software in this package is published under the terms of the BSD + * style license a copy of which has been included with this distribution in + * the LICENSE.txt file. + * + * Created on 20.11.2015 by Joerg Schaible + */ +package com.thoughtworks.xstream.benchmark.jmh; + +import java.math.BigInteger; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.BenchmarkParams; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.converters.ConversionException; +import com.thoughtworks.xstream.converters.Converter; +import com.thoughtworks.xstream.converters.MarshallingContext; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.converters.javabean.JavaBeanConverter; +import com.thoughtworks.xstream.converters.reflection.ReflectionConverter; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +import com.thoughtworks.xstream.io.xml.Xpp3Driver; + + +/** + * Benchmark for the different converter types. + * + * @author Jörg Schaible + * @since 1.4.9 + */ +@BenchmarkMode(Mode.AverageTime) +@Fork(value = 1) +@Measurement(iterations = 16) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +@Threads(4) +@Warmup(iterations = 5) +public class ConverterTypeBenchmark { + + private XStream xstream; + private Model array[]; + private String xml; + + @SuppressWarnings("javadoc") + public static class Model { + private char ch; + private int i; + private String s; + private double d; + private float f; + private BigInteger bi; + private UUID uuid; + + public Model() { + ch = 0; + i = 0; + d = 0.0; + f = 0.0f; + } + + public Model(final int i) { + ch = (char)(i % 256); + this.i = i; + s = Integer.toString(i, 2); + d = Math.PI * i; + f = (float)(Math.E * i); + bi = new BigInteger(s, 2); + uuid = UUID.randomUUID(); + } + + public char getCh() { + return ch; + } + + public void setCh(final char ch) { + this.ch = ch; + } + + public int getI() { + return i; + } + + public void setI(final int i) { + this.i = i; + } + + public String getS() { + return s; + } + + public void setS(final String s) { + this.s = s; + } + + public Double getD() { + return d; + } + + public void setD(final Double d) { + this.d = d; + } + + public Float getF() { + return f; + } + + public void setF(final Float f) { + this.f = f; + } + + public BigInteger getBi() { + return bi; + } + + public void setBi(final BigInteger bi) { + this.bi = bi; + } + + public UUID getUuid() { + return uuid; + } + + public void setUuid(final UUID uuid) { + this.uuid = uuid; + } + } + + /** + * Converter for a Model. + * + * @since 1.4.9 + */ + public static final class ModelConverter implements Converter { + + public boolean canConvert(@SuppressWarnings("rawtypes") final Class type) { + return type == Model.class; + } + + public void marshal(final Object source, final HierarchicalStreamWriter writer, + final MarshallingContext context) { + final Model type = Model.class.cast(source); + writeElement(writer, "ch", String.valueOf(type.getCh())); + writeElement(writer, "i", String.valueOf(type.getI())); + writeElement(writer, "s", type.getS()); + writeElement(writer, "d", String.valueOf(type.getD())); + writeElement(writer, "f", String.valueOf(type.getF())); + writeElement(writer, "bi", type.getBi().toString()); + writeElement(writer, "uuid", type.getUuid().toString()); + } + + private void writeElement(final HierarchicalStreamWriter writer, final String name, final String value) { + writer.startNode(name); + writer.setValue(value); + writer.endNode(); + } + + public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { + final Model type = new Model(); + while (reader.hasMoreChildren()) { + reader.moveDown(); + final String value = reader.getValue(); + final String name = reader.getNodeName(); + if (name.equals("ch")) { + if (value.length() != 1) { + throw new ConversionException("Not a single character"); + } + type.setCh(value.charAt(0)); + } else if (name.equals("i")) { + type.setI(Integer.parseInt(value)); + } else if (name.equals("s")) { + type.setS(value); + } else if (name.equals("d")) { + type.setD(Double.parseDouble(value)); + } else if (name.equals("f")) { + type.setF(Float.parseFloat(value)); + } else if (name.equals("bi")) { + type.setBi(new BigInteger(value)); + } else if (name.equals("uuid")) { + type.setUuid(UUID.fromString(value)); + } else { + throw new ConversionException("Unkown element"); + } + reader.moveUp(); + } + + return type; + } + + } + + /** + * Initialize the XML string to deserialize. + * + * @since 1.4.9 + */ + @Setup + public void init() { + array = new Model[1000]; + for (int i = 0; i < array.length; ++i) { + array[i] = new Model(i); + } + } + + /** + * Setup the data to deserialize. + * + * @param params the parameters of the benchmark + * @since 1.4.9 + */ + @Setup(Level.Trial) + public void setUp(final BenchmarkParams params) { + xstream = new XStream(new Xpp3Driver()); + xstream.allowTypes(new Class[]{Model.class}); + final String benchmark = params.getBenchmark(); + final String name = benchmark.substring(ConverterTypeBenchmark.class.getName().length() + 1); + if (name.equals("reflection")) { + xstream.registerConverter(new ReflectionConverter(xstream.getMapper(), xstream.getReflectionProvider(), + Model.class)); + } else if (name.equals("javaBean")) { + xstream.registerConverter(new JavaBeanConverter(xstream.getMapper(), Model.class)); + } else if (name.equals("custom")) { + xstream.registerConverter(new ModelConverter()); + } else { + throw new IllegalStateException("Unsupported benchmark type: " + benchmark); + } + xml = xstream.toXML(array); + // System.out.println(xstream.toXML(array[0])); + } + + /** + * Use ReflectionConverter. + * + * @since 1.4.9 + */ + @Benchmark + public void reflection() { + run(); + } + + /** + * Use JavaBeanConverter. + * + * @since 1.4.9 + */ + @Benchmark + public void javaBean() { + run(); + } + + /** + * Use custom converter. + * + * @since 1.4.9 + */ + @Benchmark + public void custom() { + run(); + } + + private void run() { + final Object o = xstream.fromXML(xml); + assert xstream.toXML(o).equals(xml) : "XML differs"; + } +} diff --git a/xstream-jmh/src/java/com/thoughtworks/xstream/benchmark/jmh/NameCoderBenchmark.java b/xstream-jmh/src/java/com/thoughtworks/xstream/benchmark/jmh/NameCoderBenchmark.java new file mode 100644 index 0000000..50fb3a1 --- /dev/null +++ b/xstream-jmh/src/java/com/thoughtworks/xstream/benchmark/jmh/NameCoderBenchmark.java @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2015 XStream Committers. + * All rights reserved. + * + * The software in this package is published under the terms of the BSD + * style license a copy of which has been included with this distribution in + * the LICENSE.txt file. + * + * Created on 16. December 2015 by Joerg Schaible, renamed from XmlFriendlyBenchmark + */ +package com.thoughtworks.xstream.benchmark.jmh; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.BenchmarkParams; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.naming.NameCoder; +import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder; +import com.thoughtworks.xstream.io.xml.Xpp3Driver; + + +/** + * Benchmark for different {@link NameCoder} implementations. + * + * @author Jörg Schaible + * @since 1.4.9 + */ +@BenchmarkMode(Mode.AverageTime) +@Fork(value = 1) +@Measurement(iterations = 25) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +@Threads(4) +@Warmup(iterations = 5) +public class NameCoderBenchmark { + + private XStream xstream; + private String xml; + private _1._2._3._4._5.Unfriendly array[]; + + /** + * No encoding, will create invalid XML for inner class types. + * + * @since 1.4.9 + */ + public static final class NoNameCoder implements NameCoder { + + public String encodeNode(final String name) { + return name; + } + + public String encodeAttribute(final String name) { + return name; + } + + public String decodeNode(final String nodeName) { + return nodeName; + } + + public String decodeAttribute(final String attributeName) { + return attributeName; + } + } + + /** + * Dollar encoding, will create invalid XML for class types in the default package. + * + * @since 1.4.9 + */ + public static final class DollarNameCoder implements NameCoder { + + public String encodeNode(final String name) { + return name.replace('$', '\u00b7'); + } + + public String encodeAttribute(final String name) { + return name.replace('$', '\u00b7'); + } + + public String decodeNode(final String nodeName) { + return nodeName.replace('\u00b7', '$'); + } + + public String decodeAttribute(final String attributeName) { + return attributeName.replace('\u00b7', '$'); + } + } + + /** + * Dollar encoding with an escaped underscore, may create invalid XML for class types defined in other languages + * running on the JVM. + * + * @since 1.4.9 + */ + public static class EscapedUnderscoreNameCoder implements NameCoder { + + public String encodeNode(final String name) { + final int length = name.length(); + final StringBuilder sb = new StringBuilder(length + 20); + for (int i = 0; i < length; ++i) { + final char ch = name.charAt(i); + switch (ch) { + case '$': + sb.append("_-"); + break; + case '_': + sb.append("__"); + break; + default: + sb.append(ch); + } + } + return sb.toString(); + } + + public String encodeAttribute(final String name) { + return encodeNode(name); + } + + public String decodeNode(final String nodeName) { + final int length = nodeName.length(); + final StringBuilder sb = new StringBuilder(length); + for (int i = 0; i < length; ++i) { + char ch = nodeName.charAt(i); + if (ch == '_') { + if (++i == length) { + throw new IllegalStateException(); + } + ch = nodeName.charAt(i); + switch (ch) { + case '_': + sb.append(ch); + break; + case '-': + sb.append('$'); + break; + default: + throw new IllegalStateException(); + } + } else { + sb.append(ch); + } + } + return sb.toString(); + } + + public String decodeAttribute(final String attributeName) { + return decodeNode(attributeName); + } + } + + /** + * Cached dollar encoding with an escaped underscore, may create invalid XML for class types defined in other + * languages running on the JVM. + * + * @since 1.4.9 + */ + public static class CachedEscapedUnderscoreNameCoder extends EscapedUnderscoreNameCoder { + private final ConcurrentMap encoderCache = new ConcurrentHashMap(); + private final ConcurrentMap decoderCache = new ConcurrentHashMap(); + + @Override + public String encodeNode(final String name) { + String encoded = encoderCache.get(name); + if (encoded == null) { + encoded = super.encodeNode(name); + encoderCache.putIfAbsent(name, encoded); + decoderCache.putIfAbsent(encoded, name); + } + return encoded; + } + + @Override + public String decodeNode(final String nodeName) { + String decoded = decoderCache.get(nodeName); + if (decoded == null) { + decoded = super.decodeNode(nodeName); + decoderCache.putIfAbsent(nodeName, decoded); + encoderCache.putIfAbsent(decoded, nodeName); + } + return decoded; + } + } + + private static class _1 { + private static class _2 { + private static class _3 { + private static class _4 { + private static class _5 { + private static class Unfriendly { + @SuppressWarnings("unused") + final int __i__i__; + @SuppressWarnings("unused") + final String x__x__; + @SuppressWarnings("unused") + final Unfriendly __; + + public Unfriendly(final int i, final Unfriendly u) { + __i__i__ = i; + x__x__ = Integer.toHexString(i); + __ = u; + } + } + } + } + } + } + } + + /** + * Initialize the XML string to deserialize. + * + * @since 1.4.9 + */ + @Setup + public void init() { + array = new _1._2._3._4._5.Unfriendly[250]; + for (int i = 0; i < array.length / 10; ++i) { + final int idx = i * 10; + for (int j = 0; j < 10; ++j) { + array[idx] = new _1._2._3._4._5.Unfriendly(idx + 9 - j, array[idx]); + if (j < 9) { + array[idx + j + 1] = array[idx]; + } + } + } + } + + /** + * Setup the data to deserialize. + * + * @param params the parameters of the benchmark + * @since 1.4.9 + */ + @Setup(Level.Trial) + public void setUp(final BenchmarkParams params) { + final String benchmark = params.getBenchmark(); + final NameCoder nameCoder; + final String name = benchmark.substring(NameCoderBenchmark.class.getName().length() + 1); + if (name.equals("noCoding")) { + nameCoder = new NoNameCoder(); + } else if (name.equals("dollarCoding")) { + nameCoder = new DollarNameCoder(); + } else if (name.equals("escapedUnderscoreCoding")) { + nameCoder = new EscapedUnderscoreNameCoder(); + } else if (name.equals("cachedEscapedUnderscoreCoding")) { + nameCoder = new CachedEscapedUnderscoreNameCoder(); + } else if (name.equals("xmlFriendlyCoding")) { + nameCoder = new XmlFriendlyNameCoder(); + } else { + throw new IllegalStateException("Unsupported benchmark type: " + benchmark); + } + xstream = new XStream(new Xpp3Driver(nameCoder)); + xstream.allowTypes(new Class[]{_1._2._3._4._5.Unfriendly.class}); + if (nameCoder.getClass() == NoNameCoder.class) { + xstream.alias(_1._2._3._4._5.Unfriendly.class.getName().replace('$', '\u00b7'), + _1._2._3._4._5.Unfriendly.class); + } + xml = xstream.toXML(array); + // System.out.println(xstream.toXML(array[0])); + } + + /** + * No encoding, will produce invalid XML for inner class types. + * + * @since 1.4.9 + */ + @Benchmark + public void noCoding() { + run(); + } + + /** + * Dollar encoding, will produce invalid XML for class types in the default package. + * + * @since 1.4.9 + */ + @Benchmark + public void dollarCoding() { + run(); + } + + /** + * Escaped underscore encoding, can encode any Java identifier. + * + * @since 1.4.9 + */ + @Benchmark + public void escapedUnderscoreCoding() { + run(); + } + + /** + * Escaped underscore encoding with caching, can encode any Java identifier. + * + * @since 1.4.9 + */ + @Benchmark + public void cachedEscapedUnderscoreCoding() { + run(); + } + + /** + * XML friendly encoding used by XStream as default, can encode any invalid XML character. + * + * @since 1.4.9 + */ + @Benchmark + public void xmlFriendlyCoding() { + run(); + } + + private void run() { + final String x = xstream.toXML(xstream.fromXML(xml)); + assert x.equals(xml) : "XML differs"; + } +} diff --git a/xstream-jmh/src/java/com/thoughtworks/xstream/benchmark/jmh/ParserBenchmark.java b/xstream-jmh/src/java/com/thoughtworks/xstream/benchmark/jmh/ParserBenchmark.java new file mode 100644 index 0000000..d7b19bf --- /dev/null +++ b/xstream-jmh/src/java/com/thoughtworks/xstream/benchmark/jmh/ParserBenchmark.java @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2015 XStream Committers. + * All rights reserved. + * + * The software in this package is published under the terms of the BSD + * style license a copy of which has been included with this distribution in + * the LICENSE.txt file. + * + * Created on 25. October 2015 by Joerg Schaible + */ +package com.thoughtworks.xstream.benchmark.jmh; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.BenchmarkParams; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.HierarchicalStreamDriver; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +import com.thoughtworks.xstream.io.binary.BinaryStreamDriver; +import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver; +import com.thoughtworks.xstream.io.xml.BEAStaxDriver; +import com.thoughtworks.xstream.io.xml.Dom4JDriver; +import com.thoughtworks.xstream.io.xml.DomDriver; +import com.thoughtworks.xstream.io.xml.JDom2Driver; +import com.thoughtworks.xstream.io.xml.JDomDriver; +import com.thoughtworks.xstream.io.xml.KXml2Driver; +import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; +import com.thoughtworks.xstream.io.xml.StandardStaxDriver; +import com.thoughtworks.xstream.io.xml.WstxDriver; +import com.thoughtworks.xstream.io.xml.XomDriver; +import com.thoughtworks.xstream.io.xml.Xpp3Driver; + + +/** + * Benchmark for the different {@link HierarchicalStreamDriver} implementations. + * + * @author Jörg Schaible + * @since 1.4.9 + */ +@BenchmarkMode(Mode.AverageTime) +@Fork(value = 1) +@Measurement(iterations = 15) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +@Threads(1) +@Warmup(iterations = 5) +public class ParserBenchmark { + + /** + * Driver factory. Enum values used as parameter for the parser benchmark methods. + * + * @author Jörg Schaible + * @since 1.4.9 + */ + public enum DriverFactory { + /** + * Factory for the {@link Xpp3Driver}. + * + * @since 1.4.9 + */ + Xpp3(new Xpp3Driver()), // + /** + * Factory for the {@link KXml2Driver}. + * + * @since 1.4.9 + */ + kXML2(new KXml2Driver()), // + /** + * Factory for the {@link StandardStaxDriver}. + * + * @since 1.4.9 + */ + JDKStax(new StandardStaxDriver()), // + /** + * Factory for the {@link WstxDriver}. + * + * @since 1.4.9 + */ + Woodstox(new WstxDriver()), // + /** + * Factory for the {@link BEAStaxDriver}. + * + * @since 1.4.9 + */ + BEAStax(new BEAStaxDriver()), // + /** + * Factory for the {@link DomDriver}. + * + * @since 1.4.9 + */ + DOM(new DomDriver()), // + /** + * Factory for the {@link Dom4JDriver}. + * + * @since 1.4.9 + */ + DOM4J(new Dom4JDriver() { // XML writer of DOM4J fails + public HierarchicalStreamWriter createWriter(final Writer out) { + return new PrettyPrintWriter(out, getNameCoder()); + } + }), // + /** + * Factory for the {@link JDomDriver}. + * + * @since 1.4.9 + */ + JDom(new JDomDriver()), // + /** + * Factory for the {@link JDom2Driver}. + * + * @since 1.4.9 + */ + JDom2(new JDom2Driver()), // + /** + * Factory for the {@link XomDriver}. + * + * @since 1.4.9 + */ + Xom(new XomDriver()), // + /** + * Factory for the {@link BinaryStreamDriver}. + * + * @since 1.4.9 + */ + Binary(new BinaryStreamDriver()), // + /** + * Factory for the {@link JettisonMappedXmlDriver}. + * + * @since 1.4.9 + */ + Jettison(new JettisonMappedXmlDriver()); + + private final HierarchicalStreamDriver driver; + + private DriverFactory(final HierarchicalStreamDriver driver) { + this.driver = driver; + } + + /** + * Request the driver of the instantiated factory. + * + * @return the driver + * @since 1.4.9 + */ + public HierarchicalStreamDriver getDriver() { + return driver; + } + } + + /** + * Data factory. Enum values used as data generator and checker for the individual parser benchmark methods. Method + * names define the data factory to use for the benchmark. + * + * @author Jörg Schaible + * @since 1.4.9 + */ + public enum DataFactory { + /** + * A single element with a text of 100.000 characters. + * + * @author Jörg Schaible + * @since 1.4.9 + */ + BigText { + private int length; + private String start; + private String end; + + @Override + public void writeData(final HierarchicalStreamWriter writer) { + int length = 100000; + final StringBuilder builder = new StringBuilder(length); + int i = 0; + while (length > 0) { + final int codePoint = i % Character.MAX_CODE_POINT; + if (Character.isLetterOrDigit(codePoint)) { + builder.appendCodePoint(codePoint); + --length; + } + ++i; + } + final String s = builder.toString(); + this.length = s.length(); + start = s.substring(0, 100); + end = s.substring(this.length - 100); + + writer.startNode("string"); + writer.setValue(s); + writer.endNode(); + } + + @Override + public void checkData(final Object o) { + final String s = String.class.cast(o); + assert length == s.length() : BigText + " fails length"; + assert start.equals(s.substring(0, 100)) : BigText + " fails start"; + assert end.equals(s.substring(length - 100)) : BigText + " fails end"; + } + }, + /** + * Nested list in list structure, 500 elements deep. + * + * @author Jörg Schaible + * @since 1.4.9 + */ + NestedElements { + private static final int DEPTH = 500; + private List list; + + @Override + public void writeData(final HierarchicalStreamWriter writer) { + for (int i = 0; i < DEPTH; ++i) { + writer.startNode("list"); + } + list = new ArrayList(Arrays.asList(42, 7, 3, -17)); + for (final Integer i : list) { + writer.startNode("int"); + writer.setValue(i.toString()); + writer.endNode(); + } + for (int i = 0; i < DEPTH; ++i) { + writer.endNode(); + } + } + + @Override + public void checkData(final Object o) { + List list = List.class.cast(o); + int depth = DEPTH; + while (depth-- > 1) { + assert list.size() == 1 : NestedElements + " fails list size"; + list = List.class.cast(list.get(0)); + } + assert this.list.equals(list) : NestedElements + " fails inner list"; + } + }, + /** + * An array with 1.000 elements. + * + * @author Jörg Schaible + * @since 1.4.9 + */ + ManyChildren { + private static final int LENGTH = 1000; + + @Override + public void writeData(final HierarchicalStreamWriter writer) { + int length = LENGTH; + writer.startNode("int-array"); + while (length-- > 0) { + writer.startNode("int"); + writer.setValue(String.valueOf(length)); + writer.endNode(); + } + writer.endNode(); + } + + @Override + public void checkData(final Object o) { + final int[] array = int[].class.cast(o); + assert LENGTH == array.length : ManyChildren + " fails length"; + assert LENGTH - 1 == array[0] : ManyChildren + " fails start"; + assert 0 == array[LENGTH - 1] : ManyChildren + " fails end"; + } + }; + /** + * Write the data of the factory into the writer of the hierarchical stream. + * + * @param writer the writer of the data + * @since 1.4.9 + */ + public abstract void writeData(HierarchicalStreamWriter writer); + + /** + * Check the deserialized object. + * + * @param o the object to check + * @since 1.4.9 + */ + public abstract void checkData(Object o); + } + + @Param + private DriverFactory driverFactory; + private DataFactory dataFactory; + private byte[] data; + private XStream xstream; + private HierarchicalStreamDriver driver; + + /** + * Initialize the XStream instance and instantiate the driver for the benchmark. + * + * @since 1.4.9 + */ + @Setup + public void init() { + xstream = new XStream(); + xstream.setMode(XStream.NO_REFERENCES); + driver = driverFactory.getDriver(); + } + + /** + * Setup the data to deserialize. + * + * @param params the parameters of the benchmark + * @since 1.4.9 + */ + @Setup(Level.Trial) + public void setUp(final BenchmarkParams params) { + final String benchmark = params.getBenchmark(); + dataFactory = DataFactory.valueOf(benchmark.substring(benchmark.lastIndexOf('.') + 6)); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(1024 * 1024); + final HierarchicalStreamWriter writer = driver.createWriter(baos); + dataFactory.writeData(writer); + writer.close(); + data = baos.toByteArray(); + } + + /** + * Parse an element with a big text as value. + * + * @since 1.4.9 + */ + @Benchmark + public void parseBigText() { + final Object o = xstream.unmarshal(driver.createReader(new ByteArrayInputStream(data))); + dataFactory.checkData(o); + } + + /** + * Parse a deeply nested structure. + * + * @since 1.4.9 + */ + @Benchmark + public void parseNestedElements() { + final Object o = xstream.unmarshal(driver.createReader(new ByteArrayInputStream(data))); + dataFactory.checkData(o); + } + + /** + * Parse an element with a lot of simple children. + * + * @since 1.4.9 + */ + @Benchmark + public void parseManyChildren() { + final Object o = xstream.unmarshal(driver.createReader(new ByteArrayInputStream(data))); + dataFactory.checkData(o); + } +} diff --git a/xstream-jmh/src/java/com/thoughtworks/xstream/benchmark/jmh/StringConverterBenchmark.java b/xstream-jmh/src/java/com/thoughtworks/xstream/benchmark/jmh/StringConverterBenchmark.java new file mode 100644 index 0000000..8b6274a --- /dev/null +++ b/xstream-jmh/src/java/com/thoughtworks/xstream/benchmark/jmh/StringConverterBenchmark.java @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2015 XStream Committers. + * All rights reserved. + * + * The software in this package is published under the terms of the BSD + * style license a copy of which has been included with this distribution in + * the LICENSE.txt file. + * + * Created on 08.11.2015 by Joerg Schaible + */ +package com.thoughtworks.xstream.benchmark.jmh; + +import java.io.StringWriter; +import java.util.Collections; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.BenchmarkParams; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.converters.SingleValueConverter; +import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter; +import com.thoughtworks.xstream.core.util.WeakCache; +import com.thoughtworks.xstream.io.xml.CompactWriter; +import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; +import com.thoughtworks.xstream.io.xml.Xpp3Driver; + + +/** + * Benchmark for different StringConverter implementations. + * + * @author Jörg Schaible + * @since 1.4.9 + */ +@BenchmarkMode(Mode.AverageTime) +@Fork(value = 1) +@Measurement(iterations = 16) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +@Threads(4) +@Warmup(iterations = 5) +public class StringConverterBenchmark { + + private XStream xstream; + private String xml; + + /** + * No memory usage for cache, but any string is a separate instance after deserialization. Memory consumption of the + * deserialized array is nearly 3 times compared to a converter that caches and reuses the strings. + * + * @since 1.4.9 + */ + public static final class NonCachingStringConverter extends AbstractSingleValueConverter { + + @Override + public boolean canConvert(@SuppressWarnings("rawtypes") final Class type) { + return type == String.class; + } + + @Override + public Object fromString(final String str) { + return str; + } + } + + /** + * Cache based on String.intern(). Uses PermGenSpace for Java 7 and below. + * + * @since 1.4.9 + */ + public static final class InternStringConverter extends AbstractSingleValueConverter { + + @Override + public boolean canConvert(@SuppressWarnings("rawtypes") final Class type) { + return type == String.class; + } + + @Override + public Object fromString(final String str) { + return str.intern(); + } + } + + /** + * Cache based on a synchronized WeakHashMap with weak keys. Ensures that the deserialized strings vanish when the + * deserialized object is GC'ed. + * + * @since 1.4.9 + */ + public class SynchronizedWeakCacheStringConverter extends AbstractSingleValueConverter { + + private final Map cache; + private final int lengthLimit; + + private SynchronizedWeakCacheStringConverter(final Map map, final int lengthLimit) { + cache = map; + this.lengthLimit = lengthLimit; + } + + /** + * Constructs a SynchronizedWeakCacheStringConverter. + * + * @param lengthLimit length limit for cached strings + * @since 1.4.9 + */ + @SuppressWarnings("unchecked") + public SynchronizedWeakCacheStringConverter(final int lengthLimit) { + this(Collections.synchronizedMap(new WeakCache()), lengthLimit); + } + + @Override + public boolean canConvert(@SuppressWarnings("rawtypes") final Class type) { + return type.equals(String.class); + } + + @Override + public Object fromString(final String str) { + if (cache != null && str != null && (lengthLimit < 0 || str.length() <= lengthLimit)) { + String s = cache.get(str); + + if (s == null) { + // fill cache + cache.put(str, str); + + s = str; + } + + return s; + } else { + return str; + } + } + } + + /** + * Cache based on a ConcurrentMap. Cache is never flushed. + * + * @since 1.4.9 + */ + public class ConcurrentHashMapStringConverter extends AbstractSingleValueConverter { + + private final ConcurrentMap cache; + private final int lengthLimit; + + private ConcurrentHashMapStringConverter(final ConcurrentMap map, final int lengthLimit) { + cache = map; + this.lengthLimit = lengthLimit; + } + + /** + * Constructs a ConcurrentHashMapStringConverter. + * + * @param lengthLimit length limit for cached strings + * @since 1.4.9 + */ + public ConcurrentHashMapStringConverter(final int lengthLimit) { + this(new ConcurrentHashMap(), lengthLimit); + } + + @Override + public boolean canConvert(@SuppressWarnings("rawtypes") final Class type) { + return type.equals(String.class); + } + + @Override + public Object fromString(final String str) { + if (cache != null && str != null && (lengthLimit < 0 || str.length() <= lengthLimit)) { + final String s = cache.putIfAbsent(str, str); + return s == null ? str : s; + } else { + return str; + } + } + } + + /** + * Initialize the XML string to deserialize. + * + * @since 1.4.9 + */ + @Setup + public void init() { + final String array[] = new String[300]; + for (int i = 0; i < 100;) { + array[i] = String.valueOf(++i); + } + for (int i = 100; i < 200;) { + array[i] = "Binary value " + i + ": " + Integer.toString(++i, 2); + } + for (int i = 200; i < 300;) { + array[i++] = UUID.randomUUID().toString().replace('-', ':'); + } + + final StringWriter stringWriter = new StringWriter(); + final PrettyPrintWriter writer = new CompactWriter(stringWriter); + writer.startNode("string-array"); + for (int i = 0; i < 10000; ++i) { + writer.startNode("string"); + final String s; + if ((i & 1) == 1) { + s = array[(i >> 1) % 100]; + } else if ((i & 2) == 2) { + s = array[100 + (i >> 2) % 100]; + } else if ((i & 4) == 4) { + s = array[200 + (i >> 3) % 100]; + } else { + s = "Random UUID: " + UUID.randomUUID().toString(); + } + writer.setValue(s); + writer.endNode(); + } + writer.endNode(); + writer.close(); + xml = stringWriter.toString(); + } + + /** + * Setup the data to deserialize. + * + * @param params the parameters of the benchmark + * @since 1.4.9 + */ + @Setup(Level.Trial) + public void setUp(final BenchmarkParams params) { + final String benchmark = params.getBenchmark(); + final SingleValueConverter converter; + final String name = benchmark.substring(StringConverterBenchmark.class.getName().length() + 1); + if ("nonCaching".equals(name)) { + converter = new NonCachingStringConverter(); + } else if ("intern".equals(name)) { + converter = new InternStringConverter(); + } else if ("unlimitedSynchronizedWeakCache".equals(name)) { + converter = new SynchronizedWeakCacheStringConverter(Integer.MAX_VALUE); + } else if ("limitedSynchronizedWeakCache".equals(name)) { + converter = new SynchronizedWeakCacheStringConverter(UUID.randomUUID().toString().length() + 2); + } else if ("unlimitedConcurrentMap".equals(name)) { + converter = new SynchronizedWeakCacheStringConverter(Integer.MAX_VALUE); + } else if ("limitedConcurrentMap".equals(name)) { + converter = new SynchronizedWeakCacheStringConverter(UUID.randomUUID().toString().length() + 2); + } else { + throw new IllegalStateException("Unsupported benchmark type: " + benchmark); + } + xstream = new XStream(new Xpp3Driver()); + xstream.registerConverter(converter); + } + + /** + * No cache for deserialized strings, each string is an own instance. + * + * @since 1.4.9 + */ + @Benchmark + public void nonCaching() { + run(); + } + + /** + * Any string is stored also in the String's internal memory space. + * + * @since 1.4.9 + */ + @Benchmark + public void intern() { + run(); + } + + /** + * Any string is cached in a weak entry. + * + * @since 1.4.9 + */ + @Benchmark + public void unlimitedSynchronizedWeakCache() { + run(); + } + + /** + * Strings of 38 characters or less are cached in a weak entry. + * + * @since 1.4.9 + */ + @Benchmark + public void limitedSynchronizedWeakCache() { + run(); + } + + /** + * Any string is cached in a concurrent map. + * + * @since 1.4.9 + */ + @Benchmark + public void unlimitedConcurrentMap() { + run(); + } + + /** + * Strings of 38 characters or less are cached in a concurrent map. + * + * @since 1.4.9 + */ + @Benchmark + public void limitedConcurrentMap() { + run(); + } + + private void run() { + final String[] array = (String[])xstream.fromXML(xml); + assert array.length == 10000 : "array length is " + array.length; + assert array[1].equals("1") : "2nd element was: " + array[1]; + assert array[9999].equals("100") : "last element was: " + array[9999]; + } +} diff --git a/xstream-jmh/src/reference/converterType.txt b/xstream-jmh/src/reference/converterType.txt new file mode 100644 index 0000000..bb4a4f1 --- /dev/null +++ b/xstream-jmh/src/reference/converterType.txt @@ -0,0 +1,4 @@ +Benchmark Mode Cnt Score Error Units +ConverterTypeBenchmark.custom avgt 16 11276718.384 ± 660395.171 ns/op +ConverterTypeBenchmark.javaBean avgt 16 28878706.293 ± 2718745.446 ns/op +ConverterTypeBenchmark.reflection avgt 16 40085786.696 ± 2712102.798 ns/op diff --git a/xstream-jmh/src/reference/nameCoder.txt b/xstream-jmh/src/reference/nameCoder.txt new file mode 100644 index 0000000..371f66b --- /dev/null +++ b/xstream-jmh/src/reference/nameCoder.txt @@ -0,0 +1,6 @@ +Benchmark Mode Cnt Score Error Units +NameCoderBenchmark.cachedEscapedUnderscoreCoding avgt 25 7867671.962 ± 513789.786 ns/op +NameCoderBenchmark.dollarCoding avgt 25 7767196.902 ± 594898.960 ns/op +NameCoderBenchmark.escapedUnderscoreCoding avgt 25 9894393.289 ± 556251.884 ns/op +NameCoderBenchmark.noCoding avgt 25 7299475.975 ± 363895.593 ns/op +NameCoderBenchmark.xmlFriendlyCoding avgt 25 8635501.208 ± 703145.664 ns/op diff --git a/xstream-jmh/src/reference/parsers.txt b/xstream-jmh/src/reference/parsers.txt new file mode 100644 index 0000000..a458738 --- /dev/null +++ b/xstream-jmh/src/reference/parsers.txt @@ -0,0 +1,37 @@ +Benchmark (driverFactory) Mode Cnt Score Error Units +ParserBenchmark.parseBigText Xpp3 avgt 15 2109341.076 ± 19369.901 ns/op +ParserBenchmark.parseBigText kXML2 avgt 15 3391204.266 ± 31040.727 ns/op +ParserBenchmark.parseBigText JDKStax avgt 15 7366387.272 ± 132117.523 ns/op +ParserBenchmark.parseBigText Woodstox avgt 15 1884858.525 ± 29364.633 ns/op +ParserBenchmark.parseBigText BEAStax avgt 15 3108517.699 ± 44905.395 ns/op +ParserBenchmark.parseBigText DOM avgt 15 10037380.795 ± 402305.418 ns/op +ParserBenchmark.parseBigText DOM4J avgt 15 7816280.084 ± 244809.864 ns/op +ParserBenchmark.parseBigText JDom avgt 15 6368317.636 ± 180516.662 ns/op +ParserBenchmark.parseBigText JDom2 avgt 15 5767640.105 ± 56112.801 ns/op +ParserBenchmark.parseBigText Xom avgt 15 7950778.533 ± 74710.412 ns/op +ParserBenchmark.parseBigText Binary avgt 15 1144243.750 ± 18924.227 ns/op +ParserBenchmark.parseBigText Jettison avgt 15 3002547.220 ± 39676.507 ns/op +ParserBenchmark.parseManyChildren Xpp3 avgt 15 1309607.210 ± 18539.487 ns/op +ParserBenchmark.parseManyChildren kXML2 avgt 15 1514514.680 ± 30170.502 ns/op +ParserBenchmark.parseManyChildren JDKStax avgt 15 1334398.501 ± 19324.267 ns/op +ParserBenchmark.parseManyChildren Woodstox avgt 15 1240767.393 ± 9572.885 ns/op +ParserBenchmark.parseManyChildren BEAStax avgt 15 1310406.961 ± 15256.069 ns/op +ParserBenchmark.parseManyChildren DOM avgt 15 54234293.351 ± 400240.973 ns/op +ParserBenchmark.parseManyChildren DOM4J avgt 15 92998322.952 ± 741924.537 ns/op +ParserBenchmark.parseManyChildren JDom avgt 15 7910979.223 ± 101267.507 ns/op +ParserBenchmark.parseManyChildren JDom2 avgt 15 10570210.653 ± 590365.314 ns/op +ParserBenchmark.parseManyChildren Xom avgt 15 38704485.310 ± 485342.779 ns/op +ParserBenchmark.parseManyChildren Binary avgt 15 1062031.901 ± 16391.974 ns/op +ParserBenchmark.parseManyChildren Jettison avgt 15 1159238.555 ± 18479.230 ns/op +ParserBenchmark.parseNestedElements Xpp3 avgt 15 3301732.767 ± 94076.135 ns/op +ParserBenchmark.parseNestedElements kXML2 avgt 15 8105934.241 ± 103517.648 ns/op +ParserBenchmark.parseNestedElements JDKStax avgt 15 688229.709 ± 2873.173 ns/op +ParserBenchmark.parseNestedElements Woodstox avgt 15 650470.623 ± 7920.722 ns/op +ParserBenchmark.parseNestedElements BEAStax avgt 15 669111.164 ± 10692.266 ns/op +ParserBenchmark.parseNestedElements DOM avgt 15 1917332.056 ± 31699.337 ns/op +ParserBenchmark.parseNestedElements DOM4J avgt 15 2108075.646 ± 98269.758 ns/op +ParserBenchmark.parseNestedElements JDom avgt 15 3862796.027 ± 74010.953 ns/op +ParserBenchmark.parseNestedElements JDom2 avgt 15 2980906.727 ± 43315.160 ns/op +ParserBenchmark.parseNestedElements Xom avgt 15 2471019.743 ± 45829.109 ns/op +ParserBenchmark.parseNestedElements Binary avgt 15 496839.565 ± 7180.978 ns/op +ParserBenchmark.parseNestedElements Jettison avgt 15 682182.733 ± 9614.467 ns/op diff --git a/xstream-jmh/src/reference/stringConverter.txt b/xstream-jmh/src/reference/stringConverter.txt new file mode 100644 index 0000000..171e589 --- /dev/null +++ b/xstream-jmh/src/reference/stringConverter.txt @@ -0,0 +1,7 @@ +Benchmark Mode Cnt Score Error Units +StringConverterBenchmark.intern avgt 16 23060982.052 ± 1760952.641 ns/op +StringConverterBenchmark.limitedConcurrentMap avgt 16 21796001.298 ± 2092865.246 ns/op +StringConverterBenchmark.limitedSynchronizedWeakCache avgt 16 21838410.801 ± 981634.755 ns/op +StringConverterBenchmark.nonCaching avgt 16 19626160.696 ± 1456804.584 ns/op +StringConverterBenchmark.unlimitedConcurrentMap avgt 16 21378299.003 ± 1913288.565 ns/op +StringConverterBenchmark.unlimitedSynchronizedWeakCache avgt 16 22011251.691 ± 1713892.622 ns/op