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

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

There is a newer version: 1.3.1
Show newest version
//========================================================================
//Copyright 2007-2011 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 java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.EnumSet;

import com.dyuproject.protostuff.GraphInput;
import com.dyuproject.protostuff.Input;
import com.dyuproject.protostuff.Output;
import com.dyuproject.protostuff.Pipe;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.CollectionSchema.MessageFactories;
import com.dyuproject.protostuff.CollectionSchema.MessageFactory;
import com.dyuproject.protostuff.WireFormat.FieldType;
import com.dyuproject.protostuff.runtime.MappedSchema.Field;
import com.dyuproject.protostuff.runtime.RuntimeSchema.HasSchema;

/**
 * Static utility for creating runtime {@link Collection} fields.
 *
 * @author David Yu
 * @created Jan 26, 2011
 */
final class RuntimeCollectionFieldFactory
{
    
    private RuntimeCollectionFieldFactory() {}
    
    /**
     * For lazy initialization called by {@link RuntimeFieldFactory}.
     */
    static RuntimeFieldFactory> getFactory()
    {
        return COLLECTION;
    }
    
    private static final DerivativeSchema POLYMORPHIC_COLLECTION_VALUE_SCHEMA = 
        new DerivativeSchema()
    {
        @SuppressWarnings("unchecked")
        protected void doMergeFrom(Input input, Schema derivedSchema, 
                Object owner) throws IOException
        {
            final Object value = derivedSchema.newMessage();
            
            // the owner will always be a Collection
            ((Collection)owner).add(value);
            
            if(input instanceof GraphInput)
            {
                // update the actual reference.
                ((GraphInput)input).updateLast(value, owner);
            }
            
            derivedSchema.mergeFrom(input, value);
        }
    };
    
    private static final ObjectSchema OBJECT_COLLECTION_VALUE_SCHEMA = new ObjectSchema()
    {
        @SuppressWarnings("unchecked")
        protected void setValue(Object value, Object owner)
        {
            // the owner will always be a Collection
            ((Collection)owner).add(value);
        }
    };
    
    private static  Field createCollectionInlineV(int number, String name, 
            final java.lang.reflect.Field f, MessageFactory messageFactory, 
            final RuntimeFieldFactory inline)
    {
        return new RuntimeCollectionField(
                inline.getFieldType(), 
                number, name, messageFactory)
        {
            {
                f.setAccessible(true);
            }
            @SuppressWarnings("unchecked")
            protected void mergeFrom(Input input, T message) throws IOException
            {
                try
                {
                    f.set(message, input.mergeObject((Collection)f.get(message), 
                            schema));
                }
                catch(IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch(IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
            }
            @SuppressWarnings("unchecked")
            protected void writeTo(Output output, T message) throws IOException
            {
                final Collection existing;
                try
                {
                    existing = (Collection)f.get(message);
                }
                catch(IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch(IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
                
                if(existing != null)
                    output.writeObject(number, existing, schema, false);
            }
            protected void transfer(Pipe pipe, Input input, Output output, 
                    boolean repeated) throws IOException
            {
                output.writeObject(number, pipe, schema.pipeSchema, repeated);
            }
            protected void addValueFrom(Input input, Collection collection) 
            throws IOException
            {
                collection.add(inline.readFrom(input));
            }
            protected void writeValueTo(Output output, int fieldNumber, Object value, 
                    boolean repeated) throws IOException
            {
                inline.writeTo(output, fieldNumber, value, repeated);
            }
            protected void transferValue(Pipe pipe, Input input, Output output, 
                    int number, boolean repeated) throws IOException
            {
                inline.transfer(pipe, input, output, number, repeated);
            }
        };
    }
    
    private static  Field createCollectionEnumV(int number, String name, 
            final java.lang.reflect.Field f, MessageFactory messageFactory,  
            Class genericType)
    {
        final EnumIO eio = EnumIO.get(genericType);
        return new RuntimeCollectionField>(
                FieldType.ENUM, 
                number, name, messageFactory)
        {
            {
                f.setAccessible(true);
            }
            @SuppressWarnings("unchecked")
            protected void mergeFrom(Input input, T message) throws IOException
            {
                try
                {
                    f.set(message, input.mergeObject((Collection>)f.get(message), 
                            schema));
                }
                catch(IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch(IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
            }
            @SuppressWarnings("unchecked")
            protected void writeTo(Output output, T message) throws IOException
            {
                final Collection> existing;
                try
                {
                    existing = (Collection>)f.get(message);
                }
                catch(IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch(IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
                
                if(existing != null)
                    output.writeObject(number, existing, schema, false);
            }
            protected void transfer(Pipe pipe, Input input, Output output, 
                    boolean repeated) throws IOException
            {
                output.writeObject(number, pipe, schema.pipeSchema, repeated);
            }
            protected void addValueFrom(Input input, Collection> collection) 
            throws IOException
            {
                collection.add(eio.readFrom(input));
            }
            protected void writeValueTo(Output output, int fieldNumber, Enum value, 
                    boolean repeated) throws IOException
            {
                EnumIO.writeTo(output, fieldNumber, repeated, value);
            }
            protected void transferValue(Pipe pipe, Input input, Output output, 
                    int number, boolean repeated) throws IOException
            {
                EnumIO.transfer(pipe, input, output, number, repeated);
            }
        };
    }
    
    private static  Field createCollectionPojoV(int number, String name, 
            final java.lang.reflect.Field f, MessageFactory messageFactory, 
            Class genericType)
    {
        final HasSchema schemaV = RuntimeSchema.getSchemaWrapper(genericType);
        return new RuntimeCollectionField(
                FieldType.MESSAGE, 
                number, name, messageFactory)
        {
            
            {
                f.setAccessible(true);
            }
            @SuppressWarnings("unchecked")
            protected void mergeFrom(Input input, T message) throws IOException
            {
                try
                {
                    f.set(message, input.mergeObject((Collection)f.get(message), 
                            schema));
                }
                catch(IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch(IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
            }
            @SuppressWarnings("unchecked")
            protected void writeTo(Output output, T message) throws IOException
            {
                final Collection existing;
                try
                {
                    existing = (Collection)f.get(message);
                }
                catch(IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch(IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
                
                if(existing != null)
                    output.writeObject(number, existing, schema, false);
            }
            protected void transfer(Pipe pipe, Input input, Output output, 
                    boolean repeated) throws IOException
            {
                output.writeObject(number, pipe, schema.pipeSchema, repeated);
            }
            protected void addValueFrom(Input input, Collection collection) 
            throws IOException
            {
                collection.add(input.mergeObject(null, schemaV.getSchema()));
            }
            protected void writeValueTo(Output output, int fieldNumber, Object value, 
                    boolean repeated) throws IOException
            {
                output.writeObject(fieldNumber, value, schemaV.getSchema(), repeated);
            }
            protected void transferValue(Pipe pipe, Input input, Output output, 
                    int number, boolean repeated) throws IOException
            {
                output.writeObject(number, pipe, schemaV.getPipeSchema(), repeated);
            }
        };
    }
    
    private static  Field createCollectionPolymorphicV(int number, String name, 
            final java.lang.reflect.Field f, MessageFactory messageFactory,  
            Class genericType)
    {
        return new RuntimeCollectionField(
                FieldType.MESSAGE, 
                number, name, messageFactory)
        {
            {
                f.setAccessible(true);
            }
            @SuppressWarnings("unchecked")
            protected void mergeFrom(Input input, T message) throws IOException
            {
                try
                {
                    f.set(message, input.mergeObject((Collection)f.get(message), 
                            schema));
                }
                catch(IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch(IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
            }
            @SuppressWarnings("unchecked")
            protected void writeTo(Output output, T message) throws IOException
            {
                final Collection existing;
                try
                {
                    existing = (Collection)f.get(message);
                }
                catch(IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch(IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
                
                if(existing != null)
                    output.writeObject(number, existing, schema, false);
            }
            protected void transfer(Pipe pipe, Input input, Output output, 
                    boolean repeated) throws IOException
            {
                output.writeObject(number, pipe, schema.pipeSchema, repeated);
            }
            protected void addValueFrom(Input input, Collection collection) 
            throws IOException
            {
                final Object value = input.mergeObject(collection, 
                        POLYMORPHIC_COLLECTION_VALUE_SCHEMA);
                
                if(input instanceof GraphInput && 
                        ((GraphInput)input).isCurrentMessageReference())
                {
                    collection.add(value);
                }
            }
            protected void writeValueTo(Output output, int fieldNumber, Object value, 
                    boolean repeated) throws IOException
            {
                output.writeObject(fieldNumber, value, 
                        POLYMORPHIC_COLLECTION_VALUE_SCHEMA, repeated);
            }
            protected void transferValue(Pipe pipe, Input input, Output output, 
                    int number, boolean repeated) throws IOException
            {
                output.writeObject(number, pipe, 
                        POLYMORPHIC_COLLECTION_VALUE_SCHEMA.pipeSchema, repeated);
            }
        };
    }
    
    private static  Field createCollectionObjectV(int number, String name, 
            final java.lang.reflect.Field f, MessageFactory messageFactory)
    {
        return new RuntimeCollectionField(
                FieldType.MESSAGE, 
                number, name, messageFactory)
        {
            {
                f.setAccessible(true);
            }
            @SuppressWarnings("unchecked")
            protected void mergeFrom(Input input, T message) throws IOException
            {
                try
                {
                    f.set(message, input.mergeObject((Collection)f.get(message), 
                            schema));
                }
                catch(IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch(IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
            }
            @SuppressWarnings("unchecked")
            protected void writeTo(Output output, T message) throws IOException
            {
                final Collection existing;
                try
                {
                    existing = (Collection)f.get(message);
                }
                catch(IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch(IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
                
                if(existing != null)
                    output.writeObject(number, existing, schema, false);
            }
            protected void transfer(Pipe pipe, Input input, Output output, 
                    boolean repeated) throws IOException
            {
                output.writeObject(number, pipe, schema.pipeSchema, repeated);
            }
            protected void addValueFrom(Input input, Collection collection) 
            throws IOException
            {
                final Object value = input.mergeObject(collection, 
                        OBJECT_COLLECTION_VALUE_SCHEMA);
                
                if(input instanceof GraphInput && 
                        ((GraphInput)input).isCurrentMessageReference())
                {
                    collection.add(value);
                }
            }
            protected void writeValueTo(Output output, int fieldNumber, Object value, 
                    boolean repeated) throws IOException
            {
                output.writeObject(fieldNumber, value, 
                        OBJECT_COLLECTION_VALUE_SCHEMA, repeated);
            }
            protected void transferValue(Pipe pipe, Input input, Output output, 
                    int number, boolean repeated) throws IOException
            {
                output.writeObject(number, pipe, 
                        OBJECT_COLLECTION_VALUE_SCHEMA.pipeSchema, repeated);
            }
        };
    }
    
    private static final RuntimeFieldFactory> COLLECTION = new RuntimeFieldFactory>(RuntimeFieldFactory.ID_COLLECTION)
    {
        @SuppressWarnings("unchecked")
        public  Field create(int number, String name, final java.lang.reflect.Field f)
        {
            if(EnumSet.class.isAssignableFrom(f.getType()))
            {
                final Class enumType;
                try
                {
                    enumType = (Class)((ParameterizedType)f.getGenericType()).getActualTypeArguments()[0];
                }
                catch(Exception e)
                {
                    // still handle the serialization of EnumSets even without generics
                    return RuntimeFieldFactory.OBJECT.create(number, name, f);
                }
                
                return createCollectionEnumV(number, name, f, 
                        EnumIO.get(enumType).getEnumSetFactory(), enumType);
            }

            final MessageFactory messageFactory = MessageFactories.getFactory(
                    (Class>)f.getType());
            
            if(messageFactory == null)
            {
                // Not a standard jdk Collection impl.
                throw new RuntimeException("Not a standard jdk collection: " + 
                        f.getType());
            }
            
            final Class genericType;
            try
            {
                genericType = (Class)((ParameterizedType)f.getGenericType()).getActualTypeArguments()[0];
            }
            catch(Exception e)
            {
                // the value is not a simple parameterized type.
                return createCollectionObjectV(number, name, f, messageFactory);
            }
            
            if(isComplexComponentType(genericType))
                return createCollectionObjectV(number, name, f, messageFactory);
            
            if(genericType.isEnum())
                return createCollectionEnumV(number, name, f, messageFactory, genericType);
            
            final RuntimeFieldFactory inline = getInline(genericType);
            if(inline != null)
                return createCollectionInlineV(number, name, f, messageFactory, inline);
            
            if(POJO == pojo(genericType) || RuntimeSchema.isRegistered(genericType))
                return createCollectionPojoV(number, name, f, messageFactory, genericType);
            
            return createCollectionPolymorphicV(number, name, f, messageFactory, genericType);
        }
        protected void transfer(Pipe pipe, Input input, Output output, int number, 
                boolean repeated) throws IOException
        {
            throw new UnsupportedOperationException();
        }
        protected Collection readFrom(Input input) throws IOException
        {
            throw new UnsupportedOperationException();
        }
        protected void writeTo(Output output, int number, Collection value, 
                boolean repeated) throws IOException
        {
            throw new UnsupportedOperationException();
        }
        protected FieldType getFieldType()
        {
            throw new UnsupportedOperationException();
        }
        protected Class typeClass()
        {
            throw new UnsupportedOperationException();
        }
    };

}