polygene-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From paulmer...@apache.org
Subject [15/19] polygene-java git commit: Serialization 3 step 1
Date Fri, 03 Feb 2017 07:58:16 GMT
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/e4cca11e/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSerializationService.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSerializationService.java b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSerializationService.java
new file mode 100644
index 0000000..2b662ea
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSerializationService.java
@@ -0,0 +1,533 @@
+/*
+ *  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.serialization.javaxjson;
+
+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.function.BiFunction;
+import java.util.function.Function;
+import javax.json.Json;
+import javax.json.JsonNumber;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import org.apache.polygene.api.entity.EntityReference;
+import org.apache.polygene.api.identity.Identity;
+import org.apache.polygene.api.identity.StringIdentity;
+import org.apache.polygene.api.injection.scope.This;
+import org.apache.polygene.api.injection.scope.Uses;
+import org.apache.polygene.api.mixin.Mixins;
+import org.apache.polygene.api.serialization.SerializationException;
+import org.apache.polygene.api.service.ServiceActivation;
+import org.apache.polygene.api.service.ServiceDescriptor;
+import org.apache.polygene.api.type.ValueType;
+
+// TODO Move into JavaxJsonSerialization
+// TODO Do the same on XML & MessagePack
+@Mixins( JavaxJsonSerializationService.Activation.class )
+public interface JavaxJsonSerializationService extends JavaxJsonSerialization, ServiceActivation
+{
+    class Activation implements ServiceActivation
+    {
+        @Uses
+        private ServiceDescriptor descriptor;
+
+        @This
+        private JavaxJsonAdapters adapters;
+
+        private boolean registrationDone = false;
+
+        @Override
+        public void activateService()
+        {
+            if( !registrationDone )
+            {
+                registerCustomAdapters();
+                registerBaseAdapters();
+                registrationDone = true;
+            }
+        }
+
+        @Override
+        public void passivateService() {}
+
+        private void registerCustomAdapters()
+        {
+            JavaxJsonSettings.orDefault( descriptor.metaInfo( JavaxJsonSettings.class ) )
+                             .getAdapters()
+                             .forEach( ( valueType, adapter ) -> adapters.registerAdapter( valueType, adapter ) );
+        }
+
+        private void registerBaseAdapters()
+        {
+            // Primitive Value types
+            adapters.registerAdapter( ValueType.STRING, new StringAdapter() );
+            adapters.registerAdapter( ValueType.CHARACTER, new CharacterAdapter() );
+            adapters.registerAdapter( ValueType.BOOLEAN, new BooleanAdapter() );
+            adapters.registerAdapter( ValueType.INTEGER, new IntegerAdapter() );
+            adapters.registerAdapter( ValueType.LONG, new LongAdapter() );
+            adapters.registerAdapter( ValueType.SHORT, new ShortAdapter() );
+            adapters.registerAdapter( ValueType.BYTE, new ByteAdapter() );
+            adapters.registerAdapter( ValueType.FLOAT, new FloatAdapter() );
+            adapters.registerAdapter( ValueType.DOUBLE, new DoubleAdapter() );
+
+            // Number types
+            adapters.registerAdapter( ValueType.BIG_DECIMAL, new BigDecimalAdapter() );
+            adapters.registerAdapter( ValueType.BIG_INTEGER, new BigIntegerAdapter() );
+
+            // Date types
+            adapters.registerAdapter( ValueType.INSTANT, new InstantAdapter() );
+            adapters.registerAdapter( ValueType.ZONED_DATE_TIME, new ZonedDateTimeAdapter() );
+            adapters.registerAdapter( ValueType.OFFSET_DATE_TIME, new OffsetDateTimeAdapter() );
+            adapters.registerAdapter( ValueType.LOCAL_DATE_TIME, new LocalDateTimeAdapter() );
+            adapters.registerAdapter( ValueType.LOCAL_DATE, new LocalDateAdapter() );
+            adapters.registerAdapter( ValueType.LOCAL_TIME, new LocalTimeAdapter() );
+            adapters.registerAdapter( ValueType.DURATION, new DurationAdapter() );
+            adapters.registerAdapter( ValueType.PERIOD, new PeriodAdapter() );
+
+            // Other supported types
+            adapters.registerAdapter( ValueType.IDENTITY, new IdentityAdapter() );
+            adapters.registerAdapter( ValueType.ENTITY_REFERENCE, new EntityReferenceAdapter() );
+        }
+
+        private static abstract class ToJsonStringAdapter<T> implements JavaxJsonAdapter<T>
+        {
+            @Override
+            public JsonValue serialize( Object object, Function<Object, JsonValue> serializeFunction )
+            {
+                return JavaxJson.toJsonString( object );
+            }
+        }
+
+        private static class StringAdapter extends ToJsonStringAdapter<String>
+        {
+            @Override
+            public Class<String> type() { return String.class; }
+
+            @Override
+            public String deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                return JavaxJson.asString( json );
+            }
+        }
+
+        private static class CharacterAdapter extends ToJsonStringAdapter<Character>
+        {
+            @Override
+            public Class<Character> type() { return Character.class; }
+
+            @Override
+            public Character deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                String string = JavaxJson.asString( json );
+                return string.isEmpty() ? null : string.charAt( 0 );
+            }
+        }
+
+        private static class BooleanAdapter implements JavaxJsonAdapter<Boolean>
+        {
+            @Override
+            public Class<Boolean> type() { return Boolean.class; }
+
+            @Override
+            public JsonValue serialize( Object object, Function<Object, JsonValue> serializeFunction )
+            {
+                return type().cast( object ) ? JsonValue.TRUE : JsonValue.FALSE;
+            }
+
+            @Override
+            public Boolean deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                switch( json.getValueType() )
+                {
+                    case TRUE:
+                        return true;
+                    case FALSE:
+                        return false;
+                    case NULL:
+                        return null;
+                    case NUMBER:
+                        return ( (JsonNumber) json ).doubleValue() > 0;
+                    case STRING:
+                        return Boolean.valueOf( ( (JsonString) json ).getString() );
+                    default:
+                        throw new SerializationException( "Don't know how to deserialize Boolean from " + json );
+                }
+            }
+        }
+
+        private static class IntegerAdapter implements JavaxJsonAdapter<Integer>
+        {
+            @Override
+            public Class<Integer> type() { return Integer.class; }
+
+            @Override
+            public JsonValue serialize( Object object, Function<Object, JsonValue> serializeFunction )
+            {
+                return Json.createObjectBuilder().add( "value", type().cast( object ) ).build()
+                           .getJsonNumber( "value" );
+            }
+
+            @Override
+            public Integer deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                switch( json.getValueType() )
+                {
+                    case NULL:
+                        return null;
+                    case NUMBER:
+                        return ( (JsonNumber) json ).intValueExact();
+                    case STRING:
+                        String string = ( (JsonString) json ).getString();
+                        return string.isEmpty() ? 0 : Integer.parseInt( string );
+                    default:
+                        throw new SerializationException( "Don't know how to deserialize Integer from " + json );
+                }
+            }
+        }
+
+        private static class LongAdapter implements JavaxJsonAdapter<Long>
+        {
+            @Override
+            public Class<Long> type() { return Long.class; }
+
+            @Override
+            public JsonValue serialize( Object object, Function<Object, JsonValue> serializeFunction )
+            {
+                return Json.createObjectBuilder().add( "value", type().cast( object ) ).build().getJsonNumber(
+                    "value" );
+            }
+
+            @Override
+            public Long deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                switch( json.getValueType() )
+                {
+                    case NULL:
+                        return null;
+                    case NUMBER:
+                        return ( (JsonNumber) json ).longValueExact();
+                    case STRING:
+                        String string = ( (JsonString) json ).getString();
+                        return string.isEmpty() ? 0L : Long.parseLong( string );
+                    default:
+                        throw new SerializationException( "Don't know how to deserialize Long from " + json );
+                }
+            }
+        }
+
+        private static class ShortAdapter implements JavaxJsonAdapter<Short>
+        {
+            @Override
+            public Class<Short> type() { return Short.class; }
+
+            @Override
+            public JsonValue serialize( Object object, Function<Object, JsonValue> serializeFunction )
+            {
+                return Json.createObjectBuilder().add( "value", type().cast( object ) ).build()
+                           .getJsonNumber( "value" );
+            }
+
+            @Override
+            public Short deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                switch( json.getValueType() )
+                {
+                    case NULL:
+                        return null;
+                    case NUMBER:
+                        return (short) ( (JsonNumber) json ).intValueExact();
+                    case STRING:
+                        String string = ( (JsonString) json ).getString();
+                        return string.isEmpty() ? 0 : Short.parseShort( string );
+                    default:
+                        throw new SerializationException( "Don't know how to deserialize Short from " + json );
+                }
+            }
+        }
+
+        private static class ByteAdapter implements JavaxJsonAdapter<Byte>
+        {
+            @Override
+            public Class<Byte> type() { return Byte.class; }
+
+            @Override
+            public JsonValue serialize( Object object, Function<Object, JsonValue> serializeFunction )
+            {
+                return Json.createObjectBuilder().add( "value", type().cast( object ) ).build()
+                           .getJsonNumber( "value" );
+            }
+
+            @Override
+            public Byte deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                switch( json.getValueType() )
+                {
+                    case NULL:
+                        return null;
+                    case NUMBER:
+                        return (byte) ( (JsonNumber) json ).intValueExact();
+                    case STRING:
+                        String string = ( (JsonString) json ).getString();
+                        return string.isEmpty() ? 0 : Byte.parseByte( string );
+                    default:
+                        throw new SerializationException( "Don't know how to deserialize Byte from " + json );
+                }
+            }
+        }
+
+        private static class FloatAdapter implements JavaxJsonAdapter<Float>
+        {
+            @Override
+            public Class<Float> type() { return Float.class; }
+
+            @Override
+            public JsonValue serialize( Object object, Function<Object, JsonValue> serializeFunction )
+            {
+                return Json.createObjectBuilder().add( "value", type().cast( object ) ).build()
+                           .getJsonNumber( "value" );
+            }
+
+            @Override
+            public Float deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                switch( json.getValueType() )
+                {
+                    case NULL:
+                        return null;
+                    case NUMBER:
+                        return (float) ( (JsonNumber) json ).doubleValue();
+                    case STRING:
+                        String string = ( (JsonString) json ).getString();
+                        return string.isEmpty() ? 0F : Float.parseFloat( string );
+                    default:
+                        throw new SerializationException( "Don't know how to deserialize Float from " + json );
+                }
+            }
+        }
+
+        private static class DoubleAdapter implements JavaxJsonAdapter<Double>
+        {
+            @Override
+            public Class<Double> type() { return Double.class; }
+
+            @Override
+            public JsonValue serialize( Object object, Function<Object, JsonValue> serializeFunction )
+            {
+                return Json.createObjectBuilder().add( "value", type().cast( object ) ).build()
+                           .getJsonNumber( "value" );
+            }
+
+            @Override
+            public Double deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                switch( json.getValueType() )
+                {
+                    case NULL:
+                        return null;
+                    case NUMBER:
+                        return ( (JsonNumber) json ).doubleValue();
+                    case STRING:
+                        String string = ( (JsonString) json ).getString();
+                        return string.isEmpty() ? 0D : Double.parseDouble( string );
+                    default:
+                        throw new SerializationException( "Don't know how to deserialize Double from " + json );
+                }
+            }
+        }
+
+        private static class BigDecimalAdapter extends ToJsonStringAdapter<BigDecimal>
+        {
+            @Override
+            public Class<BigDecimal> type() { return BigDecimal.class; }
+
+            @Override
+            public BigDecimal deserialize( JsonValue json,
+                                           BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                switch( json.getValueType() )
+                {
+                    case NULL:
+                        return null;
+                    case NUMBER:
+                        return new BigDecimal( json.toString() );
+                    case STRING:
+                        return new BigDecimal( ( (JsonString) json ).getString() );
+                    default:
+                        throw new SerializationException(
+                            "Don't know how to deserialize BigDecimal from " + json );
+                }
+            }
+        }
+
+        private static class BigIntegerAdapter extends ToJsonStringAdapter<BigInteger>
+        {
+            @Override
+            public Class<BigInteger> type() { return BigInteger.class; }
+
+            @Override
+            public BigInteger deserialize( JsonValue json,
+                                           BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                switch( json.getValueType() )
+                {
+                    case NULL:
+                        return null;
+                    case NUMBER:
+                        return new BigInteger( json.toString() );
+                    case STRING:
+                        return new BigInteger( ( (JsonString) json ).getString() );
+                    default:
+                        throw new SerializationException(
+                            "Don't know how to deserialize BigInteger from " + json );
+                }
+            }
+        }
+
+        private static class PeriodAdapter extends ToJsonStringAdapter<Period>
+        {
+            @Override
+            public Class<Period> type() { return Period.class; }
+
+            @Override
+            public Period deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                return Period.parse( JavaxJson.asString( json ) );
+            }
+        }
+
+        private static class DurationAdapter extends ToJsonStringAdapter<Duration>
+        {
+            @Override
+            public Class<Duration> type() { return Duration.class; }
+
+            @Override
+            public Duration deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                return Duration.parse( JavaxJson.asString( json ) );
+            }
+        }
+
+        private static class LocalTimeAdapter extends ToJsonStringAdapter<LocalTime>
+        {
+            @Override
+            public Class<LocalTime> type() { return LocalTime.class; }
+
+            @Override
+            public LocalTime deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                return LocalTime.parse( JavaxJson.asString( json ) );
+            }
+        }
+
+        private static class LocalDateAdapter extends ToJsonStringAdapter<LocalDate>
+        {
+            @Override
+            public Class<LocalDate> type() { return LocalDate.class; }
+
+            @Override
+            public LocalDate deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                return LocalDate.parse( JavaxJson.asString( json ) );
+            }
+        }
+
+        private static class LocalDateTimeAdapter extends ToJsonStringAdapter<LocalDateTime>
+        {
+            @Override
+            public Class<LocalDateTime> type() { return LocalDateTime.class; }
+
+            @Override
+            public LocalDateTime deserialize( JsonValue json,
+                                              BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                return LocalDateTime.parse( JavaxJson.asString( json ) );
+            }
+        }
+
+        private static class OffsetDateTimeAdapter extends ToJsonStringAdapter<OffsetDateTime>
+        {
+            @Override
+            public Class<OffsetDateTime> type() { return OffsetDateTime.class; }
+
+            @Override
+            public OffsetDateTime deserialize( JsonValue json,
+                                               BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                return OffsetDateTime.parse( JavaxJson.asString( json ) );
+            }
+        }
+
+        private static class ZonedDateTimeAdapter extends ToJsonStringAdapter<ZonedDateTime>
+        {
+            @Override
+            public Class<ZonedDateTime> type() { return ZonedDateTime.class; }
+
+            @Override
+            public ZonedDateTime deserialize( JsonValue json,
+                                              BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                return ZonedDateTime.parse( JavaxJson.asString( json ) );
+            }
+        }
+
+        private static class InstantAdapter extends ToJsonStringAdapter<Instant>
+        {
+            @Override
+            public Class<Instant> type() { return Instant.class; }
+
+            @Override
+            public Instant deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                return Instant.parse( JavaxJson.asString( json ) );
+            }
+        }
+
+        private static class IdentityAdapter extends ToJsonStringAdapter<Identity>
+        {
+            @Override
+            public Class<Identity> type() { return Identity.class; }
+
+            @Override
+            public Identity deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                return StringIdentity.fromString( JavaxJson.asString( json ) );
+            }
+        }
+
+        private static class EntityReferenceAdapter extends ToJsonStringAdapter<EntityReference>
+        {
+            @Override
+            public Class<EntityReference> type() { return EntityReference.class; }
+
+            @Override
+            public EntityReference deserialize( JsonValue json,
+                                                BiFunction<JsonValue, ValueType, Object> deserializeFunction )
+            {
+                return EntityReference.parseEntityReference( JavaxJson.asString( json ) );
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/e4cca11e/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSerializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSerializer.java b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSerializer.java
new file mode 100644
index 0000000..17f7ad2
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSerializer.java
@@ -0,0 +1,213 @@
+/*
+ *  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.serialization.javaxjson;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.UncheckedIOException;
+import java.util.Base64;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import org.apache.polygene.api.PolygeneAPI;
+import org.apache.polygene.api.association.AssociationStateHolder;
+import org.apache.polygene.api.composite.CompositeInstance;
+import org.apache.polygene.api.injection.scope.This;
+import org.apache.polygene.api.injection.scope.Uses;
+import org.apache.polygene.api.service.ServiceDescriptor;
+import org.apache.polygene.api.type.EnumType;
+import org.apache.polygene.api.type.MapType;
+import org.apache.polygene.api.type.ValueCompositeType;
+import org.apache.polygene.api.type.ValueType;
+import org.apache.polygene.api.value.ValueComposite;
+import org.apache.polygene.api.value.ValueDescriptor;
+import org.apache.polygene.spi.serialization.AbstractTextSerializer;
+import org.apache.polygene.spi.serialization.JsonSerializer;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.stream.Collectors.toList;
+import static org.apache.polygene.api.util.Collectors.toMap;
+
+public class JavaxJsonSerializer extends AbstractTextSerializer implements JsonSerializer
+{
+    @This
+    private JavaxJsonAdapters adapters;
+
+    @Uses
+    private ServiceDescriptor descriptor;
+
+    @Override
+    public <T> Function<T, JsonValue> toJsonFunction( Options options )
+    {
+        return object -> doSerialize( options, object, true );
+    }
+
+    private JsonValue doSerialize( Options options, Object object, boolean root )
+    {
+        if( object == null )
+        {
+            return JsonValue.NULL;
+        }
+        Class<?> objectClass = object.getClass();
+        JavaxJsonAdapter<?> adapter = adapters.adapterFor( objectClass );
+        if( adapter != null )
+        {
+            return adapter.serialize( object, obj -> doSerialize( options, obj, false ) );
+        }
+        if( EnumType.isEnum( objectClass ) )
+        {
+            return JavaxJson.toJsonString( object.toString() );
+        }
+        if( ValueCompositeType.isValueComposite( objectClass ) )
+        {
+            return serializeValueComposite( options, object, root );
+        }
+        if( MapType.isMap( objectClass ) )
+        {
+            return serializeMap( options, (Map<?, ?>) object );
+        }
+        if( Iterable.class.isAssignableFrom( objectClass ) )
+        {
+            return serializeIterable( options, (Iterable<?>) object );
+        }
+        if( Stream.class.isAssignableFrom( objectClass ) )
+        {
+            return serializeStream( options, (Stream<?>) object );
+        }
+        // Fallback to Java Serialization in Base 64
+        // Include all arrays!
+        return serializeBase64( object );
+    }
+
+    private JsonObject serializeValueComposite( Options options, Object composite, boolean root )
+    {
+        CompositeInstance instance = PolygeneAPI.FUNCTION_COMPOSITE_INSTANCE_OF.apply( (ValueComposite) composite );
+        ValueDescriptor descriptor = (ValueDescriptor) instance.descriptor();
+        AssociationStateHolder state = (AssociationStateHolder) instance.state();
+        ValueCompositeType valueType = descriptor.valueType();
+
+        JsonObjectBuilder builder = Json.createObjectBuilder();
+        valueType.properties().forEach(
+            property -> builder.add(
+                property.qualifiedName().name(),
+                doSerialize( options, state.propertyFor( property.accessor() ).get(), false ) ) );
+        valueType.associations().forEach(
+            association -> builder.add(
+                association.qualifiedName().name(),
+                doSerialize( options, state.associationFor( association.accessor() ).reference(), false ) ) );
+        valueType.manyAssociations().forEach(
+            association -> builder.add(
+                association.qualifiedName().name(),
+                doSerialize( options, state.manyAssociationFor( association.accessor() ).references()
+                                           .collect( toList() ),
+                             false ) ) );
+        valueType.namedAssociations().forEach(
+            association -> builder.add(
+                association.qualifiedName().name(),
+                doSerialize( options,
+                             state.namedAssociationFor( association.accessor() ).references()
+                                  .collect( toMap() ),
+                             false ) ) );
+        if( !root && options.includeTypeInfo() )
+        {
+            withTypeInfo( builder, valueType );
+        }
+        return builder.build();
+    }
+
+    private JsonObjectBuilder withTypeInfo( JsonObjectBuilder builder, ValueType valueType )
+    {
+        return builder.add( getTypeInfoPropertyName(), valueType.primaryType().getName() );
+    }
+
+    /**
+     * Map serialization.
+     *
+     * {@literal Map<String, ?>} are serialized to a {@literal JsonObject}.
+     * {@literal Map<?, ?>} are serialized to a {@literal JsonArray} or key/value {@literal JsonObject}s.
+     * Empty maps are serialized to an empty {@literal JsonObject}.
+     */
+    private JsonValue serializeMap( Options options, Map<?, ?> map )
+    {
+        if( map.isEmpty() )
+        {
+            // Defaults to {}
+            return Json.createObjectBuilder().build();
+        }
+        if( map.keySet().iterator().next() instanceof CharSequence )
+        {
+            JsonObjectBuilder builder = Json.createObjectBuilder();
+            map.entrySet().forEach( entry -> builder.add( entry.getKey().toString(),
+                                                          doSerialize( options, entry.getValue(), false ) ) );
+            return builder.build();
+        }
+        else
+        {
+            JsonArrayBuilder builder = Json.createArrayBuilder();
+            map.entrySet().forEach(
+                entry -> builder.add(
+                    Json.createObjectBuilder()
+                        .add( "key", doSerialize( options, entry.getKey(), false ) )
+                        .add( "value", doSerialize( options, entry.getValue(), false ) )
+                        .build() ) );
+            return builder.build();
+        }
+    }
+
+    private JsonArray serializeIterable( Options options, Iterable<?> iterable )
+    {
+        return serializeStream( options, StreamSupport.stream( iterable.spliterator(), false ) );
+    }
+
+    private <T> JsonArray serializeStream( Options options, Stream<?> stream )
+    {
+        JsonArrayBuilder builder = Json.createArrayBuilder();
+        stream.forEach( element -> builder.add( doSerialize( options, element, false ) ) );
+        return builder.build();
+    }
+
+    private JsonString serializeBase64( Object object )
+    {
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+        try( ObjectOutputStream out = new ObjectOutputStream( bout ) )
+        {
+            out.writeUnshared( object );
+            byte[] bytes = Base64.getEncoder().encode( bout.toByteArray() );
+            return JavaxJson.toJsonString( new String( bytes, UTF_8 ) );
+        }
+        catch( IOException ex )
+        {
+            throw new UncheckedIOException( ex );
+        }
+    }
+
+    private String getTypeInfoPropertyName()
+    {
+        return JavaxJsonSettings.orDefault( descriptor.metaInfo( JavaxJsonSettings.class ) )
+                                .getTypeInfoPropertyName();
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/e4cca11e/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSettings.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSettings.java b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSettings.java
new file mode 100644
index 0000000..266bd99
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSettings.java
@@ -0,0 +1,73 @@
+/*
+ *  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.serialization.javaxjson;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.apache.polygene.api.type.ValueType;
+
+public class JavaxJsonSettings
+{
+    public static final JavaxJsonSettings DEFAULT = new JavaxJsonSettings();
+
+    public static JavaxJsonSettings orDefault( JavaxJsonSettings settings )
+    {
+        return settings != null ? settings : DEFAULT;
+    }
+
+    private String typeInfoPropertyName;
+    private Map<ValueType, JavaxJsonAdapter<?>> adapters;
+
+    public JavaxJsonSettings()
+    {
+        typeInfoPropertyName = "_type";
+        adapters = new LinkedHashMap<>();
+    }
+
+    public String getTypeInfoPropertyName()
+    {
+        return typeInfoPropertyName;
+    }
+
+    public void setTypeInfoPropertyName( String typeInfoPropertyName )
+    {
+        this.typeInfoPropertyName = typeInfoPropertyName;
+    }
+
+    public Map<ValueType, JavaxJsonAdapter<?>> getAdapters()
+    {
+        return adapters;
+    }
+
+    public JavaxJsonSettings withTypeInfoPropertyName( String typeInfoPropertyName )
+    {
+        setTypeInfoPropertyName( typeInfoPropertyName );
+        return this;
+    }
+
+    public JavaxJsonSettings withJsonAdapter( ValueType valueType, JavaxJsonAdapter<?> adapter )
+    {
+        getAdapters().put( valueType, adapter );
+        return this;
+    }
+
+    public JavaxJsonSettings withJsonAdapter( JavaxJsonAdapter<?> adapter )
+    {
+        return withJsonAdapter( ValueType.of( adapter.type() ), adapter );
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/e4cca11e/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/package.html
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/package.html b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/package.html
new file mode 100644
index 0000000..43db1d9
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/serialization/javaxjson/package.html
@@ -0,0 +1,24 @@
+<!--
+  ~  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.
+  ~
+  ~
+  -->
+<html>
+    <body>
+        <h2>javax.json Serialization.</h2>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/e4cca11e/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/JSONEntityState.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/JSONEntityState.java b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/JSONEntityState.java
index 6bbef04..e4b5c05 100644
--- a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/JSONEntityState.java
+++ b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/JSONEntityState.java
@@ -14,28 +14,32 @@
  *  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.spi.entitystore.helpers;
 
 import java.time.Instant;
+import java.util.Map;
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonValue;
 import org.apache.polygene.api.common.QualifiedName;
 import org.apache.polygene.api.entity.EntityDescriptor;
 import org.apache.polygene.api.entity.EntityReference;
 import org.apache.polygene.api.property.PropertyDescriptor;
+import org.apache.polygene.api.serialization.SerializationException;
 import org.apache.polygene.api.structure.ModuleDescriptor;
 import org.apache.polygene.api.type.ValueType;
-import org.apache.polygene.api.value.ValueSerialization;
-import org.apache.polygene.api.value.ValueSerializationException;
+import org.apache.polygene.api.value.ValueDescriptor;
+import org.apache.polygene.serialization.javaxjson.JavaxJson;
 import org.apache.polygene.spi.entity.EntityState;
 import org.apache.polygene.spi.entity.EntityStatus;
 import org.apache.polygene.spi.entity.ManyAssociationState;
 import org.apache.polygene.spi.entity.NamedAssociationState;
 import org.apache.polygene.spi.entitystore.EntityStoreException;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
+import org.apache.polygene.spi.serialization.JsonSerialization;
 
 /**
  * Standard JSON implementation of EntityState.
@@ -43,7 +47,6 @@ import org.json.JSONObject;
 public final class JSONEntityState
     implements EntityState
 {
-    private static final String[] EMPTY_NAMES = new String[ 0 ];
     private static final String[] CLONE_NAMES =
         {
             JSONKeys.IDENTITY,
@@ -54,27 +57,27 @@ public final class JSONEntityState
         };
 
     private final ModuleDescriptor module;
-    private final ValueSerialization valueSerialization;
     private final String version;
     private final EntityReference reference;
     private final EntityDescriptor entityDescriptor;
+    private final JsonSerialization serialization;
 
     private EntityStatus status;
     private Instant lastModified;
-    private JSONObject state;
+    private JsonObject state;
 
     /* package */ JSONEntityState( ModuleDescriptor module,
-                                   ValueSerialization valueSerialization,
+                                   JsonSerialization serialization,
                                    String version,
                                    Instant lastModified,
                                    EntityReference reference,
                                    EntityStatus status,
                                    EntityDescriptor entityDescriptor,
-                                   JSONObject state
+                                   JsonObject state
     )
     {
         this.module = module;
-        this.valueSerialization = valueSerialization;
+        this.serialization = serialization;
         this.version = version;
         this.lastModified = lastModified;
         this.reference = reference;
@@ -107,22 +110,45 @@ public final class JSONEntityState
     {
         try
         {
-            Object json = state.getJSONObject( JSONKeys.PROPERTIES ).opt( stateName.name() );
-            if( JSONObject.NULL.equals( json ) )
+            JsonValue json = state.getJsonObject( JSONKeys.PROPERTIES ).get( stateName.name() );
+            if( json == null || JsonValue.NULL.equals( json ) )
             {
                 return null;
             }
             else
             {
-                PropertyDescriptor descriptor = entityDescriptor.state().findPropertyModelByQualifiedName( stateName );
-                if( descriptor == null )
+                // TODO This rely on _type explicitely :(
+                // Needed because of this mess that is JsonEntityState
+                ValueType propertyValueType = null;
+                if( json.getValueType() == JsonValue.ValueType.OBJECT )
+                {
+                    String typeInfo = ( (JsonObject) json ).getString( "_type", null );
+                    if( typeInfo != null )
+                    {
+                        ValueDescriptor valueDescriptor = module.valueDescriptor( typeInfo );
+                        if( valueDescriptor != null )
+                        {
+                            propertyValueType = valueDescriptor.valueType();
+                        }
+                    }
+                }
+                if( propertyValueType == null )
+                {
+                    PropertyDescriptor descriptor = entityDescriptor.state()
+                                                                    .findPropertyModelByQualifiedName( stateName );
+                    if( descriptor != null )
+                    {
+                        propertyValueType = descriptor.valueType();
+                    }
+                }
+                if( propertyValueType == null )
                 {
                     return null;
                 }
-                return valueSerialization.deserialize( module, descriptor.valueType(), json.toString() );
+                return serialization.fromJson( module, propertyValueType, json );
             }
         }
-        catch( ValueSerializationException | JSONException e )
+        catch( SerializationException e )
         {
             throw new EntityStoreException( e );
         }
@@ -133,129 +159,44 @@ public final class JSONEntityState
     {
         try
         {
-            Object jsonValue;
-            if( newValue == null || ValueType.isPrimitiveValue( newValue ) )
-            {
-                jsonValue = newValue;
-            }
-            else if( ValueType.isIdentity( newValue ) )
-            {
-                jsonValue = newValue.toString();
-            }
-            else
-            {
-                String serialized = valueSerialization.serialize( newValue );
-                if( serialized.startsWith( "{" ) )
-                {
-                    jsonValue = new JSONObject( serialized );
-                }
-                else if( serialized.startsWith( "[" ) )
-                {
-                    jsonValue = new JSONArray( serialized );
-                }
-                else
-                {
-                    jsonValue = serialized;
-                }
-            }
-            cloneStateIfGlobalStateLoaded();
-            state.getJSONObject( JSONKeys.PROPERTIES ).put( stateName.name(), jsonValue );
+            JsonValue jsonValue = serialization.toJson( newValue );
+            stateCloneWithProperty( stateName.name(), jsonValue );
             markUpdated();
         }
-        catch( ValueSerializationException | JSONException e )
+        catch( SerializationException e )
         {
             throw new EntityStoreException( "Unable to set property " + stateName + " value " + newValue, e );
         }
     }
 
-    private JSONObject cloneJSON( JSONObject jsonObject )
-        throws JSONException
-    {
-        String[] names = JSONObject.getNames( jsonObject );
-        if( names == null )
-        {
-            names = EMPTY_NAMES;
-        }
-        return new JSONObject( jsonObject, names );
-    }
-
     @Override
     public EntityReference associationValueOf( QualifiedName stateName )
     {
-        try
-        {
-            Object jsonValue = state.getJSONObject( JSONKeys.ASSOCIATIONS ).opt( stateName.name() );
-            if( jsonValue == null )
-            {
-                return null;
-            }
-
-            EntityReference value = jsonValue == JSONObject.NULL
-                                    ? null
-                                    : EntityReference.parseEntityReference( (String) jsonValue );
-            return value;
-        }
-        catch( JSONException e )
+        String jsonValue = state.getJsonObject( JSONKeys.ASSOCIATIONS ).getString( stateName.name(), null );
+        if( jsonValue == null )
         {
-            throw new EntityStoreException( e );
+            return null;
         }
+        return EntityReference.parseEntityReference( jsonValue );
     }
 
     @Override
     public void setAssociationValue( QualifiedName stateName, EntityReference newEntity )
     {
-        try
-        {
-            cloneStateIfGlobalStateLoaded();
-            state.getJSONObject( JSONKeys.ASSOCIATIONS ).put( stateName.name(), newEntity == null
-                                                                                ? null
-                                                                                : newEntity.identity().toString() );
-            markUpdated();
-        }
-        catch( JSONException e )
-        {
-            throw new EntityStoreException( e );
-        }
+        stateCloneWithAssociation( stateName.name(), newEntity );
+        markUpdated();
     }
 
     @Override
     public ManyAssociationState manyAssociationValueOf( QualifiedName stateName )
     {
-        try
-        {
-            JSONObject manyAssociations = state.getJSONObject( JSONKeys.MANY_ASSOCIATIONS );
-            JSONArray jsonValues = manyAssociations.optJSONArray( stateName.name() );
-            if( jsonValues == null )
-            {
-                jsonValues = new JSONArray();
-                manyAssociations.put( stateName.name(), jsonValues );
-            }
-            return new JSONManyAssociationState( this, jsonValues );
-        }
-        catch( JSONException e )
-        {
-            throw new EntityStoreException( e );
-        }
+        return new JSONManyAssociationState( this, stateName.name() );
     }
 
     @Override
     public NamedAssociationState namedAssociationValueOf( QualifiedName stateName )
     {
-        try
-        {
-            JSONObject namedAssociations = state.getJSONObject( JSONKeys.NAMED_ASSOCIATIONS );
-            JSONObject jsonValues = namedAssociations.optJSONObject( stateName.name() );
-            if( jsonValues == null )
-            {
-                jsonValues = new JSONObject();
-                namedAssociations.put( stateName.name(), jsonValues );
-            }
-            return new JSONNamedAssociationState( this, jsonValues );
-        }
-        catch( JSONException e )
-        {
-            throw new EntityStoreException( e );
-        }
+        return new JSONNamedAssociationState( this, stateName.name() );
     }
 
     @Override
@@ -282,7 +223,7 @@ public final class JSONEntityState
         return entityDescriptor;
     }
 
-    public JSONObject state()
+    public JsonObject state()
     {
         return state;
     }
@@ -293,7 +234,7 @@ public final class JSONEntityState
         return reference + "(" + state + ")";
     }
 
-    public void markUpdated()
+    void markUpdated()
     {
         if( status == EntityStatus.LOADED )
         {
@@ -301,29 +242,217 @@ public final class JSONEntityState
         }
     }
 
-    void cloneStateIfGlobalStateLoaded()
+    void stateCloneWithVersionAndModified( String version, Instant lastModified )
+    {
+        JsonObjectBuilder builder = JavaxJson.toBuilder( state );
+        builder.add( JSONKeys.VERSION, version );
+        builder.add( JSONKeys.MODIFIED, lastModified.toEpochMilli() );
+        state = builder.build();
+    }
+
+    void stateCloneWithProperty( String stateName, JsonValue value )
     {
-        if( status != EntityStatus.LOADED )
+        JsonObjectBuilder builder = stateShallowClone();
+        JsonObjectBuilder propertiesBuilder = JavaxJson.toBuilder( state.getJsonObject( JSONKeys.PROPERTIES ) );
+        if( value == null )
+        {
+            propertiesBuilder.add( stateName, JsonValue.NULL );
+        }
+        else
         {
-            return;
+            propertiesBuilder.add( stateName, value );
         }
+        builder.add( JSONKeys.PROPERTIES, propertiesBuilder.build() );
+        builder.add( JSONKeys.ASSOCIATIONS, state.get( JSONKeys.ASSOCIATIONS ) );
+        builder.add( JSONKeys.MANY_ASSOCIATIONS, state.get( JSONKeys.MANY_ASSOCIATIONS ) );
+        builder.add( JSONKeys.NAMED_ASSOCIATIONS, state.get( JSONKeys.NAMED_ASSOCIATIONS ) );
+        state = builder.build();
+    }
 
-        try
+    void stateCloneWithAssociation( String stateName, EntityReference ref )
+    {
+        JsonObjectBuilder builder = stateShallowClone();
+        JsonObjectBuilder assocBuilder = JavaxJson.toBuilder( state.getJsonObject( JSONKeys.ASSOCIATIONS ) );
+        if( ref == null )
         {
-            JSONObject newProperties = cloneJSON( state.getJSONObject( JSONKeys.PROPERTIES ) );
-            JSONObject newAssoc = cloneJSON( state.getJSONObject( JSONKeys.ASSOCIATIONS ) );
-            JSONObject newManyAssoc = cloneJSON( state.getJSONObject( JSONKeys.MANY_ASSOCIATIONS ) );
-            JSONObject newNamedAssoc = cloneJSON( state.getJSONObject( JSONKeys.NAMED_ASSOCIATIONS ) );
-            JSONObject stateClone = new JSONObject( state, CLONE_NAMES );
-            stateClone.put( JSONKeys.PROPERTIES, newProperties );
-            stateClone.put( JSONKeys.ASSOCIATIONS, newAssoc );
-            stateClone.put( JSONKeys.MANY_ASSOCIATIONS, newManyAssoc );
-            stateClone.put( JSONKeys.NAMED_ASSOCIATIONS, newNamedAssoc );
-            state = stateClone;
+            assocBuilder.add( stateName, JsonValue.NULL );
         }
-        catch( JSONException e )
+        else
         {
-            throw new EntityStoreException( e );
+            assocBuilder.add( stateName, ref.identity().toString() );
+        }
+        builder.add( JSONKeys.PROPERTIES, state.get( JSONKeys.PROPERTIES ) );
+        builder.add( JSONKeys.ASSOCIATIONS, assocBuilder.build() );
+        builder.add( JSONKeys.MANY_ASSOCIATIONS, state.get( JSONKeys.MANY_ASSOCIATIONS ) );
+        builder.add( JSONKeys.NAMED_ASSOCIATIONS, state.get( JSONKeys.NAMED_ASSOCIATIONS ) );
+        state = builder.build();
+    }
+
+    void stateCloneAddManyAssociation( int idx, String stateName, EntityReference ref )
+    {
+        JsonObjectBuilder builder = stateShallowClone();
+        JsonObjectBuilder manyAssociations = Json.createObjectBuilder();
+        JsonObject previousManyAssociations = state.getJsonObject( JSONKeys.MANY_ASSOCIATIONS );
+        for( Map.Entry<String, JsonValue> previousManyAssociation : previousManyAssociations.entrySet() )
+        {
+            String key = previousManyAssociation.getKey();
+            if( !key.equals( stateName ) )
+            {
+                manyAssociations.add( key, previousManyAssociation.getValue() );
+            }
+        }
+        JsonValue previousReferences = previousManyAssociations.get( stateName );
+        JsonArrayBuilder references = Json.createArrayBuilder();
+        String newRef = ref.identity().toString();
+        if( previousReferences == null || previousReferences.getValueType() != JsonValue.ValueType.ARRAY )
+        {
+            references.add( newRef );
+        }
+        else
+        {
+            JsonArray previousReferencesArray = (JsonArray) previousReferences;
+            boolean insert = !previousReferencesArray.contains( newRef );
+            for( int i = 0; i < previousReferencesArray.size(); i++ )
+            {
+                if( insert && i == idx )
+                {
+                    references.add( newRef );
+                }
+                references.add( previousReferencesArray.getString( i ) );
+            }
+            if( insert && idx >= previousReferencesArray.size() )
+            {
+                references.add( newRef );
+            }
+        }
+        manyAssociations.add( stateName, references.build() );
+        builder.add( JSONKeys.PROPERTIES, state.get( JSONKeys.PROPERTIES ) );
+        builder.add( JSONKeys.ASSOCIATIONS, state.get( JSONKeys.ASSOCIATIONS ) );
+        builder.add( JSONKeys.MANY_ASSOCIATIONS, manyAssociations.build() );
+        builder.add( JSONKeys.NAMED_ASSOCIATIONS, state.get( JSONKeys.NAMED_ASSOCIATIONS ) );
+        state = builder.build();
+    }
+
+    void stateCloneRemoveManyAssociation( String stateName, EntityReference ref )
+    {
+        String stringRef = ref.identity().toString();
+        JsonObjectBuilder builder = stateShallowClone();
+        JsonObjectBuilder manyAssociations = Json.createObjectBuilder();
+        JsonObject previousManyAssociations = state.getJsonObject( JSONKeys.MANY_ASSOCIATIONS );
+        for( Map.Entry<String, JsonValue> previousManyAssociation : previousManyAssociations.entrySet() )
+        {
+            String key = previousManyAssociation.getKey();
+            if( !key.equals( stateName ) )
+            {
+                manyAssociations.add( key, previousManyAssociation.getValue() );
+            }
+        }
+        JsonValue previousReferences = previousManyAssociations.get( stateName );
+        JsonArrayBuilder references = Json.createArrayBuilder();
+        if( previousReferences != null && previousReferences.getValueType() == JsonValue.ValueType.ARRAY )
+        {
+            JsonArray previousReferencesArray = (JsonArray) previousReferences;
+            for( int idx = 0; idx < previousReferencesArray.size(); idx++ )
+            {
+                String previousRef = previousReferencesArray.getString( idx );
+                if( !stringRef.equals( previousRef ) )
+                {
+                    references.add( previousRef );
+                }
+            }
+        }
+        manyAssociations.add( stateName, references.build() );
+        builder.add( JSONKeys.PROPERTIES, state.get( JSONKeys.PROPERTIES ) );
+        builder.add( JSONKeys.ASSOCIATIONS, state.get( JSONKeys.ASSOCIATIONS ) );
+        builder.add( JSONKeys.MANY_ASSOCIATIONS, manyAssociations.build() );
+        builder.add( JSONKeys.NAMED_ASSOCIATIONS, state.get( JSONKeys.NAMED_ASSOCIATIONS ) );
+        state = builder.build();
+    }
+
+    void stateCloneAddNamedAssociation( String stateName, String name, EntityReference ref )
+    {
+        JsonObjectBuilder builder = stateShallowClone();
+        JsonObject previousNamedAssociations = state.getJsonObject( JSONKeys.NAMED_ASSOCIATIONS );
+        JsonObjectBuilder namedAssociations = Json.createObjectBuilder();
+        for( Map.Entry<String, JsonValue> previousNamedAssociation : previousNamedAssociations.entrySet() )
+        {
+            String key = previousNamedAssociation.getKey();
+            if( !key.equals( stateName ) )
+            {
+                namedAssociations.add( key, previousNamedAssociation.getValue() );
+            }
+        }
+        JsonValue previousReferences = previousNamedAssociations.get( stateName );
+        JsonObjectBuilder references = Json.createObjectBuilder();
+        String newRef = ref.identity().toString();
+        if( previousReferences == null || !( previousReferences instanceof JsonObject ) )
+        {
+            references.add( name, newRef );
+        }
+        else
+        {
+            JsonObject previousReferencesObject = (JsonObject) previousReferences;
+            for( Map.Entry<String, JsonValue> previousNamedReference : previousReferencesObject.entrySet() )
+            {
+                String key = previousNamedReference.getKey();
+                if( !key.equals( name ) )
+                {
+                    references.add( key, previousNamedReference.getValue() );
+                }
+            }
+            references.add( name, ref.identity().toString() );
+        }
+        namedAssociations.add( stateName, references.build() );
+        builder.add( JSONKeys.PROPERTIES, state.get( JSONKeys.PROPERTIES ) );
+        builder.add( JSONKeys.ASSOCIATIONS, state.get( JSONKeys.ASSOCIATIONS ) );
+        builder.add( JSONKeys.MANY_ASSOCIATIONS, state.get( JSONKeys.MANY_ASSOCIATIONS ) );
+        builder.add( JSONKeys.NAMED_ASSOCIATIONS, namedAssociations.build() );
+        state = builder.build();
+    }
+
+    void stateCloneRemoveNamedAssociation( String stateName, String name )
+    {
+        JsonObjectBuilder builder = stateShallowClone();
+        JsonObjectBuilder namedAssociations = Json.createObjectBuilder();
+        JsonObject previousNamedAssociations = state.getJsonObject( JSONKeys.NAMED_ASSOCIATIONS );
+        for( Map.Entry<String, JsonValue> previousNamedAssociation : previousNamedAssociations.entrySet() )
+        {
+            String key = previousNamedAssociation.getKey();
+            if( !key.equals( stateName ) )
+            {
+                namedAssociations.add( key, previousNamedAssociation.getValue() );
+            }
+        }
+        JsonValue previousReferences = previousNamedAssociations.get( stateName );
+        JsonObjectBuilder references = Json.createObjectBuilder();
+        if( previousReferences != null && previousReferences.getValueType() == JsonValue.ValueType.OBJECT )
+        {
+            JsonObject previousReferencesObject = (JsonObject) previousReferences;
+            for( Map.Entry<String, JsonValue> previousNamedRef : previousReferencesObject.entrySet() )
+            {
+                String previousName = previousNamedRef.getKey();
+                if( !name.equals( previousName ) )
+                {
+                    references.add( previousName, previousNamedRef.getValue() );
+                }
+            }
+        }
+        namedAssociations.add( stateName, references.build() );
+        builder.add( JSONKeys.PROPERTIES, state.get( JSONKeys.PROPERTIES ) );
+        builder.add( JSONKeys.ASSOCIATIONS, state.get( JSONKeys.ASSOCIATIONS ) );
+        builder.add( JSONKeys.MANY_ASSOCIATIONS, state.get( JSONKeys.MANY_ASSOCIATIONS ) );
+        builder.add( JSONKeys.NAMED_ASSOCIATIONS, namedAssociations.build() );
+        state = builder.build();
+    }
+
+    private JsonObjectBuilder stateShallowClone()
+    {
+        JsonObjectBuilder builder = Json.createObjectBuilder();
+        for( String cloneName : CLONE_NAMES )
+        {
+            JsonValue cloneValue = state.get( cloneName );
+            builder.add( cloneName, cloneValue == null ? JsonValue.NULL : cloneValue );
         }
+        return builder;
     }
 }

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/e4cca11e/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/JSONManyAssociationState.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/JSONManyAssociationState.java b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/JSONManyAssociationState.java
index b1efbc1..e9b99c4 100644
--- a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/JSONManyAssociationState.java
+++ b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/JSONManyAssociationState.java
@@ -21,39 +21,52 @@ package org.apache.polygene.spi.entitystore.helpers;
 
 import java.util.Iterator;
 import java.util.NoSuchElementException;
