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

com.dyuproject.protostuff.runtime.PolymorphicMapSchema 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_ENUM_MAP;
import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.ID_MAP;
import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.STR_ENUM_MAP;
import static com.dyuproject.protostuff.runtime.RuntimeFieldFactory.STR_MAP;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.IdentityHashMap;
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 (Map/SortedMap).
 *
 * @author David Yu
 * @created Apr 24, 2012
 */
public abstract class PolymorphicMapSchema extends PolymorphicSchema
{
    
    static final int ID_EMPTY_MAP = 1, 
            ID_SINGLETON_MAP = 2, 
            ID_UNMODIFIABLE_MAP = 3, 
            ID_UNMODIFIABLE_SORTED_MAP = 4, 
            ID_SYNCHRONIZED_MAP = 5, 
            ID_SYNCHRONIZED_SORTED_MAP = 6, 
            ID_CHECKED_MAP = 7, 
            ID_CHECKED_SORTED_MAP = 8; 
    
    static final String STR_EMPTY_MAP = "a", 
            STR_SINGLETON_MAP = "b", 
            STR_UNMODIFIABLE_MAP = "c", 
            STR_UNMODIFIABLE_SORTED_MAP = "d", 
            STR_SYNCHRONIZED_MAP = "e", 
            STR_SYNCHRONIZED_SORTED_MAP = "f", 
            STR_CHECKED_MAP = "g", 
            STR_CHECKED_SORTED_MAP = "h";
    
    static final IdentityHashMap,Integer> __nonPublicMaps = 
            new IdentityHashMap, Integer>();
    
    static final Field fSingletonMap_k, 
            fSingletonMap_v,
            
            fUnmodifiableMap_m,
            
            fUnmodifiableSortedMap_sm,
            
            fSynchronizedMap_m, 
            
            fSynchronizedSortedMap_sm,
            
            fSynchronizedMap_mutex,
            
            fCheckedMap_m, 
            fCheckedSortedMap_sm, 
            fCheckedMap_keyType,
            fCheckedMap_valueType;
            
    static final Instantiator iSingletonMap, 
            
            iUnmodifiableMap,
            iUnmodifiableSortedMap,
            
            iSynchronizedMap,
            iSynchronizedSortedMap,
            
            iCheckedMap, 
            iCheckedSortedMap;
    
    
    static
    {
        map("java.util.Collections$EmptyMap", ID_EMPTY_MAP);
        
        Class cSingletonMap = 
                map("java.util.Collections$SingletonMap", ID_SINGLETON_MAP);
        
        Class cUnmodifiableMap = 
                map("java.util.Collections$UnmodifiableMap", ID_UNMODIFIABLE_MAP);
        
        Class cUnmodifiableSortedMap = 
                map("java.util.Collections$UnmodifiableSortedMap", ID_UNMODIFIABLE_SORTED_MAP);
        
        Class cSynchronizedMap = 
                map("java.util.Collections$SynchronizedMap", ID_SYNCHRONIZED_MAP);
        
        Class cSynchronizedSortedMap = 
                map("java.util.Collections$SynchronizedSortedMap", ID_SYNCHRONIZED_SORTED_MAP);
        
        Class cCheckedMap = 
                map("java.util.Collections$CheckedMap", ID_CHECKED_MAP);
        
        Class cCheckedSortedMap = 
                map("java.util.Collections$CheckedSortedMap", ID_CHECKED_SORTED_MAP);
        
        try
        {
            fSingletonMap_k = cSingletonMap.getDeclaredField("k");
            fSingletonMap_v = cSingletonMap.getDeclaredField("v");
            
            fUnmodifiableMap_m = cUnmodifiableMap.getDeclaredField("m");
            fUnmodifiableSortedMap_sm = cUnmodifiableSortedMap.getDeclaredField("sm");
            
            fSynchronizedMap_m = cSynchronizedMap.getDeclaredField("m");
            fSynchronizedSortedMap_sm = cSynchronizedSortedMap.getDeclaredField("sm");
            fSynchronizedMap_mutex = cSynchronizedMap.getDeclaredField("mutex");
            
            fCheckedMap_m = cCheckedMap.getDeclaredField("m");
            fCheckedSortedMap_sm = cCheckedSortedMap.getDeclaredField("sm");
            fCheckedMap_keyType = cCheckedMap.getDeclaredField("keyType");
            fCheckedMap_valueType = cCheckedMap.getDeclaredField("valueType");
            
            iSingletonMap = RuntimeEnv.newInstantiator(cSingletonMap);
            
            iUnmodifiableMap = RuntimeEnv.newInstantiator(cUnmodifiableMap);
            iUnmodifiableSortedMap = RuntimeEnv.newInstantiator(cUnmodifiableSortedMap);
            
            iSynchronizedMap = RuntimeEnv.newInstantiator(cSynchronizedMap);
            iSynchronizedSortedMap = RuntimeEnv.newInstantiator(cSynchronizedSortedMap);
            
            iCheckedMap = RuntimeEnv.newInstantiator(cCheckedMap);
            iCheckedSortedMap = RuntimeEnv.newInstantiator(cCheckedSortedMap);
        }
        catch(Exception e)
        {
            throw new RuntimeException(e);
        }
        
        fSingletonMap_k.setAccessible(true);
        fSingletonMap_v.setAccessible(true);
        
        fUnmodifiableMap_m.setAccessible(true);
        fUnmodifiableSortedMap_sm.setAccessible(true);
        
        fSynchronizedMap_m.setAccessible(true);
        fSynchronizedSortedMap_sm.setAccessible(true);
        fSynchronizedMap_mutex.setAccessible(true);
        
        fCheckedMap_m.setAccessible(true);
        fCheckedSortedMap_sm.setAccessible(true);
        fCheckedMap_keyType.setAccessible(true);
        fCheckedMap_valueType.setAccessible(true);
    }
    
