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

com.dyuproject.protostuff.runtime.RuntimeFieldFactory Maven / Gradle / Ivy

The newest version!
//========================================================================
//Copyright 2007-2009 David Yu [email protected]
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at 
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================

package com.dyuproject.protostuff.runtime;

import static com.dyuproject.protostuff.runtime.RuntimeEnv.COLLECTION_SCHEMA_ON_REPEATED_FIELDS;
import static com.dyuproject.protostuff.runtime.RuntimeEnv.MORPH_NON_FINAL_POJOS;
import static com.dyuproject.protostuff.runtime.RuntimeEnv.USE_SUN_MISC_UNSAFE;

import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import com.dyuproject.protostuff.ByteString;
import com.dyuproject.protostuff.Message;
import com.dyuproject.protostuff.Morph;
import com.dyuproject.protostuff.runtime.MappedSchema.Field;

/**
 * A factory to create runtime {@link MappedSchema.Field fields} based on reflection.
 *
 * @author David Yu
 * @created Nov 10, 2009
 */
public abstract class RuntimeFieldFactory implements Delegate
{
    
    static final int ID_BOOL = 1, ID_BYTE = 2, ID_CHAR = 3, ID_SHORT = 4, 
        ID_INT32 = 5, ID_INT64 = 6, ID_FLOAT = 7, ID_DOUBLE = 8, 
        ID_STRING = 9, ID_BYTES = 10, ID_BYTE_ARRAY = 11, 
        ID_BIGDECIMAL = 12, ID_BIGINTEGER = 13, ID_DATE = 14,
        ID_ARRAY = 15, // 1-15 is encoded as 1 byte on protobuf and protostuff format
        ID_OBJECT = 16, 
        ID_ARRAY_MAPPED = 17, 
        ID_CLASS = 18, 
        ID_CLASS_MAPPED = 19, 
        ID_CLASS_ARRAY = 20, 
        ID_CLASS_ARRAY_MAPPED = 21, 
        
        ID_ENUM_SET = 22, 
        ID_ENUM_MAP = 23, 
        ID_ENUM = 24, 
        ID_COLLECTION = 25, 
        ID_MAP = 26, 
        
        ID_POLYMORPHIC_COLLECTION = 28, 
        ID_POLYMORPHIC_MAP = 29, 
        ID_DELEGATE = 30, 
        
        ID_ARRAY_DELEGATE = 32, 
        ID_ARRAY_SCALAR = 33, 
        ID_ARRAY_ENUM = 34, 
        ID_ARRAY_POJO = 35, 
        
        ID_THROWABLE = 52, 
        
        // pojo fields limited to 126 if not explicitly using @Tag annotations
        ID_POJO = 127;
    
    static final String STR_BOOL = "a", STR_BYTE = "b", STR_CHAR = "c", STR_SHORT = "d", 
        STR_INT32 = "e", STR_INT64 = "f", STR_FLOAT = "g", STR_DOUBLE = "h", 
        STR_STRING = "i", STR_BYTES = "j", STR_BYTE_ARRAY = "k", 
        STR_BIGDECIMAL = "l", STR_BIGINTEGER = "m", STR_DATE = "n", 
        STR_ARRAY = "o", 
        STR_OBJECT = "p", 
        STR_ARRAY_MAPPED = "q", 
        STR_CLASS = "r", 
        STR_CLASS_MAPPED = "s", 
        STR_CLASS_ARRAY = "t", 
        STR_CLASS_ARRAY_MAPPED = "u", 
        
        STR_ENUM_SET = "v", 
        STR_ENUM_MAP = "w", 
        STR_ENUM = "x", 
        STR_COLLECTION = "y",
        STR_MAP = "z", 
        
        STR_POLYMORPHIC_COLLECTION = "B", 
        STR_POLYMOPRHIC_MAP = "C", 
        STR_DELEGATE = "D", 
        
        STR_ARRAY_DELEGATE = "F", 
        STR_ARRAY_SCALAR = "G", 
        STR_ARRAY_ENUM = "H", 
        STR_ARRAY_POJO = "I", 
        
