Skip to content

Commit

Permalink
Introduced "mapperWrappers" bean property
Browse files Browse the repository at this point in the history
Issue: SPR-10421
  • Loading branch information
jhoeller committed Aug 5, 2013
1 parent 3cd2eb8 commit c303234
Showing 1 changed file with 61 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Constructor;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -58,6 +59,7 @@
import com.thoughtworks.xstream.io.xml.XppDriver;
import com.thoughtworks.xstream.mapper.CannotResolveClassException;
import com.thoughtworks.xstream.mapper.Mapper;
import com.thoughtworks.xstream.mapper.MapperWrapper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
Expand All @@ -73,6 +75,7 @@
import org.springframework.oxm.UnmarshallingFailureException;
import org.springframework.oxm.XmlMappingException;
import org.springframework.oxm.support.AbstractMarshaller;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
Expand Down Expand Up @@ -123,6 +126,8 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin

private Mapper mapper;

private Class<?>[] mapperWrappers;

private ConverterLookup converterLookup = new DefaultConverterLookup();

private ConverterRegistry converterRegistry;
Expand Down Expand Up @@ -185,6 +190,16 @@ public void setMapper(Mapper mapper) {
this.mapper = mapper;
}

/**
* Set one or more custom XStream {@link MapperWrapper} classes.
* Each of those classes needs to have a constructor with a single argument
* of type {@link Mapper} or {@link MapperWrapper}.
* @since 4.0
*/
public void setMapperWrappers(Class<?>... mapperWrappers) {
this.mapperWrappers = mapperWrappers;
}

/**
* Set a custom XStream {@link ConverterLookup} to use.
* Also used as {@link ConverterRegistry} if the given reference implements it as well.
Expand Down Expand Up @@ -282,7 +297,7 @@ public void setUseAttributeFor(Map<?, ?> useAttributeFor) {
/**
* Specify implicit collection fields, as a Map consisting of {@code Class} instances
* mapped to comma separated collection field names.
*@see XStream#addImplicitCollection(Class, String)
* @see XStream#addImplicitCollection(Class, String)
*/
public void setImplicitCollections(Map<Class<?>, String> implicitCollections) {
this.implicitCollections = implicitCollections;
Expand All @@ -306,8 +321,8 @@ public void setAnnotatedClasses(Class<?>... annotatedClasses) {
}

/**
* Activate the autodetection mode of XStream.
* <p><b>Note</b> that auto-detection implies that the XStream is configured while
* Activate XStream's autodetection mode.
* <p><b>Note</b>: Autodetection implies that the XStream instance is being configured while
* it is processing the XML streams, and thus introduces a potential concurrency problem.
* @see XStream#autodetectAnnotations(boolean)
*/
Expand Down Expand Up @@ -348,7 +363,36 @@ public final void afterPropertiesSet() {
*/
protected XStream buildXStream() {
XStream xstream = new XStream(this.reflectionProvider, this.streamDriver,
this.beanClassLoader, this.mapper, this.converterLookup, this.converterRegistry);
this.beanClassLoader, this.mapper, this.converterLookup, this.converterRegistry) {
@Override
protected MapperWrapper wrapMapper(MapperWrapper next) {
MapperWrapper mapperToWrap = next;
if (mapperWrappers != null) {
for (Class<?> mapperWrapper : mapperWrappers) {
Assert.isAssignable(MapperWrapper.class, mapperWrapper);
Constructor<?> ctor;
try {
ctor = mapperWrapper.getConstructor(Mapper.class);
}
catch (NoSuchMethodException ex) {
try {
ctor = mapperWrapper.getConstructor(MapperWrapper.class);
}
catch (NoSuchMethodException ex2) {
throw new IllegalStateException("No appropriate MapperWrapper constructor found: " + mapperWrapper);
}
}
try {
mapperToWrap = (MapperWrapper) ctor.newInstance(mapperToWrap);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to construct MapperWrapper: " + mapperWrapper);
}
}
}
return mapperToWrap;
}
};

if (this.converters != null) {
for (int i = 0; i < this.converters.length; i++) {
Expand Down Expand Up @@ -391,7 +435,7 @@ else if (this.converters[i] instanceof SingleValueConverter) {
int idx = field.lastIndexOf('.');
if (idx != -1) {
String className = field.substring(0, idx);
Class clazz = ClassUtils.forName(className, this.beanClassLoader);
Class<?> clazz = ClassUtils.forName(className, this.beanClassLoader);
String fieldName = field.substring(idx + 1);
xstream.aliasField(alias, clazz, fieldName);
}
Expand All @@ -418,8 +462,7 @@ else if (this.converters[i] instanceof SingleValueConverter) {
}
else {
throw new IllegalArgumentException(
"Invalid argument 'attributes'. 'useAttributesFor' property takes map of <String, Class>," +
" when using a map key of type String");
"'useAttributesFor' takes Map<String, Class> when using a map key of type String");
}
}
else if (entry.getKey() instanceof Class) {
Expand All @@ -428,22 +471,20 @@ else if (entry.getKey() instanceof Class) {
xstream.useAttributeFor(key, (String) entry.getValue());
}
else if (entry.getValue() instanceof List) {
List list = (List) entry.getValue();

for (Object o : list) {
if (o instanceof String) {
xstream.useAttributeFor(key, (String) o);
List listValue = (List) entry.getValue();
for (Object element : listValue) {
if (element instanceof String) {
xstream.useAttributeFor(key, (String) element);
}
}
}
else {
throw new IllegalArgumentException("Invalid argument 'attributes'. " +
"'useAttributesFor' property takes either <Class, String> or <Class, List<String>> map," +
" when using a map key of type Class");
throw new IllegalArgumentException("'useAttributesFor' property takes either Map<Class, String> " +
"or Map<Class, List<String>> when using a map key of type Class");
}
}
else {
throw new IllegalArgumentException("Invalid argument 'attributes. " +
throw new IllegalArgumentException(
"'useAttributesFor' property takes either a map key of type String or Class");
}
}
Expand Down Expand Up @@ -482,16 +523,16 @@ private Map<String, Class<?>> toClassMap(Map<String, ?> map) throws ClassNotFoun
for (Map.Entry<String, ?> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Class type;
Class<?> type;
if (value instanceof Class) {
type = (Class) value;
type = (Class<?>) value;
}
else if (value instanceof String) {
String className = (String) value;
type = ClassUtils.forName(className, this.beanClassLoader);
}
else {
throw new IllegalArgumentException("Unknown value [" + value + "], expected String or Class");
throw new IllegalArgumentException("Unknown value [" + value + "] - expected String or Class");
}
result.put(key, type);
}
Expand Down Expand Up @@ -521,7 +562,7 @@ public final XStream getXStream() {


@Override
public boolean supports(Class clazz) {
public boolean supports(Class<?> clazz) {
if (ObjectUtils.isEmpty(this.supportedClasses)) {
return true;
}
Expand Down

0 comments on commit c303234

Please sign in to comment.