# ValueDeserializerAdapter.java

    /*
     * Copyright (c) 2007, Rickard Öberg. All Rights Reserved.
     * Copyright (c) 2010, Niclas Hehdman. All Rights Reserved.
     * Copyright (c) 2012-2014, Paul Merlin. All Rights Reserved.
     *
     * 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 org.qi4j.spi.value;

    import java.io.ByteArrayInputStream;
    import java.io.InputStream;
    import java.io.ObjectInputStream;
    import java.math.BigDecimal;
    import java.math.BigInteger;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Collections;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.LinkedHashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Scanner;
    import java.util.Set;
    import org.joda.time.DateTime;
    import org.joda.time.LocalDate;
    import org.joda.time.LocalDateTime;
    import org.qi4j.api.association.AssociationDescriptor;
    import org.qi4j.api.entity.EntityReference;
    import org.qi4j.api.injection.scope.Service;
    import org.qi4j.api.injection.scope.Structure;
    import org.qi4j.api.property.PropertyDescriptor;
    import org.qi4j.api.service.ServiceReference;
    import org.qi4j.api.structure.Application;
    import org.qi4j.api.structure.Module;
    import org.qi4j.api.type.CollectionType;
    import org.qi4j.api.type.EnumType;
    import org.qi4j.api.type.MapType;
    import org.qi4j.api.type.ValueCompositeType;
    import org.qi4j.api.type.ValueType;
    import org.qi4j.api.type.Serialization;
    import org.qi4j.api.util.Base64Encoder;
    import org.qi4j.api.util.Dates;
    import org.qi4j.api.value.ValueBuilder;
    import org.qi4j.api.value.ValueDescriptor;
    import org.qi4j.api.value.ValueDeserializer;
    import org.qi4j.api.value.ValueSerializationException;
    import org.qi4j.functional.Function;
    import org.qi4j.functional.Function2;

    import static org.qi4j.functional.Iterables.empty;
    import static org.qi4j.functional.Iterables.first;

    /**
     * Adapter for pull-parsing and tree-parsing capable ValueDeserializers.
     *
     * <p>
     *     Among Plain values (see {@link ValueDeserializer}) some are considered primitives to underlying serialization
     *     mechanisms and by so handed/come without conversion to/from implementations. Primitive values can be one of:
     * </p>
     * <ul>
     *     <li>String,</li>
     *     <li>Character or char,</li>
     *     <li>Boolean or boolean,</li>
     *     <li>Integer or int,</li>
     *     <li>Long or long,</li>
     *     <li>Short or short,</li>
     *     <li>Byte or byte,</li>
     *     <li>Float or float,</li>
     *     <li>Double or double.</li>
     * </ul>
     * <p>
     *     Some other Plain values are expected in given formats:
     * </p>
     * <ul>
     *     <li>BigInteger and BigDecimal depends on {@link org.qi4j.api.value.ValueSerializer.Options};</li>
     *     <li>Date as String in ISO-8601, {@literal @millis@} or {@literal /Date(..)} Microsoft format;</li>
     *     <li>DateTime (JodaTime) as a ISO-8601 String with optional timezone offset;</li>
     *     <li>LocalDateTime (JodaTime) as whatever {@link LocalDateTime#LocalDateTime(java.lang.Object)} accept as {@literal instant};</li>
     *     <li>LocalDate (JodaTime) as whatever {@link LocalDate#LocalDate(java.lang.Object)} accept as {@literal instant};</li>
     * </ul>
     *
     * @param <InputType> Implementor pull-parser type
     * @param <InputNodeType> Implementor tree-parser node type
     */
    public abstract class ValueDeserializerAdapter<InputType, InputNodeType>
        implements ValueDeserializer
    {
        public static interface ComplexDeserializer<T, InputType, InputNodeType>
        {
            T deserializePull( InputType input )
                throws Exception;

            T deserializeTree( InputNodeType inputNode )
                throws Exception;
        }

        private static final String UTF_8 = "UTF-8";
        private final Map<Class<?>, Function<Object, Object>> deserializers = new HashMap<>( 16 );
        private final Map<Class<?>, ComplexDeserializer<Object, InputType, InputNodeType>> complexDeserializers = new HashMap<>( 2 );
        private final Application application;
        private final Module module;
        private Function<Application, Module> valuesModuleFinder;
        private Module valuesModule;

        /**
         * Register a Plain Value type deserialization Function.
         *
         * @param <T> Plain Value parametrized Type
         * @param type Plain Value Type
         * @param deserializer Deserialization Function
         */
        @SuppressWarnings( "unchecked" )
        protected final <T> void registerDeserializer( Class<T> type, Function<Object, T> deserializer )
        {
            deserializers.put( type, (Function<Object, Object>) deserializer );
        }

        @SuppressWarnings( { "UnusedDeclaration", "unchecked" } )
        protected final <T> void registerComplexDeserializer( Class<T> type,
                                                              ComplexDeserializer<T, InputType, InputNodeType> deserializer )
        {
            complexDeserializers.put( type, (ComplexDeserializer<Object, InputType, InputNodeType>) deserializer );
        }

        @SuppressWarnings( "unchecked" )
        public ValueDeserializerAdapter( @Structure Application application,
                                         @Structure Module module,
                                         @Service ServiceReference<ValueDeserializer> serviceRef )
        {
            this( application, module, serviceRef.metaInfo( Function.class ) );
        }

        protected ValueDeserializerAdapter( Application application,
                                            Module module,
                                            Function<Application, Module> valuesModuleFinder )
        {

            this.application = application;
            this.module = module;
            setValuesModuleFinder( valuesModuleFinder );

            // Primitive Value types
            registerDeserializer( String.class, new Function<Object, String>()
            {
                @Override
                public String map( Object input )
                {
                    return input.toString();
                }
            } );
            registerDeserializer( Character.class, new Function<Object, Character>()
            {
                @Override
                public Character map( Object input )
                {
                    return input.toString().charAt( 0 );
                }
            } );
            registerDeserializer( Boolean.class, new Function<Object, Boolean>()
            {
                @SuppressWarnings( "UnnecessaryUnboxing" )
                @Override
                public Boolean map( Object input )
                {
                    return ( input instanceof String )
                           ? Boolean.parseBoolean( (String) input )
                           : ( (Boolean) input ).booleanValue();
                }
            } );
            registerDeserializer( Integer.class, new Function<Object, Integer>()
            {
                @Override
                public Integer map( Object input )
                {
                    return ( input instanceof String )
                           ? Integer.parseInt( (String) input )
                           : ( (Number) input ).intValue();
                }
            } );
            registerDeserializer( Long.class, new Function<Object, Long>()
            {
                @Override
                public Long map( Object input )
                {
                    return ( input instanceof String )
                           ? Long.parseLong( (String) input )
                           : ( (Number) input ).longValue();
                }
            } );
            registerDeserializer( Short.class, new Function<Object, Short>()
            {
                @Override
                public Short map( Object input )
                {
                    return ( input instanceof String )
                           ? Short.parseShort( (String) input )
                           : ( (Number) input ).shortValue();
                }
            } );
            registerDeserializer( Byte.class, new Function<Object, Byte>()
            {
                @Override
                public Byte map( Object input )
                {
                    return ( input instanceof String )
                           ? Byte.parseByte( (String) input )
                           : ( (Number) input ).byteValue();
                }
            } );
            registerDeserializer( Float.class, new Function<Object, Float>()
            {
                @Override
                public Float map( Object input )
                {
                    return ( input instanceof String )
                           ? Float.parseFloat( (String) input )
                           : ( (Number) input ).floatValue();
                }
            } );
            registerDeserializer( Double.class, new Function<Object, Double>()
            {
                @Override
                public Double map( Object input )
                {
                    return ( input instanceof String )
                           ? Double.parseDouble( (String) input )
                           : ( (Number) input ).doubleValue();
                }
            } );

            // Number types
            registerDeserializer( BigDecimal.class, new Function<Object, BigDecimal>()
            {
                @Override
                public BigDecimal map( Object input )
                {
                    return new BigDecimal( input.toString() );
                }
            } );
            registerDeserializer( BigInteger.class, new Function<Object, BigInteger>()
            {
                @Override
                public BigInteger map( Object input )
                {
                    return new BigInteger( input.toString() );
                }
            } );

            // Date types
            registerDeserializer( Date.class, new Function<Object, Date>()
            {
                @Override
                public Date map( Object input )
                {
                    return Dates.fromString( input.toString() );
                }
            } );
            registerDeserializer( DateTime.class, new Function<Object, DateTime>()
            {
                @Override
                public DateTime map( Object input )
                {
                    return DateTime.parse( input.toString() );
                }
            } );
            registerDeserializer( LocalDateTime.class, new Function<Object, LocalDateTime>()
            {
                @Override
                public LocalDateTime map( Object input )
                {
                    return new LocalDateTime( input );
                }
            } );
            registerDeserializer( LocalDate.class, new Function<Object, LocalDate>()
            {
                @Override
                public LocalDate map( Object input )
                {
                    return new LocalDate( input );
                }
            } );

            // Other supported types
            registerDeserializer( EntityReference.class, new Function<Object, EntityReference>()
            {
                @Override
                public EntityReference map( Object input )
                {
                    return EntityReference.parseEntityReference( input.toString() );
                }
            } );
        }

        private void setValuesModuleFinder( Function<Application, Module> valuesModuleFinder )
        {
            this.valuesModuleFinder = valuesModuleFinder;
            this.valuesModule = null;
        }

        private Module valuesModule()
        {
            if( valuesModule == null )
            {
                if( valuesModuleFinder == null )
                {
                    valuesModule = module;
                }
                else
                {
                    valuesModule = valuesModuleFinder.map( application );
                    if( valuesModule == null )
                    {
                        throw new ValueSerializationException( "Values Module provided by the finder Function was null." );
                    }
                }
            }
            return valuesModule;
        }

        @Override
        public <T> Function<String, T> deserialize( Class<T> type )
        {
            if( CollectionType.isCollection( type ) )
            {
                ValueType objectValueType = new ValueType( Object.class );
                return deserialize( new CollectionType( type, objectValueType ) );
            }
            if( MapType.isMap( type ) )
            {
                ValueType objectValueType = new ValueType( Object.class );
                return deserialize( new MapType( type, objectValueType, objectValueType ) );
            }
            return deserialize( new ValueType( type ) );
        }

        @Override
        public final <T> Function<String, T> deserialize( final ValueType valueType )
        {
            return new Function<String, T>()
            {
                @Override
                public T map( String input )
                {
                    return deserialize( valueType, input );
                }
            };
        }

        @Override
        public final <T> Function2<ValueType, String, T> deserialize()
        {
            return new Function2<ValueType, String, T>()
            {
                @Override
                public T map( ValueType valueType, String input )
                {
                    return deserialize( valueType, input );
                }
            };
        }

        @Override
        public final <T> T deserialize( Class<?> type, String input )
            throws ValueSerializationException
        {
            if( CollectionType.isCollection( type ) )
            {
                ValueType objectValueType = new ValueType( Object.class );
                return deserialize( new CollectionType( type, objectValueType ), input );
            }
            if( MapType.isMap( type ) )
            {
                ValueType objectValueType = new ValueType( Object.class );
                return deserialize( new MapType( type, objectValueType, objectValueType ), input );
            }
            return deserialize( new ValueType( type ), input );
        }

        @Override
        public final <T> T deserialize( ValueType valueType, String input )
            throws ValueSerializationException
        {
            try
            {
                return deserializeRoot( valueType, new ByteArrayInputStream( input.getBytes( UTF_8 ) ) );
            }
            catch( ValueSerializationException ex )
            {
                throw ex;
            }
            catch( Exception ex )
            {
                throw new ValueSerializationException( "Could not deserialize value", ex );
            }
        }

        @Override
        public final <T> T deserialize( Class<?> type, InputStream input )
            throws ValueSerializationException
        {
            if( CollectionType.isCollection( type ) )
            {
                ValueType objectValueType = new ValueType( Object.class );
                return deserialize( new CollectionType( type, objectValueType ), input );
            }
            if( MapType.isMap( type ) )
            {
                ValueType objectValueType = new ValueType( Object.class );
                return deserialize( new MapType( type, objectValueType, objectValueType ), input );
            }
            return deserialize( new ValueType( type ), input );
        }

        @Override
        public final <T> T deserialize( ValueType valueType, InputStream input )
            throws ValueSerializationException
        {
            try
            {
                return deserializeRoot( valueType, input );
            }
            catch( ValueSerializationException ex )
            {
                throw ex;
            }
            catch( Exception ex )
            {
                throw new ValueSerializationException( "Could not deserialize value", ex );
            }
        }

        @SuppressWarnings( "unchecked" )
        private <T> T deserializeRoot( ValueType valueType, InputStream input )
            throws Exception
        {
            final Class<?> type = first( valueType.types() );
            // Plain ValueType
            if( deserializers.get( type ) != null )
            {
                Scanner scanner = new Scanner( input, UTF_8 ).useDelimiter( "\\A" );
                if( !scanner.hasNext() )
                {
                    return String.class.equals( type ) ? (T) "" : null;
                }
                String string = scanner.next();
                return (T) deserializers.get( type ).map( string );
            }
            else // Array ValueType
            if( type.isArray() )
            {
                Scanner scanner = new Scanner( input, UTF_8 ).useDelimiter( "\\A" );
                if( !scanner.hasNext() )
                {
                    return null;
                }
                String string = scanner.next();
                return (T) deserializeBase64Serialized( string );
            }
            else // Complex ValueType
            {
                InputType adaptedInput = adaptInput( input );
                onDeserializationStart( valueType, adaptedInput );
                T deserialized = doDeserialize( valueType, adaptedInput );
                onDeserializationEnd( valueType, adaptedInput );
                return deserialized;
            }
        }

        @SuppressWarnings( "unchecked" )
        private <T> T doDeserialize( ValueType valueType, InputType input )
            throws Exception
        {
            final Class<?> type = first( valueType.types() );
            // Registered deserializers
            if( deserializers.get( type ) != null )
            {
                Object value = readPlainValue( input );
                if( value == null )
                {
                    return null;
                }
                return (T) deserializers.get( type ).map( value );
            }
            else if( complexDeserializers.get( type ) != null )
            {
                return (T) complexDeserializers.get( type ).deserializePull( input );
            }
            else // Explicit ValueComposite
            if( ValueCompositeType.class.isAssignableFrom( valueType.getClass() ) )
            {
                return (T) deserializeValueComposite( valueType, input );
            }
            else // Explicit Collections
            if( CollectionType.class.isAssignableFrom( valueType.getClass() ) )
            {
                return (T) deserializeCollection( (CollectionType) valueType, input );
            }
            else // Explicit Map
            if( MapType.class.isAssignableFrom( valueType.getClass() ) )
            {
                return (T) deserializeMap( (MapType) valueType, input );
            }
            else // Enum
            if( EnumType.class.isAssignableFrom( valueType.getClass() ) || type.isEnum() )
            {
                return (T) Enum.valueOf( (Class) type, readPlainValue( input ).toString() );
            }
            else // Array
            if( type.isArray() )
            {
                return (T) deserializeBase64Serialized( readPlainValue( input ).toString() );
            }
            // Guessed Deserialization
            return (T) deserializeGuessed( valueType, input );
        }

        private <T> Function<InputType, T> buildDeserializeInputFunction( final ValueType valueType )
        {
            return new Function<InputType, T>()
            {
                @Override
                public T map( InputType input )
                {
                    try
                    {
                        return doDeserialize( valueType, input );
                    }
                    catch( ValueSerializationException ex )
                    {
                        throw ex;
                    }
                    catch( Exception ex )
                    {
                        throw new ValueSerializationException( ex );
                    }
                }
            };
        }

        private <T> Collection<T> deserializeCollection( CollectionType collectionType, InputType input )
            throws Exception
        {
            Collection<T> collection;
            Class<?> collectionMainType = first( collectionType.types() );
            if( Set.class.equals( collectionMainType ) )
            {
                collection = new LinkedHashSet<>();
            }
            else
            {
                collection = new ArrayList<>();
            }
            return readArrayInCollection( input,
                                          this.<T>buildDeserializeInputFunction( collectionType.collectedType() ),
                                          collection );
        }

        private <K, V> Map<K, V> deserializeMap( MapType mapType, InputType input )
            throws Exception
        {
            return readMapInMap( input,
                                 this.<K>buildDeserializeInputFunction( mapType.keyType() ),
                                 this.<V>buildDeserializeInputFunction( mapType.valueType() ),
                                 new HashMap<K, V>() );
        }

        private <T> T deserializeValueComposite( ValueType valueType, InputType input )
            throws Exception
        {
            InputNodeType inputNode = readObjectTree( input );
            if( inputNode == null )
            {
                return null;
            }
            return deserializeNodeValueComposite( valueType, inputNode );
        }

        private <T> T deserializeNodeValueComposite( ValueType valueType, InputNodeType inputNode )
            throws Exception
        {
            ValueCompositeType valueCompositeType = (ValueCompositeType) valueType;
            Class<?> valueBuilderType = first( valueCompositeType.types() );
            String typeInfo = this.getObjectFieldValue(
                inputNode,
                "_type",
                this.<String>buildDeserializeInputNodeFunction( new ValueType( String.class ) ) );
            if( typeInfo != null )
            {
                ValueDescriptor valueDescriptor = valuesModule().valueDescriptor( typeInfo );
                if( valueDescriptor == null )
                {
                    throw new ValueSerializationException( "Specified value type could not be resolved: " + typeInfo );
                }
                valueCompositeType = valueDescriptor.valueType();
                valueBuilderType = Class.forName( typeInfo );
            }
            return deserializeValueComposite( valueCompositeType, valueBuilderType, inputNode );
        }

        @SuppressWarnings( "unchecked" )
        private <T> T deserializeValueComposite( ValueCompositeType valueCompositeType, Class<?> valueBuilderType, InputNodeType inputNode )
            throws Exception
        {
            final Map<String, Object> stateMap = new HashMap<>();

            // Properties
            for( PropertyDescriptor property : valueCompositeType.properties() )
            {
                String propertyName = property.qualifiedName().name();
                Object value;
                if( objectHasField( inputNode, propertyName ) )
                {
                    value = getObjectFieldValue(
                        inputNode,
                        propertyName,
                        buildDeserializeInputNodeFunction( property.valueType() ) );
                    if( property.isImmutable() )
                    {
                        if( value instanceof Set )
                        {
                            value = Collections.unmodifiableSet( (Set<?>) value );
                        }
                        else if( value instanceof List )
                        {
                            value = Collections.unmodifiableList( (List<?>) value );
                        }
                        else if( value instanceof Map )
                        {
                            value = Collections.unmodifiableMap( (Map<?, ?>) value );
                        }
                    }
                }
                else
                {
                    // Serialized object does not contain the field, try to default it
                    value = property.initialValue( valuesModule() );
                }
                stateMap.put( propertyName, value );
            }

            // Associations
            for( AssociationDescriptor association : valueCompositeType.associations() )
            {
                String associationName = association.qualifiedName().name();
                if( objectHasField( inputNode, associationName ) )
                {
                    Object value = getObjectFieldValue(
                        inputNode,
                        associationName,
                        buildDeserializeInputNodeFunction( new ValueType( EntityReference.class ) ) );
                    stateMap.put( associationName, value );
                }
            }

            // ManyAssociations
            for( AssociationDescriptor manyAssociation : valueCompositeType.manyAssociations() )
            {
                String manyAssociationName = manyAssociation.qualifiedName().name();
                if( objectHasField( inputNode, manyAssociationName ) )
                {
                    Object value = getObjectFieldValue(
                        inputNode,
                        manyAssociationName,
                        buildDeserializeInputNodeFunction( new CollectionType( Collection.class,
                                                                               new ValueType( EntityReference.class ) ) ) );
                    stateMap.put( manyAssociationName, value );
                }
            }

            // NamedAssociations
            for( AssociationDescriptor namedAssociation : valueCompositeType.namedAssociations() )
            {
                String namedAssociationName = namedAssociation.qualifiedName().name();
                if( objectHasField( inputNode, namedAssociationName ) )
                {
                    Object value = getObjectFieldValue(
                        inputNode,
                        namedAssociationName,
                        buildDeserializeInputNodeFunction( MapType.of( String.class, EntityReference.class, Serialization.Variant.object ) ) );
                    stateMap.put( namedAssociationName, value );
                }
            }

            ValueBuilder<?> valueBuilder = buildNewValueBuilderWithState( valueBuilderType, stateMap );
            return (T) valueBuilder.newInstance(); // Unchecked cast because the builder could use a type != T
        }

        private <T> Function<InputNodeType, T> buildDeserializeInputNodeFunction( final ValueType valueType )
        {
            return new Function<InputNodeType, T>()
            {
                @Override
                public T map( InputNodeType inputNode )
                {
                    try
                    {
                        return doDeserializeInputNodeValue( valueType, inputNode );
                    }
                    catch( ValueSerializationException ex )
                    {
                        throw ex;
                    }
                    catch( Exception ex )
                    {
                        throw new ValueSerializationException( ex );
                    }
                }
            };
        }

        @SuppressWarnings( "unchecked" )
        private <T> T doDeserializeInputNodeValue( ValueType valueType, InputNodeType inputNode )
            throws Exception
        {
            if( inputNode == null )
            {
                return null;
            }
            final Class<?> type = first( valueType.types() );
            // Registered deserializers
            if( deserializers.get( type ) != null )
            {
                Object value = asSimpleValue( inputNode );
                if( value == null )
                {
                    return null;
                }
                return (T) deserializers.get( type ).map( value );
            }
            else if( complexDeserializers.get( type ) != null )
            {
                return (T) complexDeserializers.get( type ).deserializeTree( inputNode );
            }
            else // Explicit ValueComposite
            if( ValueCompositeType.class.isAssignableFrom( valueType.getClass() ) )
            {
                return (T) deserializeNodeValueComposite( valueType, inputNode );
            }
            else // Explicit Collections
            if( CollectionType.class.isAssignableFrom( valueType.getClass() ) )
            {
                return (T) deserializeNodeCollection( (CollectionType) valueType, inputNode );
            }
            else // Explicit Map
            if( MapType.class.isAssignableFrom( valueType.getClass() ) )
            {
                MapType mapType = (MapType) valueType;
                if( mapType.variant().equals( Serialization.Variant.entry ) )
                {
                    return (T) deserializeNodeEntryMap( (MapType) valueType, inputNode );
                }
                else
                {
                    return (T) deserializeNodeObjectMap( (MapType) valueType, inputNode );
                }
            }
            else // Enum
            if( EnumType.class.isAssignableFrom( valueType.getClass() ) || type.isEnum() )
            {
                Object value = asSimpleValue( inputNode );
                if( value == null )
                {
                    return null;
                }
                return (T) Enum.valueOf( (Class) type, value.toString() );
            }
            // Guessed deserialization
            return (T) deserializeNodeGuessed( valueType, inputNode );
        }

        private ValueBuilder<?> buildNewValueBuilderWithState( Class<?> type, final Map<String, Object> stateMap )
        {
            return valuesModule().newValueBuilderWithState(
                type,
                new Function<PropertyDescriptor, Object>()
            {
                @Override
                public Object map( PropertyDescriptor property )
                {
                    return stateMap.get( property.qualifiedName().name() );
                }
                },
                new Function<AssociationDescriptor, EntityReference>()
                {
                    @Override
                    public EntityReference map( AssociationDescriptor association )
                    {
                        Object entityRef = stateMap.get( association.qualifiedName().name() );
                        if( entityRef == null )
                        {
                            return null;
                        }
                        return (EntityReference) entityRef;
                    }
                },
                new Function<AssociationDescriptor, Iterable<EntityReference>>()
                {
                    @Override
                    @SuppressWarnings( "unchecked" )
                    public Iterable<EntityReference> map( AssociationDescriptor manyAssociation )
                    {
                        Object entityRefs = stateMap.get( manyAssociation.qualifiedName().name() );
                        if( entityRefs == null )
                        {
                            return empty();
                        }
                        return (Iterable<EntityReference>) entityRefs;
                    }
                },
                new Function<AssociationDescriptor, Map<String, EntityReference>>()
                {
                    @Override
                    @SuppressWarnings( "unchecked" )
                    public Map<String, EntityReference> map( AssociationDescriptor namedAssociation )
                    {
                        Object entityRefs = stateMap.get( namedAssociation.qualifiedName().name() );
                        if( entityRefs == null )
                        {
                            return Collections.emptyMap();
                        }
                        return (Map<String, EntityReference>) entityRefs;
                    }
                } );
        }

        @SuppressWarnings( "unchecked" )
        private <T> T deserializeGuessed( ValueType valueType, InputType input )
            throws Exception
        {
            InputNodeType inputNode = readObjectTree( input );
            if( inputNode == null )
            {
                return null;
            }
            return deserializeNodeGuessed( valueType, inputNode );
        }

        private <T> Collection<T> deserializeNodeCollection( CollectionType collectionType, InputNodeType inputNode )
            throws Exception
        {
            Collection<T> collection;
            Class<?> collectionMainType = first( collectionType.types() );
            if( Set.class.equals( collectionMainType ) )
            {
                collection = new LinkedHashSet<>();
            }
            else
            {
                collection = new ArrayList<>();
            }
            putArrayNodeInCollection( inputNode,
                                      this.<T>buildDeserializeInputNodeFunction( collectionType.collectedType() ),
                                      collection );
            return collection;
        }

        private <K, V> Map<K, V> deserializeNodeEntryMap( MapType mapType, InputNodeType inputNode )
            throws Exception
        {
            Map<K, V> map = new HashMap<>();
            putArrayNodeInMap( inputNode,
                               this.<K>buildDeserializeInputNodeFunction( mapType.keyType() ),
                               this.<V>buildDeserializeInputNodeFunction( mapType.valueType() ),
                               map );
            return map;
        }

        private <V> Map<String, V> deserializeNodeObjectMap( MapType mapType, InputNodeType inputNode )
            throws Exception
        {
            Map<String, V> map = new HashMap<>();
            putObjectNodeInMap( inputNode,
                                this.<V>buildDeserializeInputNodeFunction( mapType.valueType() ),
                                map );
            return map;
        }

        @SuppressWarnings( "unchecked" )
        private <T> T deserializeNodeGuessed( ValueType valueType, InputNodeType inputNode )
            throws Exception
        {
            if( isObjectValue( inputNode ) )
            {
                // Attempt ValueComposite deserialization
                ValueCompositeType valueCompositeType;
                if( objectHasField( inputNode, "_type" ) ) // with _type info
                {
                    String typeInfo = this.getObjectFieldValue(
                        inputNode,
                        "_type",
                        this.<String>buildDeserializeInputNodeFunction( new ValueType( String.class ) ) );
                    ValueDescriptor valueDescriptor = valuesModule().valueDescriptor( typeInfo );
                    if( valueDescriptor == null )
                    {
                        throw new ValueSerializationException( "Specified value type could not be resolved: " + typeInfo );
                    }
                    valueCompositeType = valueDescriptor.valueType();
                }
                else // without _type info
                {
                    ValueDescriptor valueDescriptor = valuesModule().valueDescriptor( first( valueType.types() ).getName() );
                    if( valueDescriptor == null )
                    {
                        throw new ValueSerializationException( "Don't know how to deserialize " + inputNode );
                    }
                    valueCompositeType = valueDescriptor.valueType();
                }
                Class<?> valueBuilderType = first( valueCompositeType.types() );
                return deserializeValueComposite( valueCompositeType, valueBuilderType, inputNode );
            }
            // Last resort : base64 java deserialization
            return (T) deserializeBase64Serialized( inputNode );
        }

        @SuppressWarnings( "unchecked" )
        private <T> T deserializeBase64Serialized( InputNodeType inputNode )
            throws Exception
        {
            Object value = asSimpleValue( inputNode );
            if( value == null )
            {
                return null;
            }
            String base64 = value.toString();
            return deserializeBase64Serialized( base64 );
        }

        @SuppressWarnings( "unchecked" )
        private <T> T deserializeBase64Serialized( String inputString )
            throws Exception
        {
            byte[] bytes = inputString.getBytes( UTF_8 );
            bytes = Base64Encoder.decode( bytes );
            Object result;
            try( ObjectInputStream oin = new ObjectInputStream( new ByteArrayInputStream( bytes ) ) )
            {
                result = oin.readObject();
            }
            return (T) result;
        }

        //
        // Deserialization Extension Points
        //
        /**
         * Called by the adapter on deserialization start, after {@link #adaptInput(java.io.InputStream)}.
         *
         * @param valueType ValueType
         * @param input Input
         * @throws Exception that will be wrapped in a {@link ValueSerializationException}
         */
        @SuppressWarnings( "UnusedParameters" )
        protected void onDeserializationStart( ValueType valueType, InputType input )
            throws Exception
        {
            // NOOP
        }

        /**
         * Called by the adapter on deserialization end.
         *
         * @param valueType ValueType
         * @param input Input
         * @throws Exception that will be wrapped in a {@link ValueSerializationException}
         */
        protected void onDeserializationEnd( ValueType valueType, InputType input )
            throws Exception
        {
            // NOOP
        }

        //
        // Pull Parsing Deserialization
        //
        /**
         * This method is always called first, this is a chance to wrap the input type.
         *
         * @param input InputStream to adapt
         * @return Adapted input
         * @throws Exception that will be wrapped in a {@link ValueSerializationException}
         */
        protected abstract InputType adaptInput( InputStream input )
            throws Exception;

        /**
         * @param input Input
         * @return a Plain Value read from the input
         * @throws Exception that will be wrapped in a {@link ValueSerializationException}
         */
        protected abstract Object readPlainValue( InputType input )
            throws Exception;

        /**
         * @param <T> Parameterized collection type
         * @param input Input
         * @param deserializer Deserialization function
         * @param collection Collection
         * @return The filled collection or null if no array
         * @throws Exception that will be wrapped in a {@link ValueSerializationException}
         */
        protected abstract <T> Collection<T> readArrayInCollection( InputType input,
                                                                    Function<InputType, T> deserializer,
                                                                    Collection<T> collection )
            throws Exception;

        /**
         * A Map&lt;K,V&gt; is serialized in an array of entries objects.
         *
         * <p>Here is an example in JSON:</p>
         * <pre>
         * [
         *     { "key": "foo",       "value": "bar"   },
         *     { "key": "cathedral", "value": "bazar" }
         * ]
         * </pre>
         * <p>And an empty Map:</p>
         * <pre>[]</pre>
         * <p>
         *     This allow to use any type as keys and values while keeping the Map order at the cost of having
         *     non-predictible order of key/value inside an entry object.
         * </p>
         *
         * @param <K> Parameterized map key type
         * @param <V> Parameterized map value type
         * @param input Input
         * @param keyDeserializer Map key deserialization function
         * @param valueDeserializer Map value deserialization function
         * @param map Map
         * @return The filled map or null if no array
         * @throws Exception that will be wrapped in a {@link ValueSerializationException}
         */
        protected abstract <K, V> Map<K, V> readMapInMap( InputType input,
                                                          Function<InputType, K> keyDeserializer,
                                                          Function<InputType, V> valueDeserializer,
                                                          Map<K, V> map )
            throws Exception;

        /**
         * @param input Input
         * @return an InputNodeType or null if the value was null
         * @throws Exception that will be wrapped in a {@link ValueSerializationException}
         */
        protected abstract InputNodeType readObjectTree( InputType input )
            throws Exception;

        //
        // Tree Parsing Deserialization
        //
        protected abstract Object asSimpleValue( InputNodeType inputNode )
            throws Exception;

        protected abstract boolean isObjectValue( InputNodeType inputNode )
            throws Exception;

        protected abstract boolean objectHasField( InputNodeType inputNode, String key )
            throws Exception;

        /**
         * Return null if the field do not exists.
         * @param <T> Parameterized object field value type
         * @param inputNode Input Node
         * @param key Object key
         * @param valueDeserializer Deserialization function
         * @return The value of the field.
         * @throws Exception that will be wrapped in a {@link ValueSerializationException}
         */
        protected abstract <T> T getObjectFieldValue( InputNodeType inputNode,
                                                      String key,
                                                      Function<InputNodeType, T> valueDeserializer )
            throws Exception;

        protected abstract <T> void putArrayNodeInCollection( InputNodeType inputNode,
                                                              Function<InputNodeType, T> deserializer,
                                                              Collection<T> collection )
            throws Exception;

        protected abstract <K, V> void putArrayNodeInMap( InputNodeType inputNode,
                                                          Function<InputNodeType, K> keyDeserializer,
                                                          Function<InputNodeType, V> valueDeserializer,
                                                          Map<K, V> map
        )
            throws Exception;

        protected abstract <V> void putObjectNodeInMap( InputNodeType inputNode,
                                                        Function<InputNodeType, V> valueDeserializer,
                                                        Map<String, V> map
        )
            throws Exception;
    }