        STR_THROWABLE = "Z", 
        
        // pojo fields limited to 126 if not explicitly using @Tag annotations
        STR_POJO = "_";
    
    
    private static final HashMap> __inlineValues = 
        new HashMap>();
    
    static final RuntimeFieldFactory BIGDECIMAL;
    static final RuntimeFieldFactory BIGINTEGER;
    static final RuntimeFieldFactory BOOL;
    static final RuntimeFieldFactory BYTE;
    static final RuntimeFieldFactory BYTES;
    static final RuntimeFieldFactory BYTE_ARRAY;
    static final RuntimeFieldFactory CHAR;
    static final RuntimeFieldFactory DATE;
    static final RuntimeFieldFactory DOUBLE;
    static final RuntimeFieldFactory FLOAT;
    static final RuntimeFieldFactory INT32;
    static final RuntimeFieldFactory INT64;
    static final RuntimeFieldFactory SHORT;
    static final RuntimeFieldFactory STRING;
    
    static final RuntimeFieldFactory ENUM;
    static final RuntimeFieldFactory OBJECT;
    static final RuntimeFieldFactory POJO;
    static final RuntimeFieldFactory POLYMORPHIC_POJO;
    
    static final RuntimeFieldFactory> COLLECTION;
    
    static final RuntimeFieldFactory DELEGATE;
    
    // for repeated/collection fields.
    static final Accessor.Factory ACCESSOR_FACTORY;
    
    static
    {
        if(USE_SUN_MISC_UNSAFE)
        {
            BIGDECIMAL = RuntimeUnsafeFieldFactory.BIGDECIMAL;
            BIGINTEGER = RuntimeUnsafeFieldFactory.BIGINTEGER;
            BOOL = RuntimeUnsafeFieldFactory.BOOL;
            BYTE = RuntimeUnsafeFieldFactory.BYTE;
            BYTES = RuntimeUnsafeFieldFactory.BYTES;
            BYTE_ARRAY = RuntimeUnsafeFieldFactory.BYTE_ARRAY;
            CHAR = RuntimeUnsafeFieldFactory.CHAR;
            DATE = RuntimeUnsafeFieldFactory.DATE;
            DOUBLE = RuntimeUnsafeFieldFactory.DOUBLE;
            FLOAT = RuntimeUnsafeFieldFactory.FLOAT;
            INT32 = RuntimeUnsafeFieldFactory.INT32;
            INT64 = RuntimeUnsafeFieldFactory.INT64;
            SHORT = RuntimeUnsafeFieldFactory.SHORT;
            STRING = RuntimeUnsafeFieldFactory.STRING;
            
            ENUM = RuntimeUnsafeFieldFactory.ENUM;
            OBJECT = RuntimeUnsafeFieldFactory.OBJECT;
            POJO = RuntimeUnsafeFieldFactory.POJO;
            POLYMORPHIC_POJO = RuntimeUnsafeFieldFactory.POLYMORPHIC_POJO;
            
            DELEGATE = RuntimeUnsafeFieldFactory.DELEGATE;
            
            ACCESSOR_FACTORY = UnsafeAccessor.FACTORY;
        }
        else
        {
            BIGDECIMAL = RuntimeReflectionFieldFactory.BIGDECIMAL;
            BIGINTEGER = RuntimeReflectionFieldFactory.BIGINTEGER;
            BOOL = RuntimeReflectionFieldFactory.BOOL;
            BYTE = RuntimeReflectionFieldFactory.BYTE;
            BYTES = RuntimeReflectionFieldFactory.BYTES;
            BYTE_ARRAY = RuntimeReflectionFieldFactory.BYTE_ARRAY;
            CHAR = RuntimeReflectionFieldFactory.CHAR;
            DATE = RuntimeReflectionFieldFactory.DATE;
            DOUBLE = RuntimeReflectionFieldFactory.DOUBLE;
            FLOAT = RuntimeReflectionFieldFactory.FLOAT;
            INT32 = RuntimeReflectionFieldFactory.INT32;
            INT64 = RuntimeReflectionFieldFactory.INT64;
            SHORT = RuntimeReflectionFieldFactory.SHORT;
            STRING = RuntimeReflectionFieldFactory.STRING;
            
            ENUM = RuntimeReflectionFieldFactory.ENUM;
            OBJECT = RuntimeReflectionFieldFactory.OBJECT;
            POJO = RuntimeReflectionFieldFactory.POJO;
            POLYMORPHIC_POJO = RuntimeReflectionFieldFactory.POLYMORPHIC_POJO;
            
            DELEGATE = RuntimeReflectionFieldFactory.DELEGATE;
            
            ACCESSOR_FACTORY = ReflectAccessor.FACTORY;
        }
        
        COLLECTION = COLLECTION_SCHEMA_ON_REPEATED_FIELDS ? 
                    RuntimeCollectionFieldFactory.getFactory() : 
                        RuntimeRepeatedFieldFactory.getFactory();
        
        __inlineValues.put(Integer.TYPE.getName(), INT32);
        __inlineValues.put(Integer.class.getName(), INT32);
        __inlineValues.put(Long.TYPE.getName(), INT64);
        __inlineValues.put(Long.class.getName(), INT64);
        __inlineValues.put(Float.TYPE.getName(), FLOAT);
        __inlineValues.put(Float.class.getName(), FLOAT);
        __inlineValues.put(Double.TYPE.getName(), DOUBLE);
        __inlineValues.put(Double.class.getName(), DOUBLE);
        __inlineValues.put(Boolean.TYPE.getName(), BOOL);
        __inlineValues.put(Boolean.class.getName(), BOOL);
        __inlineValues.put(Character.TYPE.getName(), CHAR);
        __inlineValues.put(Character.class.getName(), CHAR);
        __inlineValues.put(Short.TYPE.getName(), SHORT);
        __inlineValues.put(Short.class.getName(), SHORT);
        __inlineValues.put(Byte.TYPE.getName(), BYTE);
        __inlineValues.put(Byte.class.getName(), BYTE);
        __inlineValues.put(String.class.getName(), STRING);
        __inlineValues.put(ByteString.class.getName(), BYTES);
        __inlineValues.put(byte[].class.getName(), BYTE_ARRAY);
        __inlineValues.put(BigInteger.class.getName(), BIGINTEGER);
        __inlineValues.put(BigDecimal.class.getName(), BIGDECIMAL);
        __inlineValues.put(Date.class.getName(), DATE);
    }
    
    /**
     * Gets the runtime field factory of the given {@code clazz}.
     * 
     * Method overload for backwards compatibility.
     */
    public static RuntimeFieldFactory getFieldFactory(Class clazz)
    {
        return getFieldFactory(clazz, RuntimeEnv.ID_STRATEGY);
    }

    /**
     * Gets the runtime field factory of the given {@code clazz}.
     */
    public static RuntimeFieldFactory getFieldFactory(Class clazz, 
            IdStrategy strategy)
    {
        if(strategy.isDelegateRegistered(clazz))
            return DELEGATE;
        
        final RuntimeFieldFactory inline =  __inlineValues.get(clazz.getName());
        if(inline != null)
            return inline;
        
        if(Message.class.isAssignableFrom(clazz))
            return POJO;
        
        if(clazz.isEnum())
            return ENUM;
        
        // Of all the scalar (inline) fields, java.lang.Number is the only abstract
        // super type, hence we can filter it here
        // Note that it has 10 built-in subtypes
        if(clazz.isArray() || Object.class == clazz || Number.class == clazz 
                || Class.class == clazz || Enum.class == clazz)
        {
            return OBJECT;
        }
        
        // allow user to register a custom schema for throwable/map/collection
        
        if (strategy.isRegistered(clazz))
            return clazz.isInterface() ? POJO : POLYMORPHIC_POJO;
        
        if (Throwable.class.isAssignableFrom(clazz))
            return OBJECT;
        
        if(Map.class.isAssignableFrom(clazz))
            return RuntimeMapFieldFactory.MAP;

        if(Collection.class.isAssignableFrom(clazz))
        {
            // repeated fields.
            return COLLECTION;
        }
        
        // Enums or boxed types of primitives do implement interfaces.
        // Although serializing polymorphic pojos declared as interfaces will be a 
        // little bit slower than before, this is more correct.
        //
        // In versions prior to 1.0.5, it would serialize a field declared as 
        // java.lang.Serializable like a polymorphic pojo even though it 
        // gets assigned enum/string/number types.
        //
        // If you have declared fields as serializable, it wont be compatible
        if(clazz.isInterface())
            return OBJECT;
        
        // checks delegated to POLYMORPHIC_POJO
        return POLYMORPHIC_POJO;
    }
    
    static boolean pojo(Class clazz, Morph morph, IdStrategy strategy)
    {
        if(Modifier.isFinal(clazz.getModifiers()))
            return true;
        
        // check if user mapped an impl to this class
        if(Modifier.isAbstract(clazz.getModifiers()))
            return strategy.isRegistered(clazz);
        
        // the user can annotate fields with @Morph to have full control if
        // he knows a certain field will be set with a subtype.
        // To reverse the behavior (no subtype will be set), annotate with @Morph(false)
        // This is an optimization that requires the user's full knowledge of his dataset.
        if(morph != null)
            return !morph.value();
        
        return !MORPH_NON_FINAL_POJOS;
    }
    
    static Class getGenericType(java.lang.reflect.Field f, int index)
    {
        try
        {
            Type type = ((ParameterizedType)f.getGenericType()).getActualTypeArguments()[index];
            if(type instanceof GenericArrayType)
            {
                int dimensions = 1;
                Type componentType = ((GenericArrayType)type).getGenericComponentType();
                while(componentType instanceof GenericArrayType)
                {
                    dimensions++;
                    componentType = ((GenericArrayType)componentType).getGenericComponentType();
                }
                
                // TODO is there a more efficient way (reflection) to obtain an array class? 
                
                if(dimensions == 1)
                    return Array.newInstance((Class)componentType, 0).getClass();
                
                final int[] arg = new int[dimensions];
                arg[0] = 0;
                return Array.newInstance((Class)componentType, arg).getClass();
            }
            
            if(type instanceof ParameterizedType)
            {
                // TODO in the future, we can opt to do recursive type
                // inspection which can avoid including type metadata even with 
                // very complex nested generic types (E.g a List>>).
                
                // special handling when generic type is either Class or Enum 
                Object rawType = ((ParameterizedType)type).getRawType();
                if(Class.class == rawType)
                    return Class.class;
                
                if(Enum.class == rawType)
                    return Enum.class;
                
                return null;
            }
            
            return (Class)type;
        }
        catch(Exception e)
        {
            return null;
        }
    }
    
    @SuppressWarnings("unchecked")
    static  Delegate getDelegateOrInline(Class typeClass, 
            IdStrategy strategy)
    {
        Delegate d = strategy.getDelegate(typeClass);
        if(d == null)
            d = (RuntimeFieldFactory)__inlineValues.get(typeClass.getName());
        
        return d;
    }
    
    /**
     * Returns the factory for inline (scalar) values.
     */
    @SuppressWarnings("unchecked")
    public static  RuntimeFieldFactory getInline(Class typeClass)
    {
        return (RuntimeFieldFactory)__inlineValues.get(typeClass.getName());
    }
    
    /**
     * Returns the factory for inline (scalar) values.
     */
    @SuppressWarnings("unchecked")
    static  RuntimeFieldFactory getInline(String className)
    {
        return (RuntimeFieldFactory)__inlineValues.get(className);
    }
    
    
    /**
     * Used by {@link ObjectSchema} to serialize dynamic (polymorphic) fields.
     */
    final int id;
    
    public RuntimeFieldFactory(int id)
    {
        this.id = id;
    }
    
    /**
     * Creates a runtime {@link MappedSchema.Field field} based on reflection.
     */
    public abstract  Field create(int number, java.lang.String name, 
            java.lang.reflect.Field field, IdStrategy strategy);

}