Package com.dyuproject.protostuff.runtime

Source Code of com.dyuproject.protostuff.runtime.DefaultIdStrategy

package com.dyuproject.protostuff.runtime;

import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.ID_BOOL;
import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.ID_BYTE;
import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.ID_CHAR;
import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.ID_DOUBLE;
import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.ID_FLOAT;
import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.ID_INT32;
import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.ID_INT64;
import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.ID_SHORT;

import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.Collection;
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.Message;
import com.dyuproject.protostuff.Output;
import com.dyuproject.protostuff.Pipe;
import com.dyuproject.protostuff.ProtostuffException;
import com.dyuproject.protostuff.Schema;

/***
* The FQCN(fully qualified class name) will serve as the id (string).
* Does not need any registration in the user-code (works out-of-the-box).
* The size of serialized representation may be not very efficient.  
*
* @author Leo Romanoff
* @author David Yu
*
*/
public final class DefaultIdStrategy extends IdStrategy
{
   
    final ConcurrentHashMap<String, HasSchema<?>> pojoMapping =
            new ConcurrentHashMap<String, HasSchema<?>>();
   
    final ConcurrentHashMap<String,EnumIO<?>> enumMapping =
            new ConcurrentHashMap<String,EnumIO<?>>();
   
    final ConcurrentHashMap<String,CollectionSchema.MessageFactory> collectionMapping =
            new ConcurrentHashMap<String,CollectionSchema.MessageFactory>();
   
    final ConcurrentHashMap<String,MapSchema.MessageFactory> mapMapping =
            new ConcurrentHashMap<String,MapSchema.MessageFactory>();
   
    final ConcurrentHashMap<String, Delegate<?>> delegateMapping =
            new ConcurrentHashMap<String, Delegate<?>>();
   
    public DefaultIdStrategy()
    {
        super(null, 0);
    }
   
    public DefaultIdStrategy(IdStrategy primaryGroup, int groupId)
    {
        super(primaryGroup, groupId);
    }
   
    /**
     * Registers a pojo.
     * Returns true if registration is successful or if the same exact schema
     * was previously registered.
     * Used by {@link RuntimeSchema#register(Class, Schema)}.
     */
    public <T> boolean registerPojo(Class<T> typeClass, Schema<T> schema)
    {
        assert typeClass != null && schema != null;
       
        final HasSchema<?> last = pojoMapping.putIfAbsent(typeClass.getName(),
                new Registered<T>(schema));
       
        return last == null || (last instanceof Registered<?> &&
                ((Registered<?>)last).schema == schema);
    }
   
    /**
     * Registers an enum.
     * Returns true if registration is successful.
     */
    public <T extends Enum<T>> boolean registerEnum(Class<T> enumClass)
    {
        return null == enumMapping.putIfAbsent(enumClass.getName(),
                EnumIO.newEnumIO(enumClass));
    }
   
    /**
     * Registers a delegate. Returns true if registration is successful.
     */
    public <T> boolean registerDelegate(Delegate<T> delegate)
    {
        return null == delegateMapping.putIfAbsent(delegate.typeClass().getName(),
                delegate);
    }
   
    /**
     * Registers a collection. Returns true if registration is successful.
     */
    public boolean registerCollection(CollectionSchema.MessageFactory factory)
    {
        return null == collectionMapping.putIfAbsent(factory.typeClass().getName(),
                factory);
    }
   
    /**
     * Registers a map.  Returns true if registration is successful.
     */
    public boolean registerMap(MapSchema.MessageFactory factory)
    {
        return null == mapMapping.putIfAbsent(factory.typeClass().getName(),
                factory);
    }
   
    /**
     * Used by {@link RuntimeSchema#map(Class, Class)}.
     */
    public <T> boolean map(Class<? super T> baseClass, Class<T> typeClass)
    {
        assert baseClass != null && typeClass != null;
       
        if(typeClass.isInterface() || Modifier.isAbstract(typeClass.getModifiers()))
        {
            throw new IllegalArgumentException(typeClass +
                    " cannot be an interface/abstract class.");
        }
       
        final HasSchema<?> last = pojoMapping.putIfAbsent(baseClass.getName(),
                new Mapped<T>(baseClass, typeClass, this));
       
        return last == null || (last instanceof Mapped<?> &&
                ((Mapped<?>)last).typeClass == typeClass);
    }
   

