com.feilong.lib.xstream.mapper.AnnotationMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of feilong Show documentation
Show all versions of feilong Show documentation
feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.
/*
* 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
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
* Created on 07. November 2007 by Joerg Schaible
*/
package com.feilong.lib.xstream.mapper;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.feilong.lib.xstream.InitializationException;
import com.feilong.lib.xstream.annotations.XStreamAlias;
import com.feilong.lib.xstream.annotations.XStreamAliasType;
import com.feilong.lib.xstream.annotations.XStreamAsAttribute;
import com.feilong.lib.xstream.annotations.XStreamConverter;
import com.feilong.lib.xstream.annotations.XStreamConverters;
import com.feilong.lib.xstream.annotations.XStreamImplicit;
import com.feilong.lib.xstream.annotations.XStreamImplicitCollection;
import com.feilong.lib.xstream.annotations.XStreamInclude;
import com.feilong.lib.xstream.annotations.XStreamOmitField;
import com.feilong.lib.xstream.converters.Converter;
import com.feilong.lib.xstream.converters.ConverterLookup;
import com.feilong.lib.xstream.converters.ConverterMatcher;
import com.feilong.lib.xstream.converters.ConverterRegistry;
import com.feilong.lib.xstream.converters.SingleValueConverter;
import com.feilong.lib.xstream.converters.SingleValueConverterWrapper;
import com.feilong.lib.xstream.converters.reflection.ReflectionProvider;
import com.feilong.lib.xstream.core.ClassLoaderReference;
import com.feilong.lib.xstream.core.JVM;
import com.feilong.lib.xstream.core.util.DependencyInjectionFactory;
import com.feilong.lib.xstream.core.util.TypedNull;
/**
* A mapper that uses annotations to prepare the remaining mappers in the chain.
*
* @author Jörg Schaible
* @since 1.3
*/
public class AnnotationMapper extends MapperWrapper implements AnnotationConfiguration{
private boolean locked;
private transient Object[] arguments;
private final ConverterRegistry converterRegistry;
private transient ClassAliasingMapper classAliasingMapper;
private transient DefaultImplementationsMapper defaultImplementationsMapper;
private transient ImplicitCollectionMapper implicitCollectionMapper;
private transient FieldAliasingMapper fieldAliasingMapper;
private transient ElementIgnoringMapper elementIgnoringMapper;
private transient AttributeMapper attributeMapper;
private transient LocalConversionMapper localConversionMapper;
private final Map, Map, Converter>> converterCache = new HashMap<>();
private final Set> annotatedTypes = Collections.synchronizedSet(new HashSet>());
/**
* Construct an AnnotationMapper.
*
* @param wrapped
* the next {@link Mapper} in the chain
* @since 1.4.5
*/
public AnnotationMapper(final Mapper wrapped, final ConverterRegistry converterRegistry, final ConverterLookup converterLookup,
final ClassLoaderReference classLoaderReference, final ReflectionProvider reflectionProvider){
super(wrapped);
this.converterRegistry = converterRegistry;
annotatedTypes.add(Object.class);
setupMappers();
locked = true;
final ClassLoader classLoader = classLoaderReference.getReference();
arguments = new Object[] {
this,
classLoaderReference,
reflectionProvider,
converterLookup,
new JVM(),
classLoader != null ? classLoader : new TypedNull(ClassLoader.class) };
}
@Override
public String realMember(final Class type,final String serialized){
if (!locked){
processAnnotations(type);
}
return super.realMember(type, serialized);
}
@Override
public String serializedClass(final Class type){
if (!locked){
processAnnotations(type);
}
return super.serializedClass(type);
}
@Override
public Class defaultImplementationOf(final Class type){
if (!locked){
processAnnotations(type);
}
final Class defaultImplementation = super.defaultImplementationOf(type);
if (!locked){
processAnnotations(defaultImplementation);
}
return defaultImplementation;
}
@Override
public Converter getLocalConverter(final Class definedIn,final String fieldName){
if (!locked){
processAnnotations(definedIn);
}
return super.getLocalConverter(definedIn, fieldName);
}
@Override
public void autodetectAnnotations(final boolean mode){
locked = !mode;
}
@Override
public void processAnnotations(final Class[] initialTypes){
if (initialTypes == null || initialTypes.length == 0){
return;
}
locked = true;
final Set> types = new UnprocessedTypesSet();
for (final Class initialType : initialTypes){
types.add(initialType);
}
processTypes(types);
}
private void processAnnotations(final Class initialType){
if (initialType == null){
return;
}
final Set> types = new UnprocessedTypesSet();
types.add(initialType);
processTypes(types);
}
private void processTypes(final Set> types){
while (!types.isEmpty()){
final Iterator> iter = types.iterator();
final Class type = iter.next();
iter.remove();
synchronized (type){
if (annotatedTypes.contains(type)){
continue;
}
try{
if (type.isPrimitive()){
continue;
}
addParametrizedTypes(type, types);
processConverterAnnotations(type);
processAliasAnnotation(type, types);
processAliasTypeAnnotation(type);
if (type.isInterface()){
continue;
}
processImplicitCollectionAnnotation(type);
final Field[] fields = type.getDeclaredFields();
for (final Field field : fields){
if (field.isEnumConstant() || (field.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) > 0){
continue;
}
addParametrizedTypes(field.getGenericType(), types);
if (field.isSynthetic()){
continue;
}
processFieldAliasAnnotation(field);
processAsAttributeAnnotation(field);
processImplicitAnnotation(field);
processOmitFieldAnnotation(field);
processLocalConverterAnnotation(field);
}
}finally{
annotatedTypes.add(type);
}
}
}
}
private void addParametrizedTypes(Type type,final Set> types){
final Set processedTypes = new HashSet<>();
final Set localTypes = new LinkedHashSet(){
/**
*
*/
private static final long serialVersionUID = -2331069618287693772L;
@Override
public boolean add(final Type o){
if (o instanceof Class){
return types.add((Class) o);
}
return o == null || processedTypes.contains(o) ? false : super.add(o);
}
};
while (type != null){
processedTypes.add(type);
if (type instanceof Class){
final Class clazz = (Class) type;
types.add(clazz);
if (!clazz.isPrimitive()){
final TypeVariable[] typeParameters = clazz.getTypeParameters();
for (final TypeVariable typeVariable : typeParameters){
localTypes.add(typeVariable);
}
localTypes.add(clazz.getGenericSuperclass());
for (final Type iface : clazz.getGenericInterfaces()){
localTypes.add(iface);
}
}
}else if (type instanceof TypeVariable){
final TypeVariable typeVariable = (TypeVariable) type;
final Type[] bounds = typeVariable.getBounds();
for (final Type bound : bounds){
localTypes.add(bound);
}
}else if (type instanceof ParameterizedType){
final ParameterizedType parametrizedType = (ParameterizedType) type;
localTypes.add(parametrizedType.getRawType());
final Type[] actualArguments = parametrizedType.getActualTypeArguments();
for (final Type actualArgument : actualArguments){
localTypes.add(actualArgument);
}
}else if (type instanceof GenericArrayType){
final GenericArrayType arrayType = (GenericArrayType) type;
localTypes.add(arrayType.getGenericComponentType());
}
if (!localTypes.isEmpty()){
final Iterator iter = localTypes.iterator();
type = iter.next();
iter.remove();
}else{
type = null;
}
}
}
private void processConverterAnnotations(final Class type){
if (converterRegistry != null){
final XStreamConverters convertersAnnotation = type.getAnnotation(XStreamConverters.class);
final XStreamConverter converterAnnotation = type.getAnnotation(XStreamConverter.class);
final List annotations = convertersAnnotation != null
? new ArrayList<>(Arrays.asList(convertersAnnotation.value()))
: new ArrayList<>();
if (converterAnnotation != null){
annotations.add(converterAnnotation);
}
for (final XStreamConverter annotation : annotations){
final Converter converter = cacheConverter(annotation, converterAnnotation != null ? type : null);
if (converter != null){
if (converterAnnotation != null || converter.canConvert(type)){
converterRegistry.registerConverter(converter, annotation.priority());
}else{
throw new InitializationException(
"Converter " + annotation.value().getName() + " cannot handle annotated class " + type.getName());
}
}
}
}
}
private void processAliasAnnotation(final Class type,final Set> types){
final XStreamAlias aliasAnnotation = type.getAnnotation(XStreamAlias.class);
if (aliasAnnotation != null){
if (classAliasingMapper == null){
throw new InitializationException("No " + ClassAliasingMapper.class.getName() + " available");
}
classAliasingMapper.addClassAlias(aliasAnnotation.value(), type);
if (aliasAnnotation.impl() != Void.class){
// Alias for Interface/Class with an impl
defaultImplementationsMapper.addDefaultImplementation(aliasAnnotation.impl(), type);
if (type.isInterface()){
types.add(aliasAnnotation.impl()); // alias Interface's impl
}
}
}
}
private void processAliasTypeAnnotation(final Class type){
final XStreamAliasType aliasAnnotation = type.getAnnotation(XStreamAliasType.class);
if (aliasAnnotation != null){
if (classAliasingMapper == null){
throw new InitializationException("No " + ClassAliasingMapper.class.getName() + " available");
}
classAliasingMapper.addTypeAlias(aliasAnnotation.value(), type);
}
}
@Deprecated
private void processImplicitCollectionAnnotation(final Class type){
final XStreamImplicitCollection implicitColAnnotation = type.getAnnotation(XStreamImplicitCollection.class);
if (implicitColAnnotation != null){
if (implicitCollectionMapper == null){
throw new InitializationException("No " + ImplicitCollectionMapper.class.getName() + " available");
}
final String fieldName = implicitColAnnotation.value();
final String itemFieldName = implicitColAnnotation.item();
final Field field;
try{
field = type.getDeclaredField(fieldName);
}catch (final NoSuchFieldException e){
throw new InitializationException(
type.getName() + " does not have a field named '" + fieldName + "' as required by "
+ XStreamImplicitCollection.class.getName());
}
Class itemType = null;
final Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType){
final Type typeArgument = ((ParameterizedType) genericType).getActualTypeArguments()[0];
itemType = getClass(typeArgument);
}
if (itemType == null){
implicitCollectionMapper.add(type, fieldName, null, Object.class);
}else{
if (itemFieldName.equals("")){
implicitCollectionMapper.add(type, fieldName, null, itemType);
}else{
implicitCollectionMapper.add(type, fieldName, itemFieldName, itemType);
}
}
}
}
private void processFieldAliasAnnotation(final Field field){
final XStreamAlias aliasAnnotation = field.getAnnotation(XStreamAlias.class);
if (aliasAnnotation != null){
if (fieldAliasingMapper == null){
throw new InitializationException("No " + FieldAliasingMapper.class.getName() + " available");
}
fieldAliasingMapper.addFieldAlias(aliasAnnotation.value(), field.getDeclaringClass(), field.getName());
}
}
private void processAsAttributeAnnotation(final Field field){
final XStreamAsAttribute asAttributeAnnotation = field.getAnnotation(XStreamAsAttribute.class);
if (asAttributeAnnotation != null){
if (attributeMapper == null){
throw new InitializationException("No " + AttributeMapper.class.getName() + " available");
}
attributeMapper.addAttributeFor(field);
}
}
private void processImplicitAnnotation(final Field field){
final XStreamImplicit implicitAnnotation = field.getAnnotation(XStreamImplicit.class);
if (implicitAnnotation != null){
if (implicitCollectionMapper == null){
throw new InitializationException("No " + ImplicitCollectionMapper.class.getName() + " available");
}
final String fieldName = field.getName();
final String itemFieldName = implicitAnnotation.itemFieldName();
final String keyFieldName = implicitAnnotation.keyFieldName();
boolean isMap = Map.class.isAssignableFrom(field.getType());
Class itemType = null;
if (!field.getType().isArray()){
final Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType){
final Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
final Type typeArgument = actualTypeArguments[isMap ? 1 : 0];
itemType = getClass(typeArgument);
}
}
if (isMap){
implicitCollectionMapper.add(
field.getDeclaringClass(),
fieldName,
itemFieldName != null && !"".equals(itemFieldName) ? itemFieldName : null,
itemType,
keyFieldName != null && !"".equals(keyFieldName) ? keyFieldName : null);
}else{
if (itemFieldName != null && !"".equals(itemFieldName)){
implicitCollectionMapper.add(field.getDeclaringClass(), fieldName, itemFieldName, itemType);
}else{
implicitCollectionMapper.add(field.getDeclaringClass(), fieldName, itemType);
}
}
}
}
private void processOmitFieldAnnotation(final Field field){
final XStreamOmitField omitFieldAnnotation = field.getAnnotation(XStreamOmitField.class);
if (omitFieldAnnotation != null){
if (elementIgnoringMapper == null){
throw new InitializationException("No " + ElementIgnoringMapper.class.getName() + " available");
}
elementIgnoringMapper.omitField(field.getDeclaringClass(), field.getName());
}
}
private void processLocalConverterAnnotation(final Field field){
final XStreamConverter annotation = field.getAnnotation(XStreamConverter.class);
if (annotation != null){
final Converter converter = cacheConverter(annotation, field.getType());
if (converter != null){
if (localConversionMapper == null){
throw new InitializationException("No " + LocalConversionMapper.class.getName() + " available");
}
localConversionMapper.registerLocalConverter(field.getDeclaringClass(), field.getName(), converter);
}
}
}
private Converter cacheConverter(final XStreamConverter annotation,final Class targetType){
Converter result = null;
final Object[] args;
final List