-import org.json.JSONArray;
-import org.json.JSONException;
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
 import org.apache.polygene.api.entity.EntityReference;
 import org.apache.polygene.spi.entity.ManyAssociationState;
 import org.apache.polygene.spi.entitystore.EntityStoreException;
 
 /**
  * JSON implementation of ManyAssociationState.
- * <p>Backed by a JSONArray.</p>
+ * <p>Backed by a JsonArray.</p>
  */
 public final class JSONManyAssociationState
     implements ManyAssociationState
 {
-
     private final JSONEntityState entityState;
-    private final JSONArray references;
+    private final String stateName;
 
-    public JSONManyAssociationState( JSONEntityState entityState, JSONArray references )
+    /* package */ JSONManyAssociationState( JSONEntityState entityState, String stateName )
     {
         this.entityState = entityState;
-        this.references = references;
+        this.stateName = stateName;
+    }
+
+    private JsonArray getReferences()
+    {
+        JsonObject manyAssociations = entityState.state().getJsonObject( JSONKeys.MANY_ASSOCIATIONS );
+        JsonValue references = manyAssociations.get( stateName );
+        if( references != null && references.getValueType() == JsonValue.ValueType.ARRAY )
+        {
+            return (JsonArray) references;
+        }
+        return Json.createArrayBuilder().build();
     }
 
     @Override
     public int count()
     {
-        return references.length();
+        return getReferences().size();
     }
 
     @Override
     public boolean contains( EntityReference entityReference )
     {
-        return indexOfReference( entityReference.toString() ) != -1;
+        return indexOfReference( entityReference.identity().toString() ) != -1;
     }
 
     @Override
@@ -65,12 +78,11 @@ public final class JSONManyAssociationState
             {
                 return false;
             }
-            entityState.cloneStateIfGlobalStateLoaded();
-            insertReference( idx, entityReference.identity().toString() );
+            entityState.stateCloneAddManyAssociation( idx, stateName, entityReference );
             entityState.markUpdated();
             return true;
         }
-        catch( JSONException e )
+        catch( JsonException e )
         {
             throw new EntityStoreException( e );
         }
@@ -82,8 +94,7 @@ public final class JSONManyAssociationState
         int refIndex = indexOfReference( entityReference.identity().toString() );
         if( refIndex != -1 )
         {
-            entityState.cloneStateIfGlobalStateLoaded();
-            references.remove( refIndex );
+            entityState.stateCloneRemoveManyAssociation( stateName, entityReference );
             entityState.markUpdated();
             return true;
         }
@@ -93,14 +104,7 @@ public final class JSONManyAssociationState
     @Override
     public EntityReference get( int i )
     {
-        try
-        {
-            return EntityReference.parseEntityReference( references.getString( i ) );
-        }
-        catch( JSONException e )
-        {
-            throw new EntityStoreException( e );
-        }
+        return EntityReference.parseEntityReference( getReferences().getString( i ) );
     }
 
     @Override
@@ -113,7 +117,7 @@ public final class JSONManyAssociationState
             @Override
             public boolean hasNext()
             {
-                return idx < references.length();
+                return idx < getReferences().size();
             }
 
             @Override
@@ -121,11 +125,11 @@ public final class JSONManyAssociationState
             {
                 try
                 {
-                    EntityReference ref = EntityReference.parseEntityReference( references.getString( idx ) );
+                    EntityReference ref = EntityReference.parseEntityReference( getReferences().getString( idx ) );
                     idx++;
                     return ref;
                 }
-                catch( JSONException e )
+                catch( JsonException e )
                 {
                     throw new NoSuchElementException();
                 }
@@ -142,49 +146,19 @@ public final class JSONManyAssociationState
     @Override
     public String toString()
     {
-        return references.toString();
+        return getReferences().toString();
     }
 
-    private int indexOfReference( String enityIdentityAsString )
+    private int indexOfReference( String entityIdentityAsString )
     {
-        for( int idx = 0; idx < references.length(); idx++ )
+        JsonArray references = getReferences();
+        for( int idx = 0; idx < references.size(); idx++ )
         {
-            if( enityIdentityAsString.equals( references.opt( idx ) ) )
+            if( entityIdentityAsString.equals( references.getString( idx, null ) ) )
             {
                 return idx;
             }
         }
         return -1;
     }
-
-    private void insertReference( int insert, Object item )
-        throws JSONException
-    {
-        if( insert < 0 || insert > references.length() )
-        {
-            throw new JSONException( "JSONArray[" + insert + "] is out of bounds." );
-        }
-        if( insert == references.length() )
-        {
-            // append
-            references.put( item );
-        }
-        else
-        {
-            // insert (copy/insert/apply)
-            JSONArray output = new JSONArray();
-            for( int idx = 0; idx < references.length(); idx++ )
-            {
-                if( idx == insert )
-                {
-                    output.put( item );
-                }
-                output.put( references.opt( idx ) );
-            }
-            for( int idx = 0; idx < output.length(); idx++ )
-            {
-                references.put( idx, output.opt( idx ) );
-            }
-        }
-    }
 }


Mime
View raw message