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

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

The newest version!
//================================================================================
//Copyright (c) 2012, David Yu
//All rights reserved.
//--------------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
// 3. Neither the name of protostuff nor the names of its contributors may be used
//    to endorse or promote products derived from this software without
//    specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//================================================================================


package com.dyuproject.protostuff.runtime;

import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.ID_COLLECTION;
import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.ID_ENUM;
import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.ID_ENUM_SET;
import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.STR_COLLECTION;
import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.STR_ENUM;
import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.STR_ENUM_SET;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;

import com.dyuproject.protostuff.GraphInput;
import com.dyuproject.protostuff.Input;
import com.dyuproject.protostuff.Output;
import com.dyuproject.protostuff.Pipe;
import com.dyuproject.protostuff.ProtostuffException;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.StatefulOutput;
import com.dyuproject.protostuff.runtime.IdStrategy.Wrapper;
import com.dyuproject.protostuff.runtime.RuntimeEnv.Instantiator;


/**
 * Used when the type is an interface (Collection/List/Set/SortedSet).
 *
 * @author David Yu
 * @created Apr 24, 2012
 */
public abstract class PolymorphicCollectionSchema extends PolymorphicSchema
{
    
    static final int ID_EMPTY_SET = 1, 
            ID_EMPTY_LIST = 2, 
            
            ID_SINGLETON_SET = 3, 
            ID_SINGLETON_LIST = 4, 
            
            ID_SET_FROM_MAP = 5, 
            ID_COPIES_LIST = 6,
            
            ID_UNMODIFIABLE_COLLECTION = 7, 
            ID_UNMODIFIABLE_SET = 8, 
            ID_UNMODIFIABLE_SORTED_SET = 9, 
            ID_UNMODIFIABLE_LIST = 10, 
            ID_UNMODIFIABLE_RANDOM_ACCESS_LIST =11, 
            
            ID_SYNCHRONIZED_COLLECTION = 12, 
            ID_SYNCHRONIZED_SET = 13, 
            ID_SYNCHRONIZED_SORTED_SET = 14, 
            ID_SYNCHRONIZED_LIST = 15, 
            ID_SYNCHRONIZED_RANDOM_ACCESS_LIST = 16, 

            ID_CHECKED_COLLECTION = 17, 
            ID_CHECKED_SET = 18, 
            ID_CHECKED_SORTED_SET = 19, 
            ID_CHECKED_LIST = 20, 
            ID_CHECKED_RANDOM_ACCESS_LIST = 21;
             
    
    static final String STR_EMPTY_SET = "a", 
            STR_EMPTY_LIST = "b", 
            
            STR_SINGLETON_SET = "c", 
            STR_SINGLETON_LIST = "d", 
            
            STR_SET_FROM_MAP = "e", 
            STR_COPIES_LIST = "f", 
            
            STR_UNMODIFIABLE_COLLECTION = "g", 
            STR_UNMODIFIABLE_SET = "h", 
            STR_UNMODIFIABLE_SORTED_SET = "i", 
            STR_UNMODIFIABLE_LIST = "j", 
            STR_UNMODIFIABLE_RANDOM_ACCESS_LIST = "k",
            
            STR_SYNCHRONIZED_COLLECTION = "l", 
            STR_SYNCHRONIZED_SET = "m", 
            STR_SYNCHRONIZED_SORTED_SET = "n", 
            STR_SYNCHRONIZED_LIST = "o", 
            STR_SYNCHRONIZED_RANDOM_ACCESS_LIST = "p", 

            STR_CHECKED_COLLECTION = "q", 
            STR_CHECKED_SET = "r", 
            STR_CHECKED_SORTED_SET = "s", 
            STR_CHECKED_LIST = "t", 
            STR_CHECKED_RANDOM_ACCESS_LIST = "u";
    
    static final IdentityHashMap,Integer> __nonPublicCollections = 
            new IdentityHashMap, Integer>();
    
    static final Field fSingletonSet_element, 
            fSingletonList_element, 
            
            fUnmodifiableCollection_c,
            fUnmodifiableSortedSet_ss, 
            fUnmodifiableList_list, 
            
            fSynchronizedCollection_c,
            fSynchronizedSortedSet_ss, 
            fSynchronizedList_list, 
            fSynchronizedCollection_mutex,
            
            fCheckedCollection_c, 
            fCheckedSortedSet_ss, 
            fCheckedList_list, 
            fCheckedCollection_type,
            
            fSetFromMap_m, 
            fSetFromMap_s, 
            
            fCopiesList_n, 
            fCopiesList_element;
        
    
    static final Instantiator iSingletonSet, 
            iSingletonList, 
            
            iUnmodifiableCollection,
            iUnmodifiableSet, 
            iUnmodifiableSortedSet, 
            iUnmodifiableList,
            iUnmodifiableRandomAccessList, 
            
            iSynchronizedCollection, 
            iSynchronizedSet,
            iSynchronizedSortedSet, 
            iSynchronizedList, 
            iSynchronizedRandomAccessList,
            
            iCheckedCollection, 
            iCheckedSet, 
            iCheckedSortedSet, 
            iCheckedList,
            iCheckedRandomAccessList, 
            
            iSetFromMap, 
            
