All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.thoughtworks.xstream.annotations.Annotations Maven / Gradle / Ivy

There is a newer version: 1.4.9
Show newest version
package com.thoughtworks.xstream.annotations;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter;
import com.thoughtworks.xstream.mapper.Mapper;

/**
 * Contains utility methods that enable to configure an XStream instance
 * with class and field aliases, based on a class decorated
 * with annotations defined in this package.
 *
 * @author Emil Kirschner
 * @author Chung-Onn Cheong
 */
public class Annotations {
	/**
	 * Collection of visited types.
	 */
    private static final Set> visitedTypes = new HashSet>();

    /**
     * This class is not instantiable
     */
    private Annotations() {
    }

    /**
     * Configures aliases on the specified XStream object based on annotations that decorate the specified class.
     * It will recursively invoke itself for each field annotated with XStreamContainedType. If a field containing
     * such annotation is parameterized, a recursive call for each of its parameters type will be made.
     *
     * @param topLevelClasses the class for which the XStream object is configured.
     * This class is expected to be decorated with annotations defined in this package.
     * @param xstream the XStream object that will be configured
     */
    public static synchronized void configureAliases(XStream xstream, Class... topLevelClasses) {
        visitedTypes.clear();
        for(Class topLevelClass : topLevelClasses){
            configureClass(xstream, topLevelClass);
        }
    }

    private static synchronized void configureClass(XStream xstream, Class configurableClass) {
        if (configurableClass == null
              || visitedTypes.contains(configurableClass)) {
            return;
        }

        if(Converter.class.isAssignableFrom(configurableClass)){
            Class converterType = (Class)configurableClass;
            registerConverter(xstream, converterType);
            return;
        }

        visitedTypes.add(configurableClass);

        //Do Class Level Converters
        AnnotatedElement element = configurableClass;
        if(configurableClass.isAnnotationPresent(XStreamConverters.class)){
            XStreamConverters convertersAnnotation = element.getAnnotation(XStreamConverters.class);
            for(XStreamConverter converterAnnotation : convertersAnnotation.value()){
                registerConverter(xstream, converterAnnotation.value());
            }
        }

        //Do Class Level - Converter
        if(configurableClass.isAnnotationPresent(XStreamConverter.class)){
            XStreamConverter converterAnnotation = element.getAnnotation(XStreamConverter.class);
            registerConverter(xstream, converterAnnotation.value());
        }

        //Do Class Level Alias
        if(configurableClass.isAnnotationPresent(XStreamAlias.class)){
            XStreamAlias aliasAnnotation = element.getAnnotation(XStreamAlias.class);
            if(aliasAnnotation.impl() != Void.class){
                //Alias for Interface/Class with an impl
                xstream.alias(aliasAnnotation.value(), configurableClass, aliasAnnotation.impl());
                if(configurableClass.isInterface()){
                    configureClass(xstream,aliasAnnotation.impl()); //alias Interface's impl
                    return;
                }
            }else{
                xstream.alias(aliasAnnotation.value(), configurableClass);
            }
        }

        //Do Class Level ImplicitCollection
        if(configurableClass.isAnnotationPresent(XStreamImplicitCollection.class)){
            XStreamImplicitCollection implicitColAnnotation = element.getAnnotation(XStreamImplicitCollection.class);
            String fieldName = implicitColAnnotation.value();
            String itemFieldName = implicitColAnnotation.item();
            Field field;
            try {
                field = configurableClass.getDeclaredField(fieldName);
                Class itemType = getFieldParameterizedType(field, xstream);
                if (itemType == null) {
                    xstream.addImplicitCollection(configurableClass, fieldName);
                } else {
                    if (itemFieldName.equals("")) {
                        xstream.addImplicitCollection(configurableClass, fieldName,
                                itemType);
                    } else {
                        xstream.addImplicitCollection(configurableClass, fieldName,
                                itemFieldName, itemType);
                    }
                }
            } catch (Exception e) {
                System.err.println("Fail to derive ImplicitCollection member type");
            }
        }

        //Do Member Level Field annotations
        Field[] fields = configurableClass.getDeclaredFields();
        for (Field field : fields) {
            if(field.isSynthetic()) continue;

            Class fieldType = field.getType();

            // recursive calls for fields
            if(field.isAnnotationPresent(XStreamContainedType.class)){
                configureClass(xstream, fieldType);
                configureParameterizedTypes(field, xstream);
            }

            //Alias the member's Type
            boolean shouldAlias = field.isAnnotationPresent(XStreamAlias.class);
            boolean isAttribute = field.isAnnotationPresent(XStreamAsAttribute.class);
			if(shouldAlias && !isAttribute){
                XStreamAlias fieldXStreamAliasAnnotation =  field.getAnnotation(XStreamAlias.class);
                xstream.aliasField(fieldXStreamAliasAnnotation.value(), configurableClass, field.getName());
                configureClass(xstream, field.getType());
            }
			if(isAttribute){
                xstream.useAttributeFor(configurableClass, field.getName());
                if(shouldAlias) {
                    XStreamAlias fieldXStreamAliasAnnotation =  field.getAnnotation(XStreamAlias.class);
                    xstream.aliasAttribute(configurableClass, field.getName(), fieldXStreamAliasAnnotation.value());
                }
                configureClass(xstream, field.getType());
            }
            // Do field level implicit collection
            if (field.isAnnotationPresent(XStreamImplicit.class)) {
                if (!Collection.class.isAssignableFrom(field.getType())) {
                    // TODO: We need a separate exception for runtime initialization.
                    throw new XStream.InitializationException("@XStreamImplicit must be assigned to Collection types, but \""
                        + field.getDeclaringClass().getName() + ":" + field.getName() + " is of type " + field.getType().getName());
                }
                XStreamImplicit implicitAnnotation =  field.getAnnotation(XStreamImplicit.class);
                String fieldName = field.getName();
                String itemFieldName = implicitAnnotation.itemFieldName();
                Class itemType = getFieldParameterizedType(field, xstream);
                if (itemFieldName != null && !"".equals(itemFieldName)) {
                    xstream.addImplicitCollection(configurableClass, fieldName, itemFieldName, itemType);
                } else {
                    xstream.addImplicitCollection(configurableClass, fieldName, itemType);
                }
            }

           //Do field level OmitField
           if (field.isAnnotationPresent(XStreamOmitField.class)){
               String fieldName = field.getName();
               xstream.omitField(configurableClass, fieldName);
           }

        }

        //Do Member Classes Alias
        for(ClassmemberClass : configurableClass.getDeclaredClasses()){
            configureClass(xstream, memberClass);
        }

        //Do Superclass and Superinterface Alias
        Class superClass = configurableClass.getSuperclass();
        if (superClass != null && !Object.class.equals(superClass))
            configureClass(xstream, superClass);
        Class[] interfaces = configurableClass.getInterfaces();
        for(Class intf : interfaces){
            configureClass(xstream, intf);
        }

    }


