Package de.dfki.util.xmlrpc.conversion

Source Code of de.dfki.util.xmlrpc.conversion.EnhancedTypeConverter

package de.dfki.util.xmlrpc.conversion;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import java.util.logging.Level;
import java.util.logging.Logger;

import de.dfki.util.xmlrpc.XmlRpc;
import de.dfki.util.xmlrpc.XmlRpc.Type;
import de.dfki.util.xmlrpc.common.ApiParameter;

/**
* Advanced type converter able to handle user-specific types which define conversion methods
* to XML-RPC compliant types.
* @author lauer
*/
public class EnhancedTypeConverter
                    extends StandardXmlRpcTypeConverter
{
    private static Logger mLog = Logger.getLogger( EnhancedTypeConverter.class.getName() );

    public static Logger log()
    {
        return ( mLog );
    }
   
    public static byte[] NULL_MASK_VALUE = new byte[0];
   
   
    public boolean isNullMaskValue( Object value )
    {
        return( value.getClass().equals( byte[].class )
                        && Arrays.equals( (byte[])value, NULL_MASK_VALUE ) );
    }
   
   
    @Override
    public Object convertToUserRepresentation( ApiParameter apiParam, Object parameterValueInXmlRpcRep )
        throws TypeConversionException
    {
        assert apiParam != null;
       
        Object userRepValue = null;
        final Class<?> xmlRpcRepClass = parameterValueInXmlRpcRep.getClass();
        final Type paramXmlRpcType = apiParam.getXmlRpcType();
        final Class<?> apiRepClass = apiParam.getApiRepresentationClass();
        final boolean isNullMaskValue = isNullMaskValue( parameterValueInXmlRpcRep ) && XmlRpc.usesAutomaticNullMasking();
       
        //void type?
        if (apiParam.getXmlRpcType() == Type.NONE)
        {
            return( null );
        }
       
        //we have detected a null-value mask
        if (apiParam.getXmlRpcType() != Type.BASE64 && isNullMaskValue)
        {
            parameterValueInXmlRpcRep = null ;
        }


        //does the api use a XML-RPC type?
        if (apiParam.isXmlRpcCompatible())
        {
            //using containers?
            if (Collection.class.isAssignableFrom( apiRepClass ))
            {
                userRepValue = convertCollectionToUserRepresentation( apiRepClass, apiParam.getContainerContent(), (Collection<?>)parameterValueInXmlRpcRep );
            }
            else if (Map.class.isAssignableFrom( apiRepClass ))
            {
                userRepValue = convertMapToUserRepresentation( apiRepClass, apiParam.getContainerContent(), (Map<?,?>)parameterValueInXmlRpcRep );
            }
            else if (apiParam.isArray() && apiParam.getXmlRpcType() != Type.BASE64 && apiParam.getXmlRpcType() != Type.STRINGasBASE64 )
            {
                //transform a collection into an array
                userRepValue = convertArrayToUserRepresentation( apiParam.getContainerContent(), (Collection<?>)parameterValueInXmlRpcRep );
            }
            else
            {
                //no containers, just plain objects
                userRepValue = super.convertToUserRepresentation( apiParam, parameterValueInXmlRpcRep );
            }
           
            return( userRepValue );
        }
       
        //we use an own type as api parameter (this is an extension to XML-RPC)
        if (parameterValueInXmlRpcRep != null && !areCompatible( paramXmlRpcType, parameterValueInXmlRpcRep ))
        {
            throw( new TypeConversionException( "Incompatible parameter class " + xmlRpcRepClass + ". Cannot map to " + paramXmlRpcType ) );
        }
       
        userRepValue = convertToUserRepWithConverter( apiParam, parameterValueInXmlRpcRep );
       
//        if (userRepValue == null)
//        {
//            throw( new TypeConversionException( "Failed to convert class " + xmlRpcRepClass.getName() + " to api class " + apiRepClass.getName() ) );
//        }
       
        return( userRepValue );
    }
   


    @Override
    public Object convertToXmlRpcRepresentation( final ApiParameter apiParam, final Object parameterValue )
        throws TypeConversionException
    {
        final Class<?> apiRepClass = apiParam.getApiRepresentationClass();   //type of the api-parameter to handle
        boolean isPrimitiveParam = apiRepClass.isPrimitive();
        boolean maskNullValues = XmlRpc.usesAutomaticNullMasking();
        Object xmlRpcValue = null;    //the xmlRpc value to send over the wire

        //do not handle void types
        if (apiRepClass.equals( void.class ) /*|| parameterInUserRep == null*/ )
        {
            return( null );
        }


        //do we have a valid non-null parameter. This check is necessary when using the MethodCall.add method.
        if (parameterValue != null)
        {
            final Class<?> userRepClass = parameterValue.getClass();
            if((isPrimitiveParam && !checkPrimitiveType( apiRepClass, userRepClass ))
                 || (!isPrimitiveParam && !apiRepClass.isAssignableFrom( userRepClass )))
            {
                throw( new TypeConversionException( "Incompatible parameter class " + userRepClass + ". Needed instance of class " + apiRepClass ) );
            }
        }
       
        //

        if (Collection.class.isAssignableFrom( apiRepClass ))
        {
            xmlRpcValue = convertCollectionToXmlRpc( apiParam.getContainerContent(), (Collection<?>)parameterValue );
        }
        else if (Map.class.isAssignableFrom( apiRepClass ))
        {
            xmlRpcValue = convertMapToXmlRpc( apiParam.getContainerContent(), (Map<?,?>)parameterValue );
        }
        else if (apiParam.isArray() && apiParam.getXmlRpcType() != Type.BASE64 && apiParam.getXmlRpcType() != Type.STRINGasBASE64)
        {
            xmlRpcValue = convertArrayToXmlRpc( apiParam.getContainerContent(), parameterValue );
        }

        //parameter uses converters?
        if (apiParam.usesConvertable())
        {
            xmlRpcValue = convertToXmlRpcWithConvertable( parameterValue );
        }
        else if (apiParam.getSeparateParameterConverter() != null)
        {
            xmlRpcValue = convertToXmlRpcWithSeparateConverter( parameterValue, apiParam.getSeparateParameterConverter() );
        }
       
        //if all before failed: try standard
        if (xmlRpcValue == null)
        {
            xmlRpcValue = super.convertToXmlRpcRepresentation( apiParam, parameterValue );
        }
       
        //still no success: can we mask the null?
        if (parameterValue == null
                        && maskNullValues
                        && apiParam.getXmlRpcType() != Type.BASE64)
        {
            xmlRpcValue = NULL_MASK_VALUE;
        }

        //all else failed: we cannot convert this value!
        if (xmlRpcValue == null)
        {
            //generate a special error message for null-parameter values
            if (parameterValue == null)
            {
                throw( new TypeConversionException( "'null' parameter could not be handled. Use a ParameterConverter or enable automatic null-masking by calling XmlRpc.useAutomaticNullMasking( true )." ) );
            }
           
            //we found a xml-rpc unaware type
            //standard failure message
            throw( new TypeConversionException( "Conversion failed. Incompatible type and no annotation found." ) );
         }
       
        return( xmlRpcValue );
    }
   
   


    private boolean checkPrimitiveType( Class<?> apiRepClass, Class<?> userRepClass )
    {
        boolean ok = false;
        if (apiRepClass.equals( Integer.TYPE ))
            ok = userRepClass.equals( Integer.class );
        else if (apiRepClass.equals( Double.TYPE ))
            ok = userRepClass.equals( Double.class );
        else if (apiRepClass.equals( Float.TYPE ))
            ok = userRepClass.equals( Float.class );
        else if (apiRepClass.equals( Boolean.TYPE ))
            ok = userRepClass.equals( Boolean.class );
       
        return( ok );
    }


    /** Converts a whole collection recursively. TODO: avoid collection copy, if conversion does not change values. */
    protected Object convertCollectionToXmlRpc( ApiParameter apiParam, Collection<?> parameterInUserRep )
        throws TypeConversionException
    {
        if (apiParam == null || parameterInUserRep == null)
            return( parameterInUserRep );
       
        Collection<Object> tmpCollection = new LinkedList<Object>();
        Iterator<?> i = parameterInUserRep.iterator();
        while( i.hasNext() )
        {
            Object element = i.next();
            Object newElement = convertToXmlRpcRepresentation( apiParam, element );
           
            tmpCollection.add( newElement );
        }
       
        return( tmpCollection );
       
    }

    /** Converts a whole map recursively. TODO: avoid map copy, if conversion does not change values. */
    protected Object convertMapToXmlRpc( ApiParameter apiParam, Map<?,?> parameterInUserRep )
        throws TypeConversionException
    {
        if (apiParam == null || parameterInUserRep == null)
            return( parameterInUserRep );
       
        //make a new map to avoid changing input parameters
        Map<String,Object> xmlRpcRep = new HashMap<String,Object>();
        for( Map.Entry<?,?> entry : parameterInUserRep.entrySet() )
        {
            String id = entry.getKey().toString();
            Object element = entry.getValue();
           
            Object newElement = convertToXmlRpcRepresentation( apiParam, element );
            xmlRpcRep.put( id, newElement );
        }
       
        return( xmlRpcRep );
    }
   
    protected Object convertArrayToXmlRpc( ApiParameter apiParam, Object parameterInUserRep )
        throws TypeConversionException
    {
        if (apiParam == null || parameterInUserRep == null)
            return( parameterInUserRep );
       
        assert( parameterInUserRep.getClass().isArray() );
       
        //we know it's an array. But is it a primitive type or not? Cannot cast an int[] to Object[] :-(
        int len = Array.getLength( parameterInUserRep );

        Collection<Object> tmpCollection = new LinkedList<Object>();
        for( int i=0; i<len; i++ )
        {
            Object element = Array.get( parameterInUserRep, i );
            Object newElement = convertToXmlRpcRepresentation( apiParam, element );
           
            tmpCollection.add( newElement );
        }
        return( tmpCollection );
    }

   
    protected Object convertToXmlRpcWithConvertable( Object parameterInUserRep )
        throws TypeConversionException
    {
        if (log().isLoggable( Level.FINER ))
            log().finer( "Converting parameter value '" + parameterInUserRep + "' by calling toXmlRpc method" );

        //we have a null value: skip conversion (maybe to an auto-masked xml-rpc call)
        if (parameterInUserRep == null)
        {
            return( null );
        }
       
        Object xmlRpcValue = null;
        try
        {
            //call conversion method on parameter itself
            Method m = parameterInUserRep.getClass().getMethod( "toXmlRpc" );
            xmlRpcValue = m.invoke( parameterInUserRep, (Object[])null );
        }
        catch( Exception e )
        {
            throw( new TypeConversionException( e ) );
        }           
        return( xmlRpcValue );
    }
   
    /** conversion: will pass null values to a converter. */
    protected Object convertToXmlRpcWithSeparateConverter( Object parameterValueInUserRep,
                                                           ParameterConverter<?,?> converter )
        throws TypeConversionException
    {
        if (log().isLoggable( Level.FINER ))
            log().finer( "Converting '" + parameterValueInUserRep + "' by using converter '" + converter + "'(" + converter.getClass().getName() + ")" );

        Object xmlRpcValue = null;
        try
        {
            Method m = converter.getClass().getMethod( "toXmlRpc", Object.class );
            xmlRpcValue = m.invoke( converter, parameterValueInUserRep );
        }
        catch( InvocationTargetException e )
        {
            throw( new TypeConversionException( e.getCause() ) );
        }           
        catch( Exception e )
        {
            throw( new TypeConversionException( e ) );
        }           
        return( xmlRpcValue );
    }
   
    //
    // ------  methods for conversions XML-RPC  ->  Api
    //
   
    @SuppressWarnings("unchecked")
    protected Collection<Object> createCollectionContainer( Class<?> apiContainerClass )
        throws TypeConversionException
    {
        Collection<Object> container = null;
       
        if (!apiContainerClass.isInterface())
        {
            //maybe we have a concrete type. Try to create an instance with noarg ctor
            try
            {
                Object instance = apiContainerClass.newInstance();
                return( (Collection<Object>)instance );
            }
            catch( Exception e )
            {
                //no we don't
            }
        }

        if (Set.class.isAssignableFrom( apiContainerClass ))
        {
            container = new HashSet<Object>();
        }
        else if( List.class.isAssignableFrom( apiContainerClass ))
        {
            container = new LinkedList<Object>();
        }
        else if( Collection.class.isAssignableFrom( apiContainerClass ))
        {
            container = new LinkedList<Object>();
        }
       
        if (container == null)
        {
            throw( new TypeConversionException( "unable to create an instance of " + apiContainerClass ) );
        }
       
        return( container );
    }
   
    @SuppressWarnings("unchecked")
    protected Map<String,Object> createMapContainer( Class<?> apiContainerClass )
        throws TypeConversionException
    {
        Map<String,Object> container = null;
       
        if (!apiContainerClass.isInterface())
        {
            //maybe we have a concrete type. Try to create an instance with noarg ctor
            try
            {
                Object instance = apiContainerClass.newInstance();
                return( (Map<String,Object>)instance );
            }
            catch( Exception e )
            {
                //no we don't
            }
        }

        if (Map.class.isAssignableFrom( apiContainerClass ))
        {
            container = new HashMap<String,Object>();
        }

        if (container == null)
        {
            throw( new TypeConversionException( "unable to create an instance of " + apiContainerClass ) );
        }
       
        return( container );
    }
   
   
    /** Converts a whole collection recursively. TODO: avoid collection copy, if conversion does not change values. */
    public Object convertCollectionToUserRepresentation( Class<?> apiContainerClass, ApiParameter apiParam, Collection<?> parameterInXmlRpcRep )
        throws TypeConversionException
    {
        if (apiParam == null || parameterInXmlRpcRep == null)
            return( parameterInXmlRpcRep );
       
        Collection<Object> container = createCollectionContainer( apiContainerClass );
        Iterator<?> i = parameterInXmlRpcRep.iterator();
        while( i.hasNext() )
        {
            Object element = i.next();
            Object newElement = convertToUserRepresentation( apiParam, element );
           
            container.add( newElement );
        }
       
        return( container );
    }

    /** Converts a whole map recursively. TODO: avoid map copy, if conversion does not change values. */
    public Object convertMapToUserRepresentation( Class<?> apiContainerClass, ApiParameter apiParam, Map<?,?> parameterInXmlRpcRep )
        throws TypeConversionException
    {
        if (apiParam == null || parameterInXmlRpcRep == null)
            return( parameterInXmlRpcRep );

        //make a new map to avoid changing input parameters
        Map<String,Object> userRep = createMapContainer( apiContainerClass );
        for( Map.Entry<?,?> entry : parameterInXmlRpcRep.entrySet() )
        {
            final String id = entry.getKey().toString();
            final Object element = entry.getValue();
           
            Object newElement = convertToUserRepresentation( apiParam, element );
            userRep.put( id, newElement );
        }
        
        return( userRep );
    }
   
    protected Object convertArrayToUserRepresentation( ApiParameter apiParam, Collection<?> parameterInXmlRpcRep )
        throws TypeConversionException
    {
        if (apiParam == null || parameterInXmlRpcRep == null)
            return( parameterInXmlRpcRep );

        Object array = Array.newInstance( apiParam.getApiRepresentationClass(), parameterInXmlRpcRep.size() );
        Iterator<?> i = parameterInXmlRpcRep.iterator();
        int pos = 0;
        while( i.hasNext() )
        {
            Object element = i.next();
            Object newElement = convertToUserRepresentation( apiParam, element );

            Array.set( array, pos, newElement );
            pos++;
        }
       
        return( array );
    }
  
    /**
     * Try to convert an object in XML-RPC representation into a parameter object compatible with the used api.
     * First check: do we have a bound converter (parameter class does conversion itself).
     * Second check: do we have an autonomous converter (a separate class which does the conversion)
     */
    protected Object convertToUserRepWithConverter( ApiParameter apiParam, Object parameterInXmlRpcRep )
        throws TypeConversionException
    {
        Object userRepValue = null;
        if (apiParam.usesConvertable())
        {
            userRepValue = convertToUserRepWithConvertable( parameterInXmlRpcRep, apiParam.getXmlRpcType(), apiParam.getConvertableClass() );
        }
        else if (apiParam.getSeparateParameterConverter() != null)
        {
            userRepValue = convertToUserRepWithSeparateConverter( parameterInXmlRpcRep, apiParam.getSeparateParameterConverter() );
        }
       
        return( userRepValue );
    }
   
    protected Object convertToUserRepWithConvertable( Object parameterValueInXmlRpcRep,
                                                      Type xmlRpcType,
                                                      Class<? extends Convertable<?>> convertableCls )
        throws TypeConversionException
    {
        if (parameterValueInXmlRpcRep == null)
            return( null );
       
        assert convertableCls != null;
       
        Object userRepValue = null;
        try
        {
            Constructor<?> ctor = ConversionUtils.findConvertableConstructor( convertableCls, xmlRpcType, parameterValueInXmlRpcRep.getClass() );

            if (ctor == null)
            {
                throw( new TypeConversionException( "Failed to create parameter value instance. " + convertableCls + " needs public contructor with argument type " + parameterValueInXmlRpcRep.getClass() ) );
            }
           
            userRepValue = ctor.newInstance( parameterValueInXmlRpcRep );
        }
        catch( IllegalAccessException e )
        {
            throw( new TypeConversionException( "Failed to create parameter value instance. Can't access constructor of " + convertableCls, e ) );
        }
        catch( Exception e )
        {
            throw( new TypeConversionException( "Error while creating parameter value instance.", e ) );
        }
       
        return( userRepValue );
    }


    protected Object convertToUserRepWithSeparateConverter( Object parameterValueInXmlRpcRep,
                                                            ParameterConverter<?,?> converter )
        throws TypeConversionException
    {
        Object userRepValue = null;
        try
        {
            Method m = converter.getClass().getMethod( "createFrom", Object.class );
            userRepValue = m.invoke( converter, parameterValueInXmlRpcRep );
        }
        catch( Throwable e )
        {
            if (e instanceof InvocationTargetException)
            {
                e = e.getCause();
            }
            throw ( new TypeConversionException( e ) );
        }
        return ( userRepValue );    }
}
TOP

Related Classes of de.dfki.util.xmlrpc.conversion.EnhancedTypeConverter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.