You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polygene.apache.org by pa...@apache.org on 2017/03/13 10:35:26 UTC

[34/50] [abbrv] polygene-java git commit: New (de)serialization API and SPI & new implementations

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/main/java/org/apache/polygene/api/type/HasTypesCollectors.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/type/HasTypesCollectors.java b/core/api/src/main/java/org/apache/polygene/api/type/HasTypesCollectors.java
new file mode 100644
index 0000000..ef6274e
--- /dev/null
+++ b/core/api/src/main/java/org/apache/polygene/api/type/HasTypesCollectors.java
@@ -0,0 +1,408 @@
+package org.apache.polygene.api.type;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+
+/**
+ * Collectors for HasTypes.
+ */
+public class HasTypesCollectors
+{
+    private static final String EQUAL_KEY = "equal";
+    private static final String EQUAL_TYPE_KEY = "equalType";
+    private static final String ASSIGNABLE_TYPE_KEY = "assignableType";
+
+    public static <T extends HasTypes> Collector<T, ?, Optional<T>> matchingType( T hasTypes )
+    {
+        return hasTypesFindFirstCollector( hasTypes, new HasAssignableFromType<>( hasTypes ) );
+    }
+
+    public static <T extends HasTypes> Collector<T, ?, Optional<T>> closestType( T hasTypes )
+    {
+        return hasTypesFindFirstCollector( hasTypes, new HasAssignableToType<>( hasTypes ) );
+    }
+
+    private static <T extends HasTypes> Collector<T, ?, Optional<T>>
+    hasTypesFindFirstCollector( T hasTypes, Predicate<T> assignableTypePredicate )
+    {
+        Predicate<T> equalPredicate = o -> Objects.equals( o, hasTypes );
+        Predicate<T> equalTypePredicate = new HasEqualType<>( hasTypes );
+        return new Collector<T, Map<String, Set<T>>, Optional<T>>()
+        {
+            @Override
+            public Supplier<Map<String, Set<T>>> supplier()
+            {
+                return () -> new HashMap<String, Set<T>>( 3 )
+                {{
+                    put( EQUAL_KEY, new LinkedHashSet<>( 1 ) );
+                    put( EQUAL_TYPE_KEY, new LinkedHashSet<>( 1 ) );
+                    put( ASSIGNABLE_TYPE_KEY, new LinkedHashSet<>() );
+                }};
+            }
+
+            @Override
+            public BiConsumer<Map<String, Set<T>>, T> accumulator()
+            {
+                return ( map, candidate ) ->
+                {
+                    Set<T> equalObjects = map.get( EQUAL_KEY );
+                    if( equalObjects.isEmpty() )
+                    {
+                        if( equalPredicate.test( candidate ) )
+                        {
+                            equalObjects.add( candidate );
+                        }
+                        else
+                        {
+                            Set<T> equalTypes = map.get( EQUAL_TYPE_KEY );
+                            if( equalTypes.isEmpty() )
+                            {
+                                if( equalTypePredicate.test( candidate ) )
+                                {
+                                    equalTypes.add( candidate );
+                                }
+                                else if( assignableTypePredicate.test( candidate ) )
+                                {
+                                    map.get( ASSIGNABLE_TYPE_KEY ).add( candidate );
+                                }
+                            }
+                        }
+                    }
+                };
+            }
+
+            @Override
+            public BinaryOperator<Map<String, Set<T>>> combiner()
+            {
+                return ( left, right ) ->
+                {
+                    left.get( EQUAL_KEY ).addAll( right.get( EQUAL_KEY ) );
+                    left.get( EQUAL_TYPE_KEY ).addAll( right.get( EQUAL_TYPE_KEY ) );
+                    left.get( ASSIGNABLE_TYPE_KEY ).addAll( right.get( ASSIGNABLE_TYPE_KEY ) );
+                    return left;
+                };
+            }
+
+            @Override
+            public Function<Map<String, Set<T>>, Optional<T>> finisher()
+            {
+                return map ->
+                {
+                    Set<T> equalObjects = map.get( EQUAL_KEY );
+                    if( !equalObjects.isEmpty() )
+                    {
+                        return Optional.of( equalObjects.iterator().next() );
+                    }
+                    Set<T> equalTypes = map.get( EQUAL_TYPE_KEY );
+                    if( !equalTypes.isEmpty() )
+                    {
+                        return Optional.of( equalTypes.iterator().next() );
+                    }
+                    Set<T> assignableTypes = map.get( ASSIGNABLE_TYPE_KEY );
+                    if( !assignableTypes.isEmpty() )
+                    {
+                        return Optional.of( assignableTypes.iterator().next() );
+                    }
+                    return Optional.empty();
+                };
+            }
+
+            @Override
+            public Set<Characteristics> characteristics()
+            {
+                return Collections.emptySet();
+            }
+        };
+    }
+
+
+    public static <T extends HasTypes> Collector<T, ?, List<T>> matchingTypes( T hasTypes )
+    {
+        return hasTypesToListCollector( hasTypes, new HasAssignableFromType<>( hasTypes ) );
+    }
+
+    public static <T extends HasTypes> Collector<T, ?, List<T>> closestTypes( T hasTypes )
+    {
+        return hasTypesToListCollector( hasTypes, new HasAssignableToType<>( hasTypes ) );
+    }
+
+    private static <T extends HasTypes> Collector<T, ?, List<T>>
+    hasTypesToListCollector( T hasTypes, Predicate<T> assignableTypePredicate )
+    {
+        Predicate<T> equalPredicate = o -> Objects.equals( o, hasTypes );
+        Predicate<T> equalTypePredicate = new HasEqualType<>( hasTypes );
+        return new Collector<T, Map<String, Set<T>>, List<T>>()
+        {
+            @Override
+            public Supplier<Map<String, Set<T>>> supplier()
+            {
+                return () -> new HashMap<String, Set<T>>( 3 )
+                {{
+                    put( EQUAL_KEY, new LinkedHashSet<>() );
+                    put( EQUAL_TYPE_KEY, new LinkedHashSet<>() );
+                    put( ASSIGNABLE_TYPE_KEY, new LinkedHashSet<>() );
+                }};
+            }
+
+            @Override
+            public BiConsumer<Map<String, Set<T>>, T> accumulator()
+            {
+                return ( map, candidate ) ->
+                {
+                    Set<T> equalObjects = map.get( EQUAL_KEY );
+                    if( equalObjects.isEmpty() )
+                    {
+                        if( equalPredicate.test( candidate ) )
+                        {
+                            equalObjects.add( candidate );
+                        }
+                        else
+                        {
+                            Set<T> equalTypes = map.get( EQUAL_TYPE_KEY );
+                            if( equalTypes.isEmpty() )
+                            {
+                                if( equalTypePredicate.test( candidate ) )
+                                {
+                                    equalTypes.add( candidate );
+                                }
+                                else if( assignableTypePredicate.test( candidate ) )
+                                {
+                                    map.get( ASSIGNABLE_TYPE_KEY ).add( candidate );
+                                }
+                            }
+                        }
+                    }
+                };
+            }
+
+            @Override
+            public BinaryOperator<Map<String, Set<T>>> combiner()
+            {
+                return ( left, right ) ->
+                {
+                    left.get( EQUAL_KEY ).addAll( right.get( EQUAL_KEY ) );
+                    left.get( EQUAL_TYPE_KEY ).addAll( right.get( EQUAL_TYPE_KEY ) );
+                    left.get( ASSIGNABLE_TYPE_KEY ).addAll( right.get( ASSIGNABLE_TYPE_KEY ) );
+                    return left;
+                };
+            }
+
+            @Override
+            public Function<Map<String, Set<T>>, List<T>> finisher()
+            {
+                return map ->
+                {
+                    Set<T> equalObjects = map.get( EQUAL_KEY );
+                    Set<T> equalSet = map.get( EQUAL_TYPE_KEY );
+                    Set<T> assignableSet = map.get( ASSIGNABLE_TYPE_KEY );
+                    List<T> list = new ArrayList<>( equalObjects.size() + equalSet.size() + assignableSet.size() );
+                    list.addAll( equalObjects );
+                    list.addAll( equalSet );
+                    list.addAll( assignableSet );
+                    return list;
+                };
+            }
+
+            @Override
+            public Set<Characteristics> characteristics()
+            {
+                return Collections.emptySet();
+            }
+        };
+    }
+
+
+    /**
+     * Collect a single matching HasTypes.
+     *
+     * TODO Detail
+     *
+     * @param type type to match
+     * @param <T> type of HasTypes
+     * @return an optional best matching HasTypes
+     */
+    public static <T extends HasTypes> Collector<T, ?, Optional<T>> matchingType( Type type )
+    {
+        return typeFindFirstCollector( type, new HasAssignableFromType<>( type ) );
+    }
+
+    public static <T extends HasTypes> Collector<T, ?, Optional<T>> closestType( Type type )
+    {
+        return typeFindFirstCollector( type, new HasAssignableToType<T>( type ) );
+    }
+
+    private static <T extends HasTypes> Collector<T, ?, Optional<T>>
+    typeFindFirstCollector( Type type, Predicate<T> assignableTypePredicate )
+    {
+        Predicate<T> equalTypePredicate = new HasEqualType<>( type );
+        return new Collector<T, Map<String, Set<T>>, Optional<T>>()
+        {
+            @Override
+            public Supplier<Map<String, Set<T>>> supplier()
+            {
+                return () -> new HashMap<String, Set<T>>( 2 )
+                {{
+                    put( EQUAL_TYPE_KEY, new LinkedHashSet<>( 1 ) );
+                    put( ASSIGNABLE_TYPE_KEY, new LinkedHashSet<>() );
+                }};
+            }
+
+            @Override
+            public BiConsumer<Map<String, Set<T>>, T> accumulator()
+            {
+                return ( map, candidate ) ->
+                {
+                    Set<T> equalSet = map.get( EQUAL_TYPE_KEY );
+                    if( equalSet.isEmpty() )
+                    {
+                        if( equalTypePredicate.test( candidate ) )
+                        {
+                            equalSet.add( candidate );
+                        }
+                        else if( assignableTypePredicate.test( candidate ) )
+                        {
+                            map.get( ASSIGNABLE_TYPE_KEY ).add( candidate );
+                        }
+                    }
+                };
+            }
+
+            @Override
+            public BinaryOperator<Map<String, Set<T>>> combiner()
+            {
+                return ( left, right ) ->
+                {
+                    left.get( EQUAL_TYPE_KEY ).addAll( right.get( EQUAL_TYPE_KEY ) );
+                    left.get( ASSIGNABLE_TYPE_KEY ).addAll( right.get( ASSIGNABLE_TYPE_KEY ) );
+                    return left;
+                };
+            }
+
+            @Override
+            public Function<Map<String, Set<T>>, Optional<T>> finisher()
+            {
+                return map ->
+                {
+                    Set<T> equalSet = map.get( EQUAL_TYPE_KEY );
+                    if( !equalSet.isEmpty() )
+                    {
+                        return Optional.of( equalSet.iterator().next() );
+                    }
+                    Set<T> assignableSet = map.get( ASSIGNABLE_TYPE_KEY );
+                    if( !assignableSet.isEmpty() )
+                    {
+                        return Optional.of( assignableSet.iterator().next() );
+                    }
+                    return Optional.empty();
+                };
+            }
+
+            @Override
+            public final Set<Characteristics> characteristics()
+            {
+                return Collections.emptySet();
+            }
+        };
+    }
+
+    /**
+     * Collect all matching HasTypes.
+     *
+     * First the ones with at least on equal type.
+     * Then the ones with at least one type assignable from {@literal type}.
+     *
+     * @param type type to match
+     * @param <T> type of HasTypes
+     * @return an optional best matching HasTypes
+     */
+    public static <T extends HasTypes> Collector<T, ?, List<T>> matchingTypes( Type type )
+    {
+        return typeToListCollector( type, new HasAssignableFromType<>( type ) );
+    }
+
+    public static <T extends HasTypes> Collector<T, ?, List<T>> closestTypes( Type type )
+    {
+        return typeToListCollector( type, new HasAssignableToType<>( type ) );
+    }
+
+    private static <T extends HasTypes> Collector<T, ?, List<T>>
+    typeToListCollector( Type type, Predicate<T> assignableTypePredicate )
+    {
+        Predicate<T> equalTypePredicate = new HasEqualType<>( type );
+        return new Collector<T, Map<String, Set<T>>, List<T>>()
+        {
+            @Override
+            public Supplier<Map<String, Set<T>>> supplier()
+            {
+                return () -> new HashMap<String, Set<T>>( 2 )
+                {{
+                    put( EQUAL_TYPE_KEY, new LinkedHashSet<>() );
+                    put( ASSIGNABLE_TYPE_KEY, new LinkedHashSet<>() );
+                }};
+            }
+
+            @Override
+            public BiConsumer<Map<String, Set<T>>, T> accumulator()
+            {
+                return ( map, candidate ) ->
+                {
+                    if( equalTypePredicate.test( candidate ) )
+                    {
+                        map.get( EQUAL_TYPE_KEY ).add( candidate );
+                    }
+                    else if( assignableTypePredicate.test( candidate ) )
+                    {
+                        map.get( ASSIGNABLE_TYPE_KEY ).add( candidate );
+                    }
+                };
+            }
+
+            @Override
+            public BinaryOperator<Map<String, Set<T>>> combiner()
+            {
+                return ( left, right ) ->
+                {
+                    left.get( EQUAL_TYPE_KEY ).addAll( right.get( EQUAL_TYPE_KEY ) );
+                    left.get( ASSIGNABLE_TYPE_KEY ).addAll( right.get( ASSIGNABLE_TYPE_KEY ) );
+                    return left;
+                };
+            }
+
+            @Override
+            public Function<Map<String, Set<T>>, List<T>> finisher()
+            {
+                return map ->
+                {
+                    Set<T> equalSet = map.get( EQUAL_TYPE_KEY );
+                    Set<T> assignableSet = map.get( ASSIGNABLE_TYPE_KEY );
+                    List<T> list = new ArrayList<>( equalSet.size() + assignableSet.size() );
+                    list.addAll( equalSet );
+                    list.addAll( assignableSet );
+                    return list;
+                };
+            }
+
+            @Override
+            public final Set<Characteristics> characteristics()
+            {
+                return Collections.emptySet();
+            }
+        };
+    }
+
+    private HasTypesCollectors() {}
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/main/java/org/apache/polygene/api/type/HasTypesPredicate.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/type/HasTypesPredicate.java b/core/api/src/main/java/org/apache/polygene/api/type/HasTypesPredicate.java
new file mode 100644
index 0000000..fd91a3a
--- /dev/null
+++ b/core/api/src/main/java/org/apache/polygene/api/type/HasTypesPredicate.java
@@ -0,0 +1,60 @@
+package org.apache.polygene.api.type;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
+import java.util.List;
+import java.util.function.Predicate;
+
+import static org.apache.polygene.api.util.Classes.interfacesOf;
+
+public abstract class HasTypesPredicate<T extends HasTypes> implements Predicate<T>
+{
+    protected final List<Type> matchTypes;
+
+    protected HasTypesPredicate( List<Type> types )
+    {
+        matchTypes = types;
+    }
+
+    @Override
+    public final boolean test( T hasTypes )
+    {
+        for( Type matchType : matchTypes )
+        {
+            if( matchType instanceof Class )
+            {
+                if( hasTypes.types().anyMatch( matchPredicate( matchType ) ) )
+                {
+                    return true;
+                }
+            }
+            else
+            {
+                if( matchType instanceof ParameterizedType )
+                {
+                    // Foo<Bar> check
+                    // First check Foo
+                    ParameterizedType parameterizedType = (ParameterizedType) matchType;
+                    Type rawType = parameterizedType.getRawType();
+
+                    if( hasTypes.types().anyMatch( matchPredicate( rawType ) ) )
+                    {
+                        // Then check Bar
+                        if( interfacesOf( hasTypes.types() ).anyMatch( intf -> intf.equals( matchType ) ) )
+                        {
+                            return true;
+                        }
+                    }
+                }
+                else if( matchType instanceof WildcardType )
+                {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    protected abstract Predicate<Type> matchPredicate( Type candidate );
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/main/java/org/apache/polygene/api/type/MapType.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/type/MapType.java b/core/api/src/main/java/org/apache/polygene/api/type/MapType.java
index 4046628..23124eb 100644
--- a/core/api/src/main/java/org/apache/polygene/api/type/MapType.java
+++ b/core/api/src/main/java/org/apache/polygene/api/type/MapType.java
@@ -21,6 +21,7 @@ package org.apache.polygene.api.type;
 
 import java.lang.reflect.Type;
 import java.util.Map;
+import java.util.Objects;
 import org.apache.polygene.api.util.Classes;
 
 /**
@@ -30,38 +31,40 @@ import org.apache.polygene.api.util.Classes;
 public final class MapType
     extends ValueType
 {
-
-    private ValueType keyType;
-    private ValueType valueType;
-    private final Serialization.Variant variant;
-
     public static boolean isMap( Type type )
     {
         Class<?> cl = Classes.RAW_CLASS.apply( type );
         return Map.class.isAssignableFrom( cl );
     }
 
-    public static MapType of( Class<?> keyType, Class<?> valueType )
+    public static MapType of( Class<?> mapType, ValueType keyType, ValueType valueType )
     {
-        return new MapType( Map.class, ValueType.of( keyType ), ValueType.of( valueType ) );
+        return new MapType( mapType, keyType, valueType );
     }
 
-    public static MapType of( Class<?> keyType, Class<?> valueType, Serialization.Variant variant )
+    public static MapType of( Class<?> mapType, Class<?> keyType, Class<?> valueType )
     {
-        return new MapType( Map.class, ValueType.of( keyType ), ValueType.of( valueType ), variant );
+        return of( mapType, ValueType.of( keyType ), ValueType.of( valueType ) );
     }
 
-    public MapType( Class<?> type, ValueType keyType, ValueType valueType )
+    public static MapType of( ValueType keyType, ValueType valueType )
     {
-        this( type, keyType, valueType, Serialization.Variant.entry );
+        return new MapType( Map.class, keyType, valueType );
     }
 
-    public MapType( Class<?> type, ValueType keyType, ValueType valueType, Serialization.Variant variant )
+    public static MapType of( Class<?> keyType, Class<?> valueType )
+    {
+        return of( ValueType.of( keyType ), ValueType.of( valueType ) );
+    }
+
+    private ValueType keyType;
+    private ValueType valueType;
+
+    public MapType( Class<?> type, ValueType keyType, ValueType valueType )
     {
         super( type );
         this.keyType = keyType;
         this.valueType = valueType;
-        this.variant = variant;
         if( !isMap( type ) )
         {
             throw new IllegalArgumentException( type + " is not a Map." );
@@ -78,9 +81,21 @@ public final class MapType
         return valueType;
     }
 
-    public Serialization.Variant variant()
+    @Override
+    public boolean equals( final Object o )
+    {
+        if( this == o ) { return true; }
+        if( o == null || getClass() != o.getClass() ) { return false; }
+        if( !super.equals( o ) ) { return false; }
+        MapType mapType = (MapType) o;
+        return Objects.equals( keyType, mapType.keyType ) &&
+               Objects.equals( valueType, mapType.valueType );
+    }
+
+    @Override
+    public int hashCode()
     {
-        return variant;
+        return Objects.hash( super.hashCode(), keyType, valueType );
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/main/java/org/apache/polygene/api/type/MatchTypeSpecification.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/type/MatchTypeSpecification.java b/core/api/src/main/java/org/apache/polygene/api/type/MatchTypeSpecification.java
deleted file mode 100644
index 8d8ff92..0000000
--- a/core/api/src/main/java/org/apache/polygene/api/type/MatchTypeSpecification.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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.apache.polygene.api.type;
-
-import java.util.function.Predicate;
-
-/**
- * Match Type Specification for HasTypes.
- */
-public class MatchTypeSpecification
-    implements Predicate<HasTypes>
-{
-    private final Class<?> matchType;
-
-    public MatchTypeSpecification( Class<?> matchType )
-    {
-        this.matchType = matchType;
-    }
-
-    @Override
-    public boolean test( HasTypes item )
-    {
-        return item.types().anyMatch( matchType::isAssignableFrom );
-//        for( Class<?> type : item.types() )
-//        {
-//            if( matchType.isAssignableFrom( type ) )
-//            {
-//                return true;
-//            }
-//        }
-//        return false;
-    }
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/main/java/org/apache/polygene/api/type/Serialization.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/type/Serialization.java b/core/api/src/main/java/org/apache/polygene/api/type/Serialization.java
deleted file mode 100644
index 981ab00..0000000
--- a/core/api/src/main/java/org/apache/polygene/api/type/Serialization.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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.apache.polygene.api.type;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Serialization options for Property intstances.
- * <p>
- * The {@code entry} type represents the explicit key=keyValue, value=valueValue. For JSON serialization;
- * </p>
- * <pre>
- *     [
- *         { "key1" : "value1" },
- *         { "key2" : "value2" }
- *     ]
- * </pre>
- * <p>
- * For XML serialization;
- * </p>
- * <pre>
- *     &lt;object&gt;
- *         &lt;
- *     &lt;/object&gt;
- * </pre>
- * <p>
- * The {@code object} type represents the explicit keyValue=valueValue.
- * </p>
- */
-@Retention( RetentionPolicy.RUNTIME )
-@Target( { ElementType.TYPE, ElementType.METHOD } )
-@Documented
-public @interface Serialization
-{
-    Variant value();
-
-    enum Variant
-    {
-        entry, object
-    }
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/main/java/org/apache/polygene/api/type/ValueCompositeType.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/type/ValueCompositeType.java b/core/api/src/main/java/org/apache/polygene/api/type/ValueCompositeType.java
index 4cf86a6..9190e68 100644
--- a/core/api/src/main/java/org/apache/polygene/api/type/ValueCompositeType.java
+++ b/core/api/src/main/java/org/apache/polygene/api/type/ValueCompositeType.java
@@ -20,7 +20,7 @@
 package org.apache.polygene.api.type;
 
 import java.lang.reflect.Type;
-import java.util.stream.Collectors;
+import java.util.Objects;
 import java.util.stream.Stream;
 import org.apache.polygene.api.association.AssociationDescriptor;
 import org.apache.polygene.api.property.PropertyDescriptor;
@@ -28,22 +28,28 @@ import org.apache.polygene.api.util.Classes;
 import org.apache.polygene.api.value.ValueComposite;
 import org.apache.polygene.api.value.ValueDescriptor;
 
+import static java.util.stream.Collectors.toList;
+
 /**
  * ValueComposite ValueType.
  */
-public final class ValueCompositeType
-    extends ValueType
+public final class ValueCompositeType extends ValueType
 {
-    private final ValueDescriptor model;
+    public static ValueCompositeType of( ValueDescriptor model )
+    {
+        return new ValueCompositeType( model );
+    }
 
     public static boolean isValueComposite( Type type )
     {
         return ValueComposite.class.isAssignableFrom( Classes.RAW_CLASS.apply( type ) );
     }
 
+    private final ValueDescriptor model;
+
     public ValueCompositeType( ValueDescriptor model )
     {
-        super( model.types().collect( Collectors.toList() ) );
+        super( model.types().collect( toList() ) );
         this.model = model;
     }
 
@@ -66,4 +72,20 @@ public final class ValueCompositeType
     {
         return model.state().namedAssociations();
     }
+
+    @Override
+    public boolean equals( final Object o )
+    {
+        if( this == o ) { return true; }
+        if( o == null || getClass() != o.getClass() ) { return false; }
+        if( !super.equals( o ) ) { return false; }
+        ValueCompositeType that = (ValueCompositeType) o;
+        return Objects.equals( model, that.model );
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return Objects.hash( super.hashCode(), model );
+    }
 }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/main/java/org/apache/polygene/api/type/ValueType.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/type/ValueType.java b/core/api/src/main/java/org/apache/polygene/api/type/ValueType.java
index fee41cb..d457d81 100644
--- a/core/api/src/main/java/org/apache/polygene/api/type/ValueType.java
+++ b/core/api/src/main/java/org/apache/polygene/api/type/ValueType.java
@@ -19,10 +19,21 @@
  */
 package org.apache.polygene.api.type;
 
-import java.util.Collections;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.Period;
+import java.time.ZonedDateTime;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.stream.Stream;
+import org.apache.polygene.api.entity.EntityReference;
 import org.apache.polygene.api.identity.Identity;
 
 import static java.util.stream.Collectors.joining;
@@ -30,105 +41,44 @@ import static java.util.stream.Collectors.joining;
 /**
  * Base class for types of values in ValueComposites and Properties.
  */
-public class ValueType
-    implements HasTypes
+public class ValueType implements HasTypes
 {
+    public static final ValueType OBJECT = ValueType.of( Object.class );
+    public static final ValueType STRING = ValueType.of( String.class );
+    public static final ValueType CHARACTER = ValueType.of( Character.class, char.class );
+    public static final ValueType BOOLEAN = ValueType.of( Boolean.class, boolean.class );
+    public static final ValueType INTEGER = ValueType.of( Integer.class, int.class );
+    public static final ValueType LONG = ValueType.of( Long.class, long.class );
+    public static final ValueType SHORT = ValueType.of( Short.class, short.class );
+    public static final ValueType BYTE = ValueType.of( Byte.class, byte.class );
+    public static final ValueType FLOAT = ValueType.of( Float.class, float.class );
+    public static final ValueType DOUBLE = ValueType.of( Double.class, double.class );
+    public static final ValueType BIG_DECIMAL = ValueType.of( BigDecimal.class );
+    public static final ValueType BIG_INTEGER = ValueType.of( BigInteger.class );
+    public static final ValueType INSTANT = ValueType.of( Instant.class );
+    public static final ValueType ZONED_DATE_TIME = ValueType.of( ZonedDateTime.class );
+    public static final ValueType OFFSET_DATE_TIME = ValueType.of( OffsetDateTime.class );
+    public static final ValueType LOCAL_DATE_TIME = ValueType.of( LocalDateTime.class );
+    public static final ValueType LOCAL_DATE = ValueType.of( LocalDate.class );
+    public static final ValueType LOCAL_TIME = ValueType.of( LocalTime.class );
+    public static final ValueType DURATION = ValueType.of( Duration.class );
+    public static final ValueType PERIOD = ValueType.of( Period.class );
+    public static final ValueType IDENTITY = ValueType.of( Identity.class );
+    public static final ValueType ENTITY_REFERENCE = ValueType.of( EntityReference.class );
 
-    public static ValueType of( Class<?> type )
+    public static ValueType of( Class<?>... types )
     {
-        return new ValueType( type );
+        return new ValueType( types );
     }
 
-    /**
-     * Check if a non-null object is of any of the Primitive Value Types or an array of them.
-     * <p>
-     *     String, Boolean, Integer, Double, Float, Long, Byte, Short and Character and their Java primitive types
-     *     counterparts are considered as Primitive Value Types.
-     * </p>
-     * <p>
-     *     Date, BigInteger, BigDecimal and JodaTime types are not considered as Primitive Value Types.
-     * </p>
-     *
-     * @param object Object
-     * @return true if object is a primitive value or an array of primitive values
-     * @throws IllegalArgumentException if object is null
-     */
-    public static boolean isPrimitiveValue( Object object )
-    {
-        Objects.requireNonNull( object, "object" );
-        if( object instanceof String
-            || object instanceof Character
-            || object instanceof Boolean
-            || object instanceof Integer
-            || object instanceof Double
-            || object instanceof Float
-            || object instanceof Long
-            || object instanceof Byte
-            || object instanceof Short )
-        {
-            return true;
-        }
-        if( object.getClass().isArray() )
-        {
-            return isArrayOfPrimitiveValues( object );
-        }
-        return false;
-    }
-
-    public static boolean isIdentity( Object object )
-    {
-        return object instanceof Identity;
-    }
-
-    private static boolean isArrayOfPrimitiveValues( Object array )
-    {
-        if( array instanceof String[]
-            || array instanceof char[] || array instanceof Character[]
-            || array instanceof boolean[] || array instanceof Boolean[]
-            || array instanceof int[] || array instanceof Integer[]
-            || array instanceof double[] || array instanceof Double[]
-            || array instanceof float[] || array instanceof Float[]
-            || array instanceof long[] || array instanceof Long[]
-            || array instanceof byte[] || array instanceof Byte[]
-            || array instanceof short[] || array instanceof Short[] )
-        {
-            return true;
-        }
-        return false;
-    }
-
-    public static boolean isPrimitiveValueType( ValueType valueType )
-    {
-        return isPrimitiveValueType( valueType.primaryType() );
-    }
-
-    /**
-     * @see ValueType#isPrimitiveValue(java.lang.Object)
-     * @param type Type
-     * @return true if object is a primitive value or an array of primitive values
-     */
-    public static boolean isPrimitiveValueType( Class<?> type )
-    {
-        Objects.requireNonNull( type, "type" );
-        if( String.class.isAssignableFrom( type ) )
-        {
-            return true;
-        }
-        if( type.isArray() )
-        {
-            return isPrimitiveValueType( type.getComponentType() );
-        }
-        return false;
-    }
     protected final List<Class<?>> types;
 
-    public ValueType( Class<?> type )
+    protected ValueType( Class<?>... types )
     {
-        this( Collections.singletonList( type ) );
+        this( Arrays.asList( types ) );
     }
 
-    @SuppressWarnings( "unchecked" )
-    public ValueType( List<Class<?>> types )
+    protected ValueType( List<Class<?>> types )
     {
         this.types = types;
     }
@@ -145,6 +95,21 @@ public class ValueType
     }
 
     @Override
+    public boolean equals( Object o )
+    {
+        if( this == o ) { return true; }
+        if( o == null || getClass() != o.getClass() ) { return false; }
+        ValueType valueType = (ValueType) o;
+        return Objects.equals( types, valueType.types );
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return Objects.hash( types );
+    }
+
+    @Override
     public String toString()
     {
         String name = types.stream().map( Class::getName ).collect( joining( "," ) );

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/main/java/org/apache/polygene/api/util/Collectors.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/util/Collectors.java b/core/api/src/main/java/org/apache/polygene/api/util/Collectors.java
index e419b21..906b062 100644
--- a/core/api/src/main/java/org/apache/polygene/api/util/Collectors.java
+++ b/core/api/src/main/java/org/apache/polygene/api/util/Collectors.java
@@ -17,6 +17,7 @@
  */
 package org.apache.polygene.api.util;
 
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
 import java.util.function.BinaryOperator;
@@ -24,13 +25,18 @@ import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.stream.Collector;
 
+/**
+ * Implementations of {@link Collector} missing from the JDK.
+ */
 public class Collectors
 {
     /**
      * Collect a single element.
+     *
+     * The Collector throws {@link IllegalArgumentException} if no or more than one element.
+     *
      * @param <T> Element type
-     * @return The single element
-     * @throws IllegalArgumentException if no or more than one element
+     * @return The single element collector
      */
     public static <T>
     Collector<T, ?, T> single()
@@ -46,13 +52,14 @@ public class Collectors
 
     /**
      * Eventually collect a single element.
+     *
+     * The Collector throws {@link IllegalArgumentException} if more than one element.
+     *
      * @param <T> Element type
-     * @return The single element, optional
-     * @throws IllegalArgumentException if more than one element
+     * @return The optional single element collector
      */
     public static <T>
     Collector<T, ?, Optional<T>> singleOrEmpty()
-        throws IllegalArgumentException
     {
         return java.util.stream.Collectors.reducing(
             ( left, right ) ->
@@ -69,6 +76,59 @@ public class Collectors
             } );
     }
 
+    /**
+     * Collect map entries into a {@link HashMap}.
+     *
+     * The Collector throws {@link NullPointerException} if one entry has a {@literal null} value.
+     * The Collector throws {@link IllegalStateException} if duplicate keys are found.
+     *
+     * @param <T> the Map entry type
+     * @param <K> the collected map key type
+     * @param <U> the collected map value type
+     * @return a {@code Collector} which collects elements into a {@code Map}
+     */
+    public static <T extends Map.Entry<K, U>, K, U>
+    Collector<T, ?, Map<K, U>> toMap()
+    {
+        return toMap( Map.Entry::getKey, Map.Entry::getValue, HashMap::new );
+    }
+
+    /**
+     * Collect map entries into a map.
+     *
+     * The Collector throws {@link NullPointerException} if one entry has a {@literal null} value.
+     * The Collector throws {@link IllegalStateException} if duplicate keys are found.
+     *
+     * @param <M> the type of the resulting {@code Map}
+     * @param <T> the Map entry type
+     * @param <K> the collected map key type
+     * @param <U> the collected map value type
+     * @param mapSupplier a function which returns a new, empty {@code Map} into
+     *                    which the results will be inserted
+     * @return The map collector
+     */
+    public static <T extends Map.Entry<K, U>, K, U, M extends Map<K, U>>
+    Collector<T, ?, M> toMap( Supplier<M> mapSupplier )
+    {
+        return toMap( Map.Entry::getKey, Map.Entry::getValue, mapSupplier );
+    }
+
+    /**
+     * Collect map entries into a map.
+     *
+     * The Collector throws {@link NullPointerException} if one entry has a {@literal null} value.
+     * The Collector throws {@link IllegalStateException} if duplicate keys are found.
+     *
+     * @param <M> the type of the resulting {@code Map}
+     * @param <T> the Map entry type
+     * @param <K> the collected map key type
+     * @param <U> the collected map value type
+     * @param keyMapper a mapping function to produce keys
+     * @param valueMapper a mapping function to produce values
+     * @param mapSupplier a function which returns a new, empty {@code Map} into
+     *                    which the results will be inserted
+     * @return The map collector
+     */
     public static <T, K, U, M extends Map<K, U>>
     Collector<T, ?, M> toMap( Function<? super T, ? extends K> keyMapper,
                               Function<? super T, ? extends U> valueMapper,
@@ -80,17 +140,86 @@ public class Collectors
                                                   mapSupplier );
     }
 
-
+    /**
+     * Collect map entries into a {@link HashMap}, allowing null values.
+     *
+     * The Collector throws {@link IllegalStateException} if duplicate keys are found.
+     *
+     * See https://bugs.openjdk.java.net/browse/JDK-8148463
+     *
+     * @param <T> the Map entry type
+     * @param <K> the collected map key type
+     * @param <U> the collected map value type
+     * @return The map collector
+     */
     public static <T extends Map.Entry<K, U>, K, U>
-    Collector<T, ?, Map<K, U>> toMap()
+    Collector<T, ?, Map<K, U>> toMapWithNullValues()
     {
-        return java.util.stream.Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue );
+        return toMapWithNullValues( Map.Entry::getKey, Map.Entry::getValue, HashMap::new );
     }
 
+    /**
+     * Collect map entries into a map, allowing null values.
+     *
+     * The Collector throws {@link IllegalStateException} if duplicate keys are found.
+     *
+     * See https://bugs.openjdk.java.net/browse/JDK-8148463
+     *
+     * @param <M> the type of the resulting {@code Map}
+     * @param <T> the Map entry type
+     * @param <K> the collected map key type
+     * @param <U> the collected map value type
+     * @param mapSupplier a function which returns a new, empty {@code Map} into
+     *                    which the results will be inserted
+     * @return The map collector
+     */
     public static <T extends Map.Entry<K, U>, K, U, M extends Map<K, U>>
-    Collector<T, ?, M> toMap( Supplier<M> mapSupplier )
+    Collector<T, ?, M> toMapWithNullValues( Supplier<M> mapSupplier )
     {
-        return toMap( Map.Entry::getKey, Map.Entry::getValue, mapSupplier );
+        return toMapWithNullValues( Map.Entry::getKey, Map.Entry::getValue, mapSupplier );
+    }
+
+    /**
+     * Collect map entries into a map, allowing null values.
+     *
+     * The Collector throws {@link IllegalStateException} if duplicate keys are found.
+     *
+     * See https://bugs.openjdk.java.net/browse/JDK-8148463
+     *
+     * @param <M> the type of the resulting {@code Map}
+     * @param <T> the Map entry type
+     * @param <K> the collected map key type
+     * @param <U> the collected map value type
+     * @param keyMapper a mapping function to produce keys
+     * @param valueMapper a mapping function to produce values
+     * @param mapSupplier a function which returns a new, empty {@code Map} into
+     *                    which the results will be inserted
+     * @return The map collector
+     */
+    public static <T, K, U, M extends Map<K, U>>
+    Collector<T, ?, M> toMapWithNullValues( Function<? super T, ? extends K> keyMapper,
+                                            Function<? super T, ? extends U> valueMapper,
+                                            Supplier<M> mapSupplier )
+    {
+        return java.util.stream.Collector
+            .of( mapSupplier,
+                 ( map, entry ) -> map.put( keyMapper.apply( entry ),
+                                            valueMapper.apply( entry ) ),
+                 ( left, right ) ->
+                 {
+                     M result = mapSupplier.get();
+                     result.putAll( left );
+                     for( Map.Entry<K, U> entry : right.entrySet() )
+                     {
+                         K key = entry.getKey();
+                         if( result.containsKey( key ) )
+                         {
+                             throw new IllegalStateException( String.format( "Duplicate key %s", key ) );
+                         }
+                         result.put( key, entry.getValue() );
+                     }
+                     return result;
+                 } );
     }
 
     private static <T> BinaryOperator<T> throwingMerger()

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/main/java/org/apache/polygene/api/value/MissingValueSerializationException.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/value/MissingValueSerializationException.java b/core/api/src/main/java/org/apache/polygene/api/value/MissingValueSerializationException.java
deleted file mode 100644
index 00b1af7..0000000
--- a/core/api/src/main/java/org/apache/polygene/api/value/MissingValueSerializationException.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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.apache.polygene.api.value;
-
-public class MissingValueSerializationException extends ValueSerializationException
-{
-    public MissingValueSerializationException()
-    {
-    }
-
-    public MissingValueSerializationException( String message )
-    {
-        super( message );
-    }
-
-    public MissingValueSerializationException( String message, Throwable cause )
-    {
-        super( message, cause );
-    }
-
-    public MissingValueSerializationException( Throwable cause )
-    {
-        super( cause );
-    }
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/main/java/org/apache/polygene/api/value/ValueDescriptor.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/value/ValueDescriptor.java b/core/api/src/main/java/org/apache/polygene/api/value/ValueDescriptor.java
index b9b3f54..8407fa9 100644
--- a/core/api/src/main/java/org/apache/polygene/api/value/ValueDescriptor.java
+++ b/core/api/src/main/java/org/apache/polygene/api/value/ValueDescriptor.java
@@ -20,19 +20,15 @@
 
 package org.apache.polygene.api.value;
 
-import org.apache.polygene.api.association.AssociationStateDescriptor;
 import org.apache.polygene.api.composite.CompositeDescriptor;
-import org.apache.polygene.api.composite.StatefulCompositeDescriptor;
+import org.apache.polygene.api.composite.StatefulAssociationCompositeDescriptor;
 import org.apache.polygene.api.type.ValueCompositeType;
 
 /**
  * Descriptor for ValueComposites.
  */
 public interface ValueDescriptor
-    extends CompositeDescriptor, StatefulCompositeDescriptor
+    extends CompositeDescriptor, StatefulAssociationCompositeDescriptor
 {
     ValueCompositeType valueType();
-
-    @Override
-    AssociationStateDescriptor state();
 }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/main/java/org/apache/polygene/api/value/ValueDeserializer.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/value/ValueDeserializer.java b/core/api/src/main/java/org/apache/polygene/api/value/ValueDeserializer.java
deleted file mode 100644
index 88afc3a..0000000
--- a/core/api/src/main/java/org/apache/polygene/api/value/ValueDeserializer.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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.apache.polygene.api.value;
-
-import java.io.InputStream;
-import java.util.function.Function;
-import org.apache.polygene.api.structure.ModuleDescriptor;
-import org.apache.polygene.api.type.ValueType;
-
-/**
- * Use a ValueDeserializer to create new values instances from serialized state.
- *
- * <p>
- * Serialized state must be one of:
- * </p>
- * <ul>
- * <li>a ValueComposite,</li>
- * <li>an EntityReference,</li>
- * <li>a Collection,</li>
- * <li>a Map,</li>
- * <li>a Plain Value.</li>
- * </ul>
- * <p>
- * Nested plain values, EntityReferences, Collections, Maps, ValueComposites are supported.
- * EntityReferences are deserialized as their reference string.
- * </p>
- * <p>
- * Plain 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>
- * <li>BigInteger,</li>
- * <li>BigDecimal,</li>
- * <li>Date,</li>
- * <li>DateTime (JodaTime),</li>
- * <li>LocalDateTime (JodaTime),</li>
- * <li>LocalDate (JodaTime).</li>
- * </ul>
- * <p>
- * Values of unknown types and all arrays are considered as {@link java.io.Serializable} and by so are deserialized
- * from base64 encoded bytes using pure Java serialization. If it happens that the input is invalid, a
- * ValueSerializationException is thrown.
- * </p>
- * <p>
- * Having type information in the serialized payload allows to keep actual ValueComposite types and by so
- * circumvent {@link org.apache.polygene.api.composite.AmbiguousTypeException} when deserializing.
- * </p>
- */
-public interface ValueDeserializer
-{
-
-    /**
-     * Factory method for a typed deserialize function.
-     *
-     * <p>The returned Function may throw {@link ValueSerializationException}.</p>
-     *
-     * @param <T>  the parametrized function return type
-     * @param module the module
-     * @param type the value type
-     *
-     * @return a deserialization function
-     */
-    <T> Function<String, T> deserialize( ModuleDescriptor module, Class<T> type );
-
-    /**
-     * Factory method for a typed deserialize function.
-     *
-     * <p>The returned Function may throw {@link ValueSerializationException}.</p>
-     *
-     * @param <T> the parametrized function return type
-     * @param module the module
-     * @param valueType the value type
-     *
-     * @return a deserialization function
-     */
-    <T> Function<String, T> deserialize( ModuleDescriptor module, ValueType valueType );
-
-    /**
-     * Factory method for an untyped deserialize function.
-     *
-     * <p>The returned Function may throw {@link ValueSerializationException}.</p>
-     *
-     * @param <T> the parametrized function return type
-     * @return a deserialization function
-     */
-//    <T> BiFunction<ValueType, String, T> deserialize();
-
-    /**
-     * Deserialize a value from a state.
-     *
-     * @param <T>   the parametrized returned type
-     * @param module the module
-     * @param type  the value type
-     * @param input the state
-     *
-     * @return the value
-     *
-     * @throws ValueSerializationException if the deserialization failed
-     */
-    <T> T deserialize( ModuleDescriptor module, Class<?> type, String input )
-        throws ValueSerializationException;
-
-    /**
-     * Deserialize a value from a state.
-     *
-     * @param <T>       the parametrized returned type
-     * @param module the module
-     * @param valueType the value type
-     * @param input     the state
-     *
-     * @return the value
-     *
-     * @throws ValueSerializationException if the deserialization failed
-     */
-    <T> T deserialize( ModuleDescriptor module, ValueType valueType, String input )
-        throws ValueSerializationException;
-
-    /**
-     * Deserialize a value from a state.
-     *
-     * @param <T>   the parametrized returned type
-     * @param module the module
-     * @param type  the value type
-     * @param input the state stream
-     *
-     * @return the value
-     *
-     * @throws ValueSerializationException if the deserialization failed
-     */
-    <T> T deserialize( ModuleDescriptor module, Class<?> type, InputStream input )
-        throws ValueSerializationException;
-
-    /**
-     * Deserialize a value from a state.
-     *
-     * @param <T>       the parametrized returned type
-     * @param module the module
-     * @param valueType the value type
-     * @param input     the state stream
-     *
-     * @return the value
-     *
-     * @throws ValueSerializationException if the deserialization failed
-     */
-    <T> T deserialize( ModuleDescriptor module, ValueType valueType, InputStream input )
-        throws ValueSerializationException;
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/main/java/org/apache/polygene/api/value/ValueSerialization.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/value/ValueSerialization.java b/core/api/src/main/java/org/apache/polygene/api/value/ValueSerialization.java
deleted file mode 100644
index 91c1081..0000000
--- a/core/api/src/main/java/org/apache/polygene/api/value/ValueSerialization.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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.apache.polygene.api.value;
-
-/**
- * ValueSerialization API.
- *
- * See {@link ValueSerializer} and {@link ValueDeserializer}.
- */
-public interface ValueSerialization
-    extends ValueSerializer, ValueDeserializer
-{
-
-    /**
-     * Serialization format @Service tags.
-     *
-     * <p>
-     *     ValueSerialization implementations should be tagged with theses at assembly time so that consumers can
-     *     specify which format they need.
-     * </p>
-     */
-    interface Formats
-    {
-
-        /**
-         * Tag a ValueSerialization service that support the JSON format.
-         */
-        String JSON = "json";
-        /**
-         * Tag a ValueSerialization service that support the XML format.
-         */
-        String XML = "xml";
-        /**
-         * Tag a ValueSerialization service that support the YAML format.
-         */
-        String YAML = "yaml";
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/main/java/org/apache/polygene/api/value/ValueSerializationException.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/value/ValueSerializationException.java b/core/api/src/main/java/org/apache/polygene/api/value/ValueSerializationException.java
deleted file mode 100644
index 62faa75..0000000
--- a/core/api/src/main/java/org/apache/polygene/api/value/ValueSerializationException.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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.apache.polygene.api.value;
-
-/**
- * Thrown when an error occur during value state (de)serialization.
- */
-public class ValueSerializationException
-    extends RuntimeException
-{
-
-    private static final long serialVersionUID = 1L;
-
-    public ValueSerializationException()
-    {
-        super();
-    }
-
-    public ValueSerializationException( String message )
-    {
-        super( message );
-    }
-
-    public ValueSerializationException( String message, Throwable cause )
-    {
-        super( message, cause );
-    }
-
-    public ValueSerializationException( Throwable cause )
-    {
-        super( cause.getClass().getName() + ": " + cause.getMessage(), cause );
-    }
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/main/java/org/apache/polygene/api/value/ValueSerializer.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/apache/polygene/api/value/ValueSerializer.java b/core/api/src/main/java/org/apache/polygene/api/value/ValueSerializer.java
deleted file mode 100644
index 736df9d..0000000
--- a/core/api/src/main/java/org/apache/polygene/api/value/ValueSerializer.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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.apache.polygene.api.value;
-
-import java.io.OutputStream;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Function;
-import org.apache.polygene.api.composite.AmbiguousTypeException;
-
-/**
- * Use a ValueSerializer to serialize values state.
- *
- * <p>
- *     Serialized object must be one of:
- * </p>
- * <ul>
- *     <li>a ValueComposite,</li>
- *     <li>an EntityComposite or EntityReference,</li>
- *     <li>an Iterable,</li>
- *     <li>a Map,</li>
- *     <li>a Plain Value.</li>
- * </ul>
- * <p>
- *     Nested plain values, EntityReferences, Iterables, Maps, ValueComposites and EntityComposites are supported.
- *     EntityComposites and EntityReferences are serialized as their reference string.
- * </p>
- * <p>
- *     Plain 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>
- *     <li>BigInteger,</li>
- *     <li>BigDecimal,</li>
- *     <li>Date,</li>
- *     <li>DateTime (JodaTime),</li>
- *     <li>LocalDateTime (JodaTime),</li>
- *     <li>LocalDate (JodaTime).</li>
- * </ul>
- * <p>
- *     Values of unknown types and all arrays are considered as {@link java.io.Serializable} and by so are serialized to
- *     base64 encoded bytes using pure Java serialization. If it happens that the value is not Serializable, a
- *     ValueSerializationException is thrown.
- * </p>
- * <p>
- *     Having type information in the serialized payload allows to keep actual ValueComposite types and by so
- *     circumvent {@link AmbiguousTypeException} when deserializing.
- * </p>
- */
-public interface ValueSerializer
-{
-
-    /**
-     * Factory method for a serialize function.
-     *
-     * @param <T> the parametrized function input type
-     * @return a serialization function.
-     */
-    <T> Function<T, String> serialize();
-
-    /**
-     * Factory method for a serialize function.
-     *
-     * @param <T> the parametrized function input type
-     * @param options ValueSerializer Options
-     * @return a serialization function.
-     */
-    <T> Function<T, String> serialize( Options options );
-
-    /**
-     * Serialize the state of a value with type information.
-     *
-     * @param object an Object to serialize
-     * @return the state
-     * @throws ValueSerializationException if the Value serialization failed
-     */
-    String serialize( Object object )
-        throws ValueSerializationException;
-
-    /**
-     * Serialize the state of a value.
-     *
-     * @param options ValueSerializer Options
-     * @param object an Object to serialize
-     * @return the state
-     * @throws ValueSerializationException if the Value serialization failed
-     */
-    String serialize( Options options, Object object )
-        throws ValueSerializationException;
-
-    /**
-     * Serialize the state of a value with type information.
-     *
-     * @param object an Object to serialize
-     * @param output that will be used as output
-     * @throws ValueSerializationException if the Value serialization failed
-     */
-    void serialize( Object object, OutputStream output )
-        throws ValueSerializationException;
-
-    /**
-     * Serialize the state of a value.
-     *
-     * @param options ValueSerializer Options
-     * @param object an Object to serialize
-     * @param output that will be used as output
-     * @throws ValueSerializationException if the Value serialization failed
-     */
-    void serialize( Options options, Object object, OutputStream output )
-        throws ValueSerializationException;
-
-    /**
-     * Serialization options.
-     */
-    final class Options
-    {
-        /**
-         * Boolean flag to include type information.
-         * Default to TRUE.
-         */
-        public static final String INCLUDE_TYPE_INFO = "includeTypeInfo";
-        public static final String MAP_ENTRIES_AS_OBJECTS = "mapentriesasobjects";
-        private final Map<String, String> options = new HashMap<>();
-
-        /**
-         * Create new default ValueSerializer Options.
-         */
-        public Options()
-        {
-            this.options.put( INCLUDE_TYPE_INFO, "true" );
-            this.options.put( MAP_ENTRIES_AS_OBJECTS, "false" );
-        }
-
-        /**
-         * Set {@link #INCLUDE_TYPE_INFO} option to TRUE.
-         * @return This
-         */
-        public Options withTypeInfo()
-        {
-            return put( INCLUDE_TYPE_INFO, true );
-        }
-
-        /**
-         * Set {@link #INCLUDE_TYPE_INFO} option to FALSE.
-         * @return This
-         */
-        public Options withoutTypeInfo()
-        {
-            return put( INCLUDE_TYPE_INFO, false );
-        }
-
-        public Options withMapEntriesAsObjects()
-        {
-            return put( MAP_ENTRIES_AS_OBJECTS, true );
-        }
-
-        public Options withMapEntriesAsKeyValuePairs()
-        {
-            return put( MAP_ENTRIES_AS_OBJECTS, false );
-        }
-
-        /**
-         * Get Boolean option value.
-         * @param option The option
-         * @return The boolean value of the option, or null if absent
-         */
-        public Boolean getBoolean( String option )
-        {
-            if( !options.containsKey( option ) )
-            {
-                return null;
-            }
-            return Boolean.valueOf( options.get( option ) );
-        }
-
-        /**
-         * Get Integer option value.
-         * @param option The option
-         * @return The integer value of the option, or null if absent
-         */
-        public Integer getInteger( String option )
-        {
-            if( !options.containsKey( option ) )
-            {
-                return null;
-            }
-            return Integer.valueOf( options.get( option ) );
-        }
-
-        /**
-         * Get String option value.
-         * @param option The option
-         * @return The string value of the option, or null if absent
-         */
-        public String getString( String option )
-        {
-            return options.get( option );
-        }
-
-        /**
-         * Put an option String value.
-         * @param option The option
-         * @param value The value
-         * @return This Options instance
-         */
-        public Options put( String option, String value )
-        {
-            if( value == null )
-            {
-                return remove( option );
-            }
-            options.put( option, value );
-            return this;
-        }
-
-        /**
-         * Put an option boolean value.
-         * @param option The option
-         * @param value The value
-         * @return This Options instance
-         */
-        public Options put( String option, Boolean value )
-        {
-            if( value == null )
-            {
-                return remove( option );
-            }
-            options.put( option, Boolean.toString( value ) );
-            return this;
-        }
-
-        /**
-         * Put an option Integer value.
-         * @param option The option
-         * @param value The value
-         * @return This Options instance
-         */
-        public Options put( String option, Integer value )
-        {
-            if( value == null )
-            {
-                return remove( option );
-            }
-            options.put( option, value.toString() );
-            return this;
-        }
-
-        /**
-         * Remove an option value.
-         * @param option The option
-         * @return This Options instance
-         */
-        public Options remove( String option )
-        {
-            options.remove( option );
-            return this;
-        }
-
-        /**
-         * Get all defined options as a Map.
-         * @return All defined options in a new Map
-         */
-        public Map<String, String> toMap()
-        {
-            return new HashMap<>( options );
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/test/java/org/apache/polygene/api/type/HasTypesCollectorsTest.java
----------------------------------------------------------------------
diff --git a/core/api/src/test/java/org/apache/polygene/api/type/HasTypesCollectorsTest.java b/core/api/src/test/java/org/apache/polygene/api/type/HasTypesCollectorsTest.java
new file mode 100644
index 0000000..0e92245
--- /dev/null
+++ b/core/api/src/test/java/org/apache/polygene/api/type/HasTypesCollectorsTest.java
@@ -0,0 +1,131 @@
+package org.apache.polygene.api.type;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import org.apache.polygene.api.identity.Identity;
+import org.apache.polygene.api.identity.StringIdentity;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class HasTypesCollectorsTest
+{
+    @Test
+    public void selectMatchingTypes()
+    {
+        List<ValueType> valueTypes = Arrays.asList(
+            ValueType.of( String.class ),
+            ValueType.of( Integer.class ),
+            ValueType.of( Number.class )
+        );
+
+        List<ValueType> number = valueTypes.stream().collect( HasTypesCollectors.matchingTypes( Number.class ) );
+        assertThat( number.size(), is( 2 ) );
+        assertThat( number.get( 0 ), equalTo( ValueType.of( Number.class ) ) );
+        assertThat( number.get( 1 ), equalTo( ValueType.of( Integer.class ) ) );
+
+        List<ValueType> integer = valueTypes.stream().collect( HasTypesCollectors.matchingTypes( Integer.class ) );
+        assertThat( integer.size(), is( 1 ) );
+        assertThat( integer.get( 0 ), equalTo( ValueType.of( Integer.class ) ) );
+    }
+
+    @Test
+    public void selectMatchingType()
+    {
+        List<ValueType> valueTypes = Arrays.asList(
+            ValueType.of( String.class ),
+            ValueType.of( Double.class ),
+            ValueType.of( Integer.class )
+        );
+
+        Optional<ValueType> number = valueTypes.stream()
+                                               .collect( HasTypesCollectors.matchingType( Number.class ) );
+        assertTrue( number.isPresent() );
+        assertThat( number.get(), equalTo( ValueType.of( Double.class ) ) );
+
+        Optional<ValueType> integer = valueTypes.stream()
+                                                .collect( HasTypesCollectors.matchingType( Integer.class ) );
+        assertTrue( integer.isPresent() );
+        assertThat( integer.get(), equalTo( ValueType.of( Integer.class ) ) );
+    }
+
+    @Test
+    public void selectMatchingValueTypes()
+    {
+        List<ValueType> valueTypes = Arrays.asList(
+            ValueType.of( String.class ),
+            ValueType.of( Number.class, Integer.class ),
+            ValueType.of( Integer.class ),
+            ValueType.of( Number.class )
+        );
+
+        List<ValueType> number = valueTypes.stream()
+                                           .collect( HasTypesCollectors.matchingTypes( ValueType.of( Number.class ) ) );
+        System.out.println( number );
+        assertThat( number.size(), is( 2 ) );
+        assertThat( number.get( 0 ), equalTo( ValueType.of( Number.class ) ) );
+        assertThat( number.get( 1 ), equalTo( ValueType.of( Number.class, Integer.class ) ) );
+
+        List<ValueType> integer = valueTypes.stream()
+                                            .collect(
+                                                HasTypesCollectors.matchingTypes( ValueType.of( Integer.class ) ) );
+        assertThat( integer.size(), is( 2 ) );
+        assertThat( integer.get( 0 ), equalTo( ValueType.of( Integer.class ) ) );
+        assertThat( integer.get( 1 ), equalTo( ValueType.of( Number.class, Integer.class ) ) );
+
+        List<ValueType> both = valueTypes.stream()
+                                         .collect( HasTypesCollectors.matchingTypes( ValueType.of( Number.class,
+                                                                                                   Integer.class ) ) );
+        assertThat( both.size(), is( 1 ) );
+        assertThat( both.get( 0 ), equalTo( ValueType.of( Number.class, Integer.class ) ) );
+    }
+
+    @Test
+    public void selectMatchingValueType()
+    {
+        List<ValueType> valueTypes = Arrays.asList(
+            ValueType.of( String.class ),
+            ValueType.of( Number.class, Integer.class ),
+            ValueType.of( Integer.class ),
+            ValueType.of( Number.class )
+        );
+
+        Optional<ValueType> number = valueTypes.stream()
+                                               .collect(
+                                                   HasTypesCollectors.matchingType( ValueType.of( Number.class ) ) );
+        assertTrue( number.isPresent() );
+        assertThat( number.get(), equalTo( ValueType.of( Number.class ) ) );
+
+        Optional<ValueType> integer = valueTypes.stream()
+                                                .collect(
+                                                    HasTypesCollectors.matchingType( ValueType.of( Integer.class ) ) );
+        assertTrue( integer.isPresent() );
+        assertThat( integer.get(), equalTo( ValueType.of( Integer.class ) ) );
+
+        Optional<ValueType> both = valueTypes.stream()
+                                             .collect( HasTypesCollectors.matchingType( ValueType.of( Number.class,
+                                                                                                      Integer.class ) ) );
+        assertTrue( both.isPresent() );
+        assertThat( both.get(), equalTo( ValueType.of( Number.class, Integer.class ) ) );
+    }
+
+    @Test
+    public void selectClosestValueTypes()
+    {
+        List<ValueType> list = new ArrayList<ValueType>()
+        {{
+            add( ValueType.of( String.class ) );
+            add( ValueType.of( Identity.class ) );
+        }};
+
+        List<ValueType> result = list.stream()
+                                     .collect( HasTypesCollectors.closestTypes( StringIdentity.class ) );
+        assertThat( result.size(), is( 1 ) );
+        assertThat( result.get( 0 ), equalTo( ValueType.of( Identity.class ) ) );
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/test/java/org/apache/polygene/api/type/HasTypesPredicatesTest.java
----------------------------------------------------------------------
diff --git a/core/api/src/test/java/org/apache/polygene/api/type/HasTypesPredicatesTest.java b/core/api/src/test/java/org/apache/polygene/api/type/HasTypesPredicatesTest.java
new file mode 100644
index 0000000..fc87ae6
--- /dev/null
+++ b/core/api/src/test/java/org/apache/polygene/api/type/HasTypesPredicatesTest.java
@@ -0,0 +1,54 @@
+package org.apache.polygene.api.type;
+
+import java.time.LocalDate;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class HasTypesPredicatesTest
+{
+    @Test
+    public void hasEqualTypePredicate()
+    {
+        assertTrue( new HasEqualType<>( Integer.class )
+                        .test( ValueType.of( Integer.class ) ) );
+        assertTrue( new HasEqualType<>( Integer.class )
+                        .test( ValueType.of( String.class, Integer.class ) ) );
+        assertFalse( new HasEqualType<>( Number.class )
+                         .test( ValueType.of( String.class, Integer.class ) ) );
+        assertFalse( new HasEqualType<>( String.class )
+                         .test( ValueType.of( LocalDate.class, Integer.class ) ) );
+
+        assertTrue( new HasEqualType<>( ValueType.of( Integer.class ) )
+                        .test( ValueType.of( Integer.class ) ) );
+        assertTrue( new HasEqualType<>( ValueType.of( Integer.class ) )
+                        .test( ValueType.of( String.class, Integer.class ) ) );
+        assertFalse( new HasEqualType<>( ValueType.of( Number.class ) )
+                         .test( ValueType.of( String.class, Integer.class ) ) );
+        assertFalse( new HasEqualType<>( ValueType.of( String.class ) )
+                         .test( ValueType.of( LocalDate.class, Integer.class ) ) );
+    }
+
+    @Test
+    public void hasAssignableTypePredicate()
+    {
+        assertTrue( new HasAssignableFromType<>( Number.class )
+                        .test( ValueType.of( String.class, Integer.class ) ) );
+        assertFalse( new HasAssignableFromType<>( Integer.class )
+                         .test( ValueType.of( Integer.class ) ) );
+        assertFalse( new HasAssignableFromType<>( String.class )
+                         .test( ValueType.of( LocalDate.class, Integer.class ) ) );
+    }
+
+    @Test
+    public void hasEqualOrAssignablePredicate()
+    {
+        assertTrue( new HasEqualOrAssignableFromType<>( Number.class )
+                        .test( ValueType.of( String.class, Integer.class ) ) );
+        assertTrue( new HasEqualOrAssignableFromType<>( Integer.class )
+                        .test( ValueType.of( Integer.class ) ) );
+        assertFalse( new HasEqualOrAssignableFromType<>( String.class )
+                         .test( ValueType.of( LocalDate.class, Integer.class ) ) );
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/test/java/org/apache/polygene/api/type/ValueTypeFactoryTest.java
----------------------------------------------------------------------
diff --git a/core/api/src/test/java/org/apache/polygene/api/type/ValueTypeFactoryTest.java b/core/api/src/test/java/org/apache/polygene/api/type/ValueTypeFactoryTest.java
new file mode 100644
index 0000000..5b70078
--- /dev/null
+++ b/core/api/src/test/java/org/apache/polygene/api/type/ValueTypeFactoryTest.java
@@ -0,0 +1,105 @@
+package org.apache.polygene.api.type;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+import org.apache.polygene.api.common.UseDefaults;
+import org.apache.polygene.api.property.Property;
+import org.apache.polygene.api.value.ValueDescriptor;
+import org.apache.polygene.bootstrap.ModuleAssembly;
+import org.apache.polygene.spi.module.ModuleSpi;
+import org.apache.polygene.spi.type.ValueTypeFactory;
+import org.apache.polygene.test.AbstractPolygeneTest;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertThat;
+
+public class ValueTypeFactoryTest extends AbstractPolygeneTest
+{
+    private ValueTypeFactory valueTypeFactory;
+
+    @Override
+    public void assemble( ModuleAssembly module )
+    {
+        module.values( SomeValue.class );
+    }
+
+    interface SomeValue
+    {
+        @UseDefaults
+        Property<List<String>> list();
+
+        @UseDefaults
+        Property<Map<String, Integer>> map();
+    }
+
+    @Before
+    public void setup()
+    {
+        valueTypeFactory = ( (ModuleSpi) module.instance() ).valueTypeFactory();
+    }
+
+    @Test
+    public void plainValues()
+    {
+        assertThat( valueTypeFactory.valueTypeOf( module, String.class ), equalTo( ValueType.STRING ) );
+        assertThat( valueTypeFactory.valueTypeOf( module, "" ), equalTo( ValueType.STRING ) );
+    }
+
+    @Test
+    public void enums()
+    {
+        assertThat( valueTypeFactory.valueTypeOf( module, TimeUnit.class ), instanceOf( EnumType.class ) );
+        assertThat( valueTypeFactory.valueTypeOf( module, TimeUnit.DAYS ), instanceOf( EnumType.class ) );
+    }
+
+    @Test
+    public void collections()
+    {
+        assertThat( valueTypeFactory.valueTypeOf( module, LinkedHashSet.class ),
+                    instanceOf( CollectionType.class ) );
+
+        List<String> list = new ArrayList<>();
+        ValueType listValueType = valueTypeFactory.valueTypeOf( module, list );
+        assertThat( listValueType, instanceOf( CollectionType.class ) );
+        assertThat( ( (CollectionType) listValueType ).collectedType(), equalTo( ValueType.OBJECT ) );
+    }
+
+    @Test
+    public void maps()
+    {
+        assertThat( valueTypeFactory.valueTypeOf( module, TreeMap.class ), instanceOf( MapType.class ) );
+
+        HashMap<String, Integer> map = new HashMap<>();
+        ValueType mapValueType = valueTypeFactory.valueTypeOf( module, map );
+        assertThat( mapValueType, instanceOf( MapType.class ) );
+        assertThat( ( (MapType) mapValueType ).keyType(), equalTo( ValueType.OBJECT ) );
+        assertThat( ( (MapType) mapValueType ).valueType(), equalTo( ValueType.OBJECT ) );
+    }
+
+    @Test
+    public void valueComposites()
+    {
+        assertThat( valueTypeFactory.valueTypeOf( module, SomeValue.class ),
+                    instanceOf( ValueCompositeType.class ) );
+        assertThat( valueTypeFactory.valueTypeOf( module, valueBuilderFactory.newValue( SomeValue.class ) ),
+                    instanceOf( ValueCompositeType.class ) );
+    }
+
+    @Test
+    public void genericsAreResolvedOnValueCompositeProperties()
+    {
+        ValueDescriptor descriptor = module.typeLookup().lookupValueModel( SomeValue.class );
+        assertThat( descriptor.state().findPropertyModelByName( "list" ).valueType(),
+                    equalTo( CollectionType.listOf( ValueType.STRING ) ) );
+        assertThat( descriptor.state().findPropertyModelByName( "map" ).valueType(),
+                    equalTo( MapType.of( ValueType.STRING, ValueType.INTEGER ) ) );
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/c9dd7229/core/api/src/test/java/org/apache/polygene/api/util/CollectorsTest.java
----------------------------------------------------------------------
diff --git a/core/api/src/test/java/org/apache/polygene/api/util/CollectorsTest.java b/core/api/src/test/java/org/apache/polygene/api/util/CollectorsTest.java
index 5521b93..1beb26a 100644
--- a/core/api/src/test/java/org/apache/polygene/api/util/CollectorsTest.java
+++ b/core/api/src/test/java/org/apache/polygene/api/util/CollectorsTest.java
@@ -17,59 +17,99 @@
  */
 package org.apache.polygene.api.util;
 
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.Optional;
 import java.util.stream.Stream;
 import org.junit.Test;
 
+import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
 public class CollectorsTest
 {
-    @Test
-    public void single()
+@Test
+public void single()
+{
+    assertThat( Stream.of( 1L ).collect( Collectors.single() ), is( 1L ) );
+
+    try
+    {
+        Stream.of().collect( Collectors.single() );
+        fail( "Should have failed" );
+    }
+    catch( IllegalArgumentException ex ) {}
+    try
     {
-        assertThat( Stream.of( 1L ).collect( Collectors.single() ), is( 1L ) );
+        Stream.of( 1, 1 ).collect( Collectors.single() );
+        fail( "Should have failed" );
+    }
+    catch( IllegalArgumentException ex ) {}
+    try
+    {
+        Stream.of( 1, 1, 1 ).collect( Collectors.single() );
+        fail( "Should have failed" );
+    }
+    catch( IllegalArgumentException ex ) {}
+}
 
-        try
-        {
-            Stream.of().collect( Collectors.single() );
-            fail( "Should have failed" );
-        }
-        catch( IllegalArgumentException ex ) {}
-        try
-        {
-            Stream.of( 1, 1 ).collect( Collectors.single() );
-            fail( "Should have failed" );
-        }
-        catch( IllegalArgumentException ex ) {}
-        try
-        {
-            Stream.of( 1, 1, 1 ).collect( Collectors.single() );
-            fail( "Should have failed" );
-        }
-        catch( IllegalArgumentException ex ) {}
+@Test
+public void singleOrEmpty()
+{
+    assertEquals( Optional.empty(), Stream.of().collect( Collectors.singleOrEmpty() ) );
+    assertEquals( Optional.of( 1 ), Stream.of( 1 ).collect( Collectors.singleOrEmpty() ) );
+
+    try
+    {
+        Stream.of( 1, 1 ).collect( Collectors.singleOrEmpty() );
+        fail( "Should have failed" );
     }
+    catch( IllegalArgumentException ex ) {}
+    try
+    {
+        Stream.of( 1, 1, 1 ).collect( Collectors.singleOrEmpty() );
+        fail( "Should have failed" );
+    }
+    catch( IllegalArgumentException ex ) {}
+}
 
     @Test
-    public void singleOrEmpty()
+    public void toMap()
     {
-        assertEquals( Optional.empty(), Stream.of().collect( Collectors.singleOrEmpty() ) );
-        assertEquals( Optional.of( 1 ), Stream.of( 1 ).collect( Collectors.singleOrEmpty() ) );
+        Map<String, String> input = new LinkedHashMap<>();
+        input.put( "foo", "bar" );
+        input.put( "bazar", "cathedral" );
+        Map<String, String> output = input.entrySet().stream().collect( Collectors.toMap() );
+        assertThat( output.get( "foo" ), equalTo( "bar" ) );
+        assertThat( output.get( "bazar" ), equalTo( "cathedral" ) );
+    }
 
+    @Test
+    public void toMapRejectNullValues()
+    {
+        Map<String, String> input = new LinkedHashMap<>();
+        input.put( "foo", "bar" );
+        input.put( "bazar", null );
         try
         {
-            Stream.of( 1, 1 ).collect( Collectors.singleOrEmpty() );
-            fail( "Should have failed" );
-        }
-        catch( IllegalArgumentException ex ) {}
-        try
-        {
-            Stream.of( 1, 1, 1 ).collect( Collectors.singleOrEmpty() );
-            fail( "Should have failed" );
+            input.entrySet().stream().collect( Collectors.toMap() );
+            fail( "Should have failed, that's the default Map::merge behaviour" );
         }
-        catch( IllegalArgumentException ex ) {}
+        catch( NullPointerException expected ) {}
+    }
+
+    @Test
+    public void toMapWithNullValues()
+    {
+        Map<String, String> input = new LinkedHashMap<>();
+        input.put( "foo", "bar" );
+        input.put( "bazar", null );
+        Map<String, String> output = input.entrySet().stream().collect( Collectors.toMapWithNullValues() );
+        assertThat( output.get( "foo" ), equalTo( "bar" ) );
+        assertThat( output.get( "bazar" ), nullValue() );
     }
 }