    private static Class map(String className, int id)
    {
        Class clazz = RuntimeEnv.loadClass(className);
        __nonPublicMaps.put(clazz, id);
        return clazz;
    }
    
    static String name(int number)
    {
        switch(number)
        {
            case ID_EMPTY_MAP: return STR_EMPTY_MAP;
            case ID_SINGLETON_MAP: return STR_SINGLETON_MAP;
            case ID_UNMODIFIABLE_MAP: return STR_UNMODIFIABLE_MAP;
            case ID_UNMODIFIABLE_SORTED_MAP: return STR_UNMODIFIABLE_SORTED_MAP;
            case ID_SYNCHRONIZED_MAP: return STR_SYNCHRONIZED_MAP;
            case ID_SYNCHRONIZED_SORTED_MAP: return STR_SYNCHRONIZED_SORTED_MAP;
            case ID_CHECKED_MAP: return STR_CHECKED_MAP;
            case ID_CHECKED_SORTED_MAP: return STR_CHECKED_SORTED_MAP;
            case ID_ENUM_MAP: return STR_ENUM_MAP;
            case ID_MAP: return STR_MAP;
            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 'w': return ID_ENUM_MAP;
            case 'z': return ID_MAP;
            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 PolymorphicMapSchema(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 = __nonPublicMaps.get(clazz);
        if(id == null)
            throw new RuntimeException("Unknown map: " + clazz);
        
        return id.intValue();
    }
    
    static Object instanceFrom(final int id)
    {
        switch(id)
        {
            case ID_EMPTY_MAP:
                return Collections.EMPTY_MAP;
                
            case ID_SINGLETON_MAP:
                return iSingletonMap.newInstance();
                
            case ID_UNMODIFIABLE_MAP:
                return iUnmodifiableMap.newInstance();
            case ID_UNMODIFIABLE_SORTED_MAP:
                return iUnmodifiableSortedMap.newInstance();
                
            case ID_SYNCHRONIZED_MAP:
                return iSynchronizedMap.newInstance();
            case ID_SYNCHRONIZED_SORTED_MAP:
                return iSynchronizedSortedMap.newInstance();
                
            case ID_CHECKED_MAP:
                return iCheckedMap.newInstance();
            case ID_CHECKED_SORTED_MAP:
                return iCheckedSortedMap.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())
        {
            writeNonPublicMapTo(output, value, currentSchema, strategy);
            return;
        }
        
        Class clazz = (Class)value.getClass();
        if(EnumMap.class.isAssignableFrom(clazz))
        {
            strategy.writeEnumIdTo(output, ID_ENUM_MAP, 
                    EnumIO.getKeyTypeFromEnumMap(value));
            
            // TODO use enum schema
        }
        else
        {
            strategy.writeMapIdTo(output, ID_MAP, clazz);
        }
        
        if(output instanceof StatefulOutput)
        {
            // update using the derived schema.
            ((StatefulOutput)output).updateLast(strategy.MAP_SCHEMA, currentSchema);
        }
        
        strategy.MAP_SCHEMA.writeTo(output, (Map)value);
    }
    
    static void writeNonPublicMapTo(Output output, Object value, Schema currentSchema, 
            IdStrategy strategy) throws IOException
    {
        final Integer n = __nonPublicMaps.get(value.getClass());
        if(n == null)
            throw new RuntimeException("Unknown collection: " + value.getClass());
        final int id = n.intValue();
        switch(id)
        {
            case ID_EMPTY_MAP:
                output.writeUInt32(id, 0, false);
                break;
                
            case ID_SINGLETON_MAP:
            {
                final Object k, v;
                try
                {
                    k = fSingletonMap_k.get(value);
                    v = fSingletonMap_v.get(value);
                }
                catch (IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch (IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
                
                output.writeUInt32(id, 0, false);
                if(k != null)
                    output.writeObject(1, k, strategy.OBJECT_SCHEMA, false);
                if(v != null)
                    output.writeObject(3, v, strategy.OBJECT_SCHEMA, false);
                break;
            }
                
            case ID_UNMODIFIABLE_MAP:
                writeUnmodifiableMapTo(output, value, currentSchema, strategy, id);
                break;
                
            case ID_UNMODIFIABLE_SORTED_MAP:
                writeUnmodifiableMapTo(output, value, currentSchema, strategy, id);
                break;
                
            case ID_SYNCHRONIZED_MAP:
                writeSynchronizedMapTo(output, value, currentSchema, strategy, id);
                break;
                
            case ID_SYNCHRONIZED_SORTED_MAP:
                writeSynchronizedMapTo(output, value, currentSchema, strategy, id);
                break;
                
            case ID_CHECKED_MAP:
                writeCheckedMapTo(output, value, currentSchema, strategy, id);
                break;
                
            case ID_CHECKED_SORTED_MAP:
                writeCheckedMapTo(output, value, currentSchema, strategy, id);
                break;
                
            default: 
                throw new RuntimeException("Should not happen.");
        }
    }
    
    private static void writeUnmodifiableMapTo(Output output, Object value, 
            Schema currentSchema, IdStrategy strategy, int id) throws IOException
    {
        final Object m;
        try
        {
            m = fUnmodifiableMap_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);
    }
    
    private static void writeSynchronizedMapTo(Output output, Object value, 
            Schema currentSchema, IdStrategy strategy, int id) throws IOException
    {
        final Object m, mutex;
        try
        {
            m = fSynchronizedMap_m.get(value);
            mutex = fSynchronizedMap_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, m, strategy.POLYMORPHIC_MAP_SCHEMA, false);
    }
    
    private static void writeCheckedMapTo(Output output, Object value, 
            Schema currentSchema, IdStrategy strategy, int id) throws IOException
    {
        final Object m, keyType, valueType;
        try
        {
            m = fCheckedMap_m.get(value);
            keyType = fCheckedMap_keyType.get(value);
            valueType = fCheckedMap_valueType.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);
        output.writeObject(1, keyType, strategy.CLASS_SCHEMA, false);
        output.writeObject(2, valueType, strategy.CLASS_SCHEMA, false);
    }
    
    @SuppressWarnings("unchecked")
    static Object readObjectFrom(Input input,  Schema schema, Object owner, 
            IdStrategy strategy) throws IOException
    {
        final boolean graph = input instanceof GraphInput;
        Object ret = null;
        final int number = input.readFieldNumber(schema);
        switch(number)
        {
            case ID_EMPTY_MAP:
                if(graph)
                {
                    // update the actual reference.
                    ((GraphInput)input).updateLast(Collections.EMPTY_MAP, owner);
                }
                
                if(0 != input.readUInt32())
                    throw new ProtostuffException("Corrupt input.");
                
                ret = Collections.EMPTY_MAP;
                break;
                
            case ID_SINGLETON_MAP:
            {
                final Object map = iSingletonMap.newInstance();
                if(graph)
                {
                    // update the actual reference.
                    ((GraphInput)input).updateLast(map, owner);
                }
                
                if(0 != input.readUInt32())
                    throw new ProtostuffException("Corrupt input.");
                
                return fillSingletonMapFrom(input, schema, owner, strategy, graph, map);
            }
                
            case ID_UNMODIFIABLE_MAP:
                ret = readUnmodifiableMapFrom(input, schema, owner, strategy, graph, 
                        iUnmodifiableMap.newInstance(), false);
                break;
                
            case ID_UNMODIFIABLE_SORTED_MAP:
                ret = readUnmodifiableMapFrom(input, schema, owner, strategy, graph, 
                        iUnmodifiableSortedMap.newInstance(), true);
                break;
                
            case ID_SYNCHRONIZED_MAP:
                ret = readSynchronizedMapFrom(input, schema, owner, strategy, graph, 
                        iSynchronizedMap.newInstance(), false);
                break;
                
            case ID_SYNCHRONIZED_SORTED_MAP:
                ret = readSynchronizedMapFrom(input, schema, owner, strategy, graph, 
                        iSynchronizedSortedMap.newInstance(), true);
                break;
                
            case ID_CHECKED_MAP:
                ret = readCheckedMapFrom(input, schema, owner, strategy, graph, 
                        iCheckedMap.newInstance(), false);
                break;
                
            case ID_CHECKED_SORTED_MAP:
                ret = readCheckedMapFrom(input, schema, owner, strategy, graph, 
                        iCheckedSortedMap.newInstance(), true);
                break;
                
            case ID_ENUM_MAP:
            {
                final Map em = strategy.resolveEnumFrom(input).newEnumMap();
                
                if(input instanceof GraphInput)
                {
                    // update the actual reference.
                    ((GraphInput)input).updateLast(em, owner);
                }
                
                strategy.MAP_SCHEMA.mergeFrom(input, (Map)em);
                
                return em;
            } 
            case ID_MAP:
            {
                final Map map = 
                    strategy.resolveMapFrom(input).newMessage();
                
                if(input instanceof GraphInput)
                {
                    // update the actual reference.
                    ((GraphInput)input).updateLast(map, owner);
                }
                
                strategy.MAP_SCHEMA.mergeFrom(input, map);
                
                return map;
            }
            
            default: 
                throw new ProtostuffException("Corrupt input.");
        }
        
        if(0 != input.readFieldNumber(schema))
            throw new ProtostuffException("Corrupt input.");
        
        return ret;
    }
    
    /**
     * Return true to 
     */
    private static Object fillSingletonMapFrom(Input input,  Schema schema, 
            Object owner, IdStrategy strategy, boolean graph, 
            Object map) throws IOException
    {
        switch(input.readFieldNumber(schema))
        {
            case 0:
                // both are null
                return map;
            case 1:
            {
                // key exists
                break;
            }
            case 3:
            {
                // key is null
                final Wrapper wrapper = new Wrapper();
                Object v = input.mergeObject(wrapper, strategy.OBJECT_SCHEMA);
                if(!graph || !((GraphInput)input).isCurrentMessageReference())
                    v = wrapper.value;
                
                try
                {
                    fSingletonMap_v.set(map, v);
                }
                catch (IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch (IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
                
                if(0 != input.readFieldNumber(schema))
                    throw new ProtostuffException("Corrupt input.");
                
                return map;
            }
            default:
                throw new ProtostuffException("Corrupt input.");
        }
        
        final Wrapper wrapper = new Wrapper();
        Object k = input.mergeObject(wrapper, strategy.OBJECT_SCHEMA);
        if(!graph || !((GraphInput)input).isCurrentMessageReference())
            k = wrapper.value;
        
        switch(input.readFieldNumber(schema))
        {
            case 0:
                // key exists but null value
                try
                {
                    fSingletonMap_k.set(map, k);
                }
                catch (IllegalArgumentException e)
                {
                    throw new RuntimeException(e);
                }
                catch (IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
                
                return map;
            case 3:
                // key and value exist
                break;
            default:
                throw new ProtostuffException("Corrupt input.");
        }
        
        Object v = input.mergeObject(wrapper, strategy.OBJECT_SCHEMA);
        if(!graph || !((GraphInput)input).isCurrentMessageReference())
            v = wrapper.value;
        
        try
        {
            fSingletonMap_k.set(map, k);
            fSingletonMap_v.set(map, v);
        }
        catch (IllegalArgumentException e)
        {
            throw new RuntimeException(e);
        }
        catch (IllegalAccessException e)
        {
            throw new RuntimeException(e);
        }
        
        if(0 != input.readFieldNumber(schema))
            throw new ProtostuffException("Corrupt input.");
        
        return map;
    }
    
    private static Object readUnmodifiableMapFrom(Input input,  Schema schema, 
            Object owner, IdStrategy strategy, boolean graph, 
            Object map, boolean sm) throws IOException
    {
        if(graph)
        {
            // update the actual reference.
            ((GraphInput)input).updateLast(map, owner);
        }
        
        final Wrapper wrapper = new Wrapper();
        Object m = input.mergeObject(wrapper, strategy.POLYMORPHIC_MAP_SCHEMA);
        if(!graph || !((GraphInput)input).isCurrentMessageReference())
            m = wrapper.value;
        try
        {
            fUnmodifiableMap_m.set(map, m);
            
            if(sm)
                fUnmodifiableSortedMap_sm.set(map, m);
        }
        catch (IllegalArgumentException e)
        {
            throw new RuntimeException(e);
        }
        catch (IllegalAccessException e)
        {
            throw new RuntimeException(e);
        }
        
        return map;
    }
    
    private static Object readSynchronizedMapFrom(Input input,  Schema schema, 
            Object owner, IdStrategy strategy, boolean graph, 
            Object map, boolean sm) throws IOException
    {
        if(graph)
        {
            // update the actual reference.
            ((GraphInput)input).updateLast(map, owner);
        }
        
        final Wrapper wrapper = new Wrapper();
        Object m = input.mergeObject(wrapper, strategy.POLYMORPHIC_MAP_SCHEMA);
        if(!graph || !((GraphInput)input).isCurrentMessageReference())
            m = wrapper.value;
        try
        {
            fSynchronizedMap_m.set(map, m);
            fSynchronizedMap_mutex.set(map, map);
            
            if(sm)
                fSynchronizedSortedMap_sm.set(map, m);
        }
        catch (IllegalArgumentException e)
        {
            throw new RuntimeException(e);
        }
        catch (IllegalAccessException e)
        {
            throw new RuntimeException(e);
        }
        
        return map;
    }
    
    private static Object readCheckedMapFrom(Input input,  Schema schema, 
            Object owner, IdStrategy strategy, boolean graph, 
            Object map, boolean sm) throws IOException
    {
        if(graph)
        {
            // update the actual reference.
            ((GraphInput)input).updateLast(map, owner);
        }
        
        final Wrapper wrapper = new Wrapper();
        Object m = input.mergeObject(wrapper, strategy.POLYMORPHIC_MAP_SCHEMA);
        if(!graph || !((GraphInput)input).isCurrentMessageReference())
            m = wrapper.value;
        
        if(1 != input.readFieldNumber(schema))
            throw new ProtostuffException("Corrupt input.");
        
        Object keyType = input.mergeObject(wrapper, strategy.CLASS_SCHEMA);
        if(!graph || !((GraphInput)input).isCurrentMessageReference())
            keyType = wrapper.value;
        
        if(2 != input.readFieldNumber(schema))
            throw new ProtostuffException("Corrupt input.");
        
        Object valueType = input.mergeObject(wrapper, strategy.CLASS_SCHEMA);
        if(!graph || !((GraphInput)input).isCurrentMessageReference())
            valueType = wrapper.value;
        
        try
        {
            fCheckedMap_m.set(map, m);
            fCheckedMap_keyType.set(map, keyType);
            fCheckedMap_valueType.set(map, valueType);
            
            if(sm)
                fCheckedSortedMap_sm.set(map, m);
        }
        catch (IllegalArgumentException e)
        {
            throw new RuntimeException(e);
        }
        catch (IllegalAccessException e)
        {
            throw new RuntimeException(e);
        }
        
        return map;
    }
    
    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_MAP:
                output.writeUInt32(number, input.readUInt32(), false);
                break;
                
            case ID_SINGLETON_MAP:
                if(0 != input.readUInt32())
                    throw new ProtostuffException("Corrupt input.");
                
                output.writeUInt32(number, 0, false);
                
                transferSingletonMap(pipeSchema, pipe, input, output, strategy);
                return;
                
            case ID_UNMODIFIABLE_MAP:
                output.writeObject(number, pipe, strategy.POLYMORPHIC_MAP_PIPE_SCHEMA, false);
                break;
                
            case ID_UNMODIFIABLE_SORTED_MAP:
                output.writeObject(number, pipe, strategy.POLYMORPHIC_MAP_PIPE_SCHEMA, false);
                break;
                
            case ID_SYNCHRONIZED_MAP:
                output.writeObject(number, pipe, strategy.POLYMORPHIC_MAP_PIPE_SCHEMA, false);
                break;
                
            case ID_SYNCHRONIZED_SORTED_MAP:
                output.writeObject(number, pipe, strategy.POLYMORPHIC_MAP_PIPE_SCHEMA, false);
                break;
                
            case ID_CHECKED_MAP:
                output.writeObject(number, pipe, strategy.POLYMORPHIC_MAP_PIPE_SCHEMA, false);
                
                if(1 != input.readFieldNumber(pipeSchema.wrappedSchema))
                    throw new ProtostuffException("Corrupt input.");
                
                output.writeObject(1, pipe, strategy.CLASS_PIPE_SCHEMA, false);
                
                if(2 != input.readFieldNumber(pipeSchema.wrappedSchema))
                    throw new ProtostuffException("Corrupt input.");
                
                output.writeObject(2, pipe, strategy.CLASS_PIPE_SCHEMA, false);
                break;
                
            case ID_CHECKED_SORTED_MAP:
                output.writeObject(number, pipe, strategy.POLYMORPHIC_MAP_PIPE_SCHEMA, false);
                
                if(1 != input.readFieldNumber(pipeSchema.wrappedSchema))
                    throw new ProtostuffException("Corrupt input.");
                
                output.writeObject(1, pipe, strategy.CLASS_PIPE_SCHEMA, false);
                
                if(2 != input.readFieldNumber(pipeSchema.wrappedSchema))
                    throw new ProtostuffException("Corrupt input.");
                
                output.writeObject(2, pipe, strategy.CLASS_PIPE_SCHEMA, false);
                break;
                
            case ID_ENUM_MAP:
                strategy.transferEnumId(input, output, number);
                
                if(output instanceof StatefulOutput)
                {
                    // update using the derived schema.
                    ((StatefulOutput)output).updateLast(strategy.MAP_PIPE_SCHEMA, pipeSchema);
                }
                
                Pipe.transferDirect(strategy.MAP_PIPE_SCHEMA, pipe, input, output);
                return;
            case ID_MAP:
                strategy.transferMapId(input, output, number);
                
                if(output instanceof StatefulOutput)
                {
                    // update using the derived schema.
                    ((StatefulOutput)output).updateLast(strategy.MAP_PIPE_SCHEMA, pipeSchema);
                }
                
                Pipe.transferDirect(strategy.MAP_PIPE_SCHEMA, pipe, input, output);
                return;
                
            default:
                throw new ProtostuffException("Corrupt input.");
        }
        
        if(0 != input.readFieldNumber(pipeSchema.wrappedSchema))
            throw new ProtostuffException("Corrupt input.");
    }
    
    static void transferSingletonMap(Pipe.Schema pipeSchema, Pipe pipe, 
            Input input, Output output, IdStrategy strategy) throws IOException
    {
        switch(input.readFieldNumber(pipeSchema.wrappedSchema))
        {
            case 0:
                // both are null
                return;
            case 1:
            {
                // key exists
                break;
            }
            case 3:
            {
                // key is null
                output.writeObject(3, pipe, strategy.OBJECT_PIPE_SCHEMA, false);
                
                if(0 != input.readFieldNumber(pipeSchema.wrappedSchema))
                    throw new ProtostuffException("Corrupt input.");
                
                return;
            }
            default:
                throw new ProtostuffException("Corrupt input.");
        }
        
        output.writeObject(1, pipe, strategy.OBJECT_PIPE_SCHEMA, false);
        
        switch(input.readFieldNumber(pipeSchema.wrappedSchema))
        {
            case 0:
                // key exists but null value
                return;
            case 3:
                // key and value exist
                break;
            default:
                throw new ProtostuffException("Corrupt input.");
        }
        
        output.writeObject(3, pipe, strategy.OBJECT_PIPE_SCHEMA, false);

        if(0 != input.readFieldNumber(pipeSchema.wrappedSchema))
            throw new ProtostuffException("Corrupt input.");
    }
}