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

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

There is a newer version: 1.3.1
Show newest version
//========================================================================
//Copyright 2007-2010 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.ENUMS_BY_NAME;

import java.io.IOException;
import java.util.Collection;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.dyuproject.protostuff.CollectionSchema;
import com.dyuproject.protostuff.Input;
import com.dyuproject.protostuff.MapSchema;
import com.dyuproject.protostuff.Output;
import com.dyuproject.protostuff.Pipe;

/**
 * Determines how enums are serialized/deserialized. 
 * Default is BY_NUMBER. 
 * To enable BY_NAME, set the property "protostuff.runtime.enums_by_name=true".
 *
 * @author David Yu
 * @created Oct 20, 2010
 */
public abstract class EnumIO>
{
    
    /**
     * A cache to prevent creating the same eio over and over.
     */
    private static final ConcurrentHashMap> __eioCache = 
        new ConcurrentHashMap>();
    
    // Used by ObjectSchema to ser/deser both EnumMap and EnumSet.
    private static final java.lang.reflect.Field __keyTypeFromEnumMap;
    private static final java.lang.reflect.Field __elementTypeFromEnumSet;
    
    static
    {
        boolean success = false;
        java.lang.reflect.Field keyTypeFromMap = null, valueTypeFromSet = null;
        try
        {
            keyTypeFromMap = EnumMap.class.getDeclaredField("keyType");
            keyTypeFromMap.setAccessible(true);
            valueTypeFromSet = EnumSet.class.getDeclaredField("elementType");
            valueTypeFromSet.setAccessible(true);
            success = true;
        }
        catch(Exception e)
        {
            // ignore
        }
        
        __keyTypeFromEnumMap = success ? keyTypeFromMap : null;
        __elementTypeFromEnumSet = success ? valueTypeFromSet : null;
    }
    
    /**
     * Retrieves the enum key type from the EnumMap via reflection.
     * This is used by {@link ObjectSchema}.
     */
    static Class getKeyTypeFromEnumMap(Object enumMap)
    {
        if(__keyTypeFromEnumMap == null)
        {
            throw new RuntimeException("Could not access (reflection) the private " +
                    "field *keyType* (enumClass) from: class java.util.EnumMap");
        }
        
        try
        {
            return (Class)__keyTypeFromEnumMap.get(enumMap);
        }
        catch (IllegalArgumentException e)
        {
            throw new RuntimeException(e);
        }
        catch (IllegalAccessException e)
        {
            throw new RuntimeException(e);
        }
    }
    
    /**
     * Retrieves the enum key type from the EnumMap via reflection.
     * This is used by {@link ObjectSchema}.
     */
    static Class getElementTypeFromEnumSet(Object enumSet)
    {
        if(__elementTypeFromEnumSet == null)
        {
            throw new RuntimeException("Could not access (reflection) the private " +
                    "field *elementType* (enumClass) from: class java.util.EnumSet");
        }
        
        try
        {
            return (Class)__elementTypeFromEnumSet.get(enumSet);
        }
        catch (IllegalArgumentException e)
        {
            throw new RuntimeException(e);
        }
        catch (IllegalAccessException e)
        {
            throw new RuntimeException(e);
        }
    }
    
    
    @SuppressWarnings("unchecked")
    static EnumIO> get(Class enumClass)
    {
        EnumIO eio = __eioCache.get(enumClass.getName());
        if(eio == null)
        {
            eio = ENUMS_BY_NAME ? new ByName(enumClass) : new ByNumber(enumClass);
            
            final EnumIO existing = __eioCache.putIfAbsent(enumClass.getName(), eio);
            if(existing != null)
                eio = existing;
        }
        
        return eio;
    }
    
    @SuppressWarnings("unchecked")
    static EnumIO> get(String className, boolean load)
    {
        EnumIO eio = __eioCache.get(className);
        if(eio == null)
        {
            if(!load)
                return null;
            
            final Class enumClass;
            try
            {
                enumClass = Thread.currentThread().getContextClassLoader().loadClass(
                        className);
            }
            catch (ClassNotFoundException e)
            {
                throw new RuntimeException(e);
            }
            
            eio = ENUMS_BY_NAME ? new ByName(enumClass) : new ByNumber(enumClass);
            
            final EnumIO existing = __eioCache.putIfAbsent(enumClass.getName(), eio);
            if(existing != null)
                eio = existing;
        }
        
        return eio;
    }
    