    @Override
    public boolean isDelegateRegistered(Class<?> typeClass)
    {
        return delegateMapping.containsKey(typeClass.getName());
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> Delegate<T> getDelegate(Class<? super T> typeClass)
    {
        return (Delegate<T>)delegateMapping.get(typeClass.getName());
    }
   
    public boolean isRegistered(Class<?> typeClass)
    {
        final HasSchema<?> last = pojoMapping.get(typeClass.getName());
        return last != null && !(last instanceof Lazy<?>);
    }
   
    @SuppressWarnings("unchecked")
    private <T> HasSchema<T> getSchemaWrapper(String className, boolean load)
    {
        HasSchema<T> hs = (HasSchema<T>)pojoMapping.get(className);
        if(hs == null)
        {
            if(!load)
                return null;
           
            final Class<T> typeClass = RuntimeEnv.loadClass(className);
           
            hs = new Lazy<T>(typeClass, this);
            final HasSchema<T> last = (HasSchema<T>)pojoMapping.putIfAbsent(
                    typeClass.getName(), hs);
            if(last != null)
                hs = last;
        }
       
        return hs;
    }
   
    @SuppressWarnings("unchecked")
    public <T> HasSchema<T> getSchemaWrapper(Class<T> typeClass, boolean create)
    {
        HasSchema<T> hs = (HasSchema<T>)pojoMapping.get(typeClass.getName());
        if(hs == null && create)
        {
            hs = new Lazy<T>(typeClass, this);
            final HasSchema<T> last = (HasSchema<T>)pojoMapping.putIfAbsent(
                    typeClass.getName(), hs);
            if(last != null)
                hs = last;
        }
       
        return hs;
    }
   
    private EnumIO<? extends Enum<?>> getEnumIO(String className, boolean load)
    {
        EnumIO<?> eio = enumMapping.get(className);
        if(eio == null)
        {
            if(!load)
                return null;
           
            final Class<?> enumClass = RuntimeEnv.loadClass(className);
           
            eio = EnumIO.newEnumIO(enumClass);
           
            final EnumIO<?> existing = enumMapping.putIfAbsent(enumClass.getName(), eio);
            if(existing != null)
                eio = existing;
        }
       
        return eio;
    }
   
    protected EnumIO<? extends Enum<?>> getEnumIO(Class<?> enumClass)
    {
        EnumIO<?> eio = enumMapping.get(enumClass.getName());
        if(eio == null)
        {
            eio = EnumIO.newEnumIO(enumClass);
           
            final EnumIO<?> existing = enumMapping.putIfAbsent(enumClass.getName(), eio);
            if(existing != null)
                eio = existing;
        }
       
        return eio;
    }
   
    protected CollectionSchema.MessageFactory getCollectionFactory(Class<?> clazz)
    {
        final String className = clazz.getName();
        CollectionSchema.MessageFactory factory = collectionMapping.get(className);
        if(factory == null)
        {
            if(className.startsWith("java.util"))
            {
                factory = CollectionSchema.MessageFactories.valueOf(clazz.getSimpleName());
            }
            else
            {
                factory = new RuntimeCollectionFactory(clazz);
                CollectionSchema.MessageFactory f = collectionMapping.putIfAbsent(
                        className, factory);
                if(f != null)
                    factory = f;
            }
        }
       
        return factory;
    }
   
    protected MapSchema.MessageFactory getMapFactory(Class<?> clazz)
    {
        final String className = clazz.getName();
        MapSchema.MessageFactory factory = mapMapping.get(className);
        if(factory == null)
        {
            if(className.startsWith("java.util"))
            {
                factory = MapSchema.MessageFactories.valueOf(clazz.getSimpleName());
            }
            else
            {
                factory = new RuntimeMapFactory(clazz);
                MapSchema.MessageFactory f = mapMapping.putIfAbsent(
                        className, factory);
                if(f != null)
                    factory = f;
            }
        }
       
        return factory;
    }
   
    protected void writeCollectionIdTo(Output output, int fieldNumber, Class<?> clazz)
            throws IOException
    {
        final CollectionSchema.MessageFactory factory = collectionMapping.get(clazz);
        if(factory == null && clazz.getName().startsWith("java.util"))
        {
            // jdk collection
            // better not to register the jdk collection if using this strategy
            // as it saves space by not writing the full package
            output.writeString(fieldNumber, clazz.getSimpleName(), false);
        }
        else
        {
            output.writeString(fieldNumber, clazz.getName(), false);
        }
    }
   
    protected void transferCollectionId(Input input, Output output,
            int fieldNumber) throws IOException
    {
        input.transferByteRangeTo(output, true, fieldNumber, false);
    }
   
    protected CollectionSchema.MessageFactory resolveCollectionFrom(Input input)
            throws IOException
    {
        final String className = input.readString();
        CollectionSchema.MessageFactory factory = collectionMapping.get(className);
        if(factory == null)
        {
            if(className.indexOf('.') == -1)
            {
                factory = CollectionSchema.MessageFactories.valueOf(className);
            }
            else
            {
                factory = new RuntimeCollectionFactory(RuntimeEnv.loadClass(className));
                CollectionSchema.MessageFactory f = collectionMapping.putIfAbsent(
                        className, factory);
                if(f != null)
                    factory = f;
            }
        }
       
        return factory;
    }
   
    protected void writeMapIdTo(Output output, int fieldNumber, Class<?> clazz)
            throws IOException
    {
        final MapSchema.MessageFactory factory = mapMapping.get(clazz);
        if(factory == null && clazz.getName().startsWith("java.util"))
        {
            // jdk map
            // better not to register the jdk map if using this strategy
            // as it saves space by not writing the full package
            output.writeString(fieldNumber, clazz.getSimpleName(), false);
        }
        else
        {
            output.writeString(fieldNumber, clazz.getName(), false);
        }
    }
   
    protected void transferMapId(Input input, Output output,
            int fieldNumber) throws IOException
    {
        input.transferByteRangeTo(output, true, fieldNumber, false);
    }
   
    protected MapSchema.MessageFactory resolveMapFrom(Input input)
            throws IOException
    {
        final String className = input.readString();
        MapSchema.MessageFactory factory = mapMapping.get(className);
        if(factory == null)
        {
            if(className.indexOf('.') == -1)
            {
                factory = MapSchema.MessageFactories.valueOf(className);
            }
            else
            {
                factory = new RuntimeMapFactory(RuntimeEnv.loadClass(className));
                MapSchema.MessageFactory f = mapMapping.putIfAbsent(
                        className, factory);
                if(f != null)
                    factory = f;
            }
        }
       
        return factory;
    }
   
    protected void writeEnumIdTo(Output output, int fieldNumber,
            Class<?> clazz) throws IOException
    {
        output.writeString(fieldNumber, clazz.getName(), false);
    }
   
    protected void transferEnumId(Input input, Output output,
            int fieldNumber) throws IOException
    {
        input.transferByteRangeTo(output, true, fieldNumber, false);
    }
   
    protected EnumIO<?> resolveEnumFrom(Input input) throws IOException
    {
        return getEnumIO(input.readString(), true);
    }
   
    protected <T> Delegate<T> tryWriteDelegateIdTo(Output output, int fieldNumber,
            Class<T> clazz) throws IOException
    {
        final Delegate<T> delegate = getDelegate(clazz);
        if(delegate == null)
            return null;
       
        output.writeString(fieldNumber, clazz.getName(), false);
       
        return delegate;
    }
   
    @SuppressWarnings("unchecked")
    protected <T> Delegate<T> transferDelegateId(Input input, Output output,
            int fieldNumber) throws IOException
    {
        final String className = input.readString();
       
        final Delegate<T> delegate = (Delegate<T>)delegateMapping.get(className);
        if(delegate == null)
            throw new UnknownTypeException("delegate: " + className + " (Outdated registry)");
       
        output.writeString(fieldNumber, className, false);
       
        return delegate;
    }
   
    @SuppressWarnings("unchecked")
    protected <T> Delegate<T> resolveDelegateFrom(Input input) throws IOException
    {
        final String className = input.readString();
       
        final Delegate<T> delegate = (Delegate<T>)delegateMapping.get(className);
        if(delegate == null)
            throw new UnknownTypeException("delegate: " + className + " (Outdated registry)");
       
        return delegate;
    }
   
    protected <T> Schema<T> writePojoIdTo(Output output, int fieldNumber, Class<T> clazz)
            throws IOException
    {
        output.writeString(fieldNumber, clazz.getName(), false);
       
        // it is important to return the schema initialized (if it hasn't been).
        return getSchemaWrapper(clazz, true).getSchema();
    }

    protected <T> HasSchema<T> transferPojoId(Input input, Output output,
            int fieldNumber) throws IOException
    {
        final String className = input.readString();
       
        final HasSchema<T> wrapper = getSchemaWrapper(className,
                RuntimeEnv.AUTO_LOAD_POLYMORPHIC_CLASSES);
        if(wrapper == null)
        {
            throw new ProtostuffException("polymorphic pojo not registered: " +
                    className);
        }
       
        output.writeString(fieldNumber, className, false);
       
        return wrapper;
    }
   
    protected <T> HasSchema<T> resolvePojoFrom(Input input, int fieldNumber)
            throws IOException
    {
        final String className = input.readString();
       
        final HasSchema<T> wrapper = getSchemaWrapper(className,
                RuntimeEnv.AUTO_LOAD_POLYMORPHIC_CLASSES);
        if(wrapper == null)
            throw new ProtostuffException("polymorphic pojo not registered: " + className);
       
        return wrapper;
    }
   
    protected <T> Schema<T> writeMessageIdTo(Output output, int fieldNumber, Message<T> message)
            throws IOException
    {
        output.writeString(fieldNumber, message.getClass().getName(), false);
       
        return message.cachedSchema();
    }
   
    protected void writeArrayIdTo(Output output, Class<?> componentType)
            throws IOException
    {
        output.writeString(RuntimeFieldFactory.ID_ARRAY, componentType.getName(), false);
    }
   
    protected void transferArrayId(Input input, Output output, int fieldNumber,
            boolean mapped) throws IOException
    {
        input.transferByteRangeTo(output, true, fieldNumber, false);
    }
   
    protected Class<?> resolveArrayComponentTypeFrom(Input input,
            boolean mapped) throws IOException
    {
        return resolveClass(input.readString());
    }
   
    static Class<?> resolveClass(String className)
    {
        final RuntimeFieldFactory<Object> inline = RuntimeFieldFactory.getInline(
                className);
       
        if(inline == null)
            return RuntimeEnv.loadClass(className);
       
        if(className.indexOf('.') != -1)
            return inline.typeClass();
       
        switch(inline.id)
        {
            case ID_BOOL: return boolean.class;
            case ID_BYTE: return byte.class;
            case ID_CHAR: return char.class;
            case ID_SHORT: return short.class;
            case ID_INT32: return int.class;
            case ID_INT64: return long.class;
            case ID_FLOAT: return float.class;
            case ID_DOUBLE: return double.class;
            default: throw new RuntimeException("Should never happen.");
        }
    }
   
    protected void writeClassIdTo(Output output, Class<?> componentType, boolean array)
            throws IOException
    {
        final int id = array ?
                RuntimeFieldFactory.ID_CLASS_ARRAY : RuntimeFieldFactory.ID_CLASS;
       
        output.writeString(id, componentType.getName(), false);
    }
   
    protected void transferClassId(Input input, Output output, int fieldNumber,
            boolean mapped, boolean array) throws IOException
    {
        input.transferByteRangeTo(output, true, fieldNumber, false);
    }
   
    protected Class<?> resolveClassFrom(Input input, boolean mapped, boolean array)
            throws IOException
    {
        return resolveClass(input.readString());
    }
   
    static final class RuntimeCollectionFactory implements CollectionSchema.MessageFactory
    {
       
        final Class<?> collectionClass;
        final RuntimeEnv.Instantiator<?> instantiator;
       
        public RuntimeCollectionFactory(Class<?> collectionClass)
        {
            this.collectionClass = collectionClass;
            instantiator = RuntimeEnv.newInstantiator(collectionClass);
        }

        @SuppressWarnings("unchecked")
        public <V> Collection<V> newMessage()
        {
            return (Collection<V>)instantiator.newInstance();
        }

        public Class<?> typeClass()
        {
            return collectionClass;
        }
    }
   
    static final class RuntimeMapFactory implements MapSchema.MessageFactory
    {
       
        final Class<?> mapClass;
        final RuntimeEnv.Instantiator<?> instantiator;
       
        public RuntimeMapFactory(Class<?> mapClass)
        {
            this.mapClass = mapClass;
            instantiator = RuntimeEnv.newInstantiator(mapClass);
        }

        @SuppressWarnings("unchecked")
        public <K, V> Map<K, V> newMessage()
        {
            return (Map<K, V>)instantiator.newInstance();
        }
       
        public Class<?> typeClass()
        {
            return mapClass;
        }

    }
   
    static final class Lazy<T> extends HasSchema<T>
    {
        final IdStrategy strategy;
        final Class<T> typeClass;
        private volatile Schema<T> schema;
        private volatile Pipe.Schema<T> pipeSchema;
       
        Lazy(Class<T> typeClass, IdStrategy strategy)
        {
            this.typeClass = typeClass;
            this.strategy = strategy;
        }

        @SuppressWarnings("unchecked")
        public Schema<T> getSchema()
        {
            Schema<T> schema = this.schema;
            if(schema==null)
            {
                synchronized(this)
                {
                    if((schema = this.schema) == null)
                    {
                        if(Message.class.isAssignableFrom(typeClass))
                        {
                            // use the message's schema.
                            try
                            {
                                final Message<T> m = (Message<T>)typeClass.newInstance();
                                this.schema = schema = m.cachedSchema();
                            }
                            catch (InstantiationException e)
                            {
                                throw new RuntimeException(e);
                            }
                            catch (IllegalAccessException e)
                            {
                                throw new RuntimeException(e);
                            }
                        }
                        else
                        {
                            // create new
                            this.schema = schema = strategy.newSchema(typeClass);
                        }
                    }
                }
            }

            return schema;
        }
       
        public Pipe.Schema<T> getPipeSchema()
        {
            Pipe.Schema<T> pipeSchema = this.pipeSchema;
            if(pipeSchema == null)
            {
                synchronized(this)
                {
                    if((pipeSchema = this.pipeSchema) == null)
                    {
                        this.pipeSchema = pipeSchema = RuntimeSchema.resolvePipeSchema(
                                getSchema(), typeClass, true);
                    }
                }
            }
            return pipeSchema;
        }
    }
   
    static final class Mapped<T> extends HasSchema<T>
    {
       
        final IdStrategy strategy;
        final Class<? super T> baseClass;
        final Class<T> typeClass;
        private volatile HasSchema<T> wrapper;
       
        Mapped(Class<? super T> baseClass, Class<T> typeClass, IdStrategy strategy)
        {
            this.baseClass = baseClass;
            this.typeClass = typeClass;
            this.strategy = strategy;
        }

        public Schema<T> getSchema()
        {
            HasSchema<T> wrapper = this.wrapper;
            if(wrapper == null)
            {
                synchronized(this)
                {
                    if((wrapper = this.wrapper) == null)
                    {
                        this.wrapper = wrapper = strategy.getSchemaWrapper(typeClass, true);
                    }
                }
            }
           
            return wrapper.getSchema();
        }

        public Pipe.Schema<T> getPipeSchema()
        {
            HasSchema<T> wrapper = this.wrapper;
            if(wrapper == null)
            {
                synchronized(this)
                {
                    if((wrapper = this.wrapper) == null)
                    {
                        this.wrapper = wrapper = strategy.getSchemaWrapper(typeClass, true);
                    }
                }
            }
           
            return wrapper.getPipeSchema();
        }
       
    }
   
    static final class Registered<T> extends HasSchema<T>
    {
        final Schema<T> schema;
        private volatile Pipe.Schema<T> pipeSchema;
       
        Registered(Schema<T> schema)
        {
            this.schema = schema;
        }

        public Schema<T> getSchema()
        {
            return schema;
        }
       
        public Pipe.Schema<T> getPipeSchema()
        {
            Pipe.Schema<T> pipeSchema = this.pipeSchema;
            if(pipeSchema == null)
            {
                synchronized(this)
                {
                    if((pipeSchema = this.pipeSchema) == null)
                    {
                        this.pipeSchema = pipeSchema = RuntimeSchema.resolvePipeSchema(
                                schema, schema.typeClass(), true);
                    }
                }
            }
            return pipeSchema;
        }
    }
}
TOP

Related Classes of com.dyuproject.protostuff.runtime.DefaultIdStrategy

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.