            iCopiesList;
        
    
    static
    {
        map("java.util.Collections$EmptySet", ID_EMPTY_SET);
        map("java.util.Collections$EmptyList", ID_EMPTY_LIST);
        
        Class cSingletonSet = 
                map("java.util.Collections$SingletonSet", ID_SINGLETON_SET);
        
        Class cSingletonList = 
                map("java.util.Collections$SingletonList", ID_SINGLETON_LIST);
        
        Class cSetFromMap = 
                map("java.util.Collections$SetFromMap", ID_SET_FROM_MAP);
        
        Class cCopiesList = 
                map("java.util.Collections$CopiesList", ID_COPIES_LIST);
        
        Class cUnmodifiableCollection = 
                map("java.util.Collections$UnmodifiableCollection", ID_UNMODIFIABLE_COLLECTION);
        Class cUnmodifiableSet = 
                map("java.util.Collections$UnmodifiableSet", ID_UNMODIFIABLE_SET);
        Class cUnmodifiableSortedSet = 
                map("java.util.Collections$UnmodifiableSortedSet", ID_UNMODIFIABLE_SORTED_SET);
        Class cUnmodifiableList = 
                map("java.util.Collections$UnmodifiableList", ID_UNMODIFIABLE_LIST);
        Class cUnmodifiableRandomAccessList = 
                map("java.util.Collections$UnmodifiableRandomAccessList", ID_UNMODIFIABLE_RANDOM_ACCESS_LIST);
        
        Class cSynchronizedCollection = 
                map("java.util.Collections$SynchronizedCollection", ID_SYNCHRONIZED_COLLECTION);
        Class cSynchronizedSet = 
                map("java.util.Collections$SynchronizedSet", ID_SYNCHRONIZED_SET);
        Class cSynchronizedSortedSet = 
                map("java.util.Collections$SynchronizedSortedSet", ID_SYNCHRONIZED_SORTED_SET);
        Class cSynchronizedList = 
                map("java.util.Collections$SynchronizedList", ID_SYNCHRONIZED_LIST);
        Class cSynchronizedRandomAccessList = 
                map("java.util.Collections$SynchronizedRandomAccessList", ID_SYNCHRONIZED_RANDOM_ACCESS_LIST);
        
        Class cCheckedCollection = 
                map("java.util.Collections$CheckedCollection", ID_CHECKED_COLLECTION);
        Class cCheckedSet = 
                map("java.util.Collections$CheckedSet", ID_CHECKED_SET);
        Class cCheckedSortedSet = 
                map("java.util.Collections$CheckedSortedSet", ID_CHECKED_SORTED_SET);
        Class cCheckedList = 
                map("java.util.Collections$CheckedList", ID_CHECKED_LIST);
        Class cCheckedRandomAccessList = 
                map("java.util.Collections$CheckedRandomAccessList", ID_CHECKED_RANDOM_ACCESS_LIST);
        
        try
        {
            fSingletonSet_element = cSingletonSet.getDeclaredField("element");
            
            fSingletonList_element = cSingletonList.getDeclaredField("element");
            
            fSetFromMap_m = cSetFromMap.getDeclaredField("m");
            fSetFromMap_s = cSetFromMap.getDeclaredField("s");
            
            fCopiesList_n = cCopiesList.getDeclaredField("n");
            fCopiesList_element = cCopiesList.getDeclaredField("element");
            
            fUnmodifiableCollection_c = cUnmodifiableCollection.getDeclaredField("c");
            fUnmodifiableSortedSet_ss = cUnmodifiableSortedSet.getDeclaredField("ss");
            fUnmodifiableList_list = cUnmodifiableList.getDeclaredField("list");
            
            fSynchronizedCollection_c = cSynchronizedCollection.getDeclaredField("c");
            fSynchronizedCollection_mutex = cSynchronizedCollection.getDeclaredField("mutex");
            fSynchronizedSortedSet_ss = cSynchronizedSortedSet.getDeclaredField("ss");
            fSynchronizedList_list = cSynchronizedList.getDeclaredField("list");
            
            fCheckedCollection_c = cCheckedCollection.getDeclaredField("c");
            fCheckedCollection_type = cCheckedCollection.getDeclaredField("type");
            fCheckedSortedSet_ss = cCheckedSortedSet.getDeclaredField("ss");
            fCheckedList_list = cCheckedList.getDeclaredField("list");
        }
        catch(Exception e)
        {
            throw new RuntimeException(e);
        }
        
        // field accessors
        
        fSingletonSet_element.setAccessible(true);
        
        fSingletonList_element.setAccessible(true);
        
        fSetFromMap_m.setAccessible(true);
        fSetFromMap_s.setAccessible(true);
        
        fCopiesList_n.setAccessible(true);
        fCopiesList_element.setAccessible(true);
        
        fUnmodifiableCollection_c.setAccessible(true);
        fUnmodifiableSortedSet_ss.setAccessible(true);
        fUnmodifiableList_list.setAccessible(true);
        
        fSynchronizedCollection_c.setAccessible(true);
        fSynchronizedCollection_mutex.setAccessible(true);
        fSynchronizedSortedSet_ss.setAccessible(true);
        fSynchronizedList_list.setAccessible(true);
        
        fCheckedCollection_c.setAccessible(true);
        fCheckedCollection_type.setAccessible(true);
        fCheckedSortedSet_ss.setAccessible(true);
        fCheckedList_list.setAccessible(true);
        
        // instantiators
        
        iSingletonSet = RuntimeEnv.newInstantiator(cSingletonSet);
        
        iSingletonList = RuntimeEnv.newInstantiator(cSingletonList);
        
        iSetFromMap = RuntimeEnv.newInstantiator(cSetFromMap);
        
        iCopiesList = RuntimeEnv.newInstantiator(cCopiesList);
        
        iUnmodifiableCollection = RuntimeEnv.newInstantiator(cUnmodifiableCollection);
        iUnmodifiableSet = RuntimeEnv.newInstantiator(cUnmodifiableSet);
        iUnmodifiableSortedSet = RuntimeEnv.newInstantiator(cUnmodifiableSortedSet);
        iUnmodifiableList = RuntimeEnv.newInstantiator(cUnmodifiableList);
        iUnmodifiableRandomAccessList = RuntimeEnv.newInstantiator(cUnmodifiableRandomAccessList);
        
        iSynchronizedCollection = RuntimeEnv.newInstantiator(cSynchronizedCollection);
        iSynchronizedSet = RuntimeEnv.newInstantiator(cSynchronizedSet);
        iSynchronizedSortedSet = RuntimeEnv.newInstantiator(cSynchronizedSortedSet);
        iSynchronizedList = RuntimeEnv.newInstantiator(cSynchronizedList);
        iSynchronizedRandomAccessList = RuntimeEnv.newInstantiator(cSynchronizedRandomAccessList);
        
        iCheckedCollection = RuntimeEnv.newInstantiator(cCheckedCollection);
        iCheckedSet = RuntimeEnv.newInstantiator(cCheckedSet);
        iCheckedSortedSet = RuntimeEnv.newInstantiator(cCheckedSortedSet);
        iCheckedList = RuntimeEnv.newInstantiator(cCheckedList);
        iCheckedRandomAccessList = RuntimeEnv.newInstantiator(cCheckedRandomAccessList);
    }
    
    private static Class map(String className, int id)
    {
        Class clazz = RuntimeEnv.loadClass(className);
        __nonPublicCollections.put(clazz, id);
        return clazz;
    }
    
    static String name(int number)
    {
        switch(number)
        {
            case ID_EMPTY_SET: return STR_EMPTY_SET;
            case ID_EMPTY_LIST: return STR_EMPTY_LIST;
            case ID_SINGLETON_SET: return STR_SINGLETON_SET;
            case ID_SINGLETON_LIST: return STR_SINGLETON_LIST;
            case ID_SET_FROM_MAP: return STR_SET_FROM_MAP;
            case ID_COPIES_LIST: return STR_COPIES_LIST;
            case ID_UNMODIFIABLE_COLLECTION: return STR_UNMODIFIABLE_COLLECTION;
            case ID_UNMODIFIABLE_SET: return STR_UNMODIFIABLE_SET;
            case ID_UNMODIFIABLE_SORTED_SET: return STR_UNMODIFIABLE_SORTED_SET;
            case ID_UNMODIFIABLE_LIST: return STR_UNMODIFIABLE_LIST;
            case ID_UNMODIFIABLE_RANDOM_ACCESS_LIST: return STR_UNMODIFIABLE_RANDOM_ACCESS_LIST;
            case ID_SYNCHRONIZED_COLLECTION: return STR_SYNCHRONIZED_COLLECTION;
            case ID_SYNCHRONIZED_SET: return STR_SYNCHRONIZED_SET;
            case ID_SYNCHRONIZED_SORTED_SET: return STR_SYNCHRONIZED_SORTED_SET;
            case ID_SYNCHRONIZED_LIST: return STR_SYNCHRONIZED_LIST;
            case ID_SYNCHRONIZED_RANDOM_ACCESS_LIST: return STR_SYNCHRONIZED_RANDOM_ACCESS_LIST;
            case ID_CHECKED_COLLECTION: return STR_CHECKED_COLLECTION;
            case ID_CHECKED_SET: return STR_CHECKED_SET;
            case ID_CHECKED_SORTED_SET: return STR_CHECKED_SORTED_SET;
            case ID_CHECKED_LIST: return STR_CHECKED_LIST;
            case ID_CHECKED_RANDOM_ACCESS_LIST: return STR_CHECKED_RANDOM_ACCESS_LIST;
            case ID_ENUM_SET: return STR_ENUM_SET;
            case ID_ENUM: return STR_ENUM;
            case ID_COLLECTION: return STR_COLLECTION;
            default: return null;
        }
    }
    
    static int number(String name)
    {
        if(name.length() != 1)
            return 0;
        
        switch(name.charAt(0))
        {
            case 'a': return 1;
            case 'b': return 2;
            case 'c': return 3;
            case 'd': return 4;
            case 'e': return 5;
            case 'f': return 6;
            case 'g': return 7;
            case 'h': return 8;
            case 'i': return 9;
            case 'j': return 10;
            case 'k': return 11;
            case 'l': return 12;
            case 'm': return 13;
            case 'n': return 14;
            case 'o': return 15;
            case 'p': return 16;
            case 'q': return 17;
            case 'r': return 18;
            case 's': return 19;
            case 't': return 20;
            case 'u': return 21;
            case 'v': return ID_ENUM_SET;
            case 'x': return ID_ENUM;
            case 'y': return ID_COLLECTION;
            default: return 0;
        }
    }
    
    protected final Pipe.Schema pipeSchema = new Pipe.Schema(this)
    {
        protected void transfer(Pipe pipe, Input input, Output output) throws IOException
        {
            transferObject(this, pipe, input, output, strategy);
        }
    };
    
    public PolymorphicCollectionSchema(IdStrategy strategy)
    {
        super(strategy);
    }
    
    public Pipe.Schema getPipeSchema()
    {
        return pipeSchema;
    }
    
    public String getFieldName(int number)
    {
        return name(number);
    }

    public int getFieldNumber(String name)
    {
        return number(name);
    }

    public String messageFullName()
    {
        return Collection.class.getName();
    }

    public String messageName()
    {
        return Collection.class.getSimpleName();
    }

    public void mergeFrom(Input input, Object owner) throws IOException
    {
        setValue(readObjectFrom(input, this, owner, strategy), owner);
    }

    public void writeTo(Output output, Object value) throws IOException
    {
        writeObjectTo(output, value, this, strategy);
    }
    
    static int idFrom(Class clazz)
    {
        final Integer id = __nonPublicCollections.get(clazz);
        if(id == null)
            throw new RuntimeException("Unknown collection: " + clazz);
        
        return id.intValue();
    }
    
    static Object instanceFrom(final int id)
    {
        switch(id)
        {
            case ID_EMPTY_SET:
                return Collections.EMPTY_SET;
            case ID_EMPTY_LIST:
                return Collections.EMPTY_LIST;
            
            case ID_SINGLETON_SET:
                return iSingletonSet.newInstance();
            case ID_SINGLETON_LIST:
                return iSingletonList.newInstance();
                
            case ID_SET_FROM_MAP:
                return iSetFromMap.newInstance();
            case ID_COPIES_LIST:
                return iCopiesList.newInstance();
                
            case ID_UNMODIFIABLE_COLLECTION:
                return iUnmodifiableCollection.newInstance();
            case ID_UNMODIFIABLE_SET:
                return iUnmodifiableSet.newInstance();
            case ID_UNMODIFIABLE_SORTED_SET:
                return iUnmodifiableSortedSet.newInstance();
            case ID_UNMODIFIABLE_LIST:
                return iUnmodifiableList.newInstance();
            case ID_UNMODIFIABLE_RANDOM_ACCESS_LIST:
                return iUnmodifiableRandomAccessList.newInstance();
                
            case ID_SYNCHRONIZED_COLLECTION:
                return iSynchronizedCollection.newInstance();
            case ID_SYNCHRONIZED_SET:
                return iSynchronizedSet.newInstance();
            case ID_SYNCHRONIZED_SORTED_SET:
                return iSynchronizedSortedSet.newInstance();
            case ID_SYNCHRONIZED_LIST:
                return iSynchronizedList.newInstance();
            case ID_SYNCHRONIZED_RANDOM_ACCESS_LIST:
                return iSynchronizedRandomAccessList.newInstance();
                
            case ID_CHECKED_COLLECTION:
                return iCheckedCollection.newInstance();
            case ID_CHECKED_SET:
                return iCheckedSet.newInstance();
            case ID_CHECKED_SORTED_SET:
                return iCheckedSortedSet.newInstance();
            case ID_CHECKED_LIST:
                return iCheckedList.newInstance();
            case ID_CHECKED_RANDOM_ACCESS_LIST:
                return iCheckedRandomAccessList.newInstance();
                
            default:
                throw new RuntimeException("Unknown id: " + id);
        }
    }
    
    @SuppressWarnings("unchecked")
    static void writeObjectTo(Output output, Object value, Schema currentSchema, 
            IdStrategy strategy) throws IOException
    {
        if(Collections.class == value.getClass().getDeclaringClass())
        {
            writeNonPublicCollectionTo(output, value, currentSchema, strategy);
            return;
        }
        
        if(EnumSet.class.isAssignableFrom(value.getClass()))
        {
            strategy.writeEnumIdTo(output, ID_ENUM_SET, 
                    EnumIO.getElementTypeFromEnumSet(value));
            
            // TODO optimize
        }
        else
        {
            strategy.writeCollectionIdTo(output, ID_COLLECTION, value.getClass());
        }
        
        if(output instanceof StatefulOutput)
        {
            // update using the derived schema.
            ((StatefulOutput)output).updateLast(strategy.COLLECTION_SCHEMA, currentSchema);
        }
        
        strategy.COLLECTION_SCHEMA.writeTo(output, (Collection)value);
    }
    
    static void writeNonPublicCollectionTo(Output output, Object value, 
            Schema currentSchema, IdStrategy strategy) throws IOException
    {
        final Integer num = __nonPublicCollections.get(value.getClass());
        if(num == null)
            throw new RuntimeException("Unknown collection: " + value.getClass());
        final int id = num.intValue();
        switch(id)
        {
            case ID_EMPTY_SET:
                output.writeUInt32(id, 0, false);
                break;
                
            case ID_EMPTY_LIST:
                output.writeUInt32(id, 0, false);
                break;
                
            case ID_SINGLETON_SET:
            {
                output.writeUInt32(id, 0, false);
                
                final Object element;
                try
                {
                    element = fSingletonSet_element.get(value);
                }
                catch (IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch (IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
                
                if(element != null)
                    output.writeObject(1, element, strategy.OBJECT_SCHEMA, false);
                
                break;
            }
                
            case ID_SINGLETON_LIST:
            {
                output.writeUInt32(id, 0, false);
                
                // faster path (reflection not needed to get the single element).
                final Object element = ((List)value).get(0);
                
                if(element != null)
                    output.writeObject(1, element, strategy.OBJECT_SCHEMA, false);
                
                break;
            }
                
            case ID_SET_FROM_MAP:
            {
                final Object m;
                try
                {
                    m = fSetFromMap_m.get(value);
                }
                catch (IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch (IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
                
                output.writeObject(id, m, strategy.POLYMORPHIC_MAP_SCHEMA, false);
                
                break;
            }
                
            case ID_COPIES_LIST:
            {
                output.writeUInt32(id, 0, false);
                
                final int n = ((List)value).size();
                final Object element;
                try
                {
                    element = fCopiesList_element.get(value);
                }
                catch (IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch (IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
                
                output.writeUInt32(1, n, false);
                
                if(element != null)
                    output.writeObject(2, element, strategy.OBJECT_SCHEMA, false);
                
                break;
            }   
            case ID_UNMODIFIABLE_COLLECTION:
                writeUnmodifiableCollectionTo(output, value, currentSchema, strategy, id);
                break;
            case ID_UNMODIFIABLE_SET:
                writeUnmodifiableCollectionTo(output, value, currentSchema, strategy, id);
                break;
            case ID_UNMODIFIABLE_SORTED_SET:
                writeUnmodifiableCollectionTo(output, value, currentSchema, strategy, id);
                break;
            case ID_UNMODIFIABLE_LIST:
                writeUnmodifiableCollectionTo(output, value, currentSchema, strategy, id);
                break;
            case ID_UNMODIFIABLE_RANDOM_ACCESS_LIST:
                writeUnmodifiableCollectionTo(output, value, currentSchema, strategy, id);
                break;
            
            case ID_SYNCHRONIZED_COLLECTION: 
                writeSynchronizedCollectionTo(output, value, currentSchema, strategy, id);
                break;
            case ID_SYNCHRONIZED_SET:
                writeSynchronizedCollectionTo(output, value, currentSchema, strategy, id);
                break;
            case ID_SYNCHRONIZED_SORTED_SET:
                writeSynchronizedCollectionTo(output, value, currentSchema, strategy, id);
                break;
            case ID_SYNCHRONIZED_LIST:
                writeSynchronizedCollectionTo(output, value, currentSchema, strategy, id);
                break;
            case ID_SYNCHRONIZED_RANDOM_ACCESS_LIST: 
                writeSynchronizedCollectionTo(output, value, currentSchema, strategy, id);
                break;
                
            case ID_CHECKED_COLLECTION: 
                writeCheckedCollectionTo(output, value, currentSchema, strategy, id);
                break;
            case ID_CHECKED_SET:
                writeCheckedCollectionTo(output, value, currentSchema, strategy, id);
                break;
            case ID_CHECKED_SORTED_SET:
                writeCheckedCollectionTo(output, value, currentSchema, strategy, id);
                break;
            case ID_CHECKED_LIST:
                writeCheckedCollectionTo(output, value, currentSchema, strategy, id);
                break;
            case ID_CHECKED_RANDOM_ACCESS_LIST:
                writeCheckedCollectionTo(output, value, currentSchema, strategy, id);
                break;
                
            default: 
                throw new RuntimeException("Should not happen.");
        }
    }
    
    private static void writeUnmodifiableCollectionTo(Output output, Object value, 
            Schema currentSchema, IdStrategy strategy, int id) throws IOException
    {
        final Object c;
        try
        {
            c = fUnmodifiableCollection_c.get(value);
        }
        catch (IllegalArgumentException e)
        {
            throw new RuntimeException(e);
        }
        catch (IllegalAccessException e)
        {
            throw new RuntimeException(e);
        }
        
        output.writeObject(id, c, strategy.POLYMORPHIC_COLLECTION_SCHEMA, false);
    }
    
    private static void writeSynchronizedCollectionTo(Output output, Object value, 
            Schema currentSchema, IdStrategy strategy, int id) throws IOException
    {
        final Object c, mutex;
        try
        {
            c = fSynchronizedCollection_c.get(value);
            mutex = fSynchronizedCollection_mutex.get(value);
        }
        catch (IllegalArgumentException e)
        {
            throw new RuntimeException(e);
        }
        catch (IllegalAccessException e)
        {
            throw new RuntimeException(e);
        }
        
        if(mutex != value)
        {
            // TODO for future release, introduce an interface(GraphOutput) so we 
            // can check whether the output can retain references.
            throw new RuntimeException(
                    "This exception is thrown to fail fast. " +
                    "Synchronized collections with a different mutex would only " +
                    "work if graph format is used, since the reference is retained.");
        }
        
        output.writeObject(id, c, strategy.POLYMORPHIC_COLLECTION_SCHEMA, false);
    }
    
    private static void writeCheckedCollectionTo(Output output, Object value, 
            Schema currentSchema, IdStrategy strategy, int id) throws IOException
    {
        final Object c, type;
        try
        {
            c = fCheckedCollection_c.get(value);
            type = fCheckedCollection_type.get(value);
        }
        catch (IllegalArgumentException e)
        {
            throw new RuntimeException(e);
        }
        catch (IllegalAccessException e)
        {
            throw new RuntimeException(e);
        }
        
        output.writeObject(id, c, strategy.POLYMORPHIC_COLLECTION_SCHEMA, false);
        output.writeObject(1, type, strategy.CLASS_SCHEMA, false);
    }

    @SuppressWarnings("unchecked")
    static Object readObjectFrom(Input input,  Schema schema, Object owner, 
            IdStrategy strategy) throws IOException // TODO pass in type (Class)
    {
        final boolean graph = input instanceof GraphInput;
        Object ret = null;
        final int number = input.readFieldNumber(schema);
        switch(number)
        {
            case ID_EMPTY_SET:
                if(0 != input.readUInt32())
                    throw new ProtostuffException("Corrupt input.");
                
                if(graph)
                {
                    // update the actual reference.
                    ((GraphInput)input).updateLast(Collections.EMPTY_SET, owner);
                }
                
                ret = Collections.EMPTY_SET;
                break;
                
            case ID_EMPTY_LIST:
                if(0 != input.readUInt32())
                    throw new ProtostuffException("Corrupt input.");
                
                if(graph)
                {
                    // update the actual reference.
                    ((GraphInput)input).updateLast(Collections.EMPTY_LIST, owner);
                }
                
                ret = Collections.EMPTY_LIST;
                break;
                
            case ID_SINGLETON_SET:
            {
                if(0 != input.readUInt32())
                    throw new ProtostuffException("Corrupt input.");
                
                final Object collection = iSingletonSet.newInstance();
                if(graph)
                {
                    // update the actual reference.
                    ((GraphInput)input).updateLast(collection, owner);
                }
                
                final int next = input.readFieldNumber(schema);
                if(next == 0)
                {
                    // null element
                    return collection;
                }
                
                if(next != 1)
                    throw new ProtostuffException("Corrupt input");
                
                final Wrapper wrapper = new Wrapper();
                Object element = input.mergeObject(wrapper, strategy.OBJECT_SCHEMA);
                if(!graph || !((GraphInput)input).isCurrentMessageReference())
                    element = wrapper.value;
                
                try
                {
                    fSingletonSet_element.set(collection, element);
                }
                catch (IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch (IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
                
                ret = collection;
                break;
            }
            
            case ID_SINGLETON_LIST:
            {
                if(0 != input.readUInt32())
                    throw new ProtostuffException("Corrupt input.");
                
                final Object collection = iSingletonList.newInstance();
                if(graph)
                {
                    // update the actual reference.
                    ((GraphInput)input).updateLast(collection, owner);
                }
                
                final int next = input.readFieldNumber(schema);
                if(next == 0)
                {
                    // null element
                    return collection;
                }
                
                if(next != 1)
                    throw new ProtostuffException("Corrupt input.");
                
                final Wrapper wrapper = new Wrapper();
                Object element = input.mergeObject(wrapper, strategy.OBJECT_SCHEMA);
                if(!graph || !((GraphInput)input).isCurrentMessageReference())
                    element = wrapper.value;
                
                try
                {
                    fSingletonList_element.set(collection, element);
                }
                catch (IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch (IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
                
                ret = collection;
                break;
            }
            
            case ID_SET_FROM_MAP:
            {
                final Object collection = iSetFromMap.newInstance();
                if(graph)
                {
                    // update the actual reference.
                    ((GraphInput)input).updateLast(collection, owner);
                }
                
                final Wrapper wrapper = new Wrapper();
                Object m = input.mergeObject(wrapper, strategy.POLYMORPHIC_MAP_SCHEMA);
                if(!graph || !((GraphInput)input).isCurrentMessageReference())
                    m = wrapper.value;
                
                try
                {
                    fSetFromMap_m.set(collection, m);
                    fSetFromMap_s.set(collection, ((Map)m).keySet());
                }
                catch (IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch (IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
                
                ret = collection;
                break;
            }
                
            case ID_COPIES_LIST:
            {
                if(0 != input.readUInt32())
                    throw new ProtostuffException("Corrupt input.");
                
                final Object collection = iCopiesList.newInstance();
                if(graph)
                {
                    // update the actual reference.
                    ((GraphInput)input).updateLast(collection, owner);
                }
                
                if(1 != input.readFieldNumber(schema))
                    throw new ProtostuffException("Corrupt input.");
                
                final int n = input.readUInt32(), 
                        next = input.readFieldNumber(schema);
                
                if(next == 0)
                {
                    // null element
                    try
                    {
                        fCopiesList_n.setInt(collection, n);
                    }
                    catch (IllegalArgumentException e)
                    {
                        throw new RuntimeException(e);
                    }
                    catch (IllegalAccessException e)
                    {
                        throw new RuntimeException(e);
                    }
                    
                    return collection;
                }
                
                if(next != 2)
                    throw new ProtostuffException("Corrupt input.");
                
                final Wrapper wrapper = new Wrapper();
                Object element = input.mergeObject(wrapper, strategy.OBJECT_SCHEMA);
                if(!graph || !((GraphInput)input).isCurrentMessageReference())
                    element = wrapper.value;
                
                try
                {
                    fCopiesList_n.setInt(collection, n);
                    fCopiesList_element.set(collection, element);
                }
                catch (IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch (IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
                
                ret = collection;
                break;
            }
            
            case ID_UNMODIFIABLE_COLLECTION:
                ret = readUnmodifiableCollectionFrom(input, schema, owner, strategy, graph,  
                        iUnmodifiableCollection.newInstance(), false, false);
                break;
            case ID_UNMODIFIABLE_SET:
                ret = readUnmodifiableCollectionFrom(input, schema, owner, strategy, graph,  
                        iUnmodifiableSet.newInstance(), false, false);
                break;
            case ID_UNMODIFIABLE_SORTED_SET:
                ret = readUnmodifiableCollectionFrom(input, schema, owner, strategy, graph,  
                        iUnmodifiableSortedSet.newInstance(), true, false);
                break;
            case ID_UNMODIFIABLE_LIST:
                ret = readUnmodifiableCollectionFrom(input, schema, owner, strategy, graph,  
                        iUnmodifiableList.newInstance(), false, true);
                break;
            case ID_UNMODIFIABLE_RANDOM_ACCESS_LIST:
                ret = readUnmodifiableCollectionFrom(input, schema, owner, strategy, graph,  
                        iUnmodifiableRandomAccessList.newInstance(), false, true);
                break;
                
            case ID_SYNCHRONIZED_COLLECTION:
                ret = readSynchronizedCollectionFrom(input, schema, owner, strategy, graph,  
                        iSynchronizedCollection.newInstance(), false, false);
                break;
            case ID_SYNCHRONIZED_SET:
                ret = readSynchronizedCollectionFrom(input, schema, owner, strategy, graph,  
                        iSynchronizedSet.newInstance(), false, false);
                break;
            case ID_SYNCHRONIZED_SORTED_SET:
                ret = readSynchronizedCollectionFrom(input, schema, owner, strategy, graph,  
                        iSynchronizedSortedSet.newInstance(), true, false);
                break;
            case ID_SYNCHRONIZED_LIST:
                ret = readSynchronizedCollectionFrom(input, schema, owner, strategy, graph,  
                        iSynchronizedList.newInstance(), false, true);
                break;
            case ID_SYNCHRONIZED_RANDOM_ACCESS_LIST:
                ret = readSynchronizedCollectionFrom(input, schema, owner, strategy, graph,  
                        iSynchronizedRandomAccessList.newInstance(), false, true);
                break;
                
            case ID_CHECKED_COLLECTION:
                ret = readCheckedCollectionFrom(input, schema, owner, strategy, graph,  
                        iCheckedCollection.newInstance(), false, false);
                break;
            case ID_CHECKED_SET:
                ret = readCheckedCollectionFrom(input, schema, owner, strategy, graph,  
                        iCheckedSet.newInstance(), false, false);
                break;
            case ID_CHECKED_SORTED_SET:
                ret = readCheckedCollectionFrom(input, schema, owner, strategy, graph,  
                        iCheckedSortedSet.newInstance(), true, false);
                break;
            case ID_CHECKED_LIST:
                ret = readCheckedCollectionFrom(input, schema, owner, strategy, graph,  
                        iCheckedList.newInstance(), false, true);
                break;
            case ID_CHECKED_RANDOM_ACCESS_LIST:
                ret = readCheckedCollectionFrom(input, schema, owner, strategy, graph,  
                        iCheckedRandomAccessList.newInstance(), false, true);
                break;
                
            case ID_ENUM_SET:
            {
                final Collection es = strategy.resolveEnumFrom(input).newEnumSet();
                
                if(graph)
                {
                    // update the actual reference.
                    ((GraphInput)input).updateLast(es, owner);
                }
                
                // TODO enum schema
                strategy.COLLECTION_SCHEMA.mergeFrom(input, (Collection)es);
                return es;
            }
                
            case ID_COLLECTION:
            {
                final Collection collection = strategy.resolveCollectionFrom(
                        input).newMessage();
                
                if(graph)
                {
                    // update the actual reference.
                    ((GraphInput)input).updateLast(collection, owner);
                }
                
                strategy.COLLECTION_SCHEMA.mergeFrom(input, collection);
                
                return collection;
            }
                
            default: throw new ProtostuffException("Corrupt input.");
        }
        
        if(0 != input.readFieldNumber(schema))
            throw new ProtostuffException("Corrupt input.");
        
        return ret;
    }
    
    private static Object readUnmodifiableCollectionFrom(Input input,  Schema schema, 
            Object owner, IdStrategy strategy, boolean graph, 
            Object collection, boolean ss, boolean list) throws IOException
    {
        if(graph)
        {
            // update the actual reference.
            ((GraphInput)input).updateLast(collection, owner);
        }
        
        final Wrapper wrapper = new Wrapper();
        Object c = input.mergeObject(wrapper, strategy.POLYMORPHIC_COLLECTION_SCHEMA);
        if(!graph || !((GraphInput)input).isCurrentMessageReference())
            c = wrapper.value;
        try
        {
            fUnmodifiableCollection_c.set(collection, c);
            
            if(ss)
                fUnmodifiableSortedSet_ss.set(collection, c);
            
            if(list)
                fUnmodifiableList_list.set(collection, c);
        }
        catch (IllegalArgumentException e)
        {
            throw new RuntimeException(e);
        }
        catch (IllegalAccessException e)
        {
            throw new RuntimeException(e);
        }
        
        return collection;
    }
    
    private static Object readSynchronizedCollectionFrom(Input input,  Schema schema, 
            Object owner, IdStrategy strategy, boolean graph, 
            Object collection, boolean ss, boolean list) throws IOException
    {
        if(graph)
        {
            // update the actual reference.
            ((GraphInput)input).updateLast(collection, owner);
        }
        
        final Wrapper wrapper = new Wrapper();
        Object c = input.mergeObject(wrapper, strategy.POLYMORPHIC_COLLECTION_SCHEMA);
        if(!graph || !((GraphInput)input).isCurrentMessageReference())
            c = wrapper.value;
        try
        {
            fSynchronizedCollection_c.set(collection, c);
            // mutex is the object itself.
            fSynchronizedCollection_mutex.set(collection, collection);
            
            if(ss)
                fSynchronizedSortedSet_ss.set(collection, c);
            
            if(list)
                fSynchronizedList_list.set(collection, c);
        }
        catch (IllegalArgumentException e)
        {
            throw new RuntimeException(e);
        }
        catch (IllegalAccessException e)
        {
            throw new RuntimeException(e);
        }
        
        return collection;
    }
    
    private static Object readCheckedCollectionFrom(Input input,  Schema schema, 
            Object owner, IdStrategy strategy, boolean graph, 
            Object collection, boolean ss, boolean list) throws IOException
    {
        if(graph)
        {
            // update the actual reference.
            ((GraphInput)input).updateLast(collection, owner);
        }
        
        final Wrapper wrapper = new Wrapper();
        Object c = input.mergeObject(wrapper, strategy.POLYMORPHIC_COLLECTION_SCHEMA);
        if(!graph || !((GraphInput)input).isCurrentMessageReference())
            c = wrapper.value;
        
        if(1 != input.readFieldNumber(schema))
            throw new ProtostuffException("Corrupt input.");
        
        Object type = input.mergeObject(wrapper, strategy.CLASS_SCHEMA);
        if(!graph || !((GraphInput)input).isCurrentMessageReference())
            type = wrapper.value;
        try
        {
            fCheckedCollection_c.set(collection, c);
            fCheckedCollection_type.set(collection, type);
            
            if(ss)
                fCheckedSortedSet_ss.set(collection, c);
            
            if(list)
                fCheckedList_list.set(collection, c);
        }
        catch (IllegalArgumentException e)
        {
            throw new RuntimeException(e);
        }
        catch (IllegalAccessException e)
        {
            throw new RuntimeException(e);
        }
        
        return collection;
    }

    static void transferObject(Pipe.Schema pipeSchema, Pipe pipe, 
            Input input, Output output, IdStrategy strategy) throws IOException
    {
        final int number = input.readFieldNumber(pipeSchema.wrappedSchema);
        switch(number)
        {
            case ID_EMPTY_SET:
                output.writeUInt32(number, input.readUInt32(), false);
                break;
                
            case ID_EMPTY_LIST:
                output.writeUInt32(number, input.readUInt32(), false);
                break;
                
            case ID_SINGLETON_SET: 
            case ID_SINGLETON_LIST:
            {
                output.writeUInt32(number, input.readUInt32(), false);
                
                final int next = input.readFieldNumber(pipeSchema.wrappedSchema);
                if(next == 0)
                {
                    // null element
                    return;
                }
                
                if(next != 1)
                    throw new ProtostuffException("Corrupt input.");
                
                output.writeObject(1, pipe, strategy.OBJECT_PIPE_SCHEMA, false);
                break;
            }   
            case ID_SET_FROM_MAP:
                output.writeObject(number, pipe, strategy.POLYMORPHIC_MAP_PIPE_SCHEMA, false);
                break;
                
            case ID_COPIES_LIST:
            {
                output.writeUInt32(number, input.readUInt32(), false);
                
                if(1 != input.readFieldNumber(pipeSchema.wrappedSchema))
                    throw new ProtostuffException("Corrupt input.");
                
                // size
                output.writeUInt32(1, input.readUInt32(), false);
                
                final int next = input.readFieldNumber(pipeSchema.wrappedSchema);
                if(next == 0)
                {
                    // null element
                    return;
                }
                
                if(next != 2)
                    throw new ProtostuffException("Corrupt input.");
                
                output.writeObject(2, pipe, strategy.OBJECT_PIPE_SCHEMA, false);
                break;
            }   
            case ID_UNMODIFIABLE_COLLECTION:
            case ID_UNMODIFIABLE_SET:
            case ID_UNMODIFIABLE_SORTED_SET:
            case ID_UNMODIFIABLE_LIST:
            case ID_UNMODIFIABLE_RANDOM_ACCESS_LIST:
                output.writeObject(number, pipe, strategy.POLYMORPHIC_COLLECTION_PIPE_SCHEMA, 
                        false);
                break;
                
            case ID_SYNCHRONIZED_COLLECTION:
            case ID_SYNCHRONIZED_SET:
            case ID_SYNCHRONIZED_SORTED_SET:
            case ID_SYNCHRONIZED_LIST:
            case ID_SYNCHRONIZED_RANDOM_ACCESS_LIST:
                output.writeObject(number, pipe, strategy.POLYMORPHIC_COLLECTION_PIPE_SCHEMA, 
                        false);
                break;
                
            case ID_CHECKED_COLLECTION:
            case ID_CHECKED_SET:
            case ID_CHECKED_SORTED_SET:
            case ID_CHECKED_LIST:
            case ID_CHECKED_RANDOM_ACCESS_LIST:
                output.writeObject(number, pipe, strategy.POLYMORPHIC_COLLECTION_PIPE_SCHEMA, 
                        false);
                
                if(1 != input.readFieldNumber(pipeSchema.wrappedSchema))
                    throw new ProtostuffException("Corrupt input.");
                
                output.writeObject(1, pipe, strategy.CLASS_PIPE_SCHEMA, 
                        false);
                break;
                
            case ID_ENUM_SET:
                strategy.transferEnumId(input, output, number);
                
                if(output instanceof StatefulOutput)
                {
                    // update using the derived schema.
                    ((StatefulOutput)output).updateLast(strategy.COLLECTION_PIPE_SCHEMA, pipeSchema);
                }
                
                // TODO use enum schema
                Pipe.transferDirect(strategy.COLLECTION_PIPE_SCHEMA, pipe, input, output);
                return;
            case ID_COLLECTION:
                strategy.transferCollectionId(input, output, number);
                
                if(output instanceof StatefulOutput)
                {
                    // update using the derived schema.
                    ((StatefulOutput)output).updateLast(strategy.COLLECTION_PIPE_SCHEMA, pipeSchema);
                }
                
                Pipe.transferDirect(strategy.COLLECTION_PIPE_SCHEMA, pipe, input, output);
                return;
            default:
                throw new ProtostuffException("Corrupt input.");
        }
        
        if(0 != input.readFieldNumber(pipeSchema.wrappedSchema))
            throw new ProtostuffException("Corrupt input.");
    }
}