    /**
     * Writes the {@link Enum} to the output.
     */
    public static void writeTo(Output output, int number, boolean repeated, Enum e) 
    throws IOException
    {
        if(ENUMS_BY_NAME)
            output.writeString(number, e.name(), repeated);
        else
            output.writeEnum(number, e.ordinal(), repeated);
    }
    
    /**
     * Transfers the {@link Enum} from the input to the output.
     */
    public static void transfer(Pipe pipe, Input input, Output output, int number, 
            boolean repeated) throws IOException
    {
        if(ENUMS_BY_NAME)
            input.transferByteRangeTo(output, true, number, repeated);
        else
            output.writeEnum(number, input.readEnum(), repeated);
    }
    
    private static > CollectionSchema.MessageFactory newEnumSetFactory(
            final EnumIO eio)
    {
        return new CollectionSchema.MessageFactory()
        {
            @SuppressWarnings("unchecked")
            public  Collection newMessage()
            {
                return (Collection)eio.newEnumSet();
            }
        };
    }
    
    private static > MapSchema.MessageFactory newEnumMapFactory(
            final EnumIO eio)
    {
        return new MapSchema.MessageFactory()
        {
            @SuppressWarnings("unchecked")
            public  Map newMessage()
            {
                return (Map)eio.newEnumMap();
            }
        };
    }
    
    /**
     * The enum class.
     */
    public final Class enumClass;
    private volatile CollectionSchema.MessageFactory enumSetFactory;
    private volatile MapSchema.MessageFactory enumMapFactory;
    
    public EnumIO(Class enumClass)
    {
        this.enumClass = enumClass;
    }
    
    /**
     * Returns the factory for an EnumSet (lazy).
     */
    public CollectionSchema.MessageFactory getEnumSetFactory()
    {
        CollectionSchema.MessageFactory enumSetFactory = this.enumSetFactory;
        if(enumSetFactory == null)
        {
            synchronized(this)
            {
                if((enumSetFactory = this.enumSetFactory) == null)
                    this.enumSetFactory = enumSetFactory = newEnumSetFactory(this);
            }
        }
        return enumSetFactory;
    }
    
    /**
     * Returns the factory for an EnumMap (lazy).
     */
    public MapSchema.MessageFactory getEnumMapFactory()
    {
        MapSchema.MessageFactory enumMapFactory = this.enumMapFactory;
        if(enumMapFactory == null)
        {
            synchronized(this)
            {
                if((enumMapFactory = this.enumMapFactory) == null)
                    this.enumMapFactory = enumMapFactory = newEnumMapFactory(this);
            }
        }
        return enumMapFactory;
    }
    
    /**
     * Returns an empty {@link EnumSet}.
     */
    public EnumSet newEnumSet()
    {
        return EnumSet.noneOf(enumClass);
    }
    
    /**
     * Returns an empty {@link EnumMap}.
     */
    public  EnumMap newEnumMap()
    {
        return new EnumMap(enumClass);
    }
    
    /**
     * Read the enum from the input.
     */
    public abstract E readFrom(Input input) throws IOException;
    
    /**
     * Reads the enum by its name.
     *
     */
    public static final class ByName> extends EnumIO
    {
        public ByName(Class enumClass)
        {
            super(enumClass);
        }
        
        public E readFrom(Input input) throws IOException
        {
            return Enum.valueOf(enumClass, input.readString());
        }
    }
    
    /**
     * Reads the enum by its number.
     *
     */
    public static final class ByNumber> extends EnumIO
    {
        public ByNumber(Class enumClass)
        {
            super(enumClass);
        } 
        
        public E readFrom(Input input) throws IOException
        {
            return enumClass.getEnumConstants()[input.readEnum()];
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy