# AbstractCollectionSerializationTest.java

    /*
     * Copyright (c) 2011, Niclas Hehdman. All Rights Reserved.
     * Copyright (c) 2012, 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.test.value;

    import java.math.BigDecimal;
    import java.math.BigInteger;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Collections;
    import java.util.LinkedHashMap;
    import java.util.LinkedHashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import org.junit.Before;
    import org.junit.Test;
    import org.qi4j.api.common.Optional;
    import org.qi4j.api.injection.scope.Service;
    import org.qi4j.api.property.Property;
    import org.qi4j.api.type.CollectionType;
    import org.qi4j.api.type.MapType;
    import org.qi4j.api.type.ValueCompositeType;
    import org.qi4j.api.type.ValueType;
    import org.qi4j.api.value.ValueBuilder;
    import org.qi4j.api.value.ValueSerialization;
    import org.qi4j.bootstrap.AssemblyException;
    import org.qi4j.bootstrap.ModuleAssembly;
    import org.qi4j.functional.Iterables;
    import org.qi4j.test.AbstractQi4jTest;

    import static org.junit.Assert.*;
    import static org.qi4j.io.Inputs.iterable;
    import static org.qi4j.io.Inputs.text;
    import static org.qi4j.io.Outputs.collection;
    import static org.qi4j.io.Outputs.text;
    import static org.qi4j.io.Transforms.map;

    /**
     * Assert that ValueSerialization behaviour on Collections and Maps is correct.
     */
    // TODO How to assert that given a collection of valuecomposites when serializing and deserializing we have to OOME?
    public class AbstractCollectionSerializationTest
        extends AbstractQi4jTest
    {

        @Override
        public void assemble( ModuleAssembly module )
            throws AssemblyException
        {
            module.values( SomeValue.class );
        }

        @Before
        public void before()
        {
            module.injectTo( this );
        }

        @Service
        @SuppressWarnings( "ProtectedField" )
        protected ValueSerialization valueSerialization;

        @Test
        public void testIOString()
            throws Exception
        {
            StringBuilder sb = new StringBuilder();
            iterable( byteCollection() ).transferTo( map( valueSerialization.serialize(), text( sb ) ) );
            String output = sb.toString();

            List<Byte> list = new ArrayList<Byte>();
            text( output ).transferTo( map( valueSerialization.deserialize( Byte.class ), collection( list ) ) );
            assertEquals( byteCollection(), list );
        }

        @Test
        public void givenPrimitiveArrayWithIntsWhenSerializingAndDeserializingExpectEquals()
            throws Exception
        {
            int[] primitiveArray = new int[]
            {
                23, 42, -23, -42
            };
            String output = valueSerialization.serialize( primitiveArray );
            int[] deserialized = valueSerialization.deserialize( int[].class, output );
            assertArrayEquals( primitiveArray, deserialized );
        }

        @Test
        public void givenArrayWithByteAndNullElementWhenSerializingAndDeserializingExpectEquals()
            throws Exception
        {
            Byte[] array = new Byte[]
            {
                9, null, -12, -12, 127, -128, 73
            };
            String output = valueSerialization.serialize( array );
            Byte[] deserialized = valueSerialization.deserialize( Byte[].class, output );
            assertArrayEquals( array, deserialized );
        }

        @Test
        public void givenIterableTypeWithByteAndNullElementWhenSerializingAndDeserializingExpectEquals()
            throws Exception
        {
            String output = valueSerialization.serialize( Iterables.iterable( byteCollection().toArray() ) );
            CollectionType collectionType = new CollectionType( List.class, new ValueType( Byte.class ) );
            List<Byte> list = valueSerialization.deserialize( collectionType, output );
            assertEquals( byteCollection(), list );
        }

        @Test
        public void givenCollectionTypeWithByteAndNullElementWhenSerializingAndDeserializingExpectEquals()
            throws Exception
        {
            String output = valueSerialization.serialize( byteCollection() );
            CollectionType collectionType = new CollectionType( Set.class, new ValueType( Byte.class ) );
            Set<Byte> list = valueSerialization.deserialize( collectionType, output );
            assertEquals( new LinkedHashSet<Byte>( byteCollection() ), list );
        }

        @Test
        public void givenCollectionTypeWithCharacterAndNullElementWhenSerializingAndDeserializingExpectEquals()
            throws Exception
        {
            String output = valueSerialization.serialize( characterCollection() );
            CollectionType collectionType = new CollectionType( List.class, new ValueType( Character.class ) );
            List<Character> list = valueSerialization.deserialize( collectionType, output );
            assertEquals( characterCollection(), list );
        }

        @Test
        public void givenCollectionTypeWithShortAndNullElementWhenSerializingAndDeserializingExpectEquals()
            throws Exception
        {
            String output = valueSerialization.serialize( shortCollection() );
            CollectionType collectionType = new CollectionType( List.class, new ValueType( Short.class ) );
            List<Short> list = valueSerialization.deserialize( collectionType, output );
            assertEquals( shortCollection(), list );
        }

        @Test
        public void givenCollectionTypeWithIntegerAndNullElementWhenSerializingAndDeserializingExpectEquals()
            throws Exception
        {
            String output = valueSerialization.serialize( integerCollection() );
            CollectionType collectionType = new CollectionType( List.class, new ValueType( Integer.class ) );
            List<Integer> list = valueSerialization.deserialize( collectionType, output );
            assertEquals( integerCollection(), list );
        }

        @Test
        public void givenCollectionTypeWithLongAndNullElementWhenSerializingAndDeserializingExpectEquals()
            throws Exception
        {
            String output = valueSerialization.serialize( longCollection() );
            CollectionType collectionType = new CollectionType( List.class, new ValueType( Long.class ) );
            List<Long> list = valueSerialization.deserialize( collectionType, output );
            assertEquals( longCollection(), list );
        }

        @Test
        public void givenCollectionTypeWithFloatAndNullElementWhenSerializingAndDeserializingExpectEquals()
            throws Exception
        {
            String output = valueSerialization.serialize( floatCollection() );
            CollectionType collectionType = new CollectionType( List.class, new ValueType( Float.class ) );
            List<Float> list = valueSerialization.deserialize( collectionType, output );
            assertEquals( floatCollection(), list );
        }

        @Test
        public void givenCollectionTypeWithDoubleAndNullElementWhenSerializingExpectCorrectJsonOutput()
            throws Exception
        {
            String output = valueSerialization.serialize( doubleCollection() );
            CollectionType collectionType = new CollectionType( List.class, new ValueType( Double.class ) );
            List<Double> list = valueSerialization.deserialize( collectionType, output );
            assertEquals( doubleCollection(), list );

        }

        @Test
        public void givenCollectionTypeWithBigIntegerAndNullElementWhenSerializingAndDeserializingExpectEquals()
            throws Exception
        {
            String output = valueSerialization.serialize( bigIntegerCollection() );
            CollectionType collectionType = new CollectionType( List.class, new ValueType( BigInteger.class ) );
            List<BigInteger> list = valueSerialization.deserialize( collectionType, output );
            assertEquals( bigIntegerCollection(), list );
        }

        @Test
        public void givenCollectionTypeWithBigDecimalAndNullElementWhenSerializingAndDeserializingExpectEquals()
            throws Exception
        {
            String output = valueSerialization.serialize( bigDecimalCollection() );
            CollectionType collectionType = new CollectionType( Collection.class, new ValueType( BigDecimal.class ) );
            Collection<BigDecimal> collection = valueSerialization.deserialize( collectionType, output );
            assertEquals( bigDecimalCollection(), collection );
        }

        @Test
        public void givenMapOfStringByteAndNullElementWhenSerializingAndDeserializingExpectEquals()
            throws Exception
        {
            String output = valueSerialization.serialize( stringByteMap() );
            MapType mapType = new MapType( Map.class, new ValueType( String.class ), new ValueType( Byte.class ) );
            Map<String, Byte> value = valueSerialization.deserialize( mapType, output );
            assertEquals( stringByteMap(), value );
        }

        @Test
        public void givenMapOfStringListStringAndNullElementWhenSerializingAndDeserializingExpectEquals()
            throws Exception
        {
            String output = valueSerialization.serialize( stringMultiMap() );
            CollectionType collectionType = new CollectionType( List.class, new ValueType( String.class ) );
            MapType mapType = new MapType( Map.class, new ValueType( String.class ), collectionType );
            Map<String, List<String>> value = valueSerialization.deserialize( mapType, output );
            assertEquals( stringMultiMap(), value );
        }

        @Test
        public void givenListOfMapStringStringAndNullElementWhenSerializingAndDeserializingExpectEquals()
            throws Exception
        {
            String output = valueSerialization.serialize( stringListOfMaps() );
            ValueType stringType = new ValueType( String.class );
            CollectionType collectionType = new CollectionType( List.class, new MapType( Map.class, stringType, stringType ) );
            List<Map<String, String>> value = valueSerialization.deserialize( collectionType, output );
            assertEquals( stringListOfMaps(), value );
        }

        @Test
        public void givenListOfValueCompositesAndNullElementWhenSerializingAndDeserializingExpectEquals()
            throws Exception
        {
            String output = valueSerialization.serialize( valueCompositesList() );
            ValueCompositeType valueType = module.valueDescriptor( SomeValue.class.getName() ).valueType();
            CollectionType collectionType = new CollectionType( List.class, valueType );
            List<SomeValue> value = valueSerialization.deserialize( collectionType, output );
            assertEquals( valueCompositesList(), value );
        }

        private ArrayList<Byte> byteCollection()
        {
            ArrayList<Byte> value = new ArrayList<Byte>();
            value.add( (byte) 9 );
            value.add( null );
            value.add( (byte) -12 );
            value.add( (byte) -12 );
            value.add( (byte) 127 );
            value.add( (byte) -128 );
            value.add( (byte) 73 );
            return value;
        }

        private List<Character> characterCollection()
        {
            List<Character> value = new ArrayList<Character>();
            value.add( 'Q' );
            value.add( 'i' );
            value.add( null );
            value.add( '4' );
            value.add( 'j' );
            return value;
        }

        private Collection<Short> shortCollection()
        {
            Collection<Short> value = new ArrayList<Short>();
            value.add( (short) -32768 );
            value.add( (short) 32767 );
            value.add( (short) -82 );
            value.add( null );
            return value;
        }

        private Collection<Integer> integerCollection()
        {
            Collection<Integer> value = new ArrayList<Integer>();
            value.add( Integer.MAX_VALUE );
            value.add( -283 );
            value.add( null );
            value.add( Integer.MIN_VALUE );
            value.add( 238 );
            return value;
        }

        private Collection<Long> longCollection()
        {
            Collection<Long> value = new ArrayList<Long>();
            value.add( 98239723L );
            value.add( -1298233L );
            value.add( -1L );
            value.add( 0L );
            value.add( null );
            value.add( 1L );
            value.add( Long.MAX_VALUE );
            value.add( Long.MIN_VALUE );
            return value;
        }

        private Collection<Float> floatCollection()
        {
            Collection<Float> value = new ArrayList<Float>();
            value.add( -1f );
            value.add( 1f );
            value.add( 1f );
            value.add( 0f );
            value.add( Float.MAX_VALUE );
            value.add( Float.MIN_VALUE );
            value.add( null );
            value.add( 0.123456f );
            value.add( -0.232321f );
            return value;
        }

        private Collection<Double> doubleCollection()
        {
            Collection<Double> value = new ArrayList<Double>();
            value.add( -1.0 );
            value.add( 1.0 );
            value.add( 0.0 );
            value.add( Double.MAX_VALUE );
            value.add( null );
            value.add( Double.MIN_VALUE );
            value.add( 0.123456 );
            value.add( -0.232321 );
            return value;
        }

        private Collection<BigInteger> bigIntegerCollection()
        {
            Collection<BigInteger> value = new ArrayList<BigInteger>();
            value.add( new BigInteger( "-1" ) );
            value.add( BigInteger.ZERO );
            value.add( BigInteger.ONE );
            value.add( null );
            value.add( BigInteger.TEN );
            value.add( new BigInteger( "-1827368263823729372397239829332" ) );
            value.add( new BigInteger( "2398723982982379827373972398723" ) );
            return value;
        }

        private Collection<BigDecimal> bigDecimalCollection()
        {
            Collection<BigDecimal> value = new ArrayList<BigDecimal>();
            value.add( new BigDecimal( "1.2" ) );
            value.add( new BigDecimal( "3.4" ) );
            value.add( null );
            value.add( new BigDecimal( "5.6" ) );
            return value;
        }

        private Map<String, Byte> stringByteMap()
        {
            Map<String, Byte> value = new LinkedHashMap<String, Byte>();
            value.put( "a", (byte) 9 );
            value.put( "b", null );
            value.put( "c", (byte) -12 );
            return value;
        }

        private Map<String, List<String>> stringMultiMap()
        {
            Map<String, List<String>> value = new LinkedHashMap<String, List<String>>();
            List<String> list = new ArrayList<String>();
            list.add( "foo" );
            list.add( "bar" );
            list.add( null );
            list.add( "cathedral" );
            list.add( "bazar" );
            value.put( "alpha", list );
            value.put( "beta", null );
            value.put( "gamma", Collections.<String>emptyList() );
            return value;
        }

        private List<Map<String, String>> stringListOfMaps()
        {
            List<Map<String, String>> value = new ArrayList<Map<String, String>>();
            Map<String, String> map = new LinkedHashMap<String, String>();
            map.put( "foo", "bar" );
            map.put( "cathedral", "bazar" );
            map.put( "yield", null );
            map.put( "42", "23" );
            value.add( map );
            value.add( null );
            value.add( Collections.<String, String>emptyMap() );
            return value;
        }

        private List<SomeValue> valueCompositesList()
        {
            List<SomeValue> list = new ArrayList<SomeValue>();
            list.add( newSomeValue( "", "bazar" ) );
            list.add( null );
            list.add( newSomeValue( "bar", null ) );
            return list;
        }

        public static interface SomeValue
        {

            Property<String> foo();

            @Optional
            Property<String> cathedral();
        }

        private SomeValue newSomeValue( String foo, String cathedral )
        {
            ValueBuilder<SomeValue> builder = module.newValueBuilder( SomeValue.class );
            SomeValue value = builder.prototype();
            value.foo().set( foo );
            if( cathedral != null )
            {
                value.cathedral().set( cathedral );
            }
            return builder.newInstance();
        }
    }