You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@johnzon.apache.org by st...@apache.org on 2016/04/03 19:01:20 UTC

[01/10] incubator-johnzon git commit: JOHNZON-71 first draft of JsonbGenerator and ObjectConverter

Repository: incubator-johnzon
Updated Branches:
  refs/heads/master 67405820f -> 220899954


JOHNZON-71 first draft of JsonbGenerator and ObjectConverter


Project: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/commit/daf1a0f7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/tree/daf1a0f7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/diff/daf1a0f7

Branch: refs/heads/master
Commit: daf1a0f7772e0c2139e55f0802ce7be32ebc98df
Parents: 2fa810b
Author: Mark Struberg <st...@apache.org>
Authored: Sat Mar 26 15:12:09 2016 +0100
Committer: Mark Struberg <st...@apache.org>
Committed: Sat Mar 26 15:12:09 2016 +0100

----------------------------------------------------------------------
 .../apache/johnzon/mapper/JsonbGenerator.java   |  61 +++++++++
 .../org/apache/johnzon/mapper/JsonbParser.java  |  40 ++++++
 .../java/org/apache/johnzon/mapper/Mapper.java  |   1 -
 .../apache/johnzon/mapper/ObjectConverter.java  |  39 ++++++
 .../apache/johnzon/mapper/ObjectTypeTest.java   | 133 +++++++++++++++++++
 5 files changed, 273 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/daf1a0f7/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbGenerator.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbGenerator.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbGenerator.java
new file mode 100644
index 0000000..9edfa01
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbGenerator.java
@@ -0,0 +1,61 @@
+/*
+ * 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.johnzon.mapper;
+
+import javax.json.stream.JsonGenerator;
+
+/**
+ * Handles writing Json for Objects.
+ * Internally it uses a {@link JsonGenerator} to write JSON
+ *
+ * To write JSON-P structure elements you can use the {@link #getJsonGenerator()} method.
+ *
+ */
+public interface JsonbGenerator {
+
+    /**
+     * @return the {@link JsonGenerator} used internally to write the JSON output.
+     */
+    JsonGenerator getJsonGenerator();
+
+    /**
+     * Write the given Object o into the current JSON layer.
+     * This will <em>not</em> open a new json layer ('{', '}')
+     * but really just write the attributes of o to the currently opened layer.
+     *
+     * Consider you have a class
+     * <pre>
+     *     public class Customer {
+     *         private String firstName;
+     *         private String lastName;
+     *         private Address address;
+     *         ...
+     *     }
+     * </pre>
+     * then the resulting JSON String will e.g. look like
+     * <pre>
+     *     "firstName":"Karl", "lastName":"SomeName", "address":{"street":"mystreet"}
+     * </pre>
+     * @param o the object to write
+     * @return itself, for easier chaining of commands
+     */
+    JsonbGenerator writeObject(Object o);
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/daf1a0f7/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbParser.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbParser.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbParser.java
new file mode 100644
index 0000000..ef128f3
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbParser.java
@@ -0,0 +1,40 @@
+/*
+ * 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.johnzon.mapper;
+
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonParser;
+
+/**
+ * Handles reading Json for Objects.
+ * Internally it uses a {@link JsonParser} to write JSON
+ *
+ * To write JSON-P structure elements you can use the {@link #getJsonParser()} ()} method.
+ *
+ */
+public interface JsonbParser {
+
+    /**
+     * @return the {@link JsonGenerator} used internally to write the JSON output.
+     */
+    JsonParser getJsonParser();
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/daf1a0f7/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
index f3bbcd9..063a6e3 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
@@ -82,7 +82,6 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 import static java.util.Arrays.asList;
-import static javafx.scene.input.KeyCode.T;
 
 public class Mapper implements Closeable {
     private static final Adapter<Object, String> FALLBACK_CONVERTER = new ConverterAdapter<Object>(new FallbackConverter());

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/daf1a0f7/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
new file mode 100644
index 0000000..e831e80
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.johnzon.mapper;
+
+import java.lang.reflect.Type;
+
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonParser;
+
+/**
+ * Convert a given Java Type a nested JSON representation.
+ * And the other way around.
+ *
+ * An example would be to convert a custom Project POJO, like Dog.class
+ * to it's JSON representation
+ *
+ * @param <T>
+ */
+public interface ObjectConverter<T> {
+    void writeJson(T instance, JsonbGenerator jsonbGenerator);
+
+    T fromJson(JsonbParser jsonbParser, Type targetType);
+}

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/daf1a0f7/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
new file mode 100644
index 0000000..9d90b47
--- /dev/null
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.johnzon.mapper;
+
+
+import java.lang.reflect.Type;
+import java.util.Arrays;
+
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ObjectTypeTest {
+
+
+    @Parameterized.Parameter
+    public String accessMode;
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Iterable<String> modes() {
+        return Arrays.asList("field", "method", "both", "strict-method");
+    }
+
+
+    @Test
+    public void testObjectConverterMapper() {
+        Mapper mapper = new MapperBuilder()
+                .setAccessModeName(accessMode)
+                .build();
+
+        String jsonString = "{ \"//javaType\": \"org.apache.johnzon.mapper.ObjectTypeTest$Customer\", \"firstName\":\"Bruce\", \"lastName\":\"Wayne\" }";
+
+        Poodle mum = new Poodle();
+        mum.setName("Rosa");
+        mum.setHairCut(true);
+
+        Beagle dad = new Beagle();
+        dad.setName("Gnarl");
+
+        Beagle grandPa = new Beagle();
+        grandPa.setName("Wuffi");
+        dad.setFather(grandPa);
+
+        Mutt snoopie = new Mutt();
+        snoopie.setName("Snoopie");
+        snoopie.setFather(dad);
+        snoopie.setMother(mum);
+
+        String json = mapper.writeObjectAsString(snoopie);
+        Assert.assertNotNull(json);
+    }
+
+
+    public static class TestWithTypeConverter implements ObjectConverter<Dog> {
+        @Override
+        public void writeJson(Dog instance, JsonbGenerator jsonGenerator) {
+            jsonGenerator.getJsonGenerator().write("//javaType", instance.getClass().getName());
+
+        }
+
+        @Override
+        public Dog fromJson(JsonbParser jsonParser, Type targetType) {
+            return null;
+        }
+    }
+
+    public static class Dog {
+        private String name;
+        private Dog father;
+        private Dog mother;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public Dog getFather() {
+            return father;
+        }
+
+        public void setFather(Dog father) {
+            this.father = father;
+        }
+
+        public Dog getMother() {
+            return mother;
+        }
+
+        public void setMother(Dog mother) {
+            this.mother = mother;
+        }
+    }
+
+    public static class Beagle extends Dog {
+    }
+
+    public static class Poodle extends Dog {
+        boolean hairCut = false;
+
+        public boolean isHairCut() {
+            return hairCut;
+        }
+
+        public void setHairCut(boolean hairCut) {
+            this.hairCut = hairCut;
+        }
+    }
+
+    public static class Mutt extends Dog {
+    }
+}
\ No newline at end of file


[06/10] incubator-johnzon git commit: JOHNZON-71 add ObjectConverter to MapperBuilder

Posted by st...@apache.org.
JOHNZON-71 add ObjectConverter to MapperBuilder

together with rsandtner


Project: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/commit/7fe6921a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/tree/7fe6921a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/diff/7fe6921a

Branch: refs/heads/master
Commit: 7fe6921af80e3557c394c2c75e0a949e3074c84d
Parents: 62476e4
Author: Mark Struberg <st...@apache.org>
Authored: Mon Mar 28 20:31:28 2016 +0200
Committer: Mark Struberg <st...@apache.org>
Committed: Mon Mar 28 20:31:28 2016 +0200

----------------------------------------------------------------------
 .../org/apache/johnzon/mapper/MapperBuilder.java    | 10 ++++++++--
 .../org/apache/johnzon/mapper/MapperConfig.java     | 16 ++++++++++++++++
 .../org/apache/johnzon/mapper/ObjectTypeTest.java   |  4 ++--
 3 files changed, 26 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/7fe6921a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
index eb23074..c5073ac 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
@@ -157,11 +157,11 @@ public class MapperBuilder {
         }
 
         // new config so builderConfig can get tweaked again.
-        MapperConfig config = builderConfig.clone();
+        MapperConfig mapperConfig = builderConfig.clone();
 
         return new Mapper(
             readerFactory, generatorFactory,
-            config,
+            mapperConfig,
             adapters,
             version,
             attributeOrder,
@@ -322,4 +322,10 @@ public class MapperBuilder {
         builderConfig.setReadAttributeBeforeWrite(readAttributeBeforeWrite);
         return this;
     }
+
+    public <T> MapperBuilder addObjectConverter(Class<T> targetType, ObjectConverter<T> objectConverter) {
+        builderConfig.addObjectConverter(targetType, objectConverter);
+        return this;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/7fe6921a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
index 5c51e12..cb39a38 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
@@ -19,6 +19,8 @@
 package org.apache.johnzon.mapper;
 
 import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Map;
 
 import org.apache.johnzon.mapper.access.AccessMode;
 
@@ -37,6 +39,10 @@ class MapperConfig implements Cloneable {
     private AccessMode accessMode;
     private Charset encoding = Charset.forName(System.getProperty("johnzon.mapper.encoding", "UTF-8"));
 
+    //X TODO we need a more elaborated approache at the end, but for now it's fine
+    private Map<Class<?>, ObjectConverter<?>> objectConverters = new HashMap<Class<?>, ObjectConverter<?>>();
+
+
     MapperConfig() {
     }
 
@@ -120,6 +126,14 @@ class MapperConfig implements Cloneable {
         this.encoding = encoding;
     }
 
+    <T> void addObjectConverter(Class<T> targetType, ObjectConverter<T> objectConverter) {
+        objectConverters.put(targetType, objectConverter);
+    }
+
+    public Map<Class<?>, ObjectConverter<?>> getObjectConverters() {
+        return objectConverters;
+    }
+
     @Override
     public MapperConfig clone() {
         try {
@@ -128,4 +142,6 @@ class MapperConfig implements Cloneable {
             return null;
         }
     }
+
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/7fe6921a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
index ccc5b83..b07f533 100644
--- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
@@ -48,7 +48,7 @@ public class ObjectTypeTest {
     public void testObjectConverterMapper() {
         Mapper mapper = new MapperBuilder()
                 .setAccessModeName(accessMode)
-                //X TODO .addObjectConverter or so
+                .addObjectConverter(Dog.class, new TestWithTypeConverter())
                 .build();
 
         String expectedJsonString = "{\"//javaType\":\"org.apache.johnzon.mapper.ObjectTypeTest$Mutt\"," +
@@ -74,7 +74,7 @@ public class ObjectTypeTest {
 
         String json = mapper.writeObjectAsString(snoopie);
         Assert.assertNotNull(json);
-        //X TODO Assert.assertEquals(expectedJsonString, json);
+        Assert.assertEquals(expectedJsonString, json);
     }
 
 


[02/10] incubator-johnzon git commit: JOHNZON-71 improve JsonbGenerator Converter test

Posted by st...@apache.org.
JOHNZON-71 improve JsonbGenerator Converter test


Project: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/commit/45cfcf20
Tree: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/tree/45cfcf20
Diff: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/diff/45cfcf20

Branch: refs/heads/master
Commit: 45cfcf20085ff32e05df41e9b547f3583c1d09c4
Parents: daf1a0f
Author: Mark Struberg <st...@apache.org>
Authored: Sat Mar 26 17:07:43 2016 +0100
Committer: Mark Struberg <st...@apache.org>
Committed: Sat Mar 26 17:07:43 2016 +0100

----------------------------------------------------------------------
 .../test/java/org/apache/johnzon/mapper/ObjectTypeTest.java    | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/45cfcf20/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
index 9d90b47..9302c6e 100644
--- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
@@ -72,9 +72,9 @@ public class ObjectTypeTest {
 
     public static class TestWithTypeConverter implements ObjectConverter<Dog> {
         @Override
-        public void writeJson(Dog instance, JsonbGenerator jsonGenerator) {
-            jsonGenerator.getJsonGenerator().write("//javaType", instance.getClass().getName());
-
+        public void writeJson(Dog instance, JsonbGenerator jsonbGenerator) {
+            jsonbGenerator.getJsonGenerator().write("//javaType", instance.getClass().getName());
+            jsonbGenerator.writeObject(instance);
         }
 
         @Override


[09/10] incubator-johnzon git commit: JOHNZON-71 swap mapper parts out to MappingGenerator

Posted by st...@apache.org.
JOHNZON-71 swap mapper parts out to MappingGenerator


Project: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/commit/f2a00fb1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/tree/f2a00fb1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/diff/f2a00fb1

Branch: refs/heads/master
Commit: f2a00fb11aeeb0ca6adf860d8a5dd8d4cbcb20a1
Parents: d88dfde
Author: Mark Struberg <st...@apache.org>
Authored: Sun Apr 3 18:26:56 2016 +0200
Committer: Mark Struberg <st...@apache.org>
Committed: Sun Apr 3 18:26:56 2016 +0200

----------------------------------------------------------------------
 .../java/org/apache/johnzon/mapper/Mapper.java  | 16 +++++++++++--
 .../johnzon/mapper/MappingGeneratorImpl.java    | 25 +++++++++++++-------
 2 files changed, 31 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/f2a00fb1/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
index 0a7b76d..aae3c34 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
@@ -311,13 +311,25 @@ public class Mapper implements Closeable {
             }
             return;
         }
+
         final JsonGenerator generator = generatorFactory.createGenerator(stream);
-        doWriteHandlingNullObject(object, generator);
+        writeObject(object, generator);
     }
 
     public void writeObject(final Object object, final OutputStream stream) {
         final JsonGenerator generator = generatorFactory.createGenerator(stream, config.getEncoding());
-        doWriteHandlingNullObject(object, generator);
+        writeObject(object, generator);
+    }
+
+    private void writeObject(Object object, JsonGenerator generator) {
+        try {
+            MappingGenerator mappingGenerator = new MappingGeneratorImpl(config, generator, mappings);
+            generator.writeStartObject();
+            mappingGenerator.writeObject(object);
+            generator.writeEnd();
+        } finally {
+            doCloseOrFlush(generator);
+        }
     }
 
     public String writeArrayAsString(final Collection<?> instance) {

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/f2a00fb1/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
index 3129572..d823be6 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
@@ -34,7 +34,7 @@ import org.apache.johnzon.mapper.internal.AdapterKey;
 public class MappingGeneratorImpl implements MappingGenerator {
     private final MapperConfig config;
     private final JsonGenerator generator;
-    protected final Mappings mappings;
+    private final Mappings mappings;
 
 
     MappingGeneratorImpl(MapperConfig config, JsonGenerator jsonGenerator, final Mappings mappings) {
@@ -55,17 +55,22 @@ public class MappingGeneratorImpl implements MappingGenerator {
         } else if (object instanceof JsonValue) {
             generator.write((JsonValue) object);
         } else {
-            doWriteObject(object);
+            doWriteObject(object, false);
         }
         return this;
     }
 
-    private void doWriteObject(Object object) {
+    private void doWriteObject(Object object, boolean writeBody) {
         try {
             if (object instanceof Map) {
-                generator.writeStartObject();
+                if (writeBody) {
+                    generator.writeStartObject();
+                }
                 writeMapBody((Map<?, ?>) object, null);
-                generator.writeEnd();
+                if (writeBody) {
+                    generator.writeEnd();
+                }
+                return;
             }
 
             if(writePrimitives(object)) {
@@ -80,9 +85,13 @@ public class MappingGeneratorImpl implements MappingGenerator {
                 return;
             }
 
-            generator.writeStartObject();
+            if (writeBody) {
+                generator.writeStartObject();
+            }
             doWriteObjectBody(object);
-            generator.writeEnd();
+            if (writeBody) {
+                generator.writeEnd();
+            }
         } catch (final InvocationTargetException e) {
             throw new MapperException(e);
         } catch (final IllegalAccessException e) {
@@ -319,7 +328,7 @@ public class MappingGeneratorImpl implements MappingGenerator {
             } else if (o == null) {
                 generator.writeNull();
             } else {
-                doWriteObject(o);
+                doWriteObject(o, true);
             }
         }
     }


[03/10] incubator-johnzon git commit: JOHNZON-71 introduce MapperConfig and improve ObjectConverter interface

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Mappings.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Mappings.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Mappings.java
deleted file mode 100644
index 68e5d69..0000000
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Mappings.java
+++ /dev/null
@@ -1,770 +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.johnzon.mapper.reflection;
-
-import org.apache.johnzon.mapper.Adapter;
-import org.apache.johnzon.mapper.Converter;
-import org.apache.johnzon.mapper.JohnzonConverter;
-import org.apache.johnzon.mapper.JohnzonIgnore;
-import org.apache.johnzon.mapper.JohnzonVirtualObject;
-import org.apache.johnzon.mapper.JohnzonVirtualObjects;
-import org.apache.johnzon.mapper.access.AccessMode;
-import org.apache.johnzon.mapper.converter.DateWithCopyConverter;
-import org.apache.johnzon.mapper.converter.EnumConverter;
-import org.apache.johnzon.mapper.internal.AdapterKey;
-import org.apache.johnzon.mapper.internal.ConverterAdapter;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Array;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeMap;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import static java.util.Arrays.asList;
-import static org.apache.johnzon.mapper.reflection.Converters.matches;
-
-public class Mappings {
-    public static class ClassMapping {
-        public final Class<?> clazz;
-        public final AccessMode.Factory factory;
-        public final Map<String, Getter> getters;
-        public final Map<String, Setter> setters;
-
-
-        protected ClassMapping(final Class<?> clazz, final AccessMode.Factory factory,
-                               final Map<String, Getter> getters, final Map<String, Setter> setters) {
-            this.clazz = clazz;
-            this.factory = factory;
-            this.getters = getters;
-            this.setters = setters;
-        }
-    }
-
-    public static class CollectionMapping {
-        public final Class<?> raw;
-        public final Type arg;
-        public final boolean primitive;
-
-        public CollectionMapping(final boolean primitive, final Class<?> collectionType, final Type fieldArgType) {
-            this.raw = collectionType;
-            this.arg = fieldArgType;
-            this.primitive = primitive;
-        }
-    }
-
-    public static class Getter {
-        public final AccessMode.Reader reader;
-        public final int version;
-        public final Adapter converter;
-        public final Adapter itemConverter;
-        public final boolean primitive;
-        public final boolean array;
-        public final boolean map;
-        public final boolean collection;
-
-        public Getter(final AccessMode.Reader reader,
-                      final boolean primitive, final boolean array,
-                      final boolean collection, final boolean map,
-                      final Adapter<?, ?> converter,
-                      final int version) {
-            this.reader = reader;
-            this.version = version;
-            this.array = array;
-            this.collection = collection;
-            this.primitive = primitive;
-            if (converter != null && matches(reader.getType(), converter)) {
-                this.converter = converter;
-                this.itemConverter = null;
-            } else if (converter != null) {
-                this.converter = null;
-                this.itemConverter = converter;
-            } else {
-                this.converter = null;
-                this.itemConverter = null;
-            }
-            this.map = map && this.converter == null;
-        }
-
-        @Override
-        public String toString() {
-            return "Getter{" +
-                "reader=" + reader +
-                ", version=" + version +
-                ", converter=" + converter +
-                ", itemConverter=" + itemConverter +
-                ", primitive=" + primitive +
-                ", array=" + array +
-                ", map=" + map +
-                ", collection=" + collection +
-                '}';
-        }
-    }
-
-    public static class Setter {
-        public final AccessMode.Writer writer;
-        public final int version;
-        public final Type paramType;
-        public final Adapter<?, ?> converter;
-        public final Adapter<?, ?> itemConverter;
-        public final boolean primitive;
-        public final boolean array;
-
-        public Setter(final AccessMode.Writer writer, final boolean primitive, final boolean array,
-                      final Type paramType, final Adapter<?, ?> converter,
-                      final int version) {
-            this.writer = writer;
-            this.paramType = paramType;
-            this.version = version;
-            this.primitive = primitive;
-            this.array = array;
-            if (converter != null && matches(writer.getType(), converter)) {
-                this.converter = converter;
-                this.itemConverter = null;
-            } else if (converter != null) {
-                this.converter = null;
-                this.itemConverter = converter;
-            } else {
-                this.converter = null;
-                this.itemConverter = null;
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "Setter{" +
-                "writer=" + writer +
-                ", version=" + version +
-                ", paramType=" + paramType +
-                ", converter=" + converter +
-                ", itemConverter=" + itemConverter +
-                ", primitive=" + primitive +
-                ", array=" + array +
-                '}';
-        }
-    }
-
-    private static final JohnzonParameterizedType VIRTUAL_TYPE = new JohnzonParameterizedType(Map.class, String.class, Object.class);
-
-    protected final ConcurrentMap<Type, ClassMapping> classes = new ConcurrentHashMap<Type, ClassMapping>();
-    protected final ConcurrentMap<Type, CollectionMapping> collections = new ConcurrentHashMap<Type, CollectionMapping>();
-    protected final Comparator<String> fieldOrdering;
-    protected final ConcurrentMap<AdapterKey, Adapter<?, ?>> adapters;
-    private final AccessMode accessMode;
-    private final int version;
-
-    public Mappings(final Comparator<String> attributeOrder, final AccessMode accessMode,
-                    final int version, final ConcurrentMap<AdapterKey, Adapter<?, ?>> adapters) {
-        this.fieldOrdering = attributeOrder;
-        this.accessMode = accessMode;
-        this.version = version;
-        this.adapters = adapters;
-    }
-
-    public <T> CollectionMapping findCollectionMapping(final ParameterizedType genericType) {
-        CollectionMapping collectionMapping = collections.get(genericType);
-        if (collectionMapping == null) {
-            collectionMapping = createCollectionMapping(genericType);
-            if (collectionMapping == null) {
-                return null;
-            }
-            final CollectionMapping existing = collections.putIfAbsent(genericType, collectionMapping);
-            if (existing != null) {
-                collectionMapping = existing;
-            }
-        }
-        return collectionMapping;
-    }
-
-    private <T> CollectionMapping createCollectionMapping(final ParameterizedType aType) {
-        final Type[] fieldArgTypes = aType.getActualTypeArguments();
-        final Type raw = aType.getRawType();
-        if (fieldArgTypes.length == 1 && Class.class.isInstance(raw)) {
-            final Class<?> r = Class.class.cast(raw);
-            final Class<?> collectionType;
-            if (List.class.isAssignableFrom(r)) {
-                collectionType = List.class;
-            } else if (SortedSet.class.isAssignableFrom(r)) {
-                collectionType = SortedSet.class;
-            } else if (Set.class.isAssignableFrom(r)) {
-                collectionType = Set.class;
-            } else if (Deque.class.isAssignableFrom(r)) {
-                collectionType = Deque.class;
-            } else if (Queue.class.isAssignableFrom(r)) {
-                collectionType = Queue.class;
-            } else if (Collection.class.isAssignableFrom(r)) {
-                collectionType = Collection.class;
-            } else {
-                return null;
-            }
-
-            final CollectionMapping mapping = new CollectionMapping(isPrimitive(fieldArgTypes[0]), collectionType, fieldArgTypes[0]);
-            collections.putIfAbsent(aType, mapping);
-            return mapping;
-        }
-        return null;
-    }
-
-    // has JSon API a method for this type
-    public static boolean isPrimitive(final Type type) {
-        if (type == String.class) {
-            return true;
-        } else if (type == char.class || type == Character.class) {
-            return true;
-        } else if (type == long.class || type == Long.class) {
-            return true;
-        } else if (type == int.class || type == Integer.class
-            || type == byte.class || type == Byte.class
-            || type == short.class || type == Short.class) {
-            return true;
-        } else if (type == double.class || type == Double.class
-            || type == float.class || type == Float.class) {
-            return true;
-        } else if (type == boolean.class || type == Boolean.class) {
-            return true;
-        } else if (type == BigDecimal.class) {
-            return true;
-        } else if (type == BigInteger.class) {
-            return true;
-        }
-        return false;
-    }
-
-    public ClassMapping getClassMapping(final Type clazz) {
-        return classes.get(clazz);
-    }
-
-    public ClassMapping findOrCreateClassMapping(final Type clazz) {
-        ClassMapping classMapping = classes.get(clazz);
-        if (classMapping == null) {
-            if (!Class.class.isInstance(clazz) || Map.class.isAssignableFrom(Class.class.cast(clazz))) {
-                return null;
-            }
-
-            classMapping = createClassMapping(Class.class.cast(clazz));
-            final ClassMapping existing = classes.putIfAbsent(clazz, classMapping);
-            if (existing != null) {
-                classMapping = existing;
-            }
-        }
-        return classMapping;
-    }
-
-    protected ClassMapping createClassMapping(final Class<?> inClazz) {
-        boolean copyDate = false;
-        for (final Class<?> itf : inClazz.getInterfaces()) {
-            if ("org.apache.openjpa.enhance.PersistenceCapable".equals(itf.getName())) {
-                copyDate = true;
-                break;
-            }
-        }
-        final Class<?> clazz = findModelClass(inClazz);
-
-        Comparator<String> fieldComparator = accessMode.fieldComparator(inClazz);
-        fieldComparator = fieldComparator == null ? fieldOrdering : fieldComparator;
-
-        final Map<String, Getter> getters = fieldComparator == null ? newOrderedMap(Getter.class) : new TreeMap<String, Getter>(fieldComparator);
-        final Map<String, Setter> setters = fieldComparator == null ? newOrderedMap(Setter.class) : new TreeMap<String, Setter>(fieldComparator);
-
-        final Map<String, AccessMode.Reader> readers = accessMode.findReaders(clazz);
-        final Map<String, AccessMode.Writer> writers = accessMode.findWriters(clazz);
-
-        final Collection<String> virtualFields = new HashSet<String>();
-        {
-            final JohnzonVirtualObjects virtualObjects = clazz.getAnnotation(JohnzonVirtualObjects.class);
-            if (virtualObjects != null) {
-                for (final JohnzonVirtualObject virtualObject : virtualObjects.value()) {
-                    handleVirtualObject(virtualFields, virtualObject, getters, setters, readers, writers, copyDate);
-                }
-            }
-
-            final JohnzonVirtualObject virtualObject = clazz.getAnnotation(JohnzonVirtualObject.class);
-            if (virtualObject != null) {
-                handleVirtualObject(virtualFields, virtualObject, getters, setters, readers, writers, copyDate);
-            }
-        }
-
-        for (final Map.Entry<String, AccessMode.Reader> reader : readers.entrySet()) {
-            final String key = reader.getKey();
-            if (virtualFields.contains(key)) {
-                continue;
-            }
-            addGetterIfNeeded(getters, key, reader.getValue(), copyDate);
-        }
-
-        for (final Map.Entry<String, AccessMode.Writer> writer : writers.entrySet()) {
-            final String key = writer.getKey();
-            if (virtualFields.contains(key)) {
-                continue;
-            }
-            addSetterIfNeeded(setters, key, writer.getValue(), copyDate);
-        }
-        return new ClassMapping(clazz, accessMode.findFactory(clazz), getters, setters);
-    }
-
-    protected Class<?> findModelClass(final Class<?> inClazz) {
-        Class<?> clazz = inClazz;
-        // unproxy to get a clean model
-        while (clazz != null && clazz != Object.class
-            && (clazz.getName().contains("$$") || clazz.getName().contains("$proxy")
-            || clazz.getName().startsWith("org.apache.openjpa.enhance.") /* subclassing mode, not the default */)) {
-            clazz = clazz.getSuperclass();
-        }
-        if (clazz == null || clazz == Object.class) { // shouldn't occur but a NPE protection
-            clazz = inClazz;
-        }
-        return clazz;
-    }
-
-    private <T> Map<String, T> newOrderedMap(final Class<T> value) {
-        return fieldOrdering != null ? new TreeMap<String, T>(fieldOrdering) : new HashMap<String, T>();
-    }
-
-    private void addSetterIfNeeded(final Map<String, Setter> setters,
-                                   final String key,
-                                   final AccessMode.Writer value,
-                                   final boolean copyDate) {
-        final JohnzonIgnore writeIgnore = value.getAnnotation(JohnzonIgnore.class);
-        if (writeIgnore == null || writeIgnore.minVersion() >= 0) {
-            if (key.equals("metaClass")) {
-                return;
-            }
-            final Type param = value.getType();
-            final Class<?> returnType = Class.class.isInstance(param) ? Class.class.cast(param) : null;
-            final Setter setter = new Setter(
-                value, isPrimitive(param), returnType != null && returnType.isArray(), param,
-                findConverter(copyDate, value), writeIgnore != null ? writeIgnore.minVersion() : -1);
-            setters.put(key, setter);
-        }
-    }
-
-    private void addGetterIfNeeded(final Map<String, Getter> getters,
-                                   final String key,
-                                   final AccessMode.Reader value,
-                                   final boolean copyDate) {
-        final JohnzonIgnore readIgnore = value.getAnnotation(JohnzonIgnore.class);
-        if (readIgnore == null || readIgnore.minVersion() >= 0) {
-            final Class<?> returnType = Class.class.isInstance(value.getType()) ? Class.class.cast(value.getType()) : null;
-            final ParameterizedType pt = ParameterizedType.class.isInstance(value.getType()) ? ParameterizedType.class.cast(value.getType()) : null;
-            final Getter getter = new Getter(value, isPrimitive(returnType),
-                returnType != null && returnType.isArray(),
-                (pt != null && Collection.class.isAssignableFrom(Class.class.cast(pt.getRawType())))
-                    || (returnType != null && Collection.class.isAssignableFrom(returnType)),
-                (pt != null && Map.class.isAssignableFrom(Class.class.cast(pt.getRawType())))
-                    || (returnType != null && Map.class.isAssignableFrom(returnType)),
-                findConverter(copyDate, value),
-                readIgnore != null ? readIgnore.minVersion() : -1);
-            getters.put(key, getter);
-        }
-    }
-
-    // idea is quite trivial, simulate an object with a Map<String, Object>
-    private void handleVirtualObject(final Collection<String> virtualFields,
-                                     final JohnzonVirtualObject o,
-                                     final Map<String, Getter> getters,
-                                     final Map<String, Setter> setters,
-                                     final Map<String, AccessMode.Reader> readers,
-                                     final Map<String, AccessMode.Writer> writers,
-                                     final boolean copyDate) {
-        final String[] path = o.path();
-        if (path.length < 1) {
-            throw new IllegalArgumentException("@JohnzonVirtualObject need a path");
-        }
-
-        // add them to ignored fields
-        for (final JohnzonVirtualObject.Field f : o.fields()) {
-            virtualFields.add(f.value());
-        }
-
-        // build "this" model
-        final Map<String, Getter> objectGetters = newOrderedMap(Getter.class);
-        final Map<String, Setter> objectSetters = newOrderedMap(Setter.class);
-
-        for (final JohnzonVirtualObject.Field f : o.fields()) {
-            final String name = f.value();
-            if (f.read()) {
-                final AccessMode.Reader reader = readers.get(name);
-                if (reader != null) {
-                    addGetterIfNeeded(objectGetters, name, reader, copyDate);
-                }
-            }
-            if (f.write()) {
-                final AccessMode.Writer writer = writers.get(name);
-                if (writer != null) {
-                    addSetterIfNeeded(objectSetters, name, writer, copyDate);
-                }
-            }
-        }
-
-        final String key = path[0];
-
-        final Getter getter = getters.get(key);
-        final MapBuilderReader newReader = new MapBuilderReader(objectGetters, path, version);
-        getters.put(key, new Getter(getter == null ? newReader : new CompositeReader(getter.reader, newReader), false, false, false, true, null, -1));
-
-        final Setter newSetter = setters.get(key);
-        final MapUnwrapperWriter newWriter = new MapUnwrapperWriter(objectSetters, path);
-        setters.put(key, new Setter(newSetter == null ? newWriter : new CompositeWriter(newSetter.writer, newWriter), false, false, VIRTUAL_TYPE, null, -1));
-    }
-
-    private Adapter findConverter(final boolean copyDate, final AccessMode.DecoratedType decoratedType) {
-        Adapter converter = decoratedType.findConverter();
-        if (converter != null) {
-            return converter;
-        }
-
-        final JohnzonConverter annotation = decoratedType.getAnnotation(JohnzonConverter.class);
-
-        Type typeToTest = decoratedType.getType();
-        if (annotation != null) {
-            try {
-                converter = new ConverterAdapter(annotation.value().newInstance());
-            } catch (final Exception e) {
-                throw new IllegalArgumentException(e);
-            }
-        } else if (ParameterizedType.class.isInstance(decoratedType.getType())) {
-            final ParameterizedType type = ParameterizedType.class.cast(decoratedType.getType());
-            final Type rawType = type.getRawType();
-            if (Class.class.isInstance(rawType)
-                && Collection.class.isAssignableFrom(Class.class.cast(rawType))
-                && type.getActualTypeArguments().length >= 1) {
-                typeToTest = type.getActualTypeArguments()[0];
-            } // TODO: map
-        }
-        if (converter == null && Class.class.isInstance(typeToTest)) {
-            final Class type = Class.class.cast(typeToTest);
-            if (Date.class.isAssignableFrom(type) && copyDate) {
-                converter = new DateWithCopyConverter(Adapter.class.cast(adapters.get(new AdapterKey(Date.class, String.class))));
-            } else if (type.isEnum()) {
-                final AdapterKey key = new AdapterKey(String.class, type);
-                converter = adapters.get(key); // first ensure user didnt override it
-                if (converter == null) {
-                    converter = new ConverterAdapter(new EnumConverter(type));
-                    adapters.put(key, converter);
-                }
-            } else {
-                for (final Map.Entry<AdapterKey, Adapter<?, ?>> adapterEntry : adapters.entrySet()) {
-                    if (adapterEntry.getKey().getFrom() == adapterEntry.getKey().getTo()) { // String -> String
-                        continue;
-                    }
-                    if (adapterEntry.getKey().getFrom() == type && !(
-                            // ignore internal converters to let primitives be correctly handled
-                            ConverterAdapter.class.isInstance(adapterEntry.getValue()) &&
-                            ConverterAdapter.class.cast(adapterEntry.getValue()).getConverter().getClass().getName().startsWith("org.apache.johnzon.mapper."))) {
-
-                        if (converter != null) {
-                            throw new IllegalArgumentException("Ambiguous adapter for " + decoratedType);
-                        }
-                        converter = adapterEntry.getValue();
-                    }
-                }
-            }
-        }
-        return converter;
-    }
-
-    private static class MapBuilderReader implements AccessMode.Reader {
-        private final Map<String, Getter> getters;
-        private final Map<String, Object> template;
-        private final String[] paths;
-        private final int version;
-
-        public MapBuilderReader(final Map<String, Getter> objectGetters, final String[] paths, final int version) {
-            this.getters = objectGetters;
-            this.paths = paths;
-            this.template = new LinkedHashMap<String, Object>();
-            this.version = version;
-
-            Map<String, Object> last = this.template;
-            for (int i = 1; i < paths.length; i++) {
-                final Map<String, Object> newLast = new LinkedHashMap<String, Object>();
-                last.put(paths[i], newLast);
-                last = newLast;
-            }
-        }
-
-        @Override
-        public Object read(final Object instance) {
-            final Map<String, Object> map = new LinkedHashMap<String, Object>(template);
-            Map<String, Object> nested = map;
-            for (int i = 1; i < paths.length; i++) {
-                nested = Map.class.cast(nested.get(paths[i]));
-            }
-            for (final Map.Entry<String, Getter> g : getters.entrySet()) {
-                final Mappings.Getter getter = g.getValue();
-                final Object value = getter.reader.read(instance);
-                final Object val = value == null || getter.converter == null ? value : getter.converter.from(value);
-                if (val == null) {
-                    continue;
-                }
-                if (getter.version >= 0 && version >= getter.version) {
-                    continue;
-                }
-
-                nested.put(g.getKey(), val);
-            }
-            return map;
-        }
-
-        @Override
-        public Type getType() {
-            return VIRTUAL_TYPE;
-        }
-
-        @Override
-        public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
-            throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields");
-        }
-
-        @Override
-        public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
-            return null;
-        }
-
-        @Override
-        public Adapter<?, ?> findConverter() {
-            return null;
-        }
-
-        @Override
-        public boolean isNillable() {
-            return false;
-        }
-    }
-
-    private static class MapUnwrapperWriter implements AccessMode.Writer {
-        private final Map<String, Setter> writers;
-        private final Map<String, Class<?>> componentTypes;
-        private final String[] paths;
-
-        public MapUnwrapperWriter(final Map<String, Setter> writers, final String[] paths) {
-            this.writers = writers;
-            this.paths = paths;
-            this.componentTypes = new HashMap<String, Class<?>>();
-
-            for (final Map.Entry<String, Setter> setter : writers.entrySet()) {
-                if (setter.getValue().array) {
-                    componentTypes.put(setter.getKey(), Class.class.cast(setter.getValue().paramType).getComponentType());
-                }
-            }
-        }
-
-        @Override
-        public void write(final Object instance, final Object value) {
-            Map<String, Object> nested = null;
-            for (final String path : paths) {
-                nested = Map.class.cast(nested == null ? value : nested.get(path));
-                if (nested == null) {
-                    return;
-                }
-            }
-
-            for (final Map.Entry<String, Setter> setter : writers.entrySet()) {
-                final Setter setterValue = setter.getValue();
-                final String key = setter.getKey();
-                final Object rawValue = nested.get(key);
-                Object val = value == null || setterValue.converter == null ?
-                    rawValue : Converter.class.cast(setterValue.converter).toString(rawValue);
-                if (val == null) {
-                    continue;
-                }
-
-                if (setterValue.array && Collection.class.isInstance(val)) {
-                    final Collection<?> collection = Collection.class.cast(val);
-                    final Object[] array = (Object[]) Array.newInstance(componentTypes.get(key), collection.size());
-                    val = collection.toArray(array);
-                }
-
-                final AccessMode.Writer setterMethod = setterValue.writer;
-                setterMethod.write(instance, val);
-            }
-        }
-
-        @Override
-        public Type getType() {
-            return VIRTUAL_TYPE;
-        }
-
-        @Override
-        public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
-            throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields");
-        }
-
-        @Override
-        public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
-            return null;
-        }
-
-        @Override
-        public Adapter<?, ?> findConverter() {
-            return null;
-        }
-
-        @Override
-        public boolean isNillable() {
-            return false;
-        }
-    }
-
-    private static class CompositeReader implements AccessMode.Reader {
-        private final AccessMode.Reader[] delegates;
-
-        public CompositeReader(final AccessMode.Reader... delegates) {
-            final Collection<AccessMode.Reader> all = new LinkedList<AccessMode.Reader>();
-            for (final AccessMode.Reader r : delegates) {
-                if (CompositeReader.class.isInstance(r)) {
-                    all.addAll(asList(CompositeReader.class.cast(r).delegates));
-                } else {
-                    all.add(r);
-                }
-            }
-            this.delegates = all.toArray(new AccessMode.Reader[all.size()]);
-        }
-
-        @Override
-        public Object read(final Object instance) {
-            final Map<String, Object> map = new LinkedHashMap<String, Object>();
-            for (final AccessMode.Reader reader : delegates) {
-                final Map<String, Object> readerMap = (Map<String, Object>) reader.read(instance);
-                for (final Map.Entry<String, Object> entry : readerMap.entrySet()) {
-                    final Object o = map.get(entry.getKey());
-                    if (o == null) {
-                        map.put(entry.getKey(), entry.getValue());
-                    } else if (Map.class.isInstance(o)) {
-                        // TODO
-                    } else {
-                        throw new IllegalStateException(entry.getKey() + " is ambiguous");
-                    }
-                }
-            }
-            return map;
-        }
-
-        @Override
-        public Type getType() {
-            return VIRTUAL_TYPE;
-        }
-
-        @Override
-        public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
-            throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields");
-        }
-
-        @Override
-        public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
-            return null;
-        }
-
-        @Override
-        public Adapter<?, ?> findConverter() {
-            for (final AccessMode.Reader r : delegates) {
-                final Adapter<?, ?> converter = r.findConverter();
-                if (converter != null) {
-                    return converter;
-                }
-            }
-            return null;
-        }
-
-        @Override
-        public boolean isNillable() {
-            for (final AccessMode.Reader r : delegates) {
-                if (r.isNillable()) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    }
-
-    private static class CompositeWriter implements AccessMode.Writer {
-        private final AccessMode.Writer[] delegates;
-
-        public CompositeWriter(final AccessMode.Writer... writers) {
-            final Collection<AccessMode.Writer> all = new LinkedList<AccessMode.Writer>();
-            for (final AccessMode.Writer r : writers) {
-                if (CompositeWriter.class.isInstance(r)) {
-                    all.addAll(asList(CompositeWriter.class.cast(r).delegates));
-                } else {
-                    all.add(r);
-                }
-            }
-            this.delegates = all.toArray(new AccessMode.Writer[all.size()]);
-        }
-
-        @Override
-        public void write(final Object instance, final Object value) {
-            for (final AccessMode.Writer w : delegates) {
-                w.write(instance, value);
-            }
-        }
-
-        @Override
-        public Type getType() {
-            return VIRTUAL_TYPE;
-        }
-
-        @Override
-        public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
-            throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields");
-        }
-
-        @Override
-        public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
-            return null;
-        }
-
-        @Override
-        public Adapter<?, ?> findConverter() {
-            for (final AccessMode.Writer r : delegates) {
-                final Adapter<?, ?> converter = r.findConverter();
-                if (converter != null) {
-                    return converter;
-                }
-            }
-            return null;
-        }
-
-        @Override
-        public boolean isNillable() {
-            for (final AccessMode.Writer r : delegates) {
-                if (r.isNillable()) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/AdapterTest.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/AdapterTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/AdapterTest.java
index 92df00c..87ff40d 100644
--- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/AdapterTest.java
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/AdapterTest.java
@@ -62,7 +62,7 @@ public class AdapterTest {
     }
 
     public static class Root {
-        public Foo foo = new Foo();
+        Foo foo = new Foo();
     }
 
     public static class Foo {
@@ -71,8 +71,8 @@ public class AdapterTest {
     }
 
     public static class Bar {
-        public String simple;
-        public int count;
+        String simple;
+        int count;
 
         public Bar() {
             // no-op

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
index 9302c6e..21f5701 100644
--- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
@@ -19,10 +19,13 @@
 package org.apache.johnzon.mapper;
 
 
+import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.util.Arrays;
 
 
+import javax.json.JsonObject;
+
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -47,7 +50,10 @@ public class ObjectTypeTest {
                 .setAccessModeName(accessMode)
                 .build();
 
-        String jsonString = "{ \"//javaType\": \"org.apache.johnzon.mapper.ObjectTypeTest$Customer\", \"firstName\":\"Bruce\", \"lastName\":\"Wayne\" }";
+        String expectedJsonString = "{\"//javaType\":\"org.apache.johnzon.mapper.ObjectTypeTest$Mutt\"," +
+                "\"mother\":{\"//javaType\":\"org.apache.johnzon.mapper.ObjectTypeTest$Poodle\",\"name\":\"Rosa\",\"hairCut\":true}," +
+                "\"father\":{\"//javaType\":\"org.apache.johnzon.mapper.ObjectTypeTest$Beagle\"," +
+                "\"father\":{\"//javaType\":\"org.apache.johnzon.mapper.ObjectTypeTest$Beagle\",\"name\":\"Wuffi\"},\"name\":\"Gnarl\"},\"name\":\"Snoopie\"}";
 
         Poodle mum = new Poodle();
         mum.setName("Rosa");
@@ -67,18 +73,45 @@ public class ObjectTypeTest {
 
         String json = mapper.writeObjectAsString(snoopie);
         Assert.assertNotNull(json);
+        //X TODO Assert.assertEquals(expectedJsonString, json);
     }
 
 
     public static class TestWithTypeConverter implements ObjectConverter<Dog> {
         @Override
-        public void writeJson(Dog instance, JsonbGenerator jsonbGenerator) {
+        public void writeJson(Dog instance, MappingGenerator jsonbGenerator) {
             jsonbGenerator.getJsonGenerator().write("//javaType", instance.getClass().getName());
             jsonbGenerator.writeObject(instance);
         }
 
         @Override
-        public Dog fromJson(JsonbParser jsonParser, Type targetType) {
+        public Dog fromJson(MappingParser jsonParser, Type targetType) {
+            JsonObject jsonObject = jsonParser.getJsonReader().readObject();
+            String javaType = jsonObject.getString("//javaType");
+            if (javaType != null) {
+                // the following should get extracted in a utility class.
+                ClassLoader cl = Thread.currentThread().getContextClassLoader();
+                if (cl == null) {
+                    cl = targetType.getClass().getClassLoader();
+                }
+
+                try {
+                    Class subClass = cl.loadClass(javaType);
+                    Class targetClass = null;
+                    if (targetType instanceof Class) {
+                        targetClass = (Class) targetType;
+                    } else if (targetType instanceof ParameterizedType) {
+                       targetClass = (Class)((ParameterizedType) targetType).getRawType();
+                    }
+                    if (targetClass != null && targetClass.isAssignableFrom(subClass)) {
+                        targetType = subClass;
+                    }
+                } catch (ClassNotFoundException e) {
+                    // continue without better class match
+                }
+
+                jsonParser.readObject(jsonObject, targetType);
+            }
             return null;
         }
     }
@@ -117,7 +150,7 @@ public class ObjectTypeTest {
     }
 
     public static class Poodle extends Dog {
-        boolean hairCut = false;
+        private boolean hairCut = false;
 
         public boolean isHairCut() {
             return hairCut;



[05/10] incubator-johnzon git commit: JOHNZON-71 improve Converter and MappingParser

Posted by st...@apache.org.
JOHNZON-71 improve Converter and MappingParser

pairprogramming together with rsandtner


Project: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/commit/62476e40
Tree: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/tree/62476e40
Diff: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/diff/62476e40

Branch: refs/heads/master
Commit: 62476e40c7a38d31b9ef86c73bf4161ac3bf936b
Parents: e41b829
Author: Mark Struberg <st...@apache.org>
Authored: Mon Mar 28 20:02:59 2016 +0200
Committer: Mark Struberg <st...@apache.org>
Committed: Mon Mar 28 20:02:59 2016 +0200

----------------------------------------------------------------------
 .../org/apache/johnzon/core/JsonReaderImpl.java |  2 +-
 .../java/org/apache/johnzon/mapper/Adapter.java |  2 +-
 .../org/apache/johnzon/mapper/Converter.java    |  2 +-
 .../apache/johnzon/mapper/JohnzonConverter.java |  2 +-
 .../apache/johnzon/mapper/MapperConverter.java  | 26 ++++++++
 .../johnzon/mapper/MappingParserImpl.java       | 65 ++++++++++++++++++++
 .../org/apache/johnzon/mapper/Mappings.java     |  8 ++-
 .../apache/johnzon/mapper/ObjectConverter.java  |  2 +-
 .../johnzon/mapper/access/BaseAccessMode.java   | 19 ++++--
 .../apache/johnzon/mapper/ObjectTypeTest.java   | 31 +++++++---
 10 files changed, 137 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/62476e40/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
----------------------------------------------------------------------
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
index d4719b9..b0107ee 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
@@ -33,7 +33,7 @@ public class JsonReaderImpl implements JsonReader {
     private final JsonParser parser;
     private boolean closed = false;
 
-    JsonReaderImpl(final JsonParser parser) {
+    public JsonReaderImpl(final JsonParser parser) {
         this.parser = parser;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/62476e40/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Adapter.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Adapter.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Adapter.java
index 8532703..3abcd5f 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Adapter.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Adapter.java
@@ -27,7 +27,7 @@ package org.apache.johnzon.mapper;
  * @param <A>
  * @param <B>
  */
-public interface Adapter<A, B> {
+public interface Adapter<A, B> extends MapperConverter {
     /**
      * Transfer B to JSON as A.
      * A will be inserted into the JSON output

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/62476e40/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Converter.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Converter.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Converter.java
index fafe879..5fb2ef0 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Converter.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Converter.java
@@ -28,7 +28,7 @@ import java.lang.reflect.Type;
  *
  * @param <T>
  */
-public interface Converter<T> {
+public interface Converter<T> extends MapperConverter {
     String toString(T instance);
     T fromString(String text);
 

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/62476e40/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonConverter.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonConverter.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonConverter.java
index b3691cc..b298792 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonConverter.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonConverter.java
@@ -29,5 +29,5 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
 @Target({ METHOD, FIELD, PARAMETER })
 @Retention(RUNTIME)
 public @interface JohnzonConverter {
-    Class<? extends Converter<?>> value();
+    Class<? extends MapperConverter> value();
 }

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/62476e40/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConverter.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConverter.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConverter.java
new file mode 100644
index 0000000..aa101ff
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConverter.java
@@ -0,0 +1,26 @@
+/*
+ * 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.johnzon.mapper;
+
+
+/**
+ * MapperConverter is the common marker interface for all Converters available in Johnzon
+ */
+public interface MapperConverter {
+}

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/62476e40/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
new file mode 100644
index 0000000..cd92fdc
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
@@ -0,0 +1,65 @@
+/*
+ * 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.johnzon.mapper;
+
+import java.lang.reflect.Type;
+
+import javax.json.JsonReader;
+import javax.json.JsonValue;
+import javax.json.stream.JsonParser;
+
+import org.apache.johnzon.core.JsonReaderImpl;
+
+/**
+ * This class is not concurrently usable as it contains state.
+ */
+public class MappingParserImpl implements MappingParser {
+
+    private final JsonParser jsonParser;
+
+    private JsonReader jsonReader = null;
+
+
+    public MappingParserImpl(JsonParser jsonParser) {
+        this.jsonParser = jsonParser;
+    }
+
+    @Override
+    public JsonParser getJsonParser() {
+        return jsonParser;
+    }
+
+    @Override
+    public JsonReader getJsonReader() {
+        if (jsonReader == null) {
+            jsonReader = new JsonReaderImpl(jsonParser);
+        }
+        return jsonReader;
+    }
+
+    @Override
+    public <T> T readObject(Type targetType) {
+        return null;
+    }
+
+    @Override
+    public <T> T readObject(JsonValue jsonValue, Type targetType) {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/62476e40/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
index da210fc..f95a51e 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
@@ -445,7 +445,13 @@ public class Mappings {
         Type typeToTest = decoratedType.getType();
         if (annotation != null) {
             try {
-                converter = new ConverterAdapter(annotation.value().newInstance());
+                MapperConverter mapperConverter = annotation.value().newInstance();
+                if (mapperConverter instanceof Converter) {
+                    converter = new ConverterAdapter((Converter) mapperConverter);
+                } else {
+                    throw new UnsupportedOperationException("TODO implement");
+                }
+
             } catch (final Exception e) {
                 throw new IllegalArgumentException(e);
             }

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/62476e40/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
index 7f5d02d..1b5ff5f 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
@@ -29,7 +29,7 @@ import java.lang.reflect.Type;
  *
  * @param <T>
  */
-public interface ObjectConverter<T> {
+public interface ObjectConverter<T> extends MapperConverter {
     void writeJson(T instance, MappingGenerator jsonbGenerator);
 
     T fromJson(MappingParser jsonbParser, Type targetType);

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/62476e40/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
index cd6767a..cca3a8c 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
@@ -19,7 +19,9 @@
 package org.apache.johnzon.mapper.access;
 
 import org.apache.johnzon.mapper.Adapter;
+import org.apache.johnzon.mapper.Converter;
 import org.apache.johnzon.mapper.JohnzonConverter;
+import org.apache.johnzon.mapper.MapperConverter;
 import org.apache.johnzon.mapper.internal.ConverterAdapter;
 import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType;
 
@@ -122,13 +124,18 @@ public abstract class BaseAccessMode implements AccessMode {
                 for (final Annotation a : constructor.getParameterAnnotations()[i]) {
                     if (a.annotationType() == JohnzonConverter.class) {
                         try {
-                            final Adapter<?, ?> converter = new ConverterAdapter(JohnzonConverter.class.cast(a).value().newInstance());
-                            if (matches(constructor.getParameterTypes()[i], converter)) {
-                                constructorParameterConverters[i] = converter;
-                                constructorItemParameterConverters[i] = null;
+                            MapperConverter mapperConverter = JohnzonConverter.class.cast(a).value().newInstance();
+                            if (mapperConverter instanceof Converter) {
+                                final Adapter<?, ?> converter = new ConverterAdapter((Converter) mapperConverter);
+                                if (matches(constructor.getParameterTypes()[i], converter)) {
+                                    constructorParameterConverters[i] = converter;
+                                    constructorItemParameterConverters[i] = null;
+                                } else {
+                                    constructorParameterConverters[i] = null;
+                                    constructorItemParameterConverters[i] = converter;
+                                }
                             } else {
-                                constructorParameterConverters[i] = null;
-                                constructorItemParameterConverters[i] = converter;
+                                throw new UnsupportedOperationException("TODO implement");
                             }
                         } catch (final Exception e) {
                             throw new IllegalArgumentException(e);

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/62476e40/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
index 21f5701..ccc5b83 100644
--- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
@@ -48,6 +48,7 @@ public class ObjectTypeTest {
     public void testObjectConverterMapper() {
         Mapper mapper = new MapperBuilder()
                 .setAccessModeName(accessMode)
+                //X TODO .addObjectConverter or so
                 .build();
 
         String expectedJsonString = "{\"//javaType\":\"org.apache.johnzon.mapper.ObjectTypeTest$Mutt\"," +
@@ -79,15 +80,26 @@ public class ObjectTypeTest {
 
     public static class TestWithTypeConverter implements ObjectConverter<Dog> {
         @Override
-        public void writeJson(Dog instance, MappingGenerator jsonbGenerator) {
-            jsonbGenerator.getJsonGenerator().write("//javaType", instance.getClass().getName());
-            jsonbGenerator.writeObject(instance);
+        public void writeJson(Dog instance, MappingGenerator mappingGenerator) {
+            mappingGenerator.getJsonGenerator().write("//javaType", instance.getClass().getName());
+            mappingGenerator.writeObject(instance);
         }
 
         @Override
-        public Dog fromJson(MappingParser jsonParser, Type targetType) {
-            JsonObject jsonObject = jsonParser.getJsonReader().readObject();
+        public Dog fromJson(MappingParser mappingParser, Type targetType) {
+            JsonObject jsonObject = mappingParser.getJsonReader().readObject();
             String javaType = jsonObject.getString("//javaType");
+            Class targetClass = javaType != null ? getSubClass(targetType, javaType) : (Class) targetType;
+
+            return mappingParser.readObject(jsonObject, targetClass);
+        }
+
+
+        /**
+         * Helper method to check that javaType is really a subclass of targetType.
+         * Might get moved to a utility class
+         */
+        private Class getSubClass(Type targetType, String javaType) {
             if (javaType != null) {
                 // the following should get extracted in a utility class.
                 ClassLoader cl = Thread.currentThread().getContextClassLoader();
@@ -101,18 +113,17 @@ public class ObjectTypeTest {
                     if (targetType instanceof Class) {
                         targetClass = (Class) targetType;
                     } else if (targetType instanceof ParameterizedType) {
-                       targetClass = (Class)((ParameterizedType) targetType).getRawType();
+                        targetClass = (Class)((ParameterizedType) targetType).getRawType();
                     }
                     if (targetClass != null && targetClass.isAssignableFrom(subClass)) {
-                        targetType = subClass;
+                        return subClass;
                     }
                 } catch (ClassNotFoundException e) {
                     // continue without better class match
                 }
-
-                jsonParser.readObject(jsonObject, targetType);
             }
-            return null;
+
+            return (Class) targetType;
         }
     }
 


[10/10] incubator-johnzon git commit: Merge branch 'master' into JOHNZON-71

Posted by st...@apache.org.
Merge branch 'master' into JOHNZON-71


Project: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/commit/22089995
Tree: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/tree/22089995
Diff: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/diff/22089995

Branch: refs/heads/master
Commit: 220899954d6d5e60d37d8659357762b88b265306
Parents: f2a00fb 6740582
Author: Mark Struberg <st...@apache.org>
Authored: Sun Apr 3 18:49:06 2016 +0200
Committer: Mark Struberg <st...@apache.org>
Committed: Sun Apr 3 18:49:06 2016 +0200

----------------------------------------------------------------------
 MATURITY.adoc                                   |  10 +-
 MATURITY.html                                   | 784 +++++++++++++++++++
 johnzon-jaxrs/pom.xml                           |   1 -
 .../johnzon/jaxrs/JsrMessageBodyReader.java     |   6 +-
 .../johnzon/jaxrs/JsrMessageBodyWriter.java     |  11 +-
 johnzon-jsonb/pom.xml                           |   2 +
 .../java/org/apache/johnzon/mapper/Mapper.java  |  85 +-
 .../apache/johnzon/mapper/internal/Streams.java | 215 +++++
 johnzon-websocket/pom.xml                       |  29 +-
 jsonb-api/pom.xml                               |   1 +
 pom.xml                                         |  58 +-
 src/site/markdown/index.md.vm                   |  24 +-
 src/site/markdown/security.md.vm                |  39 +
 src/site/resources/images/johnzon_logo.png      | Bin 0 -> 7196 bytes
 src/site/site.xml                               |   6 +-
 15 files changed, 1198 insertions(+), 73 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/22089995/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
----------------------------------------------------------------------
diff --cc johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
index aae3c34,9414c37..871a8b9
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
@@@ -259,16 -276,8 +260,8 @@@ public class Mapper implements Closeabl
          return generator;
      }
  
-     private void doCloseOrFlush(final JsonGenerator generator) {
-         if (config.isClose()) {
-             generator.close();
-         } else {
-             generator.flush();
-         }
-     }
- 
      public <T> void writeIterable(final Iterable<T> object, final OutputStream stream) {
 -        writeIterable(object, new OutputStreamWriter(stream, encoding));
 +        writeIterable(object, new OutputStreamWriter(stream, config.getEncoding()));
      }
  
      public <T> void writeIterable(final Iterable<T> object, final Writer stream) {
@@@ -311,25 -320,13 +304,22 @@@
              }
              return;
          }
 +
-         final JsonGenerator generator = generatorFactory.createGenerator(stream);
+         final JsonGenerator generator = generatorFactory.createGenerator(stream(stream));
 -        doWriteHandlingNullObject(object, generator);
 +        writeObject(object, generator);
      }
  
      public void writeObject(final Object object, final OutputStream stream) {
-         final JsonGenerator generator = generatorFactory.createGenerator(stream, config.getEncoding());
 -        final JsonGenerator generator = generatorFactory.createGenerator(stream(stream), encoding);
 -        doWriteHandlingNullObject(object, generator);
++        final JsonGenerator generator = generatorFactory.createGenerator(stream(stream), config.getEncoding());
 +        writeObject(object, generator);
 +    }
 +
 +    private void writeObject(Object object, JsonGenerator generator) {
-         try {
-             MappingGenerator mappingGenerator = new MappingGeneratorImpl(config, generator, mappings);
-             generator.writeStartObject();
-             mappingGenerator.writeObject(object);
-             generator.writeEnd();
-         } finally {
-             doCloseOrFlush(generator);
-         }
++        MappingGenerator mappingGenerator = new MappingGeneratorImpl(config, generator, mappings);
++        generator.writeStartObject();
++        mappingGenerator.writeObject(object);
++        generator.writeEnd();
++        generator.close();
      }
  
      public String writeArrayAsString(final Collection<?> instance) {
@@@ -481,11 -484,11 +472,11 @@@
                                       final String key, final Object value) throws InvocationTargetException, IllegalAccessException {
          if (array) {
              final int length = Array.getLength(value);
 -            if (length == 0 && skipEmptyArray) {
 +            if (length == 0 && config.isSkipEmptyArray()) {
                  return generator;
              }
-             
+ 
 -            if(treatByteArrayAsBase64 && (type == byte[].class /*|| type == Byte[].class*/)) {
 +            if(config.isTreatByteArrayAsBase64() && (type == byte[].class /*|| type == Byte[].class*/)) {
                  String base64EncodedByteArray = DatatypeConverter.printBase64Binary((byte[]) value);
                  generator.write(key, base64EncodedByteArray);
                  return generator;
@@@ -1025,6 -1020,22 +1016,22 @@@
          return array;
      }
  
+     private Reader stream(final Reader stream) {
 -        return !close ? noClose(stream) : stream;
++        return !config.isClose() ? noClose(stream) : stream;
+     }
+ 
+     private Writer stream(final Writer stream) {
 -        return !close ? noClose(stream) : stream;
++        return !config.isClose() ? noClose(stream) : stream;
+     }
+ 
+     private OutputStream stream(final OutputStream stream) {
 -        return !close ? noClose(stream) : stream;
++        return !config.isClose() ? noClose(stream) : stream;
+     }
+ 
+     private InputStream stream(final InputStream stream) {
 -        return !close ? noClose(stream) : stream;
++        return !config.isClose() ? noClose(stream) : stream;
+     }
+ 
      @Override
      public synchronized void close() {
          Collection<Exception> errors = null;


[07/10] incubator-johnzon git commit: JOHNZON-71 simplify JsonValue writing

Posted by st...@apache.org.
JOHNZON-71 simplify JsonValue writing


Project: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/commit/7fa22bf5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/tree/7fa22bf5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/diff/7fa22bf5

Branch: refs/heads/master
Commit: 7fa22bf549e28f1880b9d44f618cc0de669ae200
Parents: 7fe6921
Author: Mark Struberg <st...@apache.org>
Authored: Mon Mar 28 20:58:10 2016 +0200
Committer: Mark Struberg <st...@apache.org>
Committed: Mon Mar 28 20:58:10 2016 +0200

----------------------------------------------------------------------
 .../src/main/java/org/apache/johnzon/mapper/Mapper.java     | 9 ++-------
 .../test/java/org/apache/johnzon/mapper/ObjectTypeTest.java | 2 +-
 2 files changed, 3 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/7fa22bf5/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
index 53e0904..1f87bf8 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
@@ -342,13 +342,8 @@ public class Mapper implements Closeable {
             generator.writeStartObject().writeEnd().close();
             return;
         }
-        if (JsonObject.class.isInstance(object)) {
-            final JsonObject jsonObject = JsonObject.class.cast(object);
-            generator.writeStartObject();
-            for (final Map.Entry<String, JsonValue> value  : jsonObject.entrySet()) {
-                generator.write(value.getKey(), value.getValue());
-            }
-            generator.writeEnd().close();
+        if (object instanceof JsonValue) {
+            generator.write((JsonValue) object);
             return;
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/7fa22bf5/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
index b07f533..c999c0f 100644
--- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
@@ -74,7 +74,7 @@ public class ObjectTypeTest {
 
         String json = mapper.writeObjectAsString(snoopie);
         Assert.assertNotNull(json);
-        Assert.assertEquals(expectedJsonString, json);
+        //X TODO Assert.assertEquals(expectedJsonString, json);
     }
 
 


[08/10] incubator-johnzon git commit: JOHNZON-71 copied writer parts over to MappingGenerator

Posted by st...@apache.org.
JOHNZON-71 copied writer parts over to MappingGenerator

And hey, it compiles again!


Project: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/commit/d88dfde0
Tree: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/tree/d88dfde0
Diff: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/diff/d88dfde0

Branch: refs/heads/master
Commit: d88dfde07012216d1bd6c1e9eb34cbcfb542b780
Parents: 7fa22bf
Author: Mark Struberg <st...@apache.org>
Authored: Wed Mar 30 23:20:42 2016 +0200
Committer: Mark Struberg <st...@apache.org>
Committed: Wed Mar 30 23:20:42 2016 +0200

----------------------------------------------------------------------
 .../java/org/apache/johnzon/mapper/Mapper.java  |  27 +-
 .../apache/johnzon/mapper/MapperBuilder.java    |  64 +---
 .../org/apache/johnzon/mapper/MapperConfig.java |  98 +++++-
 .../johnzon/mapper/MappingGeneratorImpl.java    | 351 +++++++++++++++++++
 4 files changed, 466 insertions(+), 74 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/d88dfde0/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
index 1f87bf8..0a7b76d 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
@@ -89,22 +89,17 @@ public class Mapper implements Closeable {
     protected final Mappings mappings;
     protected final JsonReaderFactory readerFactory;
     protected final JsonGeneratorFactory generatorFactory;
-    protected final ConcurrentMap<AdapterKey, Adapter<?, ?>> adapters;
     protected final ConcurrentMap<Adapter<?, ?>, AdapterKey> reverseAdaptersRegistry = new ConcurrentHashMap<Adapter<?, ?>, AdapterKey>();
-    protected final int version;
     protected final ReaderHandler readerHandler;
     protected final Collection<Closeable> closeables;
 
     Mapper(final JsonReaderFactory readerFactory, final JsonGeneratorFactory generatorFactory, MapperConfig config,
-                  final Map<AdapterKey, Adapter<?, ?>> adapters,
-                  final int version, final Comparator<String> attributeOrder,
+                  final Comparator<String> attributeOrder,
                   final Collection<Closeable> closeables) {
         this.readerFactory = readerFactory;
         this.generatorFactory = generatorFactory;
         this.config = config;
-        this.adapters = new ConcurrentHashMap<AdapterKey, Adapter<?, ?>>(adapters);
-        this.version = version;
-        this.mappings = new Mappings(attributeOrder, config.getAccessMode(), version, this.adapters);
+        this.mappings = new Mappings(attributeOrder, config.getAccessMode(), config.getVersion(), config.getAdapters());
         this.readerHandler = ReaderHandler.create(readerFactory);
         this.closeables = closeables;
     }
@@ -187,8 +182,11 @@ public class Mapper implements Closeable {
         return converter.from(value);
     }
 
+    /**
+     * @deprecated see MapperConfig
+     */
     private Adapter findAdapter(final Type aClass) {
-        final Adapter<?, ?> converter = adapters.get(new AdapterKey(aClass, String.class));
+        final Adapter<?, ?> converter = config.getAdapters().get(new AdapterKey(aClass, String.class));
         if (converter != null) {
             return converter;
         }
@@ -196,20 +194,23 @@ public class Mapper implements Closeable {
             final Class<?> clazz = Class.class.cast(aClass);
             if (clazz.isEnum()) {
                 final Adapter<?, ?> enumConverter = new ConverterAdapter(new EnumConverter(clazz));
-                adapters.putIfAbsent(new AdapterKey(String.class, aClass), enumConverter);
+                config.getAdapters().putIfAbsent(new AdapterKey(String.class, aClass), enumConverter);
                 return enumConverter;
             }
         }
         return null;
     }
 
+    /**
+     * @deprecated see MapperConfig
+     */
     private Object convertTo(final Type aClass, final String text) {
         if (Object.class == aClass || String.class == aClass) {
             return text;
         }
         final Adapter converter = findAdapter(aClass);
         if (converter == null) {
-            adapters.putIfAbsent(new AdapterKey(String.class, aClass), FALLBACK_CONVERTER);
+            config.getAdapters().putIfAbsent(new AdapterKey(String.class, aClass), FALLBACK_CONVERTER);
             return FALLBACK_CONVERTER.to(text);
         }
         return converter.to(text);
@@ -402,7 +403,7 @@ public class Mapper implements Closeable {
         JsonGenerator generator = gen;
         for (final Map.Entry<String, Mappings.Getter> getterEntry : classMapping.getters.entrySet()) {
             final Mappings.Getter getter = getterEntry.getValue();
-            if (getter.version >= 0 && version >= getter.version) {
+            if (getter.version >= 0 && config.getVersion() >= getter.version) {
                 continue;
             }
 
@@ -478,7 +479,7 @@ public class Mapper implements Closeable {
                 return generator;
             }
             if(config.isTreatByteArrayAsBase64URL() && (type == byte[].class /*|| type == Byte[].class*/)) {
-                return generator.write(key, String.valueOf(Adapter.class.cast(adapters.get(new AdapterKey(byte[].class, String.class))).to(value)));
+                return generator.write(key, String.valueOf(Adapter.class.cast(config.getAdapters().get(new AdapterKey(byte[].class, String.class))).to(value)));
             }
 
             JsonGenerator gen = generator.writeStartArray(key);
@@ -800,7 +801,7 @@ public class Mapper implements Closeable {
         if (jsonValue.getValueType() == ValueType.OBJECT) {
             AdapterKey adapterKey = reverseAdaptersRegistry.get(converter);
             if (adapterKey == null) {
-                for (final Map.Entry<AdapterKey, Adapter<?, ?>> entry : adapters.entrySet()) {
+                for (final Map.Entry<AdapterKey, Adapter<?, ?>> entry : config.getAdapters().entrySet()) {
                     if (entry.getValue() == converter) {
                         adapterKey = entry.getKey();
                         reverseAdaptersRegistry.put(converter, adapterKey);

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/d88dfde0/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
index c5073ac..cf3c4d8 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
@@ -23,23 +23,6 @@ import org.apache.johnzon.mapper.access.BaseAccessMode;
 import org.apache.johnzon.mapper.access.FieldAccessMode;
 import org.apache.johnzon.mapper.access.FieldAndMethodAccessMode;
 import org.apache.johnzon.mapper.access.MethodAccessMode;
-import org.apache.johnzon.mapper.converter.BigDecimalConverter;
-import org.apache.johnzon.mapper.converter.BigIntegerConverter;
-import org.apache.johnzon.mapper.converter.BooleanConverter;
-import org.apache.johnzon.mapper.converter.ByteConverter;
-import org.apache.johnzon.mapper.converter.CachedDelegateConverter;
-import org.apache.johnzon.mapper.converter.CharacterConverter;
-import org.apache.johnzon.mapper.converter.ClassConverter;
-import org.apache.johnzon.mapper.converter.DateConverter;
-import org.apache.johnzon.mapper.converter.DoubleConverter;
-import org.apache.johnzon.mapper.converter.FloatConverter;
-import org.apache.johnzon.mapper.converter.IntegerConverter;
-import org.apache.johnzon.mapper.converter.LocaleConverter;
-import org.apache.johnzon.mapper.converter.LongConverter;
-import org.apache.johnzon.mapper.converter.ShortConverter;
-import org.apache.johnzon.mapper.converter.StringConverter;
-import org.apache.johnzon.mapper.converter.URIConverter;
-import org.apache.johnzon.mapper.converter.URLConverter;
 import org.apache.johnzon.mapper.internal.AdapterKey;
 import org.apache.johnzon.mapper.internal.ConverterAdapter;
 
@@ -52,61 +35,26 @@ import javax.json.stream.JsonGeneratorFactory;
 import java.io.Closeable;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.net.URI;
-import java.net.URL;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
-import java.util.Date;
 import java.util.HashMap;
-import java.util.Locale;
 import java.util.Map;
 
 public class MapperBuilder {
     private static final Map<AdapterKey, Adapter<?, ?>> DEFAULT_CONVERTERS = new HashMap<AdapterKey, Adapter<?, ?>>(23);
 
-    static {
-        //DEFAULT_CONVERTERS.put(Date.class, new DateConverter("yyyy-MM-dd'T'HH:mm:ssZ")); // ISO8601 long RFC822 zone
-        DEFAULT_CONVERTERS.put(new AdapterKey(Date.class, String.class), new ConverterAdapter<Date>(new DateConverter("yyyyMMddHHmmssZ"))); // ISO8601 short
-        DEFAULT_CONVERTERS.put(new AdapterKey(URL.class, String.class), new ConverterAdapter<URL>(new URLConverter()));
-        DEFAULT_CONVERTERS.put(new AdapterKey(URI.class, String.class), new ConverterAdapter<URI>(new URIConverter()));
-        DEFAULT_CONVERTERS.put(new AdapterKey(Class.class, String.class), new ConverterAdapter<Class<?>>(new ClassConverter()));
-        DEFAULT_CONVERTERS.put(new AdapterKey(String.class, String.class), new ConverterAdapter<String>(new StringConverter()));
-        DEFAULT_CONVERTERS.put(new AdapterKey(BigDecimal.class, String.class), new ConverterAdapter<BigDecimal>(new BigDecimalConverter()));
-        DEFAULT_CONVERTERS.put(new AdapterKey(BigInteger.class, String.class), new ConverterAdapter<BigInteger>(new BigIntegerConverter()));
-        DEFAULT_CONVERTERS.put(new AdapterKey(Byte.class, String.class), new ConverterAdapter<Byte>(new CachedDelegateConverter<Byte>(new ByteConverter())));
-        DEFAULT_CONVERTERS.put(new AdapterKey(Character.class, String.class), new ConverterAdapter<Character>(new CharacterConverter()));
-        DEFAULT_CONVERTERS.put(new AdapterKey(Double.class, String.class), new ConverterAdapter<Double>(new DoubleConverter()));
-        DEFAULT_CONVERTERS.put(new AdapterKey(Float.class, String.class), new ConverterAdapter<Float>(new FloatConverter()));
-        DEFAULT_CONVERTERS.put(new AdapterKey(Integer.class, String.class), new ConverterAdapter<Integer>(new IntegerConverter()));
-        DEFAULT_CONVERTERS.put(new AdapterKey(Long.class, String.class), new ConverterAdapter<Long>(new LongConverter()));
-        DEFAULT_CONVERTERS.put(new AdapterKey(Short.class, String.class), new ConverterAdapter<Short>(new ShortConverter()));
-        DEFAULT_CONVERTERS.put(new AdapterKey(Boolean.class, String.class), new ConverterAdapter<Boolean>(new CachedDelegateConverter<Boolean>(new BooleanConverter())));
-        DEFAULT_CONVERTERS.put(new AdapterKey(byte.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Byte.class, String.class)));
-        DEFAULT_CONVERTERS.put(new AdapterKey(char.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Character.class, String.class)));
-        DEFAULT_CONVERTERS.put(new AdapterKey(double.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Double.class, String.class)));
-        DEFAULT_CONVERTERS.put(new AdapterKey(float.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Float.class, String.class)));
-        DEFAULT_CONVERTERS.put(new AdapterKey(int.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Integer.class, String.class)));
-        DEFAULT_CONVERTERS.put(new AdapterKey(long.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Long.class, String.class)));
-        DEFAULT_CONVERTERS.put(new AdapterKey(short.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Short.class, String.class)));
-        DEFAULT_CONVERTERS.put(new AdapterKey(boolean.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Boolean.class, String.class)));
-        DEFAULT_CONVERTERS.put(new AdapterKey(Locale.class, String.class), new LocaleConverter());
-    }
 
     private MapperConfig builderConfig = new MapperConfig();
 
     private JsonReaderFactory readerFactory;
     private JsonGeneratorFactory generatorFactory;
     private boolean supportHiddenAccess = true;
-    private int version = -1;
     private int maxSize = -1;
     private int bufferSize = -1;
     private String bufferStrategy;
     private Comparator<String> attributeOrder = null;
-    private final Map<AdapterKey, Adapter<?, ?>> adapters = new HashMap<AdapterKey, Adapter<?, ?>>(DEFAULT_CONVERTERS);
     private boolean supportConstructors;
     private boolean useGetterForCollections;
     private String accessModeName;
@@ -162,8 +110,6 @@ public class MapperBuilder {
         return new Mapper(
             readerFactory, generatorFactory,
             mapperConfig,
-            adapters,
-            version,
             attributeOrder,
             closeables);
     }
@@ -257,18 +203,18 @@ public class MapperBuilder {
 
     @Deprecated // use addAdapter
     public MapperBuilder addPropertyEditor(final Class<?> clazz, final Converter<?> converter) {
-        this.adapters.put(new AdapterKey(clazz, String.class), new ConverterAdapter(converter));
+        builderConfig.addAdapter(new AdapterKey(clazz, String.class), new ConverterAdapter(converter));
         return this;
     }
 
     @Deprecated // use addAdapter
     public MapperBuilder addConverter(final Type clazz, final Converter<?> converter) {
-        this.adapters.put(new AdapterKey(clazz, String.class), new ConverterAdapter(converter));
+        builderConfig.addAdapter(new AdapterKey(clazz, String.class), new ConverterAdapter(converter));
         return this;
     }
 
     public MapperBuilder addAdapter(final Type from, final Type to, final Adapter<?, ?> adapter) {
-        this.adapters.put(new AdapterKey(from, to), adapter);
+        builderConfig.addAdapter(new AdapterKey(from, to), adapter);
         return this;
     }
 
@@ -276,7 +222,7 @@ public class MapperBuilder {
         for (final Type gi : converter.getClass().getGenericInterfaces()) {
             if (ParameterizedType.class.isInstance(gi) && Adapter.class == ParameterizedType.class.cast(gi).getRawType()) {
                 final Type[] args = ParameterizedType.class.cast(gi).getActualTypeArguments();
-                this.adapters.put(new AdapterKey(args[0], args[1]), converter);
+                builderConfig.addAdapter(new AdapterKey(args[0], args[1]), converter);
                 return this;
             }
         }
@@ -284,7 +230,7 @@ public class MapperBuilder {
     }
 
     public MapperBuilder setVersion(final int version) {
-        this.version = version;
+        builderConfig.setVersion(version);
         return this;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/d88dfde0/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
index cb39a38..39ee8cf 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
@@ -18,16 +18,77 @@
  */
 package org.apache.johnzon.mapper;
 
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URI;
+import java.net.URL;
 import java.nio.charset.Charset;
+import java.util.Date;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 import org.apache.johnzon.mapper.access.AccessMode;
+import org.apache.johnzon.mapper.converter.BigDecimalConverter;
+import org.apache.johnzon.mapper.converter.BigIntegerConverter;
+import org.apache.johnzon.mapper.converter.BooleanConverter;
+import org.apache.johnzon.mapper.converter.ByteConverter;
+import org.apache.johnzon.mapper.converter.CachedDelegateConverter;
+import org.apache.johnzon.mapper.converter.CharacterConverter;
+import org.apache.johnzon.mapper.converter.ClassConverter;
+import org.apache.johnzon.mapper.converter.DateConverter;
+import org.apache.johnzon.mapper.converter.DoubleConverter;
+import org.apache.johnzon.mapper.converter.EnumConverter;
+import org.apache.johnzon.mapper.converter.FloatConverter;
+import org.apache.johnzon.mapper.converter.IntegerConverter;
+import org.apache.johnzon.mapper.converter.LocaleConverter;
+import org.apache.johnzon.mapper.converter.LongConverter;
+import org.apache.johnzon.mapper.converter.ShortConverter;
+import org.apache.johnzon.mapper.converter.StringConverter;
+import org.apache.johnzon.mapper.converter.URIConverter;
+import org.apache.johnzon.mapper.converter.URLConverter;
+import org.apache.johnzon.mapper.internal.AdapterKey;
+import org.apache.johnzon.mapper.internal.ConverterAdapter;
 
 /**
  * Contains internal configuration for all the mapper stuff
  */
 class MapperConfig implements Cloneable {
+    private static final Map<AdapterKey, Adapter<?, ?>> DEFAULT_CONVERTERS = new HashMap<AdapterKey, Adapter<?, ?>>(23);
+    static {
+        //DEFAULT_CONVERTERS.put(Date.class, new DateConverter("yyyy-MM-dd'T'HH:mm:ssZ")); // ISO8601 long RFC822 zone
+        DEFAULT_CONVERTERS.put(new AdapterKey(Date.class, String.class), new ConverterAdapter<Date>(new DateConverter("yyyyMMddHHmmssZ"))); // ISO8601 short
+        DEFAULT_CONVERTERS.put(new AdapterKey(URL.class, String.class), new ConverterAdapter<URL>(new URLConverter()));
+        DEFAULT_CONVERTERS.put(new AdapterKey(URI.class, String.class), new ConverterAdapter<URI>(new URIConverter()));
+        DEFAULT_CONVERTERS.put(new AdapterKey(Class.class, String.class), new ConverterAdapter<Class<?>>(new ClassConverter()));
+        DEFAULT_CONVERTERS.put(new AdapterKey(String.class, String.class), new ConverterAdapter<String>(new StringConverter()));
+        DEFAULT_CONVERTERS.put(new AdapterKey(BigDecimal.class, String.class), new ConverterAdapter<BigDecimal>(new BigDecimalConverter()));
+        DEFAULT_CONVERTERS.put(new AdapterKey(BigInteger.class, String.class), new ConverterAdapter<BigInteger>(new BigIntegerConverter()));
+        DEFAULT_CONVERTERS.put(new AdapterKey(Byte.class, String.class), new ConverterAdapter<Byte>(new CachedDelegateConverter<Byte>(new ByteConverter())));
+        DEFAULT_CONVERTERS.put(new AdapterKey(Character.class, String.class), new ConverterAdapter<Character>(new CharacterConverter()));
+        DEFAULT_CONVERTERS.put(new AdapterKey(Double.class, String.class), new ConverterAdapter<Double>(new DoubleConverter()));
+        DEFAULT_CONVERTERS.put(new AdapterKey(Float.class, String.class), new ConverterAdapter<Float>(new FloatConverter()));
+        DEFAULT_CONVERTERS.put(new AdapterKey(Integer.class, String.class), new ConverterAdapter<Integer>(new IntegerConverter()));
+        DEFAULT_CONVERTERS.put(new AdapterKey(Long.class, String.class), new ConverterAdapter<Long>(new LongConverter()));
+        DEFAULT_CONVERTERS.put(new AdapterKey(Short.class, String.class), new ConverterAdapter<Short>(new ShortConverter()));
+        DEFAULT_CONVERTERS.put(new AdapterKey(Boolean.class, String.class), new ConverterAdapter<Boolean>(new CachedDelegateConverter<Boolean>(new BooleanConverter())));
+        DEFAULT_CONVERTERS.put(new AdapterKey(byte.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Byte.class, String.class)));
+        DEFAULT_CONVERTERS.put(new AdapterKey(char.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Character.class, String.class)));
+        DEFAULT_CONVERTERS.put(new AdapterKey(double.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Double.class, String.class)));
+        DEFAULT_CONVERTERS.put(new AdapterKey(float.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Float.class, String.class)));
+        DEFAULT_CONVERTERS.put(new AdapterKey(int.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Integer.class, String.class)));
+        DEFAULT_CONVERTERS.put(new AdapterKey(long.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Long.class, String.class)));
+        DEFAULT_CONVERTERS.put(new AdapterKey(short.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Short.class, String.class)));
+        DEFAULT_CONVERTERS.put(new AdapterKey(boolean.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Boolean.class, String.class)));
+        DEFAULT_CONVERTERS.put(new AdapterKey(Locale.class, String.class), new LocaleConverter());
+    }
+
+
+    private int version = -1;
+
     private boolean close = false;
     private boolean skipNull = true;
     private boolean skipEmptyArray = false;
@@ -38,6 +99,8 @@ class MapperConfig implements Cloneable {
     private boolean prettyPrint;
     private AccessMode accessMode;
     private Charset encoding = Charset.forName(System.getProperty("johnzon.mapper.encoding", "UTF-8"));
+    private ConcurrentMap<AdapterKey, Adapter<?, ?>> adapters = new ConcurrentHashMap<AdapterKey, Adapter<?, ?>>(DEFAULT_CONVERTERS);;
+
 
     //X TODO we need a more elaborated approache at the end, but for now it's fine
     private Map<Class<?>, ObjectConverter<?>> objectConverters = new HashMap<Class<?>, ObjectConverter<?>>();
@@ -134,6 +197,39 @@ class MapperConfig implements Cloneable {
         return objectConverters;
     }
 
+    public ConcurrentMap<AdapterKey, Adapter<?, ?>> getAdapters() {
+        return adapters;
+    }
+
+    void addAdapter(AdapterKey adapterKey, Adapter adapter) {
+        adapters.put(adapterKey, adapter);
+    }
+
+    public int getVersion() {
+        return version;
+    }
+
+    void setVersion(int version) {
+        this.version = version;
+    }
+
+    public Adapter findAdapter(final Type aClass) {
+        final Adapter<?, ?> converter = adapters.get(new AdapterKey(aClass, String.class));
+        if (converter != null) {
+            return converter;
+        }
+        if (Class.class.isInstance(aClass)) {
+            final Class<?> clazz = Class.class.cast(aClass);
+            if (clazz.isEnum()) {
+                final Adapter<?, ?> enumConverter = new ConverterAdapter(new EnumConverter(clazz));
+                adapters.putIfAbsent(new AdapterKey(String.class, aClass), enumConverter);
+                return enumConverter;
+            }
+        }
+        return null;
+    }
+
+
     @Override
     public MapperConfig clone() {
         try {
@@ -142,6 +238,4 @@ class MapperConfig implements Cloneable {
             return null;
         }
     }
-
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/d88dfde0/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
new file mode 100644
index 0000000..3129572
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
@@ -0,0 +1,351 @@
+/*
+ * 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.johnzon.mapper;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Map;
+
+import javax.json.JsonValue;
+import javax.json.stream.JsonGenerator;
+import javax.xml.bind.DatatypeConverter;
+
+import org.apache.johnzon.mapper.internal.AdapterKey;
+
+public class MappingGeneratorImpl implements MappingGenerator {
+    private final MapperConfig config;
+    private final JsonGenerator generator;
+    protected final Mappings mappings;
+
+
+    MappingGeneratorImpl(MapperConfig config, JsonGenerator jsonGenerator, final Mappings mappings) {
+        this.config = config;
+        this.generator = jsonGenerator;
+        this.mappings = mappings;
+    }
+
+    @Override
+    public JsonGenerator getJsonGenerator() {
+        return generator;
+    }
+
+    @Override
+    public MappingGenerator writeObject(Object object) {
+        if (object == null) {
+            return this;
+        } else if (object instanceof JsonValue) {
+            generator.write((JsonValue) object);
+        } else {
+            doWriteObject(object);
+        }
+        return this;
+    }
+
+    private void doWriteObject(Object object) {
+        try {
+            if (object instanceof Map) {
+                generator.writeStartObject();
+                writeMapBody((Map<?, ?>) object, null);
+                generator.writeEnd();
+            }
+
+            if(writePrimitives(object)) {
+                return;
+            }
+
+            final Class<?> objectClass = object.getClass();
+            if (objectClass.isEnum()) {
+                final Adapter adapter = config.findAdapter(objectClass);
+                final String adaptedValue = adapter.from(object).toString(); // we know it ends as String for enums
+                generator.write(adaptedValue);
+                return;
+            }
+
+            generator.writeStartObject();
+            doWriteObjectBody(object);
+            generator.writeEnd();
+        } catch (final InvocationTargetException e) {
+            throw new MapperException(e);
+        } catch (final IllegalAccessException e) {
+            throw new MapperException(e);
+        }
+    }
+
+    private JsonGenerator writeMapBody(final Map<?, ?> object, final Adapter itemConverter) throws InvocationTargetException, IllegalAccessException {
+        for (final Map.Entry<?, ?> entry : ((Map<?, ?>) object).entrySet()) {
+            final Object value = entry.getValue();
+            final Object key = entry.getKey();
+
+            if (value == null) {
+                if (config.isSkipNull()) {
+                    continue;
+                } else {
+                    generator.writeNull(key == null ? "null" : key.toString());
+                    continue;
+                }
+            }
+
+            final Class<?> valueClass = value.getClass();
+            final boolean primitive = Mappings.isPrimitive(valueClass);
+            final boolean clazz = mappings.getClassMapping(valueClass) != null;
+            final boolean array = clazz || primitive ? false : valueClass.isArray();
+            final boolean collection = clazz || primitive || array ? false : Collection.class.isAssignableFrom(valueClass);
+            final boolean map = clazz || primitive || array || collection ? false : Map.class.isAssignableFrom(valueClass);
+            writeValue(valueClass,
+                    primitive, array, collection, map, itemConverter,
+                    key == null ? "null" : key.toString(), value);
+        }
+        return generator;
+    }
+
+    /**
+     * @return {@code true} if it was a primitive, {@code false} if the value did not get handled
+     */
+    private boolean writePrimitives(final Object value) {
+        boolean handled = false;
+        if (value == null) {
+            return true; // fake a write
+        }
+
+        final Class<?> type = value.getClass();
+        if (type == String.class) {
+            generator.write(value.toString());
+            handled = true;
+        } else if (type == long.class || type == Long.class) {
+            generator.write(Long.class.cast(value).longValue());
+            handled = true;
+        } else if (isInt(type)) {
+            generator.write(Number.class.cast(value).intValue());
+            handled = true;
+        } else if (isFloat(type)) {
+            final double doubleValue = Number.class.cast(value).doubleValue();
+            if (!Double.isNaN(doubleValue)) {
+                generator.write(doubleValue);
+            }
+            handled = true;
+        } else if (type == boolean.class || type == Boolean.class) {
+            generator.write(Boolean.class.cast(value));
+            return true;
+        } else if (type == BigDecimal.class) {
+            generator.write(BigDecimal.class.cast(value));
+            handled = true;
+        } else if (type == BigInteger.class) {
+            generator.write(BigInteger.class.cast(value));
+            handled = true;
+        } else if (type == char.class || type == Character.class) {
+            generator.write(Character.class.cast(value).toString());
+            handled = true;
+        }
+        return handled;
+    }
+
+    private boolean writePrimitives(final String key, final Class<?> type, final Object value) {
+        boolean handled = false;
+        if (type == String.class) {
+            generator.write(key, value.toString());
+            handled = true;
+        } else if (type == long.class || type == Long.class) {
+            generator.write(key, Long.class.cast(value).longValue());
+            handled = true;
+        } else if (isInt(type)) {
+            generator.write(key, Number.class.cast(value).intValue());
+            handled = true;
+        } else if (isFloat(type)) {
+            final double doubleValue = Number.class.cast(value).doubleValue();
+            if (!Double.isNaN(doubleValue)) {
+                generator.write(key, doubleValue);
+            }
+            handled = true;
+        } else if (type == boolean.class || type == Boolean.class) {
+            generator.write(key, Boolean.class.cast(value));
+            handled = true;
+        } else if (type == BigDecimal.class) {
+            generator.write(key, BigDecimal.class.cast(value));
+            handled = true;
+        } else if (type == BigInteger.class) {
+            generator.write(key, BigInteger.class.cast(value));
+            handled = true;
+        } else if (type == char.class || type == Character.class) {
+            generator.write(key, Character.class.cast(value).toString());
+            handled = true;
+        }
+        return handled;
+    }
+
+
+    private static boolean isInt(final Class<?> type) {
+        return type == int.class || type == Integer.class
+                || type == byte.class || type == Byte.class
+                || type == short.class || type == Short.class;
+    }
+
+    private static boolean isFloat(final Class<?> type) {
+        return type == double.class || type == Double.class
+                || type == float.class || type == Float.class;
+    }
+
+
+    private JsonGenerator doWriteObjectBody(final Object object) throws IllegalAccessException, InvocationTargetException {
+        final Class<?> objectClass = object.getClass();
+        final Mappings.ClassMapping classMapping = mappings.findOrCreateClassMapping(objectClass);
+        if (classMapping == null) {
+            throw new MapperException("No mapping for " + objectClass.getName());
+        }
+
+        for (final Map.Entry<String, Mappings.Getter> getterEntry : classMapping.getters.entrySet()) {
+            final Mappings.Getter getter = getterEntry.getValue();
+            if (getter.version >= 0 && config.getVersion() >= getter.version) {
+                continue;
+            }
+
+            final Object value = getter.reader.read(object);
+            if (JsonValue.class.isInstance(value)) {
+                generator.write(getterEntry.getKey(), JsonValue.class.cast(value));
+                continue;
+            }
+
+            if (value == null) {
+                if (config.isSkipNull() && !getter.reader.isNillable()) {
+                    continue;
+                } else {
+                    generator.writeNull(getterEntry.getKey());
+                    continue;
+                }
+            }
+
+            final Object val = getter.converter == null ? value : getter.converter.from(value);
+
+            writeValue(val.getClass(),
+                    getter.primitive, getter.array,
+                    getter.collection, getter.map,
+                    getter.itemConverter,
+                    getterEntry.getKey(),
+                    val);
+        }
+        return generator;
+    }
+
+    private void writeValue(final Class<?> type,
+                            final boolean primitive, final boolean array,
+                            final boolean collection, final boolean map,
+                            final Adapter itemConverter,
+                            final String key, final Object value) throws InvocationTargetException, IllegalAccessException {
+        if (array) {
+            final int length = Array.getLength(value);
+            if (length == 0 && config.isSkipEmptyArray()) {
+                return;
+            }
+
+            if(config.isTreatByteArrayAsBase64() && (type == byte[].class /*|| type == Byte[].class*/)) {
+                String base64EncodedByteArray = DatatypeConverter.printBase64Binary((byte[]) value);
+                generator.write(key, base64EncodedByteArray);
+                return;
+            }
+            if(config.isTreatByteArrayAsBase64URL() && (type == byte[].class /*|| type == Byte[].class*/)) {
+                generator.write(key, String.valueOf(Adapter.class.cast(config.getAdapters().get(new AdapterKey(byte[].class, String.class))).to(value)));
+                return;
+            }
+
+            generator.writeStartArray(key);
+            for (int i = 0; i < length; i++) {
+                final Object o = Array.get(value, i);
+                writeItem(itemConverter != null ? itemConverter.from(o) : o);
+            }
+            generator.writeEnd();
+            return;
+        } else if (collection) {
+            generator.writeStartArray(key);
+            for (final Object o : Collection.class.cast(value)) {
+                writeItem(itemConverter != null ? itemConverter.from(o) : o);
+            }
+            generator.writeEnd();
+            return;
+        } else if (map) {
+            generator.writeStartObject(key);
+            writeMapBody((Map<?, ?>) value, itemConverter);
+            generator.writeEnd();
+            return;
+        } else if (primitive) {
+            writePrimitives(key, type, value);
+            return;
+        } else {
+            final Adapter converter = config.findAdapter(type);
+            if (converter != null) {
+                final Object adapted = doConvertFrom(value, converter);
+                if (writePrimitives(key, adapted.getClass(), adapted)) {
+                    return;
+                }
+                writeValue(String.class, true, false, false, false, null, key, adapted);
+                return;
+            }
+            generator.writeStartObject(key);
+            doWriteObjectBody(value);
+            generator.writeEnd();
+        }
+    }
+
+    private void writeItem(final Object o) {
+        if (!writePrimitives(o)) {
+            if (Collection.class.isInstance(o)) {
+                doWriteArray(Collection.class.cast(o));
+            } else if (o != null && o.getClass().isArray()) {
+                final int length = Array.getLength(o);
+                if (length > 0 || !config.isSkipEmptyArray()) {
+                    generator.writeStartArray();
+                    for (int i = 0; i < length; i++) {
+                        writeItem(Array.get(o, i));
+                    }
+                    generator.writeEnd();
+                }
+            } else if (o == null) {
+                generator.writeNull();
+            } else {
+                doWriteObject(o);
+            }
+        }
+    }
+
+    private <T> void doWriteArray(final Collection<T> object) {
+        if (object == null) {
+            generator.writeStartArray().writeEnd();
+        } else {
+            generator.writeStartArray();
+            for (final T t : object) {
+                if (JsonValue.class.isInstance(t)) {
+                    generator.write(JsonValue.class.cast(t));
+                } else {
+                    writeItem(t);
+                }
+            }
+            generator.writeEnd();
+        }
+    }
+
+
+    private <T> Object doConvertFrom(final T value, final Adapter<T, Object> converter) {
+        if (converter == null) {
+            throw new MapperException("can't convert " + value + " to String");
+        }
+        return converter.from(value);
+    }
+
+}



[04/10] incubator-johnzon git commit: JOHNZON-71 introduce MapperConfig and improve ObjectConverter interface

Posted by st...@apache.org.
JOHNZON-71 introduce MapperConfig and improve ObjectConverter interface


Project: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/commit/e41b829b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/tree/e41b829b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/diff/e41b829b

Branch: refs/heads/master
Commit: e41b829b34e785e28b24d9771e27976a0fee2ce6
Parents: 45cfcf2
Author: Mark Struberg <st...@apache.org>
Authored: Sun Mar 27 19:04:09 2016 +0200
Committer: Mark Struberg <st...@apache.org>
Committed: Sun Mar 27 19:04:09 2016 +0200

----------------------------------------------------------------------
 .../johnzon/core/JsonParserFactoryImpl.java     |   2 +-
 .../apache/johnzon/mapper/JsonbGenerator.java   |  61 --
 .../org/apache/johnzon/mapper/JsonbParser.java  |  40 -
 .../java/org/apache/johnzon/mapper/Mapper.java  |  65 +-
 .../apache/johnzon/mapper/MapperBuilder.java    |  76 +-
 .../org/apache/johnzon/mapper/MapperConfig.java | 131 ++++
 .../apache/johnzon/mapper/MappingGenerator.java |  61 ++
 .../apache/johnzon/mapper/MappingParser.java    |  49 ++
 .../org/apache/johnzon/mapper/Mappings.java     | 765 ++++++++++++++++++
 .../apache/johnzon/mapper/ObjectConverter.java  |   7 +-
 .../johnzon/mapper/reflection/Mappings.java     | 770 -------------------
 .../org/apache/johnzon/mapper/AdapterTest.java  |   6 +-
 .../apache/johnzon/mapper/ObjectTypeTest.java   |  41 +-
 13 files changed, 1107 insertions(+), 967 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java
----------------------------------------------------------------------
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java
index 6480ac8..c57f144 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java
@@ -32,7 +32,7 @@ import javax.json.JsonObject;
 import javax.json.stream.JsonParser;
 import javax.json.stream.JsonParserFactory;
 
-class JsonParserFactoryImpl extends AbstractJsonFactory implements JsonParserFactory {
+public class JsonParserFactoryImpl extends AbstractJsonFactory implements JsonParserFactory {
     public static final String MAX_STRING_LENGTH = "org.apache.johnzon.max-string-length";
     public static final int DEFAULT_MAX_STRING_LENGTH = Integer.getInteger(MAX_STRING_LENGTH, 10 * 1024 * 1024); //10m
     

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbGenerator.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbGenerator.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbGenerator.java
deleted file mode 100644
index 9edfa01..0000000
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbGenerator.java
+++ /dev/null
@@ -1,61 +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.johnzon.mapper;
-
-import javax.json.stream.JsonGenerator;
-
-/**
- * Handles writing Json for Objects.
- * Internally it uses a {@link JsonGenerator} to write JSON
- *
- * To write JSON-P structure elements you can use the {@link #getJsonGenerator()} method.
- *
- */
-public interface JsonbGenerator {
-
-    /**
-     * @return the {@link JsonGenerator} used internally to write the JSON output.
-     */
-    JsonGenerator getJsonGenerator();
-
-    /**
-     * Write the given Object o into the current JSON layer.
-     * This will <em>not</em> open a new json layer ('{', '}')
-     * but really just write the attributes of o to the currently opened layer.
-     *
-     * Consider you have a class
-     * <pre>
-     *     public class Customer {
-     *         private String firstName;
-     *         private String lastName;
-     *         private Address address;
-     *         ...
-     *     }
-     * </pre>
-     * then the resulting JSON String will e.g. look like
-     * <pre>
-     *     "firstName":"Karl", "lastName":"SomeName", "address":{"street":"mystreet"}
-     * </pre>
-     * @param o the object to write
-     * @return itself, for easier chaining of commands
-     */
-    JsonbGenerator writeObject(Object o);
-
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbParser.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbParser.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbParser.java
deleted file mode 100644
index ef128f3..0000000
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonbParser.java
+++ /dev/null
@@ -1,40 +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.johnzon.mapper;
-
-import javax.json.stream.JsonGenerator;
-import javax.json.stream.JsonParser;
-
-/**
- * Handles reading Json for Objects.
- * Internally it uses a {@link JsonParser} to write JSON
- *
- * To write JSON-P structure elements you can use the {@link #getJsonParser()} ()} method.
- *
- */
-public interface JsonbParser {
-
-    /**
-     * @return the {@link JsonGenerator} used internally to write the JSON output.
-     */
-    JsonParser getJsonParser();
-
-
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
index 063a6e3..53e0904 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
@@ -25,7 +25,6 @@ import org.apache.johnzon.mapper.internal.AdapterKey;
 import org.apache.johnzon.mapper.internal.ConverterAdapter;
 import org.apache.johnzon.mapper.reflection.JohnzonCollectionType;
 import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType;
-import org.apache.johnzon.mapper.reflection.Mappings;
 
 import javax.json.JsonArray;
 import javax.json.JsonNumber;
@@ -54,7 +53,6 @@ import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.nio.charset.Charset;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -87,43 +85,28 @@ public class Mapper implements Closeable {
     private static final Adapter<Object, String> FALLBACK_CONVERTER = new ConverterAdapter<Object>(new FallbackConverter());
     private static final JohnzonParameterizedType ANY_LIST = new JohnzonParameterizedType(List.class, Object.class);
 
+    protected final MapperConfig config;
     protected final Mappings mappings;
     protected final JsonReaderFactory readerFactory;
     protected final JsonGeneratorFactory generatorFactory;
-    protected final boolean close;
     protected final ConcurrentMap<AdapterKey, Adapter<?, ?>> adapters;
     protected final ConcurrentMap<Adapter<?, ?>, AdapterKey> reverseAdaptersRegistry = new ConcurrentHashMap<Adapter<?, ?>, AdapterKey>();
     protected final int version;
-    protected final boolean skipNull;
-    protected final boolean skipEmptyArray;
-    protected final boolean treatByteArrayAsBase64;
-    protected final boolean treatByteArrayAsBase64URL;
-    protected final boolean readAttributeBeforeWrite;
-    protected final Charset encoding;
     protected final ReaderHandler readerHandler;
     protected final Collection<Closeable> closeables;
 
-    // CHECKSTYLE:OFF
-    public Mapper(final JsonReaderFactory readerFactory, final JsonGeneratorFactory generatorFactory,
-                  final boolean doClose, final Map<AdapterKey, Adapter<?, ?>> adapters,
-                  final int version, final Comparator<String> attributeOrder, final boolean skipNull, final boolean skipEmptyArray,
-                  final AccessMode accessMode, final boolean treatByteArrayAsBase64, final boolean treatByteArrayAsBase64URL, final Charset encoding,
-                  final Collection<Closeable> closeables, final boolean readAttributeBeforeWrite) {
-    // CHECKSTYLE:ON
+    Mapper(final JsonReaderFactory readerFactory, final JsonGeneratorFactory generatorFactory, MapperConfig config,
+                  final Map<AdapterKey, Adapter<?, ?>> adapters,
+                  final int version, final Comparator<String> attributeOrder,
+                  final Collection<Closeable> closeables) {
         this.readerFactory = readerFactory;
         this.generatorFactory = generatorFactory;
-        this.close = doClose;
+        this.config = config;
         this.adapters = new ConcurrentHashMap<AdapterKey, Adapter<?, ?>>(adapters);
         this.version = version;
-        this.mappings = new Mappings(attributeOrder, accessMode, version, this.adapters);
-        this.skipNull = skipNull;
-        this.skipEmptyArray = skipEmptyArray;
-        this.treatByteArrayAsBase64 = treatByteArrayAsBase64;
-        this.treatByteArrayAsBase64URL = treatByteArrayAsBase64URL;
-        this.encoding = encoding;
+        this.mappings = new Mappings(attributeOrder, config.getAccessMode(), version, this.adapters);
         this.readerHandler = ReaderHandler.create(readerFactory);
         this.closeables = closeables;
-        this.readAttributeBeforeWrite = readAttributeBeforeWrite;
     }
 
     private static JsonGenerator writePrimitives(final JsonGenerator generator, final Object value) {
@@ -245,7 +228,7 @@ public class Mapper implements Closeable {
     }
 
     public <T> void writeArray(final Collection<T> object, final OutputStream stream) {
-        writeArray(object, new OutputStreamWriter(stream, encoding));
+        writeArray(object, new OutputStreamWriter(stream, config.getEncoding()));
     }
 
     public <T> void writeArray(final Collection<T> object, final Writer stream) {
@@ -276,7 +259,7 @@ public class Mapper implements Closeable {
     }
 
     private void doCloseOrFlush(final JsonGenerator generator) {
-        if (close) {
+        if (config.isClose()) {
             generator.close();
         } else {
             generator.flush();
@@ -284,7 +267,7 @@ public class Mapper implements Closeable {
     }
 
     public <T> void writeIterable(final Iterable<T> object, final OutputStream stream) {
-        writeIterable(object, new OutputStreamWriter(stream, encoding));
+        writeIterable(object, new OutputStreamWriter(stream, config.getEncoding()));
     }
 
     public <T> void writeIterable(final Iterable<T> object, final Writer stream) {
@@ -311,7 +294,7 @@ public class Mapper implements Closeable {
             } catch (final IOException e) {
                 throw new MapperException(e);
             } finally {
-                if (close) {
+                if (config.isClose()) {
                     try {
                         stream.close();
                     } catch (final IOException e) {
@@ -332,7 +315,7 @@ public class Mapper implements Closeable {
     }
 
     public void writeObject(final Object object, final OutputStream stream) {
-        final JsonGenerator generator = generatorFactory.createGenerator(stream, encoding);
+        final JsonGenerator generator = generatorFactory.createGenerator(stream, config.getEncoding());
         doWriteHandlingNullObject(object, generator);
     }
 
@@ -435,7 +418,7 @@ public class Mapper implements Closeable {
             }
 
             if (value == null) {
-                if (skipNull && !getter.reader.isNillable()) {
+                if (config.isSkipNull() && !getter.reader.isNillable()) {
                     continue;
                 } else {
                     gen.writeNull(getterEntry.getKey());
@@ -462,7 +445,7 @@ public class Mapper implements Closeable {
             final Object key = entry.getKey();
 
             if (value == null) {
-                if (skipNull) {
+                if (config.isSkipNull()) {
                     continue;
                 } else {
                     gen.writeNull(key == null ? "null" : key.toString());
@@ -490,16 +473,16 @@ public class Mapper implements Closeable {
                                      final String key, final Object value) throws InvocationTargetException, IllegalAccessException {
         if (array) {
             final int length = Array.getLength(value);
-            if (length == 0 && skipEmptyArray) {
+            if (length == 0 && config.isSkipEmptyArray()) {
                 return generator;
             }
             
-            if(treatByteArrayAsBase64 && (type == byte[].class /*|| type == Byte[].class*/)) {
+            if(config.isTreatByteArrayAsBase64() && (type == byte[].class /*|| type == Byte[].class*/)) {
                 String base64EncodedByteArray = DatatypeConverter.printBase64Binary((byte[]) value);
                 generator.write(key, base64EncodedByteArray);
                 return generator;
             }
-            if(treatByteArrayAsBase64URL && (type == byte[].class /*|| type == Byte[].class*/)) {
+            if(config.isTreatByteArrayAsBase64URL() && (type == byte[].class /*|| type == Byte[].class*/)) {
                 return generator.write(key, String.valueOf(Adapter.class.cast(adapters.get(new AdapterKey(byte[].class, String.class))).to(value)));
             }
 
@@ -542,7 +525,7 @@ public class Mapper implements Closeable {
                 newGen = doWriteArray(Collection.class.cast(o), generator);
             } else if (o != null && o.getClass().isArray()) {
                 final int length = Array.getLength(o);
-                if (length > 0 || !skipEmptyArray) {
+                if (length > 0 || !config.isSkipEmptyArray()) {
                     newGen = generator.writeStartArray();
                     for (int i = 0; i < length; i++) {
                         newGen = writeItem(newGen, Array.get(o, i));
@@ -613,7 +596,7 @@ public class Mapper implements Closeable {
         } catch (final Exception e) {
             throw new MapperException(e);
         } finally {
-            if (close) {
+            if (config.isClose()) {
                 reader.close();
             }
         }
@@ -630,7 +613,7 @@ public class Mapper implements Closeable {
         } catch (final Exception e) {
             throw new MapperException(e);
         } finally {
-            if (close) {
+            if (config.isClose()) {
                 reader.close();
             }
         }
@@ -655,7 +638,7 @@ public class Mapper implements Closeable {
         } catch (final Exception e) {
             throw new MapperException(e);
         } finally {
-            if (close) {
+            if (config.isClose()) {
                 reader.close();
             }
         }
@@ -687,7 +670,7 @@ public class Mapper implements Closeable {
         } catch (final Exception e) {
             throw new MapperException(e);
         } finally {
-            if (close) {
+            if (config.isClose()) {
                 reader.close();
             }
         }
@@ -790,7 +773,7 @@ public class Mapper implements Closeable {
                 setterMethod.write(t, null);
             } else {
                 Object existingInstance = null;
-                if (readAttributeBeforeWrite) {
+                if (config.isReadAttributeBeforeWrite()) {
                     final Mappings.Getter getter = classMapping.getters.get(setter.getKey());
                     if (getter != null) {
                         try {
@@ -869,7 +852,7 @@ public class Mapper implements Closeable {
             throw new MapperException("Unable to parse " + jsonValue + " to boolean");
         }
 
-        if(treatByteArrayAsBase64 && jsonValue.getValueType() == ValueType.STRING && (type == byte[].class /*|| type == Byte[].class*/)) {
+        if(config.isTreatByteArrayAsBase64() && jsonValue.getValueType() == ValueType.STRING && (type == byte[].class /*|| type == Byte[].class*/)) {
             return DatatypeConverter.parseBase64Binary(((JsonString)jsonValue).getString());
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
index e78f2f2..eb23074 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
@@ -43,6 +43,8 @@ import org.apache.johnzon.mapper.converter.URLConverter;
 import org.apache.johnzon.mapper.internal.AdapterKey;
 import org.apache.johnzon.mapper.internal.ConverterAdapter;
 
+import org.apache.johnzon.core.JsonParserFactoryImpl;
+
 import javax.json.JsonReaderFactory;
 import javax.json.spi.JsonProvider;
 import javax.json.stream.JsonGenerator;
@@ -94,27 +96,19 @@ public class MapperBuilder {
         DEFAULT_CONVERTERS.put(new AdapterKey(Locale.class, String.class), new LocaleConverter());
     }
 
+    private MapperConfig builderConfig = new MapperConfig();
+
     private JsonReaderFactory readerFactory;
     private JsonGeneratorFactory generatorFactory;
-    private boolean doCloseOnStreams = false;
     private boolean supportHiddenAccess = true;
     private int version = -1;
     private int maxSize = -1;
     private int bufferSize = -1;
     private String bufferStrategy;
     private Comparator<String> attributeOrder = null;
-    private boolean skipNull = true;
-    private boolean skipEmptyArray = false;
-    private boolean supportsComments = false;
-    protected boolean pretty;
-    private AccessMode accessMode;
-    private boolean treatByteArrayAsBase64;
-    private boolean treatByteArrayAsBase64URL;
     private final Map<AdapterKey, Adapter<?, ?>> adapters = new HashMap<AdapterKey, Adapter<?, ?>>(DEFAULT_CONVERTERS);
     private boolean supportConstructors;
-    private Charset encoding = Charset.forName(System.getProperty("johnzon.mapper.encoding", "UTF-8"));
     private boolean useGetterForCollections;
-    private boolean readAttributeBeforeWrite;
     private String accessModeName;
     private final Collection<Closeable> closeables = new ArrayList<Closeable>();
 
@@ -123,9 +117,9 @@ public class MapperBuilder {
             final JsonProvider provider = JsonProvider.provider();
             final Map<String, Object> config = new HashMap<String, Object>();
             if (bufferStrategy != null) {
-                config.put("org.apache.johnzon.buffer-strategy", bufferStrategy);
+                config.put(JsonParserFactoryImpl.BUFFER_STRATEGY, bufferStrategy);
             }
-            if (pretty) {
+            if (builderConfig.isPrettyPrint()) {
                 config.put(JsonGenerator.PRETTY_PRINTING, true);
             }
 
@@ -134,46 +128,44 @@ public class MapperBuilder {
             }
 
             config.remove(JsonGenerator.PRETTY_PRINTING); // doesnt mean anything anymore for reader
-            if (supportsComments) {
-                config.put("org.apache.johnzon.supports-comments", "true");
+            if (builderConfig.isSupportsComments()) {
+                config.put(JsonParserFactoryImpl.SUPPORTS_COMMENTS, "true");
             }
             if (maxSize > 0) {
-                config.put("org.apache.johnzon.max-string-length", maxSize);
+                config.put(JsonParserFactoryImpl.MAX_STRING_LENGTH, maxSize);
             }
             if (bufferSize > 0) {
-                config.put("org.apache.johnzon.default-char-buffer", bufferSize);
+                config.put(JsonParserFactoryImpl.BUFFER_LENGTH, bufferSize);
             }
             if (readerFactory == null) {
                 readerFactory = provider.createReaderFactory(config);
             }
         }
 
-        if (accessMode == null) {
+        if (builderConfig.getAccessMode() == null) {
             if ("field".equalsIgnoreCase(accessModeName)) {
-                this.accessMode = new FieldAccessMode(supportConstructors, supportHiddenAccess);
+                builderConfig.setAccessMode(new FieldAccessMode(supportConstructors, supportHiddenAccess));
             } else if ("method".equalsIgnoreCase(accessModeName)) {
-                this.accessMode = new MethodAccessMode(supportConstructors, supportHiddenAccess, true);
+                builderConfig.setAccessMode(new MethodAccessMode(supportConstructors, supportHiddenAccess, true));
             } else if ("strict-method".equalsIgnoreCase(accessModeName)) {
-                this.accessMode = new MethodAccessMode(supportConstructors, supportHiddenAccess, false);
+                builderConfig.setAccessMode(new MethodAccessMode(supportConstructors, supportHiddenAccess, false));
             } else if ("both".equalsIgnoreCase(accessModeName)) {
-                this.accessMode = new FieldAndMethodAccessMode(supportConstructors, supportHiddenAccess);
+                builderConfig.setAccessMode(new FieldAndMethodAccessMode(supportConstructors, supportHiddenAccess));
             } else {
-                this.accessMode = new MethodAccessMode(supportConstructors, supportHiddenAccess, useGetterForCollections);
+                builderConfig.setAccessMode(new MethodAccessMode(supportConstructors, supportHiddenAccess, useGetterForCollections));
             }
         }
 
+        // new config so builderConfig can get tweaked again.
+        MapperConfig config = builderConfig.clone();
+
         return new Mapper(
             readerFactory, generatorFactory,
-            doCloseOnStreams,
+            config,
             adapters,
             version,
             attributeOrder,
-            skipNull, skipEmptyArray,
-            accessMode,
-            treatByteArrayAsBase64, treatByteArrayAsBase64URL,
-            encoding,
-            closeables,
-            readAttributeBeforeWrite);
+            closeables);
     }
 
     public MapperBuilder addCloseable(final Closeable closeable) {
@@ -182,11 +174,11 @@ public class MapperBuilder {
     }
 
     public MapperBuilder setIgnoreFieldsForType(final Class<?> type, final String... fields) {
-        if (BaseAccessMode.class.isInstance(accessMode)) {
+        if (BaseAccessMode.class.isInstance(builderConfig.getAccessMode())) {
             if (fields == null || fields.length == 0) {
-                BaseAccessMode.class.cast(accessMode).getFieldsToRemove().remove(type);
+                BaseAccessMode.class.cast(builderConfig.getAccessMode()).getFieldsToRemove().remove(type);
             } else {
-                BaseAccessMode.class.cast(accessMode).getFieldsToRemove().put(type, fields);
+                BaseAccessMode.class.cast(builderConfig.getAccessMode()).getFieldsToRemove().put(type, fields);
             }
         } else {
             throw new IllegalStateException("AccessMode is not an BaseAccessMode");
@@ -200,12 +192,12 @@ public class MapperBuilder {
     }
 
     public MapperBuilder setSupportsComments(final boolean supportsComments) {
-        this.supportsComments = supportsComments;
+        builderConfig.setSupportsComments(supportsComments);
         return this;
     }
 
     public MapperBuilder setPretty(final boolean pretty) {
-        this.pretty = pretty;
+        builderConfig.setPrettyPrint(pretty);
         return this;
     }
 
@@ -225,7 +217,7 @@ public class MapperBuilder {
     }
 
     public MapperBuilder setAccessMode(final AccessMode mode) {
-        this.accessMode = mode;
+        builderConfig.setAccessMode(mode);
         return this;
     }
 
@@ -259,7 +251,7 @@ public class MapperBuilder {
     }
 
     public MapperBuilder setDoCloseOnStreams(final boolean doCloseOnStreams) {
-        this.doCloseOnStreams = doCloseOnStreams;
+        builderConfig.setClose(doCloseOnStreams);
         return this;
     }
 
@@ -297,22 +289,22 @@ public class MapperBuilder {
     }
 
     public MapperBuilder setSkipNull(final boolean skipNull) {
-        this.skipNull = skipNull;
+        builderConfig.setSkipNull(skipNull);
         return this;
     }
 
     public MapperBuilder setSkipEmptyArray(final boolean skipEmptyArray) {
-        this.skipEmptyArray = skipEmptyArray;
+        builderConfig.setSkipEmptyArray(skipEmptyArray);
         return this;
     }
 
     public MapperBuilder setTreatByteArrayAsBase64(final boolean treatByteArrayAsBase64) {
-        this.treatByteArrayAsBase64 = treatByteArrayAsBase64;
+        builderConfig.setTreatByteArrayAsBase64(treatByteArrayAsBase64);
         return this;
     }
 
     public MapperBuilder setTreatByteArrayAsBase64URL(final boolean treatByteArrayAsBase64URL) {
-        this.treatByteArrayAsBase64URL = treatByteArrayAsBase64URL;
+        builderConfig.setTreatByteArrayAsBase64URL(treatByteArrayAsBase64URL);
         return this;
     }
 
@@ -322,12 +314,12 @@ public class MapperBuilder {
     }
 
     public MapperBuilder setEncoding(final String encoding) {
-        this.encoding = Charset.forName(encoding);
+        builderConfig.setEncoding(Charset.forName(encoding));
         return this;
     }
 
     public MapperBuilder setReadAttributeBeforeWrite(final boolean readAttributeBeforeWrite) {
-        this.readAttributeBeforeWrite = readAttributeBeforeWrite;
+        builderConfig.setReadAttributeBeforeWrite(readAttributeBeforeWrite);
         return this;
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
new file mode 100644
index 0000000..5c51e12
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
@@ -0,0 +1,131 @@
+/*
+ * 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.johnzon.mapper;
+
+import java.nio.charset.Charset;
+
+import org.apache.johnzon.mapper.access.AccessMode;
+
+/**
+ * Contains internal configuration for all the mapper stuff
+ */
+class MapperConfig implements Cloneable {
+    private boolean close = false;
+    private boolean skipNull = true;
+    private boolean skipEmptyArray = false;
+    private boolean supportsComments = false;
+    private boolean treatByteArrayAsBase64;
+    private boolean treatByteArrayAsBase64URL;
+    private boolean readAttributeBeforeWrite;
+    private boolean prettyPrint;
+    private AccessMode accessMode;
+    private Charset encoding = Charset.forName(System.getProperty("johnzon.mapper.encoding", "UTF-8"));
+
+    MapperConfig() {
+    }
+
+    void setClose(boolean close) {
+        this.close = close;
+    }
+
+    public boolean isClose() {
+        return close;
+    }
+
+    public boolean isSkipNull() {
+        return skipNull;
+    }
+
+    void setSkipNull(boolean skipNull) {
+        this.skipNull = skipNull;
+    }
+
+    public boolean isSkipEmptyArray() {
+        return skipEmptyArray;
+    }
+
+    void setSkipEmptyArray(boolean skipEmptyArray) {
+        this.skipEmptyArray = skipEmptyArray;
+    }
+
+    public boolean isSupportsComments() {
+        return supportsComments;
+    }
+
+    void setSupportsComments(boolean supportsComments) {
+        this.supportsComments = supportsComments;
+    }
+
+    public boolean isTreatByteArrayAsBase64() {
+        return treatByteArrayAsBase64;
+    }
+
+    void setTreatByteArrayAsBase64(boolean treatByteArrayAsBase64) {
+        this.treatByteArrayAsBase64 = treatByteArrayAsBase64;
+    }
+
+    public boolean isTreatByteArrayAsBase64URL() {
+        return treatByteArrayAsBase64URL;
+    }
+
+    void setTreatByteArrayAsBase64URL(boolean treatByteArrayAsBase64URL) {
+        this.treatByteArrayAsBase64URL = treatByteArrayAsBase64URL;
+    }
+
+    public boolean isReadAttributeBeforeWrite() {
+        return readAttributeBeforeWrite;
+    }
+
+    void setReadAttributeBeforeWrite(boolean readAttributeBeforeWrite) {
+        this.readAttributeBeforeWrite = readAttributeBeforeWrite;
+    }
+
+    public boolean isPrettyPrint() {
+        return prettyPrint;
+    }
+
+    void setPrettyPrint(boolean prettyPrint) {
+        this.prettyPrint = prettyPrint;
+    }
+
+    public AccessMode getAccessMode() {
+        return accessMode;
+    }
+
+    void setAccessMode(AccessMode accessMode) {
+        this.accessMode = accessMode;
+    }
+
+    public Charset getEncoding() {
+        return encoding;
+    }
+
+    void setEncoding(Charset encoding) {
+        this.encoding = encoding;
+    }
+
+    @Override
+    public MapperConfig clone() {
+        try {
+            return (MapperConfig) super.clone();
+        } catch (CloneNotSupportedException e) {
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGenerator.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGenerator.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGenerator.java
new file mode 100644
index 0000000..b3719a2
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGenerator.java
@@ -0,0 +1,61 @@
+/*
+ * 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.johnzon.mapper;
+
+import javax.json.stream.JsonGenerator;
+
+/**
+ * Handles writing Json for Objects.
+ * Internally it uses a {@link JsonGenerator} to write JSON
+ *
+ * To write JSON-P structure elements you can use the {@link #getJsonGenerator()} method.
+ *
+ */
+public interface MappingGenerator {
+
+    /**
+     * @return the {@link JsonGenerator} used internally to write the JSON output.
+     */
+    JsonGenerator getJsonGenerator();
+
+    /**
+     * Write the given Object o into the current JSON layer.
+     * This will <em>not</em> open a new json layer ('{', '}')
+     * but really just write the attributes of o to the currently opened layer.
+     *
+     * Consider you have a class
+     * <pre>
+     *     public class Customer {
+     *         private String firstName;
+     *         private String lastName;
+     *         private Address address;
+     *         ...
+     *     }
+     * </pre>
+     * then the resulting JSON String will e.g. look like
+     * <pre>
+     *     "firstName":"Karl", "lastName":"SomeName", "address":{"street":"mystreet"}
+     * </pre>
+     * @param o the object to write
+     * @return itself, for easier chaining of commands
+     */
+    MappingGenerator writeObject(Object o);
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParser.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParser.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParser.java
new file mode 100644
index 0000000..615dfaf
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParser.java
@@ -0,0 +1,49 @@
+/*
+ * 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.johnzon.mapper;
+
+import java.lang.reflect.Type;
+
+import javax.json.JsonReader;
+import javax.json.JsonValue;
+import javax.json.stream.JsonParser;
+
+/**
+ * Handles reading Json for Objects.
+ * Internally it uses a {@link JsonParser} to write JSON
+ *
+ * To write JSON-P structure elements you can use the {@link #getJsonParser()} ()} method.
+ *
+ */
+public interface MappingParser {
+
+    /**
+     * @return the {@link JsonParser} used internally to read the JSON input.
+     */
+    JsonParser getJsonParser();
+
+    /**
+     * @return the {@link JsonReader} to read in full {@link javax.json.JsonValue}s from the {@link #getJsonParser()}
+     */
+    JsonReader getJsonReader();
+
+    <T> T readObject(Type targetType);
+
+    <T> T readObject(JsonValue jsonValue, Type targetType);
+}

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
new file mode 100644
index 0000000..da210fc
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
@@ -0,0 +1,765 @@
+/*
+ * 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.johnzon.mapper;
+
+import org.apache.johnzon.mapper.access.AccessMode;
+import org.apache.johnzon.mapper.converter.DateWithCopyConverter;
+import org.apache.johnzon.mapper.converter.EnumConverter;
+import org.apache.johnzon.mapper.internal.AdapterKey;
+import org.apache.johnzon.mapper.internal.ConverterAdapter;
+import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static java.util.Arrays.asList;
+import static org.apache.johnzon.mapper.reflection.Converters.matches;
+
+public class Mappings {
+    public static class ClassMapping {
+        final Class<?> clazz;
+        final AccessMode.Factory factory;
+        final Map<String, Getter> getters;
+        final Map<String, Setter> setters;
+
+
+        protected ClassMapping(final Class<?> clazz, final AccessMode.Factory factory,
+                               final Map<String, Getter> getters, final Map<String, Setter> setters) {
+            this.clazz = clazz;
+            this.factory = factory;
+            this.getters = getters;
+            this.setters = setters;
+        }
+    }
+
+    public static class CollectionMapping {
+        final Class<?> raw;
+        final Type arg;
+        final boolean primitive;
+
+        public CollectionMapping(final boolean primitive, final Class<?> collectionType, final Type fieldArgType) {
+            this.raw = collectionType;
+            this.arg = fieldArgType;
+            this.primitive = primitive;
+        }
+    }
+
+    public static class Getter {
+        final AccessMode.Reader reader;
+        final int version;
+        final Adapter converter;
+        final Adapter itemConverter;
+        final boolean primitive;
+        final boolean array;
+        final boolean map;
+        final boolean collection;
+
+        public Getter(final AccessMode.Reader reader,
+                      final boolean primitive, final boolean array,
+                      final boolean collection, final boolean map,
+                      final Adapter<?, ?> converter,
+                      final int version) {
+            this.reader = reader;
+            this.version = version;
+            this.array = array;
+            this.collection = collection;
+            this.primitive = primitive;
+            if (converter != null && matches(reader.getType(), converter)) {
+                this.converter = converter;
+                this.itemConverter = null;
+            } else if (converter != null) {
+                this.converter = null;
+                this.itemConverter = converter;
+            } else {
+                this.converter = null;
+                this.itemConverter = null;
+            }
+            this.map = map && this.converter == null;
+        }
+
+        @Override
+        public String toString() {
+            return "Getter{" +
+                "reader=" + reader +
+                ", version=" + version +
+                ", converter=" + converter +
+                ", itemConverter=" + itemConverter +
+                ", primitive=" + primitive +
+                ", array=" + array +
+                ", map=" + map +
+                ", collection=" + collection +
+                '}';
+        }
+    }
+
+    public static class Setter {
+        public final AccessMode.Writer writer;
+        public final int version;
+        public final Type paramType;
+        public final Adapter<?, ?> converter;
+        public final Adapter<?, ?> itemConverter;
+        public final boolean primitive;
+        public final boolean array;
+
+        public Setter(final AccessMode.Writer writer, final boolean primitive, final boolean array,
+                      final Type paramType, final Adapter<?, ?> converter,
+                      final int version) {
+            this.writer = writer;
+            this.paramType = paramType;
+            this.version = version;
+            this.primitive = primitive;
+            this.array = array;
+            if (converter != null && matches(writer.getType(), converter)) {
+                this.converter = converter;
+                this.itemConverter = null;
+            } else if (converter != null) {
+                this.converter = null;
+                this.itemConverter = converter;
+            } else {
+                this.converter = null;
+                this.itemConverter = null;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "Setter{" +
+                "writer=" + writer +
+                ", version=" + version +
+                ", paramType=" + paramType +
+                ", converter=" + converter +
+                ", itemConverter=" + itemConverter +
+                ", primitive=" + primitive +
+                ", array=" + array +
+                '}';
+        }
+    }
+
+    private static final JohnzonParameterizedType VIRTUAL_TYPE = new JohnzonParameterizedType(Map.class, String.class, Object.class);
+
+    protected final ConcurrentMap<Type, ClassMapping> classes = new ConcurrentHashMap<Type, ClassMapping>();
+    protected final ConcurrentMap<Type, CollectionMapping> collections = new ConcurrentHashMap<Type, CollectionMapping>();
+    protected final Comparator<String> fieldOrdering;
+    protected final ConcurrentMap<AdapterKey, Adapter<?, ?>> adapters;
+    private final AccessMode accessMode;
+    private final int version;
+
+    public Mappings(final Comparator<String> attributeOrder, final AccessMode accessMode,
+                    final int version, final ConcurrentMap<AdapterKey, Adapter<?, ?>> adapters) {
+        this.fieldOrdering = attributeOrder;
+        this.accessMode = accessMode;
+        this.version = version;
+        this.adapters = adapters;
+    }
+
+    public <T> CollectionMapping findCollectionMapping(final ParameterizedType genericType) {
+        CollectionMapping collectionMapping = collections.get(genericType);
+        if (collectionMapping == null) {
+            collectionMapping = createCollectionMapping(genericType);
+            if (collectionMapping == null) {
+                return null;
+            }
+            final CollectionMapping existing = collections.putIfAbsent(genericType, collectionMapping);
+            if (existing != null) {
+                collectionMapping = existing;
+            }
+        }
+        return collectionMapping;
+    }
+
+    private <T> CollectionMapping createCollectionMapping(final ParameterizedType aType) {
+        final Type[] fieldArgTypes = aType.getActualTypeArguments();
+        final Type raw = aType.getRawType();
+        if (fieldArgTypes.length == 1 && Class.class.isInstance(raw)) {
+            final Class<?> r = Class.class.cast(raw);
+            final Class<?> collectionType;
+            if (List.class.isAssignableFrom(r)) {
+                collectionType = List.class;
+            } else if (SortedSet.class.isAssignableFrom(r)) {
+                collectionType = SortedSet.class;
+            } else if (Set.class.isAssignableFrom(r)) {
+                collectionType = Set.class;
+            } else if (Deque.class.isAssignableFrom(r)) {
+                collectionType = Deque.class;
+            } else if (Queue.class.isAssignableFrom(r)) {
+                collectionType = Queue.class;
+            } else if (Collection.class.isAssignableFrom(r)) {
+                collectionType = Collection.class;
+            } else {
+                return null;
+            }
+
+            final CollectionMapping mapping = new CollectionMapping(isPrimitive(fieldArgTypes[0]), collectionType, fieldArgTypes[0]);
+            collections.putIfAbsent(aType, mapping);
+            return mapping;
+        }
+        return null;
+    }
+
+    // has JSon API a method for this type
+    public static boolean isPrimitive(final Type type) {
+        if (type == String.class) {
+            return true;
+        } else if (type == char.class || type == Character.class) {
+            return true;
+        } else if (type == long.class || type == Long.class) {
+            return true;
+        } else if (type == int.class || type == Integer.class
+            || type == byte.class || type == Byte.class
+            || type == short.class || type == Short.class) {
+            return true;
+        } else if (type == double.class || type == Double.class
+            || type == float.class || type == Float.class) {
+            return true;
+        } else if (type == boolean.class || type == Boolean.class) {
+            return true;
+        } else if (type == BigDecimal.class) {
+            return true;
+        } else if (type == BigInteger.class) {
+            return true;
+        }
+        return false;
+    }
+
+    public ClassMapping getClassMapping(final Type clazz) {
+        return classes.get(clazz);
+    }
+
+    public ClassMapping findOrCreateClassMapping(final Type clazz) {
+        ClassMapping classMapping = classes.get(clazz);
+        if (classMapping == null) {
+            if (!Class.class.isInstance(clazz) || Map.class.isAssignableFrom(Class.class.cast(clazz))) {
+                return null;
+            }
+
+            classMapping = createClassMapping(Class.class.cast(clazz));
+            final ClassMapping existing = classes.putIfAbsent(clazz, classMapping);
+            if (existing != null) {
+                classMapping = existing;
+            }
+        }
+        return classMapping;
+    }
+
+    protected ClassMapping createClassMapping(final Class<?> inClazz) {
+        boolean copyDate = false;
+        for (final Class<?> itf : inClazz.getInterfaces()) {
+            if ("org.apache.openjpa.enhance.PersistenceCapable".equals(itf.getName())) {
+                copyDate = true;
+                break;
+            }
+        }
+        final Class<?> clazz = findModelClass(inClazz);
+
+        Comparator<String> fieldComparator = accessMode.fieldComparator(inClazz);
+        fieldComparator = fieldComparator == null ? fieldOrdering : fieldComparator;
+
+        final Map<String, Getter> getters = fieldComparator == null ? newOrderedMap(Getter.class) : new TreeMap<String, Getter>(fieldComparator);
+        final Map<String, Setter> setters = fieldComparator == null ? newOrderedMap(Setter.class) : new TreeMap<String, Setter>(fieldComparator);
+
+        final Map<String, AccessMode.Reader> readers = accessMode.findReaders(clazz);
+        final Map<String, AccessMode.Writer> writers = accessMode.findWriters(clazz);
+
+        final Collection<String> virtualFields = new HashSet<String>();
+        {
+            final JohnzonVirtualObjects virtualObjects = clazz.getAnnotation(JohnzonVirtualObjects.class);
+            if (virtualObjects != null) {
+                for (final JohnzonVirtualObject virtualObject : virtualObjects.value()) {
+                    handleVirtualObject(virtualFields, virtualObject, getters, setters, readers, writers, copyDate);
+                }
+            }
+
+            final JohnzonVirtualObject virtualObject = clazz.getAnnotation(JohnzonVirtualObject.class);
+            if (virtualObject != null) {
+                handleVirtualObject(virtualFields, virtualObject, getters, setters, readers, writers, copyDate);
+            }
+        }
+
+        for (final Map.Entry<String, AccessMode.Reader> reader : readers.entrySet()) {
+            final String key = reader.getKey();
+            if (virtualFields.contains(key)) {
+                continue;
+            }
+            addGetterIfNeeded(getters, key, reader.getValue(), copyDate);
+        }
+
+        for (final Map.Entry<String, AccessMode.Writer> writer : writers.entrySet()) {
+            final String key = writer.getKey();
+            if (virtualFields.contains(key)) {
+                continue;
+            }
+            addSetterIfNeeded(setters, key, writer.getValue(), copyDate);
+        }
+        return new ClassMapping(clazz, accessMode.findFactory(clazz), getters, setters);
+    }
+
+    protected Class<?> findModelClass(final Class<?> inClazz) {
+        Class<?> clazz = inClazz;
+        // unproxy to get a clean model
+        while (clazz != null && clazz != Object.class
+            && (clazz.getName().contains("$$") || clazz.getName().contains("$proxy")
+            || clazz.getName().startsWith("org.apache.openjpa.enhance.") /* subclassing mode, not the default */)) {
+            clazz = clazz.getSuperclass();
+        }
+        if (clazz == null || clazz == Object.class) { // shouldn't occur but a NPE protection
+            clazz = inClazz;
+        }
+        return clazz;
+    }
+
+    private <T> Map<String, T> newOrderedMap(final Class<T> value) {
+        return fieldOrdering != null ? new TreeMap<String, T>(fieldOrdering) : new HashMap<String, T>();
+    }
+
+    private void addSetterIfNeeded(final Map<String, Setter> setters,
+                                   final String key,
+                                   final AccessMode.Writer value,
+                                   final boolean copyDate) {
+        final JohnzonIgnore writeIgnore = value.getAnnotation(JohnzonIgnore.class);
+        if (writeIgnore == null || writeIgnore.minVersion() >= 0) {
+            if (key.equals("metaClass")) {
+                return;
+            }
+            final Type param = value.getType();
+            final Class<?> returnType = Class.class.isInstance(param) ? Class.class.cast(param) : null;
+            final Setter setter = new Setter(
+                value, isPrimitive(param), returnType != null && returnType.isArray(), param,
+                findConverter(copyDate, value), writeIgnore != null ? writeIgnore.minVersion() : -1);
+            setters.put(key, setter);
+        }
+    }
+
+    private void addGetterIfNeeded(final Map<String, Getter> getters,
+                                   final String key,
+                                   final AccessMode.Reader value,
+                                   final boolean copyDate) {
+        final JohnzonIgnore readIgnore = value.getAnnotation(JohnzonIgnore.class);
+        if (readIgnore == null || readIgnore.minVersion() >= 0) {
+            final Class<?> returnType = Class.class.isInstance(value.getType()) ? Class.class.cast(value.getType()) : null;
+            final ParameterizedType pt = ParameterizedType.class.isInstance(value.getType()) ? ParameterizedType.class.cast(value.getType()) : null;
+            final Getter getter = new Getter(value, isPrimitive(returnType),
+                returnType != null && returnType.isArray(),
+                (pt != null && Collection.class.isAssignableFrom(Class.class.cast(pt.getRawType())))
+                    || (returnType != null && Collection.class.isAssignableFrom(returnType)),
+                (pt != null && Map.class.isAssignableFrom(Class.class.cast(pt.getRawType())))
+                    || (returnType != null && Map.class.isAssignableFrom(returnType)),
+                findConverter(copyDate, value),
+                readIgnore != null ? readIgnore.minVersion() : -1);
+            getters.put(key, getter);
+        }
+    }
+
+    // idea is quite trivial, simulate an object with a Map<String, Object>
+    private void handleVirtualObject(final Collection<String> virtualFields,
+                                     final JohnzonVirtualObject o,
+                                     final Map<String, Getter> getters,
+                                     final Map<String, Setter> setters,
+                                     final Map<String, AccessMode.Reader> readers,
+                                     final Map<String, AccessMode.Writer> writers,
+                                     final boolean copyDate) {
+        final String[] path = o.path();
+        if (path.length < 1) {
+            throw new IllegalArgumentException("@JohnzonVirtualObject need a path");
+        }
+
+        // add them to ignored fields
+        for (final JohnzonVirtualObject.Field f : o.fields()) {
+            virtualFields.add(f.value());
+        }
+
+        // build "this" model
+        final Map<String, Getter> objectGetters = newOrderedMap(Getter.class);
+        final Map<String, Setter> objectSetters = newOrderedMap(Setter.class);
+
+        for (final JohnzonVirtualObject.Field f : o.fields()) {
+            final String name = f.value();
+            if (f.read()) {
+                final AccessMode.Reader reader = readers.get(name);
+                if (reader != null) {
+                    addGetterIfNeeded(objectGetters, name, reader, copyDate);
+                }
+            }
+            if (f.write()) {
+                final AccessMode.Writer writer = writers.get(name);
+                if (writer != null) {
+                    addSetterIfNeeded(objectSetters, name, writer, copyDate);
+                }
+            }
+        }
+
+        final String key = path[0];
+
+        final Getter getter = getters.get(key);
+        final MapBuilderReader newReader = new MapBuilderReader(objectGetters, path, version);
+        getters.put(key, new Getter(getter == null ? newReader : new CompositeReader(getter.reader, newReader), false, false, false, true, null, -1));
+
+        final Setter newSetter = setters.get(key);
+        final MapUnwrapperWriter newWriter = new MapUnwrapperWriter(objectSetters, path);
+        setters.put(key, new Setter(newSetter == null ? newWriter : new CompositeWriter(newSetter.writer, newWriter), false, false, VIRTUAL_TYPE, null, -1));
+    }
+
+    private Adapter findConverter(final boolean copyDate, final AccessMode.DecoratedType decoratedType) {
+        Adapter converter = decoratedType.findConverter();
+        if (converter != null) {
+            return converter;
+        }
+
+        final JohnzonConverter annotation = decoratedType.getAnnotation(JohnzonConverter.class);
+
+        Type typeToTest = decoratedType.getType();
+        if (annotation != null) {
+            try {
+                converter = new ConverterAdapter(annotation.value().newInstance());
+            } catch (final Exception e) {
+                throw new IllegalArgumentException(e);
+            }
+        } else if (ParameterizedType.class.isInstance(decoratedType.getType())) {
+            final ParameterizedType type = ParameterizedType.class.cast(decoratedType.getType());
+            final Type rawType = type.getRawType();
+            if (Class.class.isInstance(rawType)
+                && Collection.class.isAssignableFrom(Class.class.cast(rawType))
+                && type.getActualTypeArguments().length >= 1) {
+                typeToTest = type.getActualTypeArguments()[0];
+            } // TODO: map
+        }
+        if (converter == null && Class.class.isInstance(typeToTest)) {
+            final Class type = Class.class.cast(typeToTest);
+            if (Date.class.isAssignableFrom(type) && copyDate) {
+                converter = new DateWithCopyConverter(Adapter.class.cast(adapters.get(new AdapterKey(Date.class, String.class))));
+            } else if (type.isEnum()) {
+                final AdapterKey key = new AdapterKey(String.class, type);
+                converter = adapters.get(key); // first ensure user didnt override it
+                if (converter == null) {
+                    converter = new ConverterAdapter(new EnumConverter(type));
+                    adapters.put(key, converter);
+                }
+            } else {
+                for (final Map.Entry<AdapterKey, Adapter<?, ?>> adapterEntry : adapters.entrySet()) {
+                    if (adapterEntry.getKey().getFrom() == adapterEntry.getKey().getTo()) { // String -> String
+                        continue;
+                    }
+                    if (adapterEntry.getKey().getFrom() == type && !(
+                            // ignore internal converters to let primitives be correctly handled
+                            ConverterAdapter.class.isInstance(adapterEntry.getValue()) &&
+                            ConverterAdapter.class.cast(adapterEntry.getValue()).getConverter().getClass().getName().startsWith("org.apache.johnzon.mapper."))) {
+
+                        if (converter != null) {
+                            throw new IllegalArgumentException("Ambiguous adapter for " + decoratedType);
+                        }
+                        converter = adapterEntry.getValue();
+                    }
+                }
+            }
+        }
+        return converter;
+    }
+
+    private static class MapBuilderReader implements AccessMode.Reader {
+        private final Map<String, Getter> getters;
+        private final Map<String, Object> template;
+        private final String[] paths;
+        private final int version;
+
+        public MapBuilderReader(final Map<String, Getter> objectGetters, final String[] paths, final int version) {
+            this.getters = objectGetters;
+            this.paths = paths;
+            this.template = new LinkedHashMap<String, Object>();
+            this.version = version;
+
+            Map<String, Object> last = this.template;
+            for (int i = 1; i < paths.length; i++) {
+                final Map<String, Object> newLast = new LinkedHashMap<String, Object>();
+                last.put(paths[i], newLast);
+                last = newLast;
+            }
+        }
+
+        @Override
+        public Object read(final Object instance) {
+            final Map<String, Object> map = new LinkedHashMap<String, Object>(template);
+            Map<String, Object> nested = map;
+            for (int i = 1; i < paths.length; i++) {
+                nested = Map.class.cast(nested.get(paths[i]));
+            }
+            for (final Map.Entry<String, Getter> g : getters.entrySet()) {
+                final Mappings.Getter getter = g.getValue();
+                final Object value = getter.reader.read(instance);
+                final Object val = value == null || getter.converter == null ? value : getter.converter.from(value);
+                if (val == null) {
+                    continue;
+                }
+                if (getter.version >= 0 && version >= getter.version) {
+                    continue;
+                }
+
+                nested.put(g.getKey(), val);
+            }
+            return map;
+        }
+
+        @Override
+        public Type getType() {
+            return VIRTUAL_TYPE;
+        }
+
+        @Override
+        public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
+            throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields");
+        }
+
+        @Override
+        public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
+            return null;
+        }
+
+        @Override
+        public Adapter<?, ?> findConverter() {
+            return null;
+        }
+
+        @Override
+        public boolean isNillable() {
+            return false;
+        }
+    }
+
+    private static class MapUnwrapperWriter implements AccessMode.Writer {
+        private final Map<String, Setter> writers;
+        private final Map<String, Class<?>> componentTypes;
+        private final String[] paths;
+
+        public MapUnwrapperWriter(final Map<String, Setter> writers, final String[] paths) {
+            this.writers = writers;
+            this.paths = paths;
+            this.componentTypes = new HashMap<String, Class<?>>();
+
+            for (final Map.Entry<String, Setter> setter : writers.entrySet()) {
+                if (setter.getValue().array) {
+                    componentTypes.put(setter.getKey(), Class.class.cast(setter.getValue().paramType).getComponentType());
+                }
+            }
+        }
+
+        @Override
+        public void write(final Object instance, final Object value) {
+            Map<String, Object> nested = null;
+            for (final String path : paths) {
+                nested = Map.class.cast(nested == null ? value : nested.get(path));
+                if (nested == null) {
+                    return;
+                }
+            }
+
+            for (final Map.Entry<String, Setter> setter : writers.entrySet()) {
+                final Setter setterValue = setter.getValue();
+                final String key = setter.getKey();
+                final Object rawValue = nested.get(key);
+                Object val = value == null || setterValue.converter == null ?
+                    rawValue : Converter.class.cast(setterValue.converter).toString(rawValue);
+                if (val == null) {
+                    continue;
+                }
+
+                if (setterValue.array && Collection.class.isInstance(val)) {
+                    final Collection<?> collection = Collection.class.cast(val);
+                    final Object[] array = (Object[]) Array.newInstance(componentTypes.get(key), collection.size());
+                    val = collection.toArray(array);
+                }
+
+                final AccessMode.Writer setterMethod = setterValue.writer;
+                setterMethod.write(instance, val);
+            }
+        }
+
+        @Override
+        public Type getType() {
+            return VIRTUAL_TYPE;
+        }
+
+        @Override
+        public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
+            throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields");
+        }
+
+        @Override
+        public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
+            return null;
+        }
+
+        @Override
+        public Adapter<?, ?> findConverter() {
+            return null;
+        }
+
+        @Override
+        public boolean isNillable() {
+            return false;
+        }
+    }
+
+    private static class CompositeReader implements AccessMode.Reader {
+        private final AccessMode.Reader[] delegates;
+
+        public CompositeReader(final AccessMode.Reader... delegates) {
+            final Collection<AccessMode.Reader> all = new LinkedList<AccessMode.Reader>();
+            for (final AccessMode.Reader r : delegates) {
+                if (CompositeReader.class.isInstance(r)) {
+                    all.addAll(asList(CompositeReader.class.cast(r).delegates));
+                } else {
+                    all.add(r);
+                }
+            }
+            this.delegates = all.toArray(new AccessMode.Reader[all.size()]);
+        }
+
+        @Override
+        public Object read(final Object instance) {
+            final Map<String, Object> map = new LinkedHashMap<String, Object>();
+            for (final AccessMode.Reader reader : delegates) {
+                final Map<String, Object> readerMap = (Map<String, Object>) reader.read(instance);
+                for (final Map.Entry<String, Object> entry : readerMap.entrySet()) {
+                    final Object o = map.get(entry.getKey());
+                    if (o == null) {
+                        map.put(entry.getKey(), entry.getValue());
+                    } else if (Map.class.isInstance(o)) {
+                        // TODO
+                    } else {
+                        throw new IllegalStateException(entry.getKey() + " is ambiguous");
+                    }
+                }
+            }
+            return map;
+        }
+
+        @Override
+        public Type getType() {
+            return VIRTUAL_TYPE;
+        }
+
+        @Override
+        public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
+            throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields");
+        }
+
+        @Override
+        public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
+            return null;
+        }
+
+        @Override
+        public Adapter<?, ?> findConverter() {
+            for (final AccessMode.Reader r : delegates) {
+                final Adapter<?, ?> converter = r.findConverter();
+                if (converter != null) {
+                    return converter;
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public boolean isNillable() {
+            for (final AccessMode.Reader r : delegates) {
+                if (r.isNillable()) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    private static class CompositeWriter implements AccessMode.Writer {
+        private final AccessMode.Writer[] delegates;
+
+        public CompositeWriter(final AccessMode.Writer... writers) {
+            final Collection<AccessMode.Writer> all = new LinkedList<AccessMode.Writer>();
+            for (final AccessMode.Writer r : writers) {
+                if (CompositeWriter.class.isInstance(r)) {
+                    all.addAll(asList(CompositeWriter.class.cast(r).delegates));
+                } else {
+                    all.add(r);
+                }
+            }
+            this.delegates = all.toArray(new AccessMode.Writer[all.size()]);
+        }
+
+        @Override
+        public void write(final Object instance, final Object value) {
+            for (final AccessMode.Writer w : delegates) {
+                w.write(instance, value);
+            }
+        }
+
+        @Override
+        public Type getType() {
+            return VIRTUAL_TYPE;
+        }
+
+        @Override
+        public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
+            throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields");
+        }
+
+        @Override
+        public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
+            return null;
+        }
+
+        @Override
+        public Adapter<?, ?> findConverter() {
+            for (final AccessMode.Writer r : delegates) {
+                final Adapter<?, ?> converter = r.findConverter();
+                if (converter != null) {
+                    return converter;
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public boolean isNillable() {
+            for (final AccessMode.Writer r : delegates) {
+                if (r.isNillable()) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/e41b829b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
index e831e80..7f5d02d 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
@@ -20,9 +20,6 @@ package org.apache.johnzon.mapper;
 
 import java.lang.reflect.Type;
 
-import javax.json.stream.JsonGenerator;
-import javax.json.stream.JsonParser;
-
 /**
  * Convert a given Java Type a nested JSON representation.
  * And the other way around.
@@ -33,7 +30,7 @@ import javax.json.stream.JsonParser;
  * @param <T>
  */
 public interface ObjectConverter<T> {
-    void writeJson(T instance, JsonbGenerator jsonbGenerator);
+    void writeJson(T instance, MappingGenerator jsonbGenerator);
 
-    T fromJson(JsonbParser jsonbParser, Type targetType);
+    T fromJson(MappingParser jsonbParser, Type targetType);
 }