    private static void registerConverter(XStream xstream, Class converterType) {
        Converter converter;
        if(visitedTypes.contains(converterType))
            return;
        visitedTypes.add(converterType);
        if (AbstractCollectionConverter.class.isAssignableFrom(converterType)) {
            try {
                Constructor converterConstructor = converterType.getConstructor(Mapper.class);
                converter = converterConstructor.newInstance(xstream.getMapper());
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }

        } else {
            try {
                converter = converterType.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
        }
        xstream.registerConverter(converter);

    }

    /*
     * Return a concrete class
     */
    private static Class getFieldParameterizedType(Field field, XStream xstream){
        if(field.getGenericType() instanceof ParameterizedType) {
            ParameterizedType pType = (ParameterizedType) field.getGenericType();
            Class type =  (Class) pType.getActualTypeArguments()[0];
            //Get the interface Impl
            if(type.isInterface()){
                AnnotatedElement element = type;
                XStreamAlias alias =  element.getAnnotation(XStreamAlias.class);
                configureClass(xstream, type);
                type = alias.impl();
                assert !type.isInterface()  : type;
            }
            return type;
        }
        assert false : "Field is raw type :" + field;
        return null;
    }

    /**
     * Invokes configureClass for each parameterized type declared within this field.
     */
    private static void configureParameterizedTypes(Field field, XStream xstream){
        if(field.getGenericType() instanceof ParameterizedType) {
            ParameterizedType pType = (ParameterizedType) field.getGenericType();
            Type[] types = pType.getActualTypeArguments();
            for(Type parameter : types) {
            	Class type = (Class) parameter;
            	configureClass(xstream, type);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy