You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by pa...@apache.org on 2017/11/24 22:11:08 UTC

[sling-org-apache-sling-commons-johnzon] 01/01: SLING-7267: Update commons johnzon to johnzon 1.1.4 and make it work with java7.

This is an automated email from the ASF dual-hosted git repository.

pauls pushed a commit to branch SLING-7267
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-johnzon.git

commit 62ab59b4b2af5ea9989943a67440a75898c0e9ee
Author: Karl Pauls <ka...@gmail.com>
AuthorDate: Fri Nov 24 23:10:46 2017 +0100

    SLING-7267: Update commons johnzon to johnzon 1.1.4 and make it work with java7.
---
 pom.xml                                            |   37 +-
 src/main/java/java/util/function/Function.java     |   23 +
 src/main/java/java/util/stream/Stream.java         |   22 +
 src/main/java/javax/json/Json.java                 |  359 +++++++
 src/main/java/javax/json/JsonArray.java            |  153 +++
 src/main/java/javax/json/JsonArrayBuilder.java     |   94 ++
 src/main/java/javax/json/JsonBuilderFactory.java   |   81 ++
 src/main/java/javax/json/JsonException.java        |   27 +
 src/main/java/javax/json/JsonMergePatch.java       |   40 +
 src/main/java/javax/json/JsonNumber.java           |   61 ++
 src/main/java/javax/json/JsonObject.java           |  103 ++
 src/main/java/javax/json/JsonObjectBuilder.java    |  148 +++
 src/main/java/javax/json/JsonPatch.java            |  208 ++++
 src/main/java/javax/json/JsonPatchBuilder.java     |  193 ++++
 src/main/java/javax/json/JsonPointer.java          |   94 ++
 src/main/java/javax/json/JsonReader.java           |   32 +
 src/main/java/javax/json/JsonReaderFactory.java    |   32 +
 src/main/java/javax/json/JsonString.java           |   32 +
 src/main/java/javax/json/JsonStructure.java        |   28 +
 src/main/java/javax/json/JsonValue.java            |  149 +++
 src/main/java/javax/json/JsonWriter.java           |   39 +
 src/main/java/javax/json/JsonWriterFactory.java    |   32 +
 src/main/java/javax/json/spi/JsonProvider.java     |  105 +-
 .../javax/json/stream/JsonGenerationException.java |   32 +
 src/main/java/javax/json/stream/JsonGenerator.java |   88 ++
 .../javax/json/stream/JsonGeneratorFactory.java    |   32 +
 src/main/java/javax/json/stream/JsonLocation.java  |   26 +
 src/main/java/javax/json/stream/JsonParser.java    |   71 ++
 .../java/javax/json/stream/JsonParserFactory.java  |   39 +
 .../javax/json/stream/JsonParsingException.java    |   39 +
 .../apache/johnzon/core/AbstractJsonFactory.java   |   84 ++
 .../org/apache/johnzon/core/BufferStrategy.java    |  272 +++++
 .../johnzon/core/CommentsJsonStreamParserImpl.java |   73 ++
 .../java/org/apache/johnzon/core/DiffBase.java     |   34 +
 .../java/org/apache/johnzon/core/Experimental.java |   33 +
 src/main/java/org/apache/johnzon/core/HStack.java  |   66 ++
 .../org/apache/johnzon/core/JohnzonJsonParser.java |  134 +++
 .../apache/johnzon/core/JsonArrayBuilderImpl.java  |  330 ++++++
 .../org/apache/johnzon/core/JsonArrayImpl.java     |  235 +++++
 .../johnzon/core/JsonBuilderFactoryImpl.java       |   92 ++
 .../java/org/apache/johnzon/core/JsonChars.java    |   82 ++
 .../org/apache/johnzon/core/JsonDoubleImpl.java    |  137 +++
 .../johnzon/core/JsonGeneratorFactoryImpl.java     |   79 ++
 .../org/apache/johnzon/core/JsonGeneratorImpl.java |  827 +++++++++++++++
 .../apache/johnzon/core/JsonInMemoryParser.java    |  314 ++++++
 .../org/apache/johnzon/core/JsonLocationImpl.java  |   80 ++
 .../java/org/apache/johnzon/core/JsonLongImpl.java |  121 +++
 .../apache/johnzon/core/JsonMergePatchDiff.java    |   81 ++
 .../apache/johnzon/core/JsonMergePatchImpl.java    |   80 ++
 .../org/apache/johnzon/core/JsonNumberImpl.java    |  132 +++
 .../apache/johnzon/core/JsonObjectBuilderImpl.java |  178 ++++
 .../org/apache/johnzon/core/JsonObjectImpl.java    |  206 ++++
 .../apache/johnzon/core/JsonParserFactoryImpl.java |  137 +++
 .../apache/johnzon/core/JsonPatchBuilderImpl.java  |  181 ++++
 .../org/apache/johnzon/core/JsonPatchDiff.java     |  102 ++
 .../org/apache/johnzon/core/JsonPatchImpl.java     |  213 ++++
 .../org/apache/johnzon/core/JsonPointerImpl.java   |  484 +++++++++
 .../org/apache/johnzon/core/JsonPointerUtil.java   |   49 +
 .../org/apache/johnzon/core/JsonProviderImpl.java  |  384 +++++++
 .../apache/johnzon/core/JsonReaderFactoryImpl.java |   63 ++
 .../org/apache/johnzon/core/JsonReaderImpl.java    |  261 +++++
 .../apache/johnzon/core/JsonStreamParserImpl.java  | 1071 ++++++++++++++++++++
 .../org/apache/johnzon/core/JsonStringImpl.java    |   89 ++
 .../apache/johnzon/core/JsonWriterFactoryImpl.java |   66 ++
 .../org/apache/johnzon/core/JsonWriterImpl.java    |   92 ++
 .../core/RFC4627AwareInputStreamReader.java        |  141 +++
 .../org/apache/johnzon/core/SerializableValue.java |   56 +
 .../java/org/apache/johnzon/core/SimpleStack.java  |   61 ++
 src/main/java/org/apache/johnzon/core/Strings.java |  127 +++
 .../johnzon/core/ThreadLocalBufferCache.java       |   53 +
 70 files changed, 9699 insertions(+), 20 deletions(-)

diff --git a/pom.xml b/pom.xml
index 080b849..2761c6a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -15,7 +15,7 @@
     <parent>
         <groupId>org.apache.sling</groupId>
         <artifactId>sling</artifactId>
-        <version>30</version>
+        <version>32</version>
         <relativePath />
     </parent>
 
@@ -38,17 +38,44 @@
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
                 <extensions>true</extensions>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
                 <configuration>
+                    <skip>true</skip>
                     <instructions>
                         <Export-Package>
-                            javax.json.*;-split-package:=first
+                            javax.json;-split-package:=first,
+                            javax.json.stream;-split-package:=merge-first,
+                            javax.json.spi;-split-package:=first
                         </Export-Package>
                         <Private-Package>org.apache.johnzon.core</Private-Package>
                         <Import-Package />
+                        <_noee>true</_noee>
                     </instructions>
                 </configuration>
             </plugin>
             <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <forceCreation>true</forceCreation>
+                    <archive>
+                        <manifestFile>
+                            ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+                        </manifestFile>
+                        <manifestEntries>
+                            <Require-Capability>osgi.ee;filter:="(&amp;(osgi.ee=JavaSE)(version=1.7))"</Require-Capability>
+                        </manifestEntries>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
                 <groupId>org.apache.rat</groupId>
                 <artifactId>apache-rat-plugin</artifactId>
                 <configuration>
@@ -65,11 +92,5 @@
             <artifactId>geronimo-json_1.1_spec</artifactId>
             <version>1.0</version>
         </dependency>
-        <dependency>
-            <groupId>org.apache.johnzon</groupId>
-            <artifactId>johnzon-core</artifactId>
-            <version>1.1.1</version>
-            <scope>provided</scope>
-        </dependency>
     </dependencies>
 </project>
diff --git a/src/main/java/java/util/function/Function.java b/src/main/java/java/util/function/Function.java
new file mode 100644
index 0000000..79877d6
--- /dev/null
+++ b/src/main/java/java/util/function/Function.java
@@ -0,0 +1,23 @@
+/*
+ * 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 java.util.function;
+
+public interface Function<V,R>{
+    R apply(V v);
+}
diff --git a/src/main/java/java/util/stream/Stream.java b/src/main/java/java/util/stream/Stream.java
new file mode 100644
index 0000000..7653f24
--- /dev/null
+++ b/src/main/java/java/util/stream/Stream.java
@@ -0,0 +1,22 @@
+/*
+ * 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 java.util.stream;
+
+public interface Stream<T> {
+}
diff --git a/src/main/java/javax/json/Json.java b/src/main/java/javax/json/Json.java
new file mode 100644
index 0000000..6675879
--- /dev/null
+++ b/src/main/java/javax/json/Json.java
@@ -0,0 +1,359 @@
+/*
+ * 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 javax.json;
+
+import javax.json.spi.JsonProvider;
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonGeneratorFactory;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParserFactory;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Map;
+
+public final class Json
+{
+    private Json()
+    {
+        // no-op
+    }
+
+    public static JsonParser createParser(Reader reader)
+    {
+        return JsonProvider.provider().createParser(reader);
+    }
+
+    public static JsonParser createParser(InputStream in)
+    {
+        return JsonProvider.provider().createParser(in);
+    }
+
+    public static JsonGenerator createGenerator(Writer writer)
+    {
+        return JsonProvider.provider().createGenerator(writer);
+    }
+
+    public static JsonGenerator createGenerator(OutputStream out)
+    {
+        return JsonProvider.provider().createGenerator(out);
+    }
+
+    public static JsonParserFactory createParserFactory(Map<String, ?> config)
+    {
+        return JsonProvider.provider().createParserFactory(config);
+    }
+
+    public static JsonGeneratorFactory createGeneratorFactory(Map<String, ?> config)
+    {
+        return JsonProvider.provider().createGeneratorFactory(config);
+    }
+
+    public static JsonWriter createWriter(Writer writer)
+    {
+        return JsonProvider.provider().createWriter(writer);
+    }
+
+    public static JsonWriter createWriter(OutputStream out)
+    {
+        return JsonProvider.provider().createWriter(out);
+    }
+
+    public static JsonReader createReader(Reader reader)
+    {
+        return JsonProvider.provider().createReader(reader);
+    }
+
+    public static JsonReader createReader(InputStream in)
+    {
+        return JsonProvider.provider().createReader(in);
+    }
+
+    public static JsonReaderFactory createReaderFactory(Map<String, ?> config)
+    {
+        return JsonProvider.provider().createReaderFactory(config);
+    }
+
+    public static JsonWriterFactory createWriterFactory(Map<String, ?> config)
+    {
+        return JsonProvider.provider().createWriterFactory(config);
+    }
+
+    public static JsonArrayBuilder createArrayBuilder()
+    {
+        return JsonProvider.provider().createArrayBuilder();
+    }
+
+    /**
+     * Create an empty JsonObjectBuilder
+     *
+     * @since 1.0
+     */
+    public static JsonObjectBuilder createObjectBuilder()
+    {
+        return JsonProvider.provider().createObjectBuilder();
+    }
+
+    /**
+     * Creates a JSON object builder, initialized with the specified JsonObject.
+     *
+     * @since 1.1
+     */
+    public static JsonObjectBuilder createObjectBuilder(JsonObject object)
+    {
+        return JsonProvider.provider().createObjectBuilder(object);
+    }
+
+    /**
+     * Creates a JSON object builder, initialized with the specified Map.
+     *
+     * @since 1.1
+     */
+    public static JsonObjectBuilder createObjectBuilder(Map<String, Object> map)
+    {
+        return JsonProvider.provider().createObjectBuilder(map);
+    }
+
+    public static JsonBuilderFactory createBuilderFactory(Map<String, ?> config)
+    {
+        return JsonProvider.provider().createBuilderFactory(config);
+    }
+
+    /**
+     * Creates a JSON array builder, initialized with an initial content
+     * taken from a JsonArray
+     *
+     * @param initialData the initial array in the builder
+     * @return a JSON array builder
+     * @since 1.1
+     */
+    public static JsonArrayBuilder createArrayBuilder(JsonArray initialData)
+    {
+        return JsonProvider.provider().createArrayBuilder(initialData);
+    }
+
+    /**
+     * Creates a JSON array builder, initialized with an initial content
+     *
+     * @param initialData the initial array in the builder
+     * @return a JSON array builder
+     * @since 1.1
+     */
+    public static JsonArrayBuilder createArrayBuilder(Collection<?> initialData)
+    {
+        return JsonProvider.provider().createArrayBuilder(initialData);
+    }
+
+    public static JsonString createValue(String value)
+    {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    public static JsonNumber createValue(int value)
+    {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    public static JsonNumber createValue(long value)
+    {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    public static JsonNumber createValue(double value)
+    {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    public static JsonNumber createValue(BigDecimal value)
+    {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    public static JsonNumber createValue(BigInteger value)
+    {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    /**
+     * Create a {@link JsonPatch} as defined in
+     * <a href="https://tools.ietf.org/html/rfc6902">RFC-6902</a>.
+     *
+     * @param array with the patch operations
+     * @return the JsonPatch based on the given operations
+     * @since 1.1
+     */
+    public static JsonPatch createPatch(JsonArray array)
+    {
+        return JsonProvider.provider().createPatch(array);
+    }
+
+    /**
+     * Create a {@link JsonPatch} by comparing the source to the target as defined in
+     * <a href="https://tools.ietf.org/html/rfc6902">RFC-6902</a>.
+     * <p>
+     * Applying this {@link JsonPatch} to the source you will give you the target.
+     *
+     * @see #createPatch(JsonArray)
+     * @since 1.1
+     */
+    public static JsonPatch createDiff(JsonStructure source, JsonStructure target)
+    {
+        return JsonProvider.provider().createDiff(source, target);
+    }
+
+    /**
+     * Create a new JsonPatchBuilder
+     *
+     * @since 1.1
+     */
+    public static JsonPatchBuilder createPatchBuilder()
+    {
+        return JsonProvider.provider().createPatchBuilder();
+    }
+
+    /**
+     * Create a new JsonPatchBuilder
+     *
+     * @param initialData the initial patch operations
+     * @since 1.1
+     */
+    public static JsonPatchBuilder createPatchBuilder(JsonArray initialData)
+    {
+        return JsonProvider.provider().createPatchBuilder(initialData);
+    }
+
+    /**
+     * Creates JSON Merge Patch (<a href="http://tools.ietf.org/html/rfc7396">RFC 7396</a>)
+     * from a specified {@link JsonValue}.
+     * Create a merged patch by comparing the source to the target.
+     * <p>
+     * Applying this JsonPatch to the source will give you the target.
+     * <p>
+     * If you have a JSON like
+     * <pre>
+     * {
+     *   "a": "b",
+     *   "c": {
+     *     "d": "e",
+     *     "f": "g"
+     *   }
+     * }
+     * </pre>
+     * <p>
+     * Then you can change the value of "a" and removing "f" by sending:
+     * <pre>
+     * {
+     *   "a":"z",
+     *   "c": {
+     *     "f": null
+     *   }
+     * }
+     * </pre>
+     *
+     * @param patch the patch
+     * @return a JSON Merge Patch
+     * @since 1.1
+     */
+    public static JsonMergePatch createMergePatch(JsonValue patch)
+    {
+        return JsonProvider.provider().createMergePatch(patch);
+    }
+
+    /**
+     * Create a JSON Merge Patch (<a href="http://tools.ietf.org/html/rfc7396">RFC 7396</a>)
+     * from the source and target {@link JsonValue JsonValues}.
+     * Create a merged patch by comparing the source to the target.
+     * <p>
+     * Applying this JsonPatch to the source will give you the target.
+     * <p>
+     * If you have a JSON like
+     * <pre>
+     * {
+     *   "a": "b",
+     *   "c": {
+     *     "d": "e",
+     *     "f": "g"
+     *   }
+     * }
+     * </pre>
+     * <p>
+     * Then you can change the value of "a" and removing "f" by sending:
+     * <pre>
+     * {
+     *   "a":"z",
+     *   "c": {
+     *     "f": null
+     *   }
+     * }
+     * </pre>
+     *
+     * @param source the source
+     * @param target the target
+     * @return a JSON Merge Patch
+     * @since 1.1
+     */
+    public static JsonMergePatch createMergeDiff(JsonValue source, JsonValue target)
+    {
+        return JsonProvider.provider().createMergeDiff(source, target);
+    }
+
+    /**
+     * Create a {@link JsonPointer} for the given path
+     *
+     * @since 1.1
+     */
+    public static JsonPointer createPointer(String path)
+    {
+        return JsonProvider.provider().createPointer(path);
+    }
+
+    /**
+     * @param pointer to encode
+     * @return the properly encoded JsonPointer string
+     * @since 1.1
+     */
+    public static String encodePointer(String pointer)
+    {
+        if (pointer == null || pointer.length() == 0)
+        {
+            return pointer;
+        }
+
+        return pointer.replace("~", "~0").replace("/", "~1");
+    }
+
+    /**
+     * @param escapedPointer
+     * @return the de-escaped JsonPointer
+     *
+     * @since 1.1
+     */
+    public static String decodePointer(String escapedPointer)
+    {
+        if (escapedPointer == null || escapedPointer.length() == 0)
+        {
+            return escapedPointer;
+        }
+
+        return escapedPointer.replace("~1", "/").replace("~0", "~");
+    }
+}
diff --git a/src/main/java/javax/json/JsonArray.java b/src/main/java/javax/json/JsonArray.java
new file mode 100644
index 0000000..4955214
--- /dev/null
+++ b/src/main/java/javax/json/JsonArray.java
@@ -0,0 +1,153 @@
+/*
+ * 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 javax.json;
+
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * A JsonArray e.g.
+ * <pre>
+ * [1,5,8]
+ * </pre>
+ * or
+ * <pre>
+ *  [
+ *   {"name":"karl", "age": 38},
+ *   {"name":"sue", "age": 42},
+ *  ]
+ * </pre>
+ *
+ *
+ */
+public interface JsonArray extends JsonStructure, List<JsonValue> {
+
+    /**
+     * @return the JsonObject at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to the JsonObject
+     */
+    JsonObject getJsonObject(int index);
+
+    /**
+     * @return the JsonArray at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to the JsonArray
+     */
+    JsonArray getJsonArray(int index);
+
+    /**
+     * @return the JsonNumber at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to the JsonNumber
+     */
+    JsonNumber getJsonNumber(int index);
+
+    /**
+     * @return the JsonString at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to the JsonString
+     */
+    JsonString getJsonString(int index);
+
+    /**
+     * @return the respective JsonValue at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to the given slazz
+     */
+    <T extends JsonValue> List<T> getValuesAs(Class<T> clazz);
+
+    /**
+     * Returns a list for the array. The value and the type of the elements
+     * in the list is specified by the {@code func} argument.
+     * <p>This method can be used to obtain a list of the unwrapped types, such as
+     * <pre>{@code
+     *     List<String> strings = ary1.getValuesAs(JsonString::getString);
+     *     List<Integer> ints = ary2.getValuesAs(JsonNumber::intValue);
+     * } </pre>
+     * It can also be used to obtain a list of simple projections, such as
+     * <pre> {@code
+     *     Lsit<Integer> stringsizes = arr.getValueAs((JsonString v) -> v.getString().length();
+     * } </pre>
+     * @param <K> The element type (must be a subtype of JsonValue) of this JsonArray.
+     * @param <T> The element type of the returned List
+     * @param func The function that maps the elements of this JsonArray to the target elements.
+     * @return A List of the specified values and type.
+     * @throws ClassCastException if the {@code JsonArray} contains a value of wrong type
+     */
+    <T, K extends JsonValue> List<T> getValuesAs(Function<K, T> func);
+
+    /**
+     * @return the native String at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to a String
+     */
+    String getString(int index);
+
+    /**
+     * @return the native String at the given position or the defaultValue if null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to a String
+     */
+    String getString(int index, String defaultValue);
+
+    /**
+     * @return the native int value at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to an int
+     */
+    int getInt(int index);
+
+    /**
+     * @return the native int value at the given position or the defaultValue if null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to an int
+     */
+    int getInt(int index, int defaultValue);
+
+    /**
+     * @return the native boolean value at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to a boolean
+     */
+    boolean getBoolean(int index);
+
+    /**
+     * @return the native boolean value at the given position or the defaultValue if null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to a boolean
+     */
+    boolean getBoolean(int index, boolean defaultValue);
+
+    /**
+     * @return whether the value at the given position is {@link JsonValue#NULL}.
+     * @throws IndexOutOfBoundsException if the index is out of range
+     */
+    boolean isNull(int index);
+}
+
diff --git a/src/main/java/javax/json/JsonArrayBuilder.java b/src/main/java/javax/json/JsonArrayBuilder.java
new file mode 100644
index 0000000..3de276f
--- /dev/null
+++ b/src/main/java/javax/json/JsonArrayBuilder.java
@@ -0,0 +1,94 @@
+/*
+ * 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 javax.json;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public interface JsonArrayBuilder {
+    JsonArrayBuilder add(JsonValue value);
+
+    JsonArrayBuilder add(String value);
+
+    JsonArrayBuilder add(BigDecimal value);
+
+    JsonArrayBuilder add(BigInteger value);
+
+    JsonArrayBuilder add(int value);
+
+    JsonArrayBuilder add(long value);
+
+    JsonArrayBuilder add(double value);
+
+    JsonArrayBuilder add(boolean value);
+
+    JsonArrayBuilder addNull();
+
+    JsonArrayBuilder add(JsonObjectBuilder builder);
+
+    JsonArrayBuilder add(JsonArrayBuilder builder);
+
+    JsonArray build();
+
+    JsonArrayBuilder addAll(JsonArrayBuilder builder);
+
+    JsonArrayBuilder add(int index, JsonValue value);
+
+    JsonArrayBuilder add(int index, String value);
+
+    JsonArrayBuilder add(int index, BigDecimal value);
+
+    JsonArrayBuilder add(int index, BigInteger value);
+
+    JsonArrayBuilder add(int index, int value);
+
+    JsonArrayBuilder add(int index, long value);
+
+    JsonArrayBuilder add(int index, double value);
+
+    JsonArrayBuilder add(int index, boolean value);
+
+    JsonArrayBuilder addNull(int index);
+
+    JsonArrayBuilder add(int index, JsonObjectBuilder builder);
+
+    JsonArrayBuilder add(int index, JsonArrayBuilder builder);
+
+    JsonArrayBuilder set(int index, JsonValue value);
+
+    JsonArrayBuilder set(int index, String value);
+
+    JsonArrayBuilder set(int index, BigDecimal value);
+
+    JsonArrayBuilder set(int index, BigInteger value);
+
+    JsonArrayBuilder set(int index, int value);
+
+    JsonArrayBuilder set(int index, long value);
+
+    JsonArrayBuilder set(int index, double value);
+
+    JsonArrayBuilder set(int index, boolean value);
+
+    JsonArrayBuilder setNull(int index);
+
+    JsonArrayBuilder set(int index, JsonObjectBuilder builder);
+
+    JsonArrayBuilder set(int index, JsonArrayBuilder builder);
+
+    JsonArrayBuilder remove(int index);
+}
diff --git a/src/main/java/javax/json/JsonBuilderFactory.java b/src/main/java/javax/json/JsonBuilderFactory.java
new file mode 100644
index 0000000..0958d26
--- /dev/null
+++ b/src/main/java/javax/json/JsonBuilderFactory.java
@@ -0,0 +1,81 @@
+/*
+ * 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 javax.json;
+
+import java.util.Map;
+
+public interface JsonBuilderFactory {
+    /**
+     * @return a new empty JsonObjectBuilder
+     */
+    JsonObjectBuilder createObjectBuilder();
+
+    /**
+     * @return a new empty JsonArrayBuilder
+     */
+    JsonArrayBuilder createArrayBuilder();
+
+    /**
+     * @return the config which got used when creating this builder factory.
+     */
+    Map<String, ?> getConfigInUse();
+
+    /**
+     * Create a JsonObjectBuilder filled with the given initial data.
+     *
+     * @throws NullPointerException if initialData is {@code null}
+     *
+     * @return a new pre initialised JsonObjectBuilder
+     *
+     * @since 1.1
+     */
+    JsonObjectBuilder createObjectBuilder(JsonObject initialData);
+
+    /**
+     * Create a JsonObjectBuilder filled with the given initial data.
+     *
+     * @throws NullPointerException if initialData is {@code null}
+     *
+     * @return a new pre initialised JsonObjectBuilder
+     *
+     * @since 1.1
+     */
+    JsonObjectBuilder createObjectBuilder(Map<String, Object> initialData);
+
+    /**
+     * Create a JsonArrayBuilder filled with the given initial data.
+     *
+     * @throws NullPointerException if initialData is {@code null}
+     *
+     * @return a new pre initialised JsonArrayBuilder
+     *
+     * @since 1.1
+     */
+    JsonArrayBuilder createArrayBuilder(JsonArray initialData);
+
+    /**
+     * Create a {@link JsonArrayBuilder} which is filled with the given initial content.
+     * @param initialData the content to immediately add to the JsonArrayBuilder
+     *
+     * @throws NullPointerException if initialData is {@code null}
+     *
+     * @return a new pre initialised JsonArrayBuilder
+     * @since 1.1
+     */
+    JsonArrayBuilder createArrayBuilder(java.util.Collection<?> initialData);
+}
+
diff --git a/src/main/java/javax/json/JsonException.java b/src/main/java/javax/json/JsonException.java
new file mode 100644
index 0000000..47c4d61
--- /dev/null
+++ b/src/main/java/javax/json/JsonException.java
@@ -0,0 +1,27 @@
+/*
+ * 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 javax.json;
+
+public class JsonException extends RuntimeException {
+    public JsonException(final String message) {
+        super(message);
+    }
+
+    public JsonException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/main/java/javax/json/JsonMergePatch.java b/src/main/java/javax/json/JsonMergePatch.java
new file mode 100644
index 0000000..0ff3b7d
--- /dev/null
+++ b/src/main/java/javax/json/JsonMergePatch.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 javax.json;
+
+/**
+ * A JSON MergePatch as defined in http://tools.ietf.org/html/rfc7396
+ *
+ * @see javax.json.spi.JsonProvider#createMergePatch(JsonValue)
+ * @see javax.json.spi.JsonProvider#createMergeDiff(JsonValue, JsonValue)
+ */
+public interface JsonMergePatch  {
+
+    /**
+     * Applies the current JsonMergePatch to the given value
+     * @param valueToApplyPatchOn the base value. Will not get changed itself.
+     * @return the patched new JsonValue
+     */
+    JsonValue apply(JsonValue valueToApplyPatchOn);
+
+    /**
+     * @return the Patch as JsonValue
+     */
+    JsonValue toJsonValue();
+}
+
diff --git a/src/main/java/javax/json/JsonNumber.java b/src/main/java/javax/json/JsonNumber.java
new file mode 100644
index 0000000..ab0ace2
--- /dev/null
+++ b/src/main/java/javax/json/JsonNumber.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 javax.json;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * JsonValue which represents a number.
+ *
+ * The decimal point is defined as dot '.'.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc4627">RFC-4627 JSON Specification</a>
+ */
+public interface JsonNumber extends JsonValue {
+    boolean isIntegral();
+
+    int intValue();
+
+    int intValueExact();
+
+    long longValue();
+
+    long longValueExact();
+
+    BigInteger bigIntegerValue();
+
+    BigInteger bigIntegerValueExact();
+
+    double doubleValue();
+
+    BigDecimal bigDecimalValue();
+
+    /**
+     * @since 1.1
+     */
+    Number numberValue();
+
+    @Override
+    String toString();
+
+    @Override
+    boolean equals(Object obj);
+
+    @Override
+    int hashCode();
+}
diff --git a/src/main/java/javax/json/JsonObject.java b/src/main/java/javax/json/JsonObject.java
new file mode 100644
index 0000000..c1e6f82
--- /dev/null
+++ b/src/main/java/javax/json/JsonObject.java
@@ -0,0 +1,103 @@
+/*
+ * 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 javax.json;
+
+import java.util.Map;
+
+/**
+ * A JsonObject, e.g.
+ * <pre>
+ * {
+ *     "name":"karl",
+ *     "age":38,
+ *     "address": {
+ *         "street":"dummystreet"
+ *         "housenumber":12
+ *     }
+ * }
+ * </pre>
+ *
+ * A JsonObject is always also a Map which uses the attribute names as key mapping
+ * to their JsonValues.
+ */
+public interface JsonObject extends JsonStructure, Map<String, JsonValue> {
+
+    /**
+     * @return the JsonArray with the given name or {@code null} if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    JsonArray getJsonArray(String name);
+
+    /**
+     * @return the JsonObject with the given name or {@code null} if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    JsonObject getJsonObject(String name);
+
+    /**
+     * @return the JsonNumber with the given name or {@code null} if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    JsonNumber getJsonNumber(String name);
+
+    /**
+     * @return the JsonString with the given name or {@code null} if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    JsonString getJsonString(String name);
+
+    /**
+     * @return the native string with the given name or {@code null} if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    String getString(String name);
+
+    /**
+     * @return the native string with the given name or the default value if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    String getString(String name, String defaultValue);
+
+    /**
+     * @return the int with the given name or {@code null} if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    int getInt(String name);
+
+    /**
+     * @return the int with the given name or the default value if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    int getInt(String name, int defaultValue);
+
+    /**
+     * @return the boolean with the given name or {@code null} if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    boolean getBoolean(String name);
+
+    /**
+     * @return the boolean with the given name or the default value if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    boolean getBoolean(String name, boolean defaultValue);
+
+    /**
+     * @return whether the attribute with the given name is {@link JsonValue#NULL}
+     */
+    boolean isNull(String name);
+}
diff --git a/src/main/java/javax/json/JsonObjectBuilder.java b/src/main/java/javax/json/JsonObjectBuilder.java
new file mode 100644
index 0000000..e472a64
--- /dev/null
+++ b/src/main/java/javax/json/JsonObjectBuilder.java
@@ -0,0 +1,148 @@
+/*
+ * 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 javax.json;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * A JsonObjectBuilder can be used to build {@link JsonObject JsonObjects}.
+ * Instances are not thread safe.
+ *
+ * Calling any of those methods with either the {@code name} or {@code value} param as {@code null}
+ * will result in a {@code NullPointerException}
+ */
+public interface JsonObjectBuilder {
+    /**
+     * Add the given JsonValue value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value the JsonValue to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, JsonValue value);
+
+    /**
+     * Add the given String value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value the String value to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, String value);
+
+    /**
+     * Add the given BigInteger value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value the BigInteger value to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, BigInteger value);
+
+    /**
+     * Add the given BigDecimal value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value the BigDecimal value to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, BigDecimal value);
+
+    /**
+     * Add the given int value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, int value);
+
+    /**
+     * Add the given long value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, long value);
+
+    /**
+     * Add the given double value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, double value);
+
+    /**
+     * Add the given boolean value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, boolean value);
+
+    /**
+     * Add a {@link JsonValue#NULL} value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the null value.
+     * @param name the JSON attribute name
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder addNull(String name);
+
+    /**
+     * Use the given {@link JsonObjectBuilder} to create a {@link JsonObject} which will be
+     * added to the JsonObject to be created by this builder.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param builder for creating the JsonObject to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, JsonObjectBuilder builder);
+
+    /**
+     * Use the given {@link JsonArrayBuilder} to create a {@link JsonArray} which will be
+     * added to the JsonObject to be created by this builder.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param builder for creating the JsonArray to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, JsonArrayBuilder builder);
+
+    /**
+     * @return a {@link JsonObject} based on the added values.
+     */
+    JsonObject build();
+
+    /**
+     * Add all of the attributes of the given {@link JsonObjectBuilder} to the current one
+     *
+     * @since 1.1
+     */
+    JsonObjectBuilder addAll(JsonObjectBuilder builder);
+
+    /**
+     * Remove the attribute with the given name from the builder.
+     *
+     * @since 1.1
+     */
+    JsonObjectBuilder remove(String name);
+}
diff --git a/src/main/java/javax/json/JsonPatch.java b/src/main/java/javax/json/JsonPatch.java
new file mode 100644
index 0000000..7a36fcf
--- /dev/null
+++ b/src/main/java/javax/json/JsonPatch.java
@@ -0,0 +1,208 @@
+/*
+ * 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 javax.json;
+
+/**
+ * <p>
+ * JsonPatch is a format for expressing a sequence of operations to apply on
+ * a target JSON document.
+ * </p>
+ * <p>
+ * A JsonPatch is an array of 'operations' in the form e.g.
+ * <p>
+ * <pre>
+ * [
+ *   { "op": "add", "path": "/foo/-", "value": ["abc", "def"] }
+ *   { "path": "/a/b/c", "op": "add", "value": "foo" }
+ * ]
+ * </pre>
+ * <p>
+ * The 'operations' are performed in the order they are in the JsonPatch and applied
+ * to the 'result' JSON document from the previous operation.
+ * <p>
+ *
+ * @see Operation
+ *
+ * @since 1.1
+ */
+public interface JsonPatch {
+
+    /**
+     * <p>
+     * An enumeration of available operations for {@link JsonPatch}.<br>
+     * <p>
+     * NOTICE: the behavoir of some operations depends on which {@link JsonValue} they are performed.
+     * for details please check the documentation of the very operation.
+     *
+     * @see <a href="http://tools.ietf.org/html/rfc6902">RFC-6902</a>
+     *
+     * @since 1.1
+     */
+    enum Operation {
+
+        /**
+         * <p>
+         * required members are:
+         * <ul>
+         * <li>"op": "add"</li>
+         * <li>"path": "path/to/add"</li>
+         * <li>"value": "{@link JsonValue}ToAdd"</li>
+         * </ul>
+         * <p>
+         * if the "path/to" does not exist, this operation will result in an error.<br>
+         * if the "path/to/add" already exists, the value will be <strong>replaced</strong>
+         * <p>
+         * for {@link JsonArray}s the new value will be inserted at the specified index
+         * and the element(s) at/after are shifted to the right. the '-' character is used to append the value
+         * at the and of the {@link JsonArray}.
+         */
+        ADD("add"),
+
+        /**
+         * <p>
+         * required members are:
+         * <ul>
+         * <li>"op": "remove"</li>
+         * <li>"path": "path/to/remove"</li>
+         * </ul>
+         * <p>
+         * if the "path/to/remove" does not exist, the operation will fail.
+         * <p>
+         * for {@link JsonArray}s the values after the removed value are shifted to the left
+         */
+        REMOVE("remove"),
+
+        /**
+         * <p>
+         * required members are:
+         * <ul>
+         * <li>"op": "replace"</li>
+         * <li>"path": "path/to/replace"</li>
+         * <li>"value": "the new {@link JsonValue}"</li>
+         * </ul>
+         * <p>
+         * this operation is identical to {@link #REMOVE} followed by {@link #ADD}
+         */
+        REPLACE("replace"),
+
+        /**
+         * <p>
+         * required members are:
+         * <ul>
+         * <li>"op": "move"</li>
+         * <li>"from": "path/to/move/from"</li>
+         * <li>"path": "path/to/move/to"</li>
+         * </ul>
+         * <p>
+         * the operation will fail it the "path/to/move/from" does not exist
+         * <p>
+         * NOTICE: a location can not be moved into one of it's children. (from /a/b/c to /a/b/c/d)
+         * <p>
+         * this operation is identical to {@link #REMOVE} from "from" and {@link #ADD} to the "path"
+         */
+        MOVE("move"),
+
+        /**
+         * <p>
+         * required members are:
+         * <ul>
+         * <li>"op": "copy"</li>
+         * <li>"from": "path/to/copy/from"</li>
+         * <li>"path": "path/to/add"</li>
+         * </ul>
+         * <p>
+         * the operation will result in an error if the "from" location does not exist
+         * <p>
+         * this operation is identical to {@link #ADD} with the "from" value
+         */
+        COPY("copy"),
+
+        /**
+         * <p>
+         * required members are:
+         * <ul>
+         * <li>"op": "test"</li>
+         * <li>"path": "/path/to/test"</li>
+         * <li>"value": "{@link JsonValue} to test"</li>
+         * </ul>
+         * <p>
+         * this operation fails, if the value is NOT equal with the /path/to/test
+         * <p>
+         * ordering of the elements in a {@link JsonObject} is NOT significant however
+         * the position of an element in a {@link JsonArray} is significant for equality.
+         */
+        TEST("test");
+
+
+        private final String operationName;
+
+
+        Operation(String operationName) {
+            this.operationName = operationName;
+        }
+
+
+        /**
+         * @return the JSON operation name
+         */
+        public String operationName() {
+            return operationName;
+        }
+
+        /**
+         * Returns the {@link Operation} for the given {@code operationName}. If no {@link Operation}
+         * is present, a {@link JsonException} will be thrown.
+         *
+         * @param operationName {@code operationName} to convert to the enum constant.
+         *
+         * @return the {@link Operation Operation-constant} for given {@code operationName}
+         *
+         * @throws JsonException if no {@link Operation} has been found for the given {@code operationName}
+         */
+        public static Operation fromOperationName(String operationName) {
+
+            for (Operation op : values()) {
+                if (op.operationName().equalsIgnoreCase(operationName)) {
+                    return op;
+                }
+            }
+
+            throw new JsonException("unknown value for the operationName of the JSON patch operation: " + operationName);
+        }
+    }
+
+
+    /**
+     * Applies the {@link JsonPatch} to the given {@code target}. If the
+     * given {@code target} is {@code null} a {@link NullPointerException}
+     * will be thrown.
+     *
+     * @param target - the target to apply the {@link JsonPatch}
+     *
+     * @return 'patched' {@link JsonStructure}
+     *
+     * @throws NullPointerException if {@code target} is {@code null}
+     */
+    <T extends JsonStructure> T apply(T target);
+
+    /**
+     * @return the JsonPatch as {@link JsonArray}
+     */
+    JsonArray toJsonArray();
+}
+
diff --git a/src/main/java/javax/json/JsonPatchBuilder.java b/src/main/java/javax/json/JsonPatchBuilder.java
new file mode 100644
index 0000000..775e048
--- /dev/null
+++ b/src/main/java/javax/json/JsonPatchBuilder.java
@@ -0,0 +1,193 @@
+/*
+ * 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 javax.json;
+
+/**
+ * TODO this is a final class in the spec, but that makes no sense.
+ * This class can be used to easily create {@link JsonPatch}es.
+ * To get an instance use {@link Json#createPatchBuilder()} or {@link Json#createPatchBuilder(JsonArray)}
+ * The order of the operations corresponds to the order they are builded.
+ * <p>
+ * NOTICE: A JsonPatchBuilder contains state and therefore is NOT threadsafe and should not be used concurrently.
+ * <p>
+ * The following {@link JsonPatch}
+ * <pre>
+ *     "[
+ *         {
+ *           "op": "add",
+ *           "path": "/add/object",
+ *           "value": {
+ *             "foo": "bar"
+ *           }
+ *         },
+ *         {
+ *           "op": "remove",
+ *           "path": "/remove/it"
+ *         },
+ *         {
+ *           "op": "move",
+ *           "path": "move/to",
+ *           "from": "move/from"
+ *         }
+ *     ]"
+ * </pre>
+ * can be build with the JsonPatchBuilder
+ * <pre>
+ *
+ *     JsonPatch patch = Json.createJsonPatchBuilder()
+ *                           .add("/add/object", Json.createObjectBuilder()
+ *                                                   .add("foo", "bar")
+ *                                                   .build())
+ *                           .remove("/remove/it")
+ *                           .move("/move/to", "move/from")
+ *                           .build();
+ *
+ * </pre>
+ * <p>
+ * An instance of a JsonPatchBuilder can be reused for another {@link JsonPatch} after
+ * the {@link #build()}-Method was called.
+ */
+public interface JsonPatchBuilder {
+
+    /**
+     * Adds an 'add'-operation to the {@link JsonPatch}
+     *
+     * @param path as {@link JsonPointer} where the value should be added
+     * @param value the value to add
+     *
+     * @return the builder instance for chained method calls
+     *
+     * @throws NullPointerException if the given {@code path} is {@code null}
+     */
+    JsonPatchBuilder add(String path, JsonValue value);
+
+    /**
+     * @see #add(String, JsonValue)
+     */
+    JsonPatchBuilder add(String path, String value);
+
+    /**
+     * @see #add(String, JsonValue)
+     */
+    JsonPatchBuilder add(String path, int value);
+
+    /**
+     * @see #add(String, JsonValue)
+     */
+    JsonPatchBuilder add(String path, boolean value);
+
+
+    /**
+     * Adds a 'remove'-operation to the {@link JsonPatch}
+     *
+     * @param path as {@link JsonPointer} of the value which should get removed
+     *
+     * @return the builder instance for chained method calls
+     *
+     * @throws NullPointerException if the given {@code path} is {@code null}
+     */
+    JsonPatchBuilder remove(String path);
+
+
+    /**
+     * Adds a 'replace'-operation to the {@link JsonPatch}
+     *
+     * @param path as {@link JsonPointer} to the value which should get replaced
+     * @param value the new value
+     *
+     * @return the builder instance for chained method calls
+     *
+     * @throws NullPointerException if the given {@code path} is {@code null}
+     */
+    JsonPatchBuilder replace(String path, JsonValue value);
+
+    /**
+     * @see #replace(String, JsonValue)
+     */
+    JsonPatchBuilder replace(String path, String value);
+
+    /**
+     * @see #replace(String, JsonValue)
+     */
+    JsonPatchBuilder replace(String path, int value);
+
+    /**
+     * @see #replace(String, JsonValue)
+     */
+    JsonPatchBuilder replace(String path, boolean value);
+
+
+    /**
+     * Adds a 'move'-operation to the {@link JsonPatch}
+     *
+     * @param path where the value should get inserted as {@link JsonPointer}
+     * @param from where the value should be taken from as {@link JsonPointer}
+     *
+     * @return the builder instance for chained method calls
+     *
+     * @throws NullPointerException if the given {@code path} is {@code null}
+     *                              if the given {@code from} is {@code null}
+     */
+    JsonPatchBuilder move(String path, String from);
+
+
+    /**
+     * Adds a 'copy'-operation to the {@link JsonPatch}
+     *
+     * @param path where the copied value should get inserted as {@link JsonPointer}
+     * @param from value to copy as {@link JsonPointer}
+     *
+     * @return the builder instance for chained method calls
+     *
+     * @throws NullPointerException if the given {@code path} is {@code null}
+     *                              if the given {@code from} is {@code null}
+     */
+    JsonPatchBuilder copy(String path, String from);
+
+
+    /**
+     * Adds a 'test'-operation to the {@link JsonPointer}
+     *
+     * @param path as {@link JsonPointer} to the value to test
+     * @param value value to test
+
+     * @return the builder instance for chained method calls
+
+     * @throws NullPointerException if the given {@code path} is {@code null}
+     */
+    JsonPatchBuilder test(String path, JsonValue value);
+
+    /**
+     * @see #test(String, JsonValue)
+     */
+    JsonPatchBuilder test(String path, String value);
+
+    /**
+     * @see #test(String, JsonValue)
+     */
+    JsonPatchBuilder test(String path, int value);
+
+    /**
+     * @see #test(String, JsonValue)
+     */
+    JsonPatchBuilder test(String path, boolean value);
+
+
+    JsonPatch build();
+}
+
diff --git a/src/main/java/javax/json/JsonPointer.java b/src/main/java/javax/json/JsonPointer.java
new file mode 100644
index 0000000..a4018d2
--- /dev/null
+++ b/src/main/java/javax/json/JsonPointer.java
@@ -0,0 +1,94 @@
+/*
+ * 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 javax.json;
+
+
+/**
+ * <p>This class is an immutable representation of a JSON Pointer as specified in
+ * <a href="http://tools.ietf.org/html/rfc6901">RFC 6901</a>.
+ *
+ *
+ * <p>JSON Pointer is a string syntax for identifying a specific value
+ * within a JavaScript Object Notation (JSON) document [RFC4627].
+ * JSON Pointer is intended to be easily expressed in JSON string values
+ * as well as Uniform Resource Identifier (URI) [RFC3986] fragment identifiers.
+ *
+ * <p> The method {@link #getValue getValue()} returns the referenced value.
+ * The methods {@link #add add()}, {@link #replace replace()},
+ * and {@link #remove remove()} executes the operations specified in
+ * <a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>.
+ *
+ * @since 1.1
+ */
+public interface JsonPointer {
+
+    /**
+     * Get the JsonValue at the position referenced by this JsonPointer.
+     * @param target the JSON to apply this JsonPointer on
+     * @return the
+     * @throws JsonException if no value exists at the referenced location
+     * @throws NullPointerException if the target is {@code null}
+     */
+    JsonValue getValue(JsonStructure target);
+
+
+    /**
+     * Add or replace the value at the position referenced by this JsonPointer with
+     * the new value
+     * @param target structure in which the newValue should be added or replaced
+     * @param newValue the new value to set
+     * @param <T>
+     * @return the new structure after modifying the original JsonStrucure
+     * @throws JsonException if no value exists at the referenced location
+     * @throws NullPointerException if the target is {@code null}
+     */
+    <T extends JsonStructure> T add(T target, JsonValue newValue);
+
+
+    /**
+     * Remove the value from the referenced position inside the target JSON.
+     * @param target to remove the value from
+     * @param <T> the type of the passed JsonStructure
+     * @return a new JsonStructure with the value removed from the target
+     * @throws JsonException if no value exists at the referenced location
+     * @throws NullPointerException if the target is {@code null}
+     */
+    <T extends JsonStructure> T remove(T target);
+
+    /**
+     * Replace the value at the position referenced by this JsonPointer with
+     * the newValue.
+     *
+     * @param target structure in which the newValue should be replaced
+     * @param newValue the new value to set
+     * @param <T>
+     * @return the new structure after modifying the original JsonStrucure
+     * @throws JsonException if no value exists at the referenced location
+     * @throws NullPointerException if the target is {@code null}
+     */
+    <T extends JsonStructure> T  replace(T target, JsonValue newValue);
+
+    /**
+     * Verify if the target JSON structure contains a value at the referenced location.
+     * @param target to check
+     * @return {@code true} if there is a value at the referenced location, {@code false} otherwise
+     * @throws NullPointerException if the target is {@code null}
+     */
+    boolean containsValue(JsonStructure target);
+
+}
diff --git a/src/main/java/javax/json/JsonReader.java b/src/main/java/javax/json/JsonReader.java
new file mode 100644
index 0000000..4284410
--- /dev/null
+++ b/src/main/java/javax/json/JsonReader.java
@@ -0,0 +1,32 @@
+/*
+ * 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 javax.json;
+
+import java.io.Closeable;
+
+public interface JsonReader extends Closeable {
+    JsonStructure read();
+
+    JsonObject readObject();
+
+    JsonArray readArray();
+
+    @Override
+    void close();
+    
+    JsonValue readValue();
+}
diff --git a/src/main/java/javax/json/JsonReaderFactory.java b/src/main/java/javax/json/JsonReaderFactory.java
new file mode 100644
index 0000000..8212fe1
--- /dev/null
+++ b/src/main/java/javax/json/JsonReaderFactory.java
@@ -0,0 +1,32 @@
+/*
+ * 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 javax.json;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+public interface JsonReaderFactory {
+    JsonReader createReader(Reader reader);
+
+    JsonReader createReader(InputStream in);
+
+    JsonReader createReader(InputStream in, Charset charset);
+
+    Map<String, ?> getConfigInUse();
+}
diff --git a/src/main/java/javax/json/JsonString.java b/src/main/java/javax/json/JsonString.java
new file mode 100644
index 0000000..868cfad
--- /dev/null
+++ b/src/main/java/javax/json/JsonString.java
@@ -0,0 +1,32 @@
+/*
+ * 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 javax.json;
+
+/**
+ * JsonValue which represents a string.
+ */
+public interface JsonString extends JsonValue {
+    String getString();
+
+    CharSequence getChars();
+
+    @Override
+    boolean equals(Object obj);
+
+    @Override
+    int hashCode();
+}
diff --git a/src/main/java/javax/json/JsonStructure.java b/src/main/java/javax/json/JsonStructure.java
new file mode 100644
index 0000000..c4d353c
--- /dev/null
+++ b/src/main/java/javax/json/JsonStructure.java
@@ -0,0 +1,28 @@
+/*
+ * 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 javax.json;
+
+import javax.json.spi.JsonProvider;
+
+/**
+ * A complex type as JsonValue.
+ * This could either be a {@link JsonArray} or a {@link JsonObject}.
+ */
+public interface JsonStructure extends JsonValue {
+
+    JsonValue getValue(String jsonPointer);
+}
\ No newline at end of file
diff --git a/src/main/java/javax/json/JsonValue.java b/src/main/java/javax/json/JsonValue.java
new file mode 100644
index 0000000..8598a1c
--- /dev/null
+++ b/src/main/java/javax/json/JsonValue.java
@@ -0,0 +1,149 @@
+/*
+ * 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 javax.json;
+
+/**
+ * A single value in a JSON expression.
+ */
+public interface JsonValue {
+
+    /**
+     * The empty JSON object.
+     */
+    JsonObject EMPTY_JSON_OBJECT = Json.createObjectBuilder().build();
+
+    /**
+     * The empty JSON array.
+     */
+    JsonArray EMPTY_JSON_ARRAY = Json.createArrayBuilder().build();
+
+
+    /**
+     * A constant JsonValue for null values
+     */
+    JsonValue NULL = new JsonValue() {
+        @Override
+        public ValueType getValueType() {
+            return ValueType.NULL;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            return obj instanceof JsonValue && getValueType().equals(JsonValue.class.cast(obj).getValueType());
+        }
+
+        @Override
+        public int hashCode() {
+            return ValueType.NULL.hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return "null";
+        }
+
+        public JsonObject asJsonObject() {
+            return JsonObject.class.cast(this);
+        }
+
+        public JsonArray asJsonArray() {
+            return JsonArray.class.cast(this);
+        }
+    };
+
+    /**
+     * A constant JsonValue for TRUE
+     */
+    JsonValue TRUE = new JsonValue() {
+        @Override
+        public ValueType getValueType() {
+            return ValueType.TRUE;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            return obj instanceof JsonValue && getValueType().equals(JsonValue.class.cast(obj).getValueType());
+        }
+
+        @Override
+        public int hashCode() {
+            return ValueType.TRUE.hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return Boolean.TRUE.toString();
+        }
+
+        public JsonObject asJsonObject() {
+            return JsonObject.class.cast(this);
+        }
+
+        public JsonArray asJsonArray() {
+            return JsonArray.class.cast(this);
+        }
+    };
+
+    /**
+     * A constant JsonValue for FALSE
+     */
+    JsonValue FALSE = new JsonValue() {
+        @Override
+        public ValueType getValueType() {
+            return ValueType.FALSE;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            return obj instanceof JsonValue && getValueType().equals(JsonValue.class.cast(obj).getValueType());
+        }
+
+        @Override
+        public int hashCode() {
+            return ValueType.FALSE.hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return Boolean.FALSE.toString();
+        }
+
+        public JsonObject asJsonObject() {
+            return JsonObject.class.cast(this);
+        }
+
+        public JsonArray asJsonArray() {
+            return JsonArray.class.cast(this);
+        }
+    };
+
+    ValueType getValueType();
+
+    @Override
+    String toString();
+
+    enum ValueType {
+        ARRAY,
+        OBJECT, STRING, NUMBER,
+        TRUE, FALSE,
+        NULL
+    }
+
+    JsonObject asJsonObject();
+
+    JsonArray asJsonArray();
+}
diff --git a/src/main/java/javax/json/JsonWriter.java b/src/main/java/javax/json/JsonWriter.java
new file mode 100644
index 0000000..da2756a
--- /dev/null
+++ b/src/main/java/javax/json/JsonWriter.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 javax.json;
+
+import java.io.Closeable;
+
+/**
+ * Write a JsonObject, JsonArray JsonStructure or more generic
+ * a JsonValue to the output.
+ *
+ * @see JsonWriterFactory
+ */
+public interface JsonWriter extends Closeable {
+
+    void writeArray(JsonArray array);
+
+    void writeObject(JsonObject object);
+
+    void write(JsonStructure value);
+
+    void write(JsonValue value);
+
+    @Override
+    void close();
+}
diff --git a/src/main/java/javax/json/JsonWriterFactory.java b/src/main/java/javax/json/JsonWriterFactory.java
new file mode 100644
index 0000000..e292947
--- /dev/null
+++ b/src/main/java/javax/json/JsonWriterFactory.java
@@ -0,0 +1,32 @@
+/*
+ * 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 javax.json;
+
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+public interface JsonWriterFactory {
+    JsonWriter createWriter(Writer writer);
+
+    JsonWriter createWriter(OutputStream out);
+
+    JsonWriter createWriter(OutputStream out, Charset charset);
+
+    Map<String, ?> getConfigInUse();
+}
diff --git a/src/main/java/javax/json/spi/JsonProvider.java b/src/main/java/javax/json/spi/JsonProvider.java
index 5464ce6..63b2809 100644
--- a/src/main/java/javax/json/spi/JsonProvider.java
+++ b/src/main/java/javax/json/spi/JsonProvider.java
@@ -22,13 +22,26 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.Reader;
 import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
 import java.util.Map;
 
+import javax.json.JsonArray;
 import javax.json.JsonArrayBuilder;
 import javax.json.JsonBuilderFactory;
+import javax.json.JsonMergePatch;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
 import javax.json.JsonObjectBuilder;
+import javax.json.JsonPatch;
+import javax.json.JsonPatchBuilder;
+import javax.json.JsonPointer;
 import javax.json.JsonReader;
 import javax.json.JsonReaderFactory;
+import javax.json.JsonString;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
 import javax.json.JsonWriter;
 import javax.json.JsonWriterFactory;
 import javax.json.stream.JsonGenerator;
@@ -51,32 +64,100 @@ public abstract class JsonProvider {
 
     public abstract JsonParser createParser(Reader reader);
 
-    public abstract JsonParser createParser(InputStream in);
+    public abstract JsonParser createParser(InputStream var1);
 
-    public abstract JsonParserFactory createParserFactory(Map<String, ?> config);
+    public abstract JsonParserFactory createParserFactory(Map<String, ?> var1);
 
-    public abstract JsonGenerator createGenerator(Writer writer);
+    public abstract JsonGenerator createGenerator(Writer var1);
 
-    public abstract JsonGenerator createGenerator(OutputStream out);
+    public abstract JsonGenerator createGenerator(OutputStream var1);
 
-    public abstract JsonGeneratorFactory createGeneratorFactory(Map<String, ?> config);
+    public abstract JsonGeneratorFactory createGeneratorFactory(Map<String, ?> var1);
 
-    public abstract JsonReader createReader(Reader reader);
+    public abstract JsonReader createReader(Reader var1);
 
-    public abstract JsonReader createReader(InputStream in);
+    public abstract JsonReader createReader(InputStream var1);
 
-    public abstract JsonWriter createWriter(Writer writer);
+    public abstract JsonWriter createWriter(Writer var1);
 
-    public abstract JsonWriter createWriter(OutputStream out);
+    public abstract JsonWriter createWriter(OutputStream var1);
 
-    public abstract JsonWriterFactory createWriterFactory(Map<String, ?> config);
+    public abstract JsonWriterFactory createWriterFactory(Map<String, ?> var1);
 
-    public abstract JsonReaderFactory createReaderFactory(Map<String, ?> config);
+    public abstract JsonReaderFactory createReaderFactory(Map<String, ?> var1);
 
     public abstract JsonObjectBuilder createObjectBuilder();
 
+    public JsonObjectBuilder createObjectBuilder(JsonObject jsonObject) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonObjectBuilder createObjectBuilder(Map<String, Object> map) {
+        throw new UnsupportedOperationException();
+    }
+
     public abstract JsonArrayBuilder createArrayBuilder();
 
-    public abstract JsonBuilderFactory createBuilderFactory(Map<String, ?> config);
+    public JsonArrayBuilder createArrayBuilder(JsonArray initialData) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonArrayBuilder createArrayBuilder(Collection<?> initialData) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonPointer createPointer(String path) {
+        throw new UnsupportedOperationException();
+    }
+
+    public abstract JsonBuilderFactory createBuilderFactory(Map<String, ?> var1);
+
+    public JsonString createValue(String value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonNumber createValue(int value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonNumber createValue(long value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonNumber createValue(double value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonNumber createValue(BigDecimal value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonNumber createValue(BigInteger value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonPatch createPatch(JsonArray array) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonPatch createDiff(JsonStructure source, JsonStructure target) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonPatchBuilder createPatchBuilder() {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonPatchBuilder createPatchBuilder(JsonArray initialData) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonMergePatch createMergePatch(JsonValue patch) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonMergePatch createMergeDiff(JsonValue source, JsonValue target) {
+        throw new UnsupportedOperationException();
+    }
 }
 
diff --git a/src/main/java/javax/json/stream/JsonGenerationException.java b/src/main/java/javax/json/stream/JsonGenerationException.java
new file mode 100644
index 0000000..46e12d8
--- /dev/null
+++ b/src/main/java/javax/json/stream/JsonGenerationException.java
@@ -0,0 +1,32 @@
+/*
+ * 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 javax.json.stream;
+
+import javax.json.JsonException;
+
+public class JsonGenerationException extends JsonException {
+    public JsonGenerationException(final String message) {
+        super(message);
+    }
+
+    public JsonGenerationException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+
+}
+
+
diff --git a/src/main/java/javax/json/stream/JsonGenerator.java b/src/main/java/javax/json/stream/JsonGenerator.java
new file mode 100644
index 0000000..de9587a
--- /dev/null
+++ b/src/main/java/javax/json/stream/JsonGenerator.java
@@ -0,0 +1,88 @@
+/*
+ * 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 javax.json.stream;
+
+import javax.json.JsonValue;
+import java.io.Closeable;
+import java.io.Flushable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public interface JsonGenerator extends Flushable, Closeable {
+    String PRETTY_PRINTING = "javax.json.stream.JsonGenerator.prettyPrinting"; // TODO: ensure it exists before releasing
+
+    JsonGenerator writeStartObject();
+
+    JsonGenerator writeStartObject(String name);
+
+    JsonGenerator writeStartArray();
+
+    JsonGenerator writeStartArray(String name);
+
+    /**
+     * Write the key with a colon;
+     *
+     * @throws JsonGenerationException if this method is not called within an object context
+     * @since 1.1
+     */
+    JsonGenerator writeKey(String name);
+
+    JsonGenerator write(String name, JsonValue value);
+
+    JsonGenerator write(String name, String value);
+
+    JsonGenerator write(String name, BigInteger value);
+
+    JsonGenerator write(String name, BigDecimal value);
+
+    JsonGenerator write(String name, int value);
+
+    JsonGenerator write(String name, long value);
+
+    JsonGenerator write(String name, double value);
+
+    JsonGenerator write(String name, boolean value);
+
+    JsonGenerator writeNull(String name);
+
+    JsonGenerator writeEnd();
+
+    JsonGenerator write(JsonValue value);
+
+    JsonGenerator write(String value);
+
+    JsonGenerator write(BigDecimal value);
+
+    JsonGenerator write(BigInteger value);
+
+    JsonGenerator write(int value);
+
+    JsonGenerator write(long value);
+
+    JsonGenerator write(double value);
+
+    JsonGenerator write(boolean value);
+
+    JsonGenerator writeNull();
+
+    @Override
+    void close();
+
+    @Override
+    void flush();
+
+}
diff --git a/src/main/java/javax/json/stream/JsonGeneratorFactory.java b/src/main/java/javax/json/stream/JsonGeneratorFactory.java
new file mode 100644
index 0000000..fbfc6c2
--- /dev/null
+++ b/src/main/java/javax/json/stream/JsonGeneratorFactory.java
@@ -0,0 +1,32 @@
+/*
+ * 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 javax.json.stream;
+
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+public interface JsonGeneratorFactory {
+    JsonGenerator createGenerator(Writer writer);
+
+    JsonGenerator createGenerator(OutputStream out);
+
+    JsonGenerator createGenerator(OutputStream out, Charset charset);
+
+    Map<String, ?> getConfigInUse();
+}
diff --git a/src/main/java/javax/json/stream/JsonLocation.java b/src/main/java/javax/json/stream/JsonLocation.java
new file mode 100644
index 0000000..0fc1501
--- /dev/null
+++ b/src/main/java/javax/json/stream/JsonLocation.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 javax.json.stream;
+
+public interface JsonLocation {
+    long getLineNumber();
+
+    long getColumnNumber();
+
+    long getStreamOffset();
+}
+
diff --git a/src/main/java/javax/json/stream/JsonParser.java b/src/main/java/javax/json/stream/JsonParser.java
new file mode 100644
index 0000000..cbfc720
--- /dev/null
+++ b/src/main/java/javax/json/stream/JsonParser.java
@@ -0,0 +1,71 @@
+/*
+ * 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 javax.json.stream;
+
+import java.io.Closeable;
+import java.math.BigDecimal;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+public interface JsonParser extends Closeable {
+    boolean hasNext();
+
+    Event next();
+
+    String getString();
+
+    boolean isIntegralNumber();
+
+    int getInt();
+
+    long getLong();
+
+    BigDecimal getBigDecimal();
+
+    JsonLocation getLocation();
+
+    @Override
+    void close();
+
+    enum Event {
+        START_ARRAY, START_OBJECT,
+        KEY_NAME,
+        VALUE_STRING, VALUE_NUMBER, VALUE_TRUE, VALUE_FALSE, VALUE_NULL,
+        END_OBJECT, END_ARRAY
+    }
+    
+    JsonObject getObject();
+
+    JsonValue getValue();
+
+    JsonArray getArray();
+
+    Stream<JsonValue> getArrayStream();
+
+    Stream<Map.Entry<String,JsonValue>> getObjectStream();
+
+    Stream<JsonValue> getValueStream();
+
+    void skipArray();
+
+    void skipObject();
+}
+
diff --git a/src/main/java/javax/json/stream/JsonParserFactory.java b/src/main/java/javax/json/stream/JsonParserFactory.java
new file mode 100644
index 0000000..aa28fb7
--- /dev/null
+++ b/src/main/java/javax/json/stream/JsonParserFactory.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 javax.json.stream;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+public interface JsonParserFactory {
+    JsonParser createParser(Reader reader);
+
+    JsonParser createParser(InputStream in);
+
+    JsonParser createParser(InputStream in, Charset charset);
+
+    JsonParser createParser(JsonObject obj);
+
+    JsonParser createParser(JsonArray array);
+
+    Map<String, ?> getConfigInUse();
+}
+
diff --git a/src/main/java/javax/json/stream/JsonParsingException.java b/src/main/java/javax/json/stream/JsonParsingException.java
new file mode 100644
index 0000000..757a8bc
--- /dev/null
+++ b/src/main/java/javax/json/stream/JsonParsingException.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 javax.json.stream;
+
+import javax.json.JsonException;
+
+public class JsonParsingException extends JsonException {
+    private final JsonLocation location;
+
+    public JsonParsingException(String message, JsonLocation location) {
+        super(message);
+        this.location = location;
+    }
+
+    public JsonParsingException(String message, Throwable cause, JsonLocation location) {
+        super(message, cause);
+        this.location = location;
+    }
+
+    public JsonLocation getLocation() {
+        return location;
+    }
+}
+
+
diff --git a/src/main/java/org/apache/johnzon/core/AbstractJsonFactory.java b/src/main/java/org/apache/johnzon/core/AbstractJsonFactory.java
new file mode 100644
index 0000000..4ab42d5
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/AbstractJsonFactory.java
@@ -0,0 +1,84 @@
+/*
+ * 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.core;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.logging.Logger;
+
+public abstract class AbstractJsonFactory implements Serializable{
+
+    protected final Logger logger = Logger.getLogger(this.getClass().getName());
+    
+    public static final String BUFFER_STRATEGY = "org.apache.johnzon.buffer-strategy";
+    public static final BufferStrategy DEFAULT_BUFFER_STRATEGY = BufferStrategy.QUEUE;
+    
+    protected final Map<String, Object> internalConfig = new HashMap<String, Object>();
+    
+    protected AbstractJsonFactory(final Map<String, ?> config, Collection<String> supportedConfigKeys, Collection<String> defaultSupportedConfigKeys) {
+        if(config != null && config.size() > 0) {
+            
+            if(defaultSupportedConfigKeys != null) {
+                supportedConfigKeys = new ArrayList<String>(supportedConfigKeys);
+                supportedConfigKeys.addAll(defaultSupportedConfigKeys);
+            }
+            
+            for (String configKey : config.keySet()) {
+                if(supportedConfigKeys.contains(configKey)) {
+                    internalConfig.put(configKey, config.get(configKey));
+                } else {
+                    logger.warning(configKey + " is not supported by " + getClass().getName());
+                }
+            }
+        }
+    }
+
+    protected BufferStrategy getBufferProvider() {
+        final Object name = internalConfig.get(BUFFER_STRATEGY);
+        if (name != null) {
+            return BufferStrategy.valueOf(name.toString().toUpperCase(Locale.ENGLISH));
+        }
+        return DEFAULT_BUFFER_STRATEGY;
+    }
+
+    protected int getInt(final String key, final int defaultValue) {
+        final Object intValue = internalConfig.get(key);
+        if (intValue == null) {
+            return defaultValue;
+        } else if (Number.class.isInstance(intValue)) {
+            return Number.class.cast(intValue).intValue();
+        }
+        return Integer.parseInt(intValue.toString());
+    }
+
+    protected boolean getBool(final String key, final boolean defaultValue) {
+        final Object boolValue = internalConfig.get(key);
+        if (boolValue == null) {
+            return defaultValue;
+        } else if (Boolean.class.isInstance(boolValue)) {
+            return Boolean.class.cast(boolValue);
+        }
+        return Boolean.parseBoolean(boolValue.toString());
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/BufferStrategy.java b/src/main/java/org/apache/johnzon/core/BufferStrategy.java
new file mode 100644
index 0000000..c37ecfb
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/BufferStrategy.java
@@ -0,0 +1,272 @@
+/*
+ * 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.core;
+
+import java.io.Serializable;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+public enum BufferStrategy {
+    BY_INSTANCE {
+        @Override
+        public BufferProvider<char[]> newCharProvider(final int size) {
+            return new CharBufferByInstanceProvider(size);
+        }
+
+        @Override
+        public BufferProvider<StringBuilder> newStringBuilderProvider(final int size) {
+            return new StringBuilderByInstanceProvider(size);
+        }
+    },
+    THREAD_LOCAL {
+        @Override
+        public BufferProvider<char[]> newCharProvider(final int size) {
+            return new CharBufferThreadLocalProvider(size);
+        }
+
+        @Override
+        public BufferProvider<StringBuilder> newStringBuilderProvider(final int size) {
+            return new StringBuilderThreadLocalProvider(size);
+        }
+    },
+    QUEUE {
+        @Override
+        public BufferProvider<char[]> newCharProvider(final int size) {
+            return new CharBufferQueueProvider(size);
+        }
+
+        @Override
+        public BufferProvider<StringBuilder> newStringBuilderProvider(final int size) {
+            return new StringBuilderQueueProvider(size);
+        }
+    },
+    SINGLETON {
+        @Override
+        public BufferProvider<char[]> newCharProvider(final int size) {
+            return new CharBufferSingletonProvider(size);
+        }
+
+        @Override
+        public BufferProvider<StringBuilder> newStringBuilderProvider(final int size) {
+            return new StringBuilderSingletonProvider(size);
+        }
+    };
+
+    public abstract BufferProvider<char[]> newCharProvider(int size);
+    public abstract BufferProvider<StringBuilder> newStringBuilderProvider(int size);
+
+    public static interface BufferProvider<T> extends Serializable {
+        T newBuffer();
+
+        void release(T value);
+    }
+
+    private static class CharBufferSingletonProvider extends SingletonProvider<char[]> {
+        public CharBufferSingletonProvider(final int size) {
+            super(size);
+        }
+
+        @Override
+        protected char[] newInstance(int size) {
+            return new char[size];
+        }
+
+        @Override
+        public void release(final char[] value) {
+            // no-op
+        }
+    }
+
+    private static class StringBuilderSingletonProvider extends SingletonProvider<StringBuilder> {
+        public StringBuilderSingletonProvider(final int size) {
+            super(size);
+        }
+
+        @Override
+        protected StringBuilder newInstance(final int size) {
+            return new StringBuilder(size);
+        }
+
+        @Override
+        public void release(final StringBuilder value) {
+            value.setLength(0);
+        }
+    }
+
+    private static abstract class SingletonProvider<T> implements BufferProvider<T> {
+        protected final T buffer;
+
+        public SingletonProvider(final int size) {
+            buffer = newInstance(size);
+        }
+
+        protected abstract T newInstance(int size);
+
+        @Override
+        public T newBuffer() {
+            return buffer;
+        }
+
+        @Override
+        public void release(final T value) {
+            // no-op
+        }
+    }
+
+    private static abstract class ThreadLocalProvider<T> implements BufferProvider<T> {
+        private final ThreadLocalBufferCache<T> cache;
+
+        public ThreadLocalProvider(final int size) {
+            cache = new ThreadLocalBufferCache<T>(size) {
+                @Override
+                protected T newValue(int defaultSize) {
+                    return newInstance(size);
+                }
+            };
+        }
+
+        protected abstract T newInstance(int size);
+
+        @Override
+        public T newBuffer() {
+            return cache.getCache();
+        }
+
+        @Override
+        public void release(final T value) {
+            cache.release(value);
+        }
+    }
+
+    private static class CharBufferThreadLocalProvider extends ThreadLocalProvider<char[]> {
+        public CharBufferThreadLocalProvider(int size) {
+            super(size);
+        }
+
+        @Override
+        protected char[] newInstance(final int size) {
+            return new char[size];
+        }
+    }
+
+    private static class StringBuilderThreadLocalProvider extends ThreadLocalProvider<StringBuilder> {
+        public StringBuilderThreadLocalProvider(int size) {
+            super(size);
+        }
+
+        @Override
+        protected StringBuilder newInstance(final int size) {
+            return new StringBuilder(size);
+        }
+
+        @Override
+        public void release(final StringBuilder value) {
+            value.setLength(0);
+            super.release(value);
+        }
+    }
+
+    private static class CharBufferByInstanceProvider implements BufferProvider<char[]> {
+        private final int size;
+
+        public CharBufferByInstanceProvider(final int size) {
+            this.size = size;
+        }
+
+        @Override
+        public char[] newBuffer() {
+            return new char[size];
+        }
+
+        @Override
+        public void release(final char[] value) {
+            // no-op
+        }
+    }
+
+    private static class StringBuilderByInstanceProvider implements BufferProvider<StringBuilder> {
+        private final int size;
+
+        public StringBuilderByInstanceProvider(final int size) {
+            this.size = size;
+        }
+
+        @Override
+        public StringBuilder newBuffer() {
+            return new StringBuilder(size);
+        }
+
+        @Override
+        public void release(final StringBuilder value) {
+            // no-op
+        }
+    }
+
+    private static abstract class QueueProvider<T> implements BufferProvider<T> {
+        private final int size;
+        private final ConcurrentLinkedQueue<T> queue = new ConcurrentLinkedQueue<T>();
+
+        public QueueProvider(final int size) {
+            this.size = size;
+        }
+
+        protected abstract T newInstance(int size);
+
+        @Override
+        public T newBuffer() {
+            final T buffer = queue.poll();
+            if (buffer == null) {
+                return newInstance(size);
+            }
+            return buffer;
+        }
+
+        @Override
+        public void release(final T value) {
+            queue.offer(value);
+        }
+    }
+
+    private static class CharBufferQueueProvider extends QueueProvider<char[]> {
+        public CharBufferQueueProvider(final int size) {
+            super(size);
+        }
+
+        @Override
+        protected char[] newInstance(int size) {
+            return new char[size];
+        }
+    }
+
+    private static class StringBuilderQueueProvider extends QueueProvider<StringBuilder> {
+        public StringBuilderQueueProvider(final int size) {
+            super(size);
+        }
+
+        @Override
+        protected StringBuilder newInstance(int size) {
+            return new StringBuilder(size);
+        }
+
+        @Override
+        public void release(final StringBuilder value) {
+            value.setLength(0);
+            super.release(value);
+        }
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/CommentsJsonStreamParserImpl.java b/src/main/java/org/apache/johnzon/core/CommentsJsonStreamParserImpl.java
new file mode 100644
index 0000000..f9d298e
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/CommentsJsonStreamParserImpl.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+
+public class CommentsJsonStreamParserImpl extends JsonStreamParserImpl {
+    public CommentsJsonStreamParserImpl(final InputStream inputStream,
+                                        final int maxStringLength,
+                                        final BufferStrategy.BufferProvider<char[]> bufferProvider,
+                                        final BufferStrategy.BufferProvider<char[]> valueBuffer,
+                                        final boolean autoAdjust) {
+        super(inputStream, maxStringLength, bufferProvider, valueBuffer, autoAdjust);
+    }
+
+    public CommentsJsonStreamParserImpl(final InputStream inputStream,
+                                        final Charset encoding,
+                                        final int maxStringLength,
+                                        final BufferStrategy.BufferProvider<char[]> bufferProvider,
+                                        final BufferStrategy.BufferProvider<char[]> valueBuffer,
+                                        final boolean autoAdjust) {
+        super(inputStream, encoding, maxStringLength, bufferProvider, valueBuffer, autoAdjust);
+    }
+
+    public CommentsJsonStreamParserImpl(final Reader reader,
+                                        final int maxStringLength,
+                                        final BufferStrategy.BufferProvider<char[]> bufferProvider,
+                                        final BufferStrategy.BufferProvider<char[]> valueBuffer,
+                                        final boolean autoAdjust) {
+        super(reader, maxStringLength, bufferProvider, valueBuffer, autoAdjust);
+    }
+
+    @Override
+    protected Event defaultHandling(final char c) {
+        if (c == '/') {
+            char next = readNextChar();
+            if (next == '/') { // fail
+                do {
+                    next = readNextChar();
+                } while (next != EOL);
+                return next();
+            } else if (next == '*') {
+                next = 0;
+                char previous;
+                do {
+                    previous = next;
+                    next = readNextNonWhitespaceChar(readNextChar());
+                } while (next != '/' || previous != '*');
+                readNextNonWhitespaceChar(next);
+                return next();
+            }
+        }
+        return super.defaultHandling(c);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/DiffBase.java b/src/main/java/org/apache/johnzon/core/DiffBase.java
new file mode 100644
index 0000000..d6abfdf
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/DiffBase.java
@@ -0,0 +1,34 @@
+/*
+ * 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.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+/**
+ * Commonly used methods for diffs
+ */
+class DiffBase {
+    protected boolean isJsonObject(JsonValue jsonValue) {
+        return jsonValue instanceof JsonObject;
+    }
+
+    protected boolean isJsonArray(JsonValue targetValue) {
+        return targetValue instanceof JsonArray;
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/Experimental.java b/src/main/java/org/apache/johnzon/core/Experimental.java
new file mode 100644
index 0000000..b645c99
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/Experimental.java
@@ -0,0 +1,33 @@
+/*
+ * 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.core;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Marker for experimental API.
+ */
+@Target(TYPE)
+@Retention(RUNTIME)
+public @interface Experimental {
+}
diff --git a/src/main/java/org/apache/johnzon/core/HStack.java b/src/main/java/org/apache/johnzon/core/HStack.java
new file mode 100644
index 0000000..b2d57bd
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/HStack.java
@@ -0,0 +1,66 @@
+/*
+ * 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.core;
+
+import java.io.Serializable;
+
+public class HStack<T> implements Serializable {
+
+    private Node<T> topElement = null;
+    private int size;
+
+
+    private static final class Node<T> implements Serializable {
+        private final Node<T> previous;
+        private final T object;
+
+        private Node(final Node<T> previous, final T object) {
+            super();
+            this.previous = previous;
+            this.object = object;
+        }
+    }
+
+
+    void push(T object) {
+        topElement = new Node<T>(topElement, object);
+        size++;
+    }
+
+    T pop() {
+
+        if (topElement == null) {
+            return null;
+        }
+
+        T tmp = topElement.object;
+        topElement = topElement.previous;
+        size--;
+        return tmp;
+    }
+
+    T peek() {
+        return topElement == null ? null : topElement.object;
+    }
+
+    int size() {
+        return size;
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JohnzonJsonParser.java b/src/main/java/org/apache/johnzon/core/JohnzonJsonParser.java
new file mode 100644
index 0000000..b13852b
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JohnzonJsonParser.java
@@ -0,0 +1,134 @@
+/*
+ * 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.core;
+
+import java.math.BigDecimal;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+import javax.json.stream.JsonLocation;
+import javax.json.stream.JsonParser;
+
+/**
+ * JsonParser with extended functionality
+ */
+public interface JohnzonJsonParser extends JsonParser {
+
+    boolean isNotTooLong();
+
+
+    public static class JohnzonJsonParserWrapper implements JohnzonJsonParser {
+        private final JsonParser jsonParser;
+
+        public JohnzonJsonParserWrapper(JsonParser jsonParser) {
+            this.jsonParser = jsonParser;
+        }
+
+        @Override
+        public boolean isNotTooLong() {
+            return true;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return jsonParser.hasNext();
+        }
+
+        @Override
+        public Event next() {
+            return jsonParser.next();
+        }
+
+        @Override
+        public String getString() {
+            return jsonParser.getString();
+        }
+
+        @Override
+        public boolean isIntegralNumber() {
+            return jsonParser.isIntegralNumber();
+        }
+
+        @Override
+        public int getInt() {
+            return jsonParser.getInt();
+        }
+
+        @Override
+        public long getLong() {
+            return jsonParser.getLong();
+        }
+
+        @Override
+        public BigDecimal getBigDecimal() {
+            return jsonParser.getBigDecimal();
+        }
+
+        @Override
+        public JsonLocation getLocation() {
+            return jsonParser.getLocation();
+        }
+
+        @Override
+        public void close() {
+            jsonParser.close();
+        }
+
+        @Override
+        public JsonObject getObject() {
+            return jsonParser.getObject();
+        }
+
+        @Override
+        public JsonValue getValue() {
+            return jsonParser.getValue();
+        }
+
+        @Override
+        public JsonArray getArray() {
+            return jsonParser.getArray();
+        }
+
+        @Override
+        public Stream<JsonValue> getArrayStream() {
+            return jsonParser.getArrayStream();
+        }
+
+        @Override
+        public Stream<Map.Entry<String, JsonValue>> getObjectStream() {
+            return jsonParser.getObjectStream();
+        }
+
+        @Override
+        public Stream<JsonValue> getValueStream() {
+            return jsonParser.getValueStream();
+        }
+
+        @Override
+        public void skipArray() {
+            jsonParser.skipArray();
+        }
+
+        @Override
+        public void skipObject() {
+            jsonParser.skipObject();
+        }
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonArrayBuilderImpl.java b/src/main/java/org/apache/johnzon/core/JsonArrayBuilderImpl.java
new file mode 100644
index 0000000..7790288
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonArrayBuilderImpl.java
@@ -0,0 +1,330 @@
+/*
+ * 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.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonException;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonValue;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+class JsonArrayBuilderImpl implements JsonArrayBuilder, Serializable {
+    private List<JsonValue> tmpList;
+
+    public JsonArrayBuilderImpl() {
+    }
+
+    public JsonArrayBuilderImpl(JsonArray initialData) {
+        tmpList = new ArrayList<>(initialData);
+    }
+
+    public JsonArrayBuilderImpl(Collection<?> initialData) {
+        tmpList = new ArrayList<>();
+        for (Object initialValue : initialData) {
+            add(initialValue);
+        }
+    }
+
+    @Override
+    public JsonArrayBuilder addAll(final JsonArrayBuilder builder) {
+        for (JsonValue value : builder.build())
+        {
+            add(value);
+        }
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final JsonValue value) {
+        addValue(index, value);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final String value) {
+        addValue(index, new JsonStringImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final BigDecimal value) {
+        addValue(index, new JsonNumberImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final BigInteger value) {
+        addValue(index, new JsonNumberImpl(new BigDecimal(value)));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final int value) {
+        addValue(index, new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final long value) {
+        addValue(index, new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final double value) {
+        addValue(index, new JsonDoubleImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final boolean value) {
+        addValue(index, value ? JsonValue.TRUE : JsonValue.FALSE);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder addNull(final int index) {
+        addValue(index, JsonValue.NULL);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final JsonObjectBuilder builder) {
+        addValue(index, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final JsonArrayBuilder builder) {
+        addValue(index, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final JsonValue value) {
+        setValue(index, value);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final String value) {
+        setValue(index, new JsonStringImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final BigDecimal value) {
+        setValue(index, new JsonNumberImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final BigInteger value) {
+        setValue(index, new JsonNumberImpl(new BigDecimal(value)));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final int value) {
+        setValue(index, new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final long value) {
+        setValue(index, new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final double value) {
+        setValue(index, new JsonDoubleImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final boolean value) {
+        setValue(index, value ? JsonValue.TRUE : JsonValue.FALSE);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder setNull(final int index) {
+        setValue(index, JsonValue.NULL);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final JsonObjectBuilder builder) {
+        setValue(index, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final JsonArrayBuilder builder) {
+        setValue(index, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder remove(final int index) {
+        tmpList.remove(index);
+        return this;
+    }
+
+    public JsonArrayBuilder add(final Object value) {
+        if (value instanceof JsonValue) {
+            add((JsonValue) value);
+        } else if (value instanceof BigDecimal) {
+            add((BigDecimal) value);
+        } else if (value instanceof BigInteger) {
+            add((BigInteger) value);
+        } else if (value instanceof Boolean) {
+            add((boolean) value);
+        } else if (value instanceof Double) {
+            add((double) value);
+        } else if (value instanceof Integer) {
+            add((int) value);
+        } else if (value instanceof Long) {
+            add((long) value);
+        } else if (value instanceof String) {
+            add((String) value);
+        } else {
+            throw new JsonException("Illegal JSON type! type=" + value.getClass());
+        }
+
+        return this;
+    }
+
+        @Override
+    public JsonArrayBuilder add(final JsonValue value) {
+        addValue(value);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final String value) {
+        addValue(new JsonStringImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final BigDecimal value) {
+        addValue(new JsonNumberImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final BigInteger value) {
+        addValue(new JsonNumberImpl(new BigDecimal(value)));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int value) {
+        addValue(new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final long value) {
+        addValue(new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final double value) {
+        addValue(new JsonDoubleImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final boolean value) {
+        addValue(value ? JsonValue.TRUE : JsonValue.FALSE);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder addNull() {
+        addValue(JsonValue.NULL);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final JsonObjectBuilder builder) {
+        addValue(builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final JsonArrayBuilder builder) {
+        addValue(builder.build());
+        return this;
+    }
+    
+    private void setValue(int idx, JsonValue value) {
+        if (value == null || tmpList == null) {
+            throw npe();
+        }
+        tmpList.set(idx, value);
+    }
+
+    private void addValue(JsonValue value) {
+        if (value == null) {
+            throw npe();
+        }
+
+        if(tmpList==null){
+            tmpList=new ArrayList<>();
+        }
+
+        tmpList.add(value);
+    }
+
+    private void addValue(int idx, JsonValue value) {
+        if (value == null) {
+            throw npe();
+        }
+
+        if(tmpList==null){
+            tmpList=new ArrayList<>();
+        }
+
+        tmpList.add(idx, value);
+    }
+
+    @Override
+    public JsonArray build() {
+        if(tmpList == null) {
+            return new JsonArrayImpl(Collections.<JsonValue>emptyList());
+        }
+        return new JsonArrayImpl(Collections.unmodifiableList(tmpList));
+    }
+
+    private static NullPointerException npe() {
+        throw new NullPointerException("value/builder must not be null");
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonArrayImpl.java b/src/main/java/org/apache/johnzon/core/JsonArrayImpl.java
new file mode 100644
index 0000000..a8d85e4
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonArrayImpl.java
@@ -0,0 +1,235 @@
+/*
+ * 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.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+class JsonArrayImpl extends AbstractList<JsonValue> implements JsonArray, Serializable {
+    private Integer hashCode = null;
+    private final List<JsonValue> unmodifieableBackingList;
+    private int size = -1;
+
+    JsonArrayImpl(final List<JsonValue> backingList) {
+        super();
+        this.unmodifieableBackingList = backingList;
+    }
+
+    private <T> T value(final int idx, final Class<T> type) {
+        if (idx > unmodifieableBackingList.size()) {
+            throw new IndexOutOfBoundsException(idx + "/" + unmodifieableBackingList.size());
+        }
+        return type.cast(unmodifieableBackingList.get(idx));
+    }
+
+    @Override
+    public JsonObject getJsonObject(final int index) {
+        return value(index, JsonObject.class);
+    }
+
+    @Override
+    public JsonArray getJsonArray(final int index) {
+        return value(index, JsonArray.class);
+    }
+
+    @Override
+    public JsonNumber getJsonNumber(final int index) {
+        return value(index, JsonNumber.class);
+    }
+
+    @Override
+    public JsonString getJsonString(final int index) {
+        return value(index, JsonString.class);
+    }
+
+    @Override
+    public <T extends JsonValue> List<T> getValuesAs(Class<T> clazz) {
+        return (List<T>) unmodifieableBackingList;
+    }
+
+    @Override
+    public <T, K extends JsonValue> List<T> getValuesAs(Function<K, T> func) {
+        List<T> result = new ArrayList<T>();
+        for (K value : (List<K>) this) {
+            result.add(func.apply(value));
+        }
+        return result;
+    }
+
+    @Override
+    public String getString(final int index) {
+        return value(index, JsonString.class).getString();
+    }
+
+    @Override
+    public String getString(final int index, final String defaultValue) {
+        JsonValue val = null;
+        int s = size;
+
+        if (s == -1) {
+            s = unmodifieableBackingList.size();
+            size = s;
+        }
+
+        if (index > s - 1 || !((val = get(index)) instanceof JsonString)) {
+            return defaultValue;
+        } else {
+            return JsonString.class.cast(val).getString();
+        }
+    }
+
+    @Override
+    public int getInt(final int index) {
+        return value(index, JsonNumber.class).intValue();
+    }
+
+    @Override
+    public int getInt(final int index, final int defaultValue) {
+        JsonValue val = null;
+        int s = size;
+
+        if (s == -1) {
+            s = unmodifieableBackingList.size();
+            size = s;
+        }
+
+        if (index > s - 1 || !((val = get(index)) instanceof JsonNumber)) {
+            return defaultValue;
+        } else {
+            return JsonNumber.class.cast(val).intValue();
+        }
+    }
+
+    @Override
+    public boolean getBoolean(final int index) {
+        final JsonValue val = value(index, JsonValue.class);
+
+        if (JsonValue.TRUE.equals(val)) {
+            return true;
+        } else if (JsonValue.FALSE.equals(val)) {
+            return false;
+        } else {
+            throw new ClassCastException();
+        }
+
+    }
+
+    @Override
+    public boolean getBoolean(final int index, final boolean defaultValue) {
+
+        int s = size;
+
+        if (s == -1) {
+            s = unmodifieableBackingList.size();
+            size = s;
+        }
+
+        if (index > s - 1) {
+            return defaultValue;
+        }
+
+        final JsonValue val = get(index);
+        return JsonValue.TRUE.equals(val) || !JsonValue.FALSE.equals(val) && defaultValue;
+    }
+
+    @Override
+    public boolean isNull(final int index) {
+        return JsonValue.NULL.equals(value(index, JsonValue.class));
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.ARRAY;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder("[");
+        final Iterator<JsonValue> it = unmodifieableBackingList.iterator();
+        boolean hasNext = it.hasNext();
+        while (hasNext) {
+            final JsonValue jsonValue = it.next();
+            if (JsonString.class.isInstance(jsonValue)) {
+                builder.append(jsonValue.toString());
+            } else {
+                builder.append(jsonValue != JsonValue.NULL ? jsonValue.toString() : JsonChars.NULL);
+            }
+            hasNext = it.hasNext();
+            if (hasNext) {
+                builder.append(",");
+            }
+        }
+        return builder.append(']').toString();
+    }
+
+    @Override
+    public JsonObject asJsonObject() {
+        return JsonObject.class.cast(this);
+    }
+
+    @Override
+    public JsonArray asJsonArray() {
+        return JsonArray.class.cast(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return JsonArray.class.isInstance(obj) && super.equals(obj);
+    }
+
+    @Override
+    public int hashCode() {
+        Integer h = hashCode;
+        if (h == null) {
+            h = unmodifieableBackingList.hashCode();
+            hashCode = h;
+        }
+        return h;
+    }
+
+    @Override
+    public JsonValue get(final int index) {
+        return unmodifieableBackingList.get(index);
+    }
+
+    @Override
+    public int size() {
+        return unmodifieableBackingList.size();
+    }
+
+    private Object writeReplace() throws ObjectStreamException {
+        return new SerializableValue(toString());
+    }
+
+    @Override
+    public JsonValue getValue(String jsonPointer) {
+        return null;
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonBuilderFactoryImpl.java b/src/main/java/org/apache/johnzon/core/JsonBuilderFactoryImpl.java
new file mode 100644
index 0000000..81719ff
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonBuilderFactoryImpl.java
@@ -0,0 +1,92 @@
+/*
+ * 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.core;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonBuilderFactory;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+
+class JsonBuilderFactoryImpl implements JsonBuilderFactory {
+    private final Map<String, Object> internalConfig = new HashMap<String, Object>();
+    private static final String[] SUPPORTED_CONFIG_KEYS = new String[] {
+    //nothing yet
+
+    };
+    protected final Logger logger = Logger.getLogger(this.getClass().getName());
+
+    JsonBuilderFactoryImpl(final Map<String, ?> config) {
+        if (config != null && config.size() > 0) {
+            final List<String> supportedConfigKeys = Arrays.asList(SUPPORTED_CONFIG_KEYS);
+            for (String configKey : config.keySet()) {
+                if(supportedConfigKeys.contains(configKey)) {
+                    internalConfig.put(configKey, config.get(configKey));
+                } else {
+                    logger.warning(configKey + " is not supported by " + getClass().getName());
+                }
+            }
+        }
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder() {
+        return new JsonObjectBuilderImpl();
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder(JsonObject initialData) {
+        return new JsonObjectBuilderImpl(initialData);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder() {
+        return new JsonArrayBuilderImpl();
+    }
+
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder(JsonArray initialData) {
+        return new JsonArrayBuilderImpl(initialData);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder(Collection<?> initialData) {
+        return new JsonArrayBuilderImpl(initialData);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return Collections.unmodifiableMap(internalConfig);
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder(Map<String, Object> initialValues) {
+        return new JsonObjectBuilderImpl(initialValues);
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonChars.java b/src/main/java/org/apache/johnzon/core/JsonChars.java
new file mode 100644
index 0000000..1e36913
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonChars.java
@@ -0,0 +1,82 @@
+/*
+ * 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.core;
+
+import javax.json.stream.JsonParser.Event;
+
+public interface JsonChars {
+    char EOF = Character.MIN_VALUE;
+
+    char START_OBJECT_CHAR = '{';
+    char END_OBJECT_CHAR = '}';
+    char START_ARRAY_CHAR = '[';
+    char END_ARRAY_CHAR = ']';
+    char QUOTE_CHAR = '"';
+    char COMMA_CHAR = ',';
+    char KEY_SEPARATOR = ':';
+    
+    char EOL = '\n';
+    char SPACE = ' ';
+    
+    char TRUE_T = 't';
+    char TRUE_R = 'r';
+    char TRUE_U = 'u';
+    char TRUE_E = 'e';
+    char FALSE_F = 'f';
+    char FALSE_A = 'a';
+    char FALSE_L = 'l';
+    char FALSE_S = 's';
+    char FALSE_E = 'e';
+    char NULL_N = 'n';
+    char NULL_U = 'u';
+    char NULL_L = 'l';
+ 
+    char ZERO = '0';
+    char NINE = '9';
+    char DOT = '.';
+    char MINUS = '-';
+    char PLUS = '+';
+    char EXP_LOWERCASE = 'e';
+    char EXP_UPPERCASE = 'E';
+    char ESCAPE_CHAR = '\\';
+    
+    char TAB = '\t';
+    char BACKSPACE = '\b';
+    char FORMFEED = '\f';
+    char CR = '\r';
+
+    String NULL = "null".intern();
+    
+    static final byte START_ARRAY = (byte) Event.START_ARRAY.ordinal();
+    static final byte START_OBJECT = (byte) Event.START_OBJECT.ordinal();
+    static final byte KEY_NAME=(byte) Event.KEY_NAME.ordinal();
+    static final byte VALUE_STRING=(byte) Event.VALUE_STRING.ordinal(); 
+    static final byte VALUE_NUMBER=(byte) Event.VALUE_NUMBER.ordinal(); 
+    static final byte VALUE_TRUE=(byte) Event.VALUE_TRUE.ordinal();
+    static final byte VALUE_FALSE=(byte) Event.VALUE_FALSE.ordinal(); 
+    static final byte VALUE_NULL=(byte) Event.VALUE_NULL.ordinal();
+    static final byte END_OBJECT=(byte) Event.END_OBJECT.ordinal();
+    static final byte END_ARRAY=(byte) Event.END_ARRAY.ordinal();
+    
+    static final byte COMMA_EVENT=Byte.MAX_VALUE;
+    static final byte KEY_SEPARATOR_EVENT=Byte.MIN_VALUE;
+    
+    static final Event[] EVT_MAP =Event.values();
+    
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonDoubleImpl.java b/src/main/java/org/apache/johnzon/core/JsonDoubleImpl.java
new file mode 100644
index 0000000..2e99c8a
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonDoubleImpl.java
@@ -0,0 +1,137 @@
+/*
+ * 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.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Objects;
+
+final class JsonDoubleImpl implements JsonNumber, Serializable {
+    private final double value;
+
+    private Integer hashCode = null;
+
+    JsonDoubleImpl(final double value) {
+        
+        if(Double.isInfinite(value) || Double.isNaN(value)) {
+            throw new NumberFormatException("double value must not be NaN or Infinite");
+        }
+        
+        this.value = value;
+    }
+
+    @Override
+    public Number numberValue() {
+        return value;
+    }
+
+    @Override
+    public boolean isIntegral() {
+        return false;
+    }
+
+    @Override
+    public int intValue() {
+        return (int) value;
+    }
+
+    @Override
+    public int intValueExact() {
+        checkFractionalPart();
+        return intValue();
+    }
+
+    @Override
+    public long longValue() {
+        return (long) value;
+    }
+
+    @Override
+    public long longValueExact() {
+        checkFractionalPart();
+        return (long) value;
+    }
+
+    @Override
+    public BigInteger bigIntegerValue() {
+        return new BigDecimal(toString()).toBigInteger();
+    }
+
+    @Override
+    public BigInteger bigIntegerValueExact() {
+        return new BigDecimal(toString()).toBigIntegerExact();
+    }
+
+    @Override
+    public double doubleValue() {
+        return value;
+    }
+
+    @Override
+    public BigDecimal bigDecimalValue() {
+        return new BigDecimal(toString());
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.NUMBER;
+    }
+
+    @Override
+    public String toString() {
+        return Double.toString(value);
+    }
+
+    @Override
+    public int hashCode() {
+        if (hashCode == null) {
+            hashCode = bigDecimalValue().hashCode();
+        }
+
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (JsonDoubleImpl.class.isInstance(obj)) {
+            return JsonDoubleImpl.class.cast(obj).value == value;
+        }
+        return JsonNumber.class.isInstance(obj) && Objects.equals(JsonNumber.class.cast(obj).bigDecimalValue(), bigDecimalValue());
+    }
+
+    private void checkFractionalPart() {
+        if ((value % 1) != 0) {
+            throw new ArithmeticException("Not an int/long, use other value readers");
+        }
+    }
+
+    @Override
+    public JsonObject asJsonObject() {
+        return JsonObject.class.cast(this);
+    }
+
+    @Override
+    public JsonArray asJsonArray() {
+        return JsonArray.class.cast(this);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonGeneratorFactoryImpl.java b/src/main/java/org/apache/johnzon/core/JsonGeneratorFactoryImpl.java
new file mode 100644
index 0000000..92abbcd
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonGeneratorFactoryImpl.java
@@ -0,0 +1,79 @@
+/*
+ * 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.core;
+
+import static java.util.Arrays.asList;
+
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonGeneratorFactory;
+
+public class JsonGeneratorFactoryImpl extends AbstractJsonFactory implements JsonGeneratorFactory {    
+    public static final String GENERATOR_BUFFER_LENGTH = "org.apache.johnzon.default-char-buffer-generator";
+    public static final int DEFAULT_GENERATOR_BUFFER_LENGTH =  Integer.getInteger(GENERATOR_BUFFER_LENGTH, 64 * 1024); //64k
+   
+    static final Collection<String> SUPPORTED_CONFIG_KEYS = asList(
+        JsonGenerator.PRETTY_PRINTING, GENERATOR_BUFFER_LENGTH, BUFFER_STRATEGY
+    );
+    //key caching currently disabled
+    private final ConcurrentMap<String, String> cache = null;//new ConcurrentHashMap<String, String>();
+    private final boolean pretty;
+    private final BufferStrategy.BufferProvider<char[]> bufferProvider;
+
+    public JsonGeneratorFactoryImpl(final Map<String, ?> config) {
+        
+          super(config, SUPPORTED_CONFIG_KEYS, null); 
+          
+          this.pretty = getBool(JsonGenerator.PRETTY_PRINTING, false);
+          
+          final int bufferSize = getInt(GENERATOR_BUFFER_LENGTH, DEFAULT_GENERATOR_BUFFER_LENGTH);
+          if (bufferSize <= 0) {
+              throw new IllegalArgumentException("buffer length must be greater than zero");
+          }
+
+          this.bufferProvider = getBufferProvider().newCharProvider(bufferSize);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(final Writer writer) {
+        return new JsonGeneratorImpl(writer, bufferProvider, cache, pretty);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(final OutputStream out) {
+        return new JsonGeneratorImpl(out, bufferProvider, cache, pretty);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(final OutputStream out, final Charset charset) {
+        return new JsonGeneratorImpl(out,charset, bufferProvider, cache, pretty);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return Collections.unmodifiableMap(internalConfig);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java b/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java
new file mode 100644
index 0000000..bc17340
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java
@@ -0,0 +1,827 @@
+/*
+ * 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.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonException;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import javax.json.stream.JsonGenerationException;
+import javax.json.stream.JsonGenerator;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Serializable;
+import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+class JsonGeneratorImpl implements JsonGenerator, JsonChars, Serializable {
+    private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
+
+    private final transient Writer writer;
+    private final BufferStrategy.BufferProvider<char[]> bufferProvider;
+    private final char[] buffer;
+    private int bufferPos = 0;
+    private final boolean prettyPrint;
+    private static final String INDENT = "  ";
+    //private final ConcurrentMap<String, String> cache;
+    private int depth = 0;
+
+    private final HStack<GeneratorState> state = new HStack<GeneratorState>();
+
+    private enum GeneratorState {
+        INITIAL(false, true), START_OBJECT(true, false), IN_OBJECT(true, false), AFTER_KEY(false, true), START_ARRAY(false, true), IN_ARRAY(
+                false, true), END(false, false);
+
+        private final boolean acceptsKey;
+        private final boolean acceptsValue;
+
+        GeneratorState(final boolean acceptsKey, final boolean acceptsValue) {
+            this.acceptsKey = acceptsKey;
+            this.acceptsValue = acceptsValue;
+        }
+    }
+
+    JsonGeneratorImpl(final Writer writer, final BufferStrategy.BufferProvider<char[]> bufferProvider,
+            final ConcurrentMap<String, String> cache, final boolean prettyPrint) {
+        this.writer = writer;
+        //this.cache = cache;
+        this.buffer = bufferProvider.newBuffer();
+        this.bufferProvider = bufferProvider;
+        this.prettyPrint = prettyPrint;
+        state.push(GeneratorState.INITIAL);
+    }
+
+    JsonGeneratorImpl(final OutputStream out, final BufferStrategy.BufferProvider<char[]> bufferProvider,
+            final ConcurrentMap<String, String> cache, final boolean prettyPrint) {
+        this(new OutputStreamWriter(out, UTF8_CHARSET), bufferProvider, cache, prettyPrint);
+    }
+
+    JsonGeneratorImpl(final OutputStream out, final Charset encoding, final BufferStrategy.BufferProvider<char[]> bufferProvider,
+            final ConcurrentMap<String, String> cache, final boolean prettyPrint) {
+        this(new OutputStreamWriter(out, encoding), bufferProvider, cache, prettyPrint);
+    }
+
+    private void writeEol() {
+        if (prettyPrint) {
+            justWrite(EOL);
+        }
+    }
+
+    private void writeIndent() {
+        if (prettyPrint && depth > 0) {
+            for (int i = 0; i < depth; i++) {
+                justWrite(INDENT);
+            }
+        }
+    }
+
+    //caching currently disabled
+    //two problems:
+    // 1) not easy to get the escaped value efficiently when its streamed and the buffer is full and needs to be flushed
+    // 2) we have to use a kind of bounded threadsafe map to let the cache not grow indefinitely
+    private void writeCachedKey(final String name) {
+        /*  String k = cache.get(name);
+
+          if (k == null) {
+
+                  justWrite(QUOTE_CHAR);
+                  int start = bufferPos;
+                  writeEscaped0(name);
+                  int end = bufferPos;
+                  String escaped= get from buffer
+                  ---
+                  //FIXME if buffer is flushed this will not work here
+                  cache.putIfAbsent(name, escaped);
+                  justWrite(QUOTE_CHAR);
+                  justWrite(KEY_SEPARATOR);
+          }else*/
+        {
+            justWrite(QUOTE_CHAR);
+            writeEscaped0(name);
+            justWrite(QUOTE_CHAR);
+            justWrite(KEY_SEPARATOR);
+        }
+
+    }
+
+    @Override
+    public JsonGenerator writeStartObject() {
+        prepareValue();
+        state.push(GeneratorState.START_OBJECT);
+
+        writeIndent();
+        depth++;
+        justWrite(START_OBJECT_CHAR);
+
+        writeEol();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject(final String name) {
+        checkObject(false);
+        writeKey(name);
+        justWrite(START_OBJECT_CHAR);
+        writeEol();
+        state.push(GeneratorState.START_OBJECT);
+        depth++;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray() {
+        prepareValue();
+        writeIndent();
+        state.push(GeneratorState.START_ARRAY);
+        justWrite(START_ARRAY_CHAR);
+        depth++;
+        writeEol();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray(final String name) {
+        checkObject(false);
+        writeKey(name);
+        justWrite(START_ARRAY_CHAR);
+        writeEol();
+        state.push(GeneratorState.START_ARRAY);
+        depth++;
+        return this;
+    }
+
+    private void writeJsonValue(final String name, final JsonValue value) {
+        checkObject(false);
+        //TODO check null handling
+        switch (value.getValueType()) {
+            case ARRAY:
+                writeStartArray(name);
+                final JsonArray array = JsonArray.class.cast(value);
+                final Iterator<JsonValue> ait = array.iterator();
+                while (ait.hasNext()) {
+                    write(ait.next());
+                }
+                writeEnd();
+
+                break;
+            case OBJECT:
+                writeStartObject(name);
+                final JsonObject object = JsonObject.class.cast(value);
+                final Iterator<Map.Entry<String, JsonValue>> oit = object.entrySet().iterator();
+                while (oit.hasNext()) {
+                    final Map.Entry<String, JsonValue> keyval = oit.next();
+                    write(keyval.getKey(), keyval.getValue());
+                }
+                writeEnd();
+
+                break;
+            case STRING:
+                write(name, JsonString.class.cast(value).getString());
+                break;
+            case NUMBER:
+                //TODO optimize
+                final JsonNumber number = JsonNumber.class.cast(value);
+                if (number.isIntegral()) {
+                    write(name, number.longValueExact());
+                } else {
+                    write(name, number.bigDecimalValue());
+                }
+                break;
+            case TRUE:
+                write(name, true);
+                break;
+            case FALSE:
+                write(name, false);
+                break;
+            case NULL:
+                writeNull(name);
+                break;
+            default:
+                throw new JsonGenerationException("Unknown JsonValue type");
+        }
+    }
+
+    private void writeJsonValue(final JsonValue value) {
+        checkArray(true);
+        //TODO check null handling
+        switch (value.getValueType()) {
+            case ARRAY:
+                writeStartArray();
+                final JsonArray array = JsonArray.class.cast(value);
+                final Iterator<JsonValue> ait = array.iterator();
+                while (ait.hasNext()) {
+                    write(ait.next());
+                }
+                writeEnd();
+
+                break;
+            case OBJECT:
+                writeStartObject();
+                final JsonObject object = JsonObject.class.cast(value);
+                final Iterator<Map.Entry<String, JsonValue>> oit = object.entrySet().iterator();
+                while (oit.hasNext()) {
+                    final Map.Entry<String, JsonValue> keyval = oit.next();
+                    write(keyval.getKey(), keyval.getValue());
+                }
+                writeEnd();
+
+                break;
+            case STRING:
+                write(JsonString.class.cast(value).getString());
+                break;
+            case NUMBER:
+                //TODO optimize
+                final JsonNumber number = JsonNumber.class.cast(value);
+                if (number.isIntegral()) {
+                    write(number.longValueExact());
+                } else {
+                    write(number.bigDecimalValue());
+                }
+                break;
+            case TRUE:
+                write(true);
+                break;
+            case FALSE:
+                write(false);
+                break;
+            case NULL:
+                writeNull();
+                break;
+            default:
+                throw new JsonGenerationException("Unknown JsonValue type");
+        }
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final JsonValue value) {
+        checkObject(false);
+        writeJsonValue(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final String value) {
+        checkObject(false);
+        writeKey(name);
+        writeValueAsJsonString(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final BigInteger value) {
+        checkObject(false);
+        writeKey(name);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final BigDecimal value) {
+        checkObject(false);
+        writeKey(name);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final int value) {
+        checkObject(false);
+        writeKey(name);
+        writeValue(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final long value) {
+        checkObject(false);
+        writeKey(name);
+        writeValue(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final double value) {
+        checkObject(false);
+        checkDoubleRange(value);
+        writeKey(name);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final boolean value) {
+        checkObject(false);
+        writeKey(name);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeNull(final String name) {
+        checkObject(false);
+        writeKey(name);
+        writeValue(NULL);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeEnd() {
+        checkArrayOrObject(false);
+        final GeneratorState last = state.pop();
+        depth--;
+        if (last != GeneratorState.START_ARRAY) {
+            writeEol();
+        }
+        writeIndent();
+        if (last == GeneratorState.IN_ARRAY || last == GeneratorState.START_ARRAY) {
+            justWrite(END_ARRAY_CHAR);
+        } else {
+            justWrite(END_OBJECT_CHAR);
+        }
+        alignState();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final JsonValue value) {
+        checkArray(true);
+        writeJsonValue(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String value) {
+        checkArray(false);
+        writeValueAsJsonString(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final BigDecimal value) {
+        checkArray(false);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final BigInteger value) {
+        checkArray(false);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final int value) {
+        checkArray(false);
+        writeValue(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final long value) {
+        checkArray(false);
+        writeValue(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final double value) {
+        checkArray(false);
+        checkDoubleRange(value);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final boolean value) {
+        checkArray(false);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeNull() {
+        checkArray(false);
+        writeValue(NULL);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeKey(final String key) {
+        final GeneratorState currentState = currentState();
+        if (!currentState.acceptsKey) {
+            throw new JsonGenerationException("state " + currentState + " does not accept a key");
+        }
+        if (currentState == GeneratorState.IN_OBJECT) {
+            justWrite(COMMA_CHAR);
+            writeEol();
+        }
+
+        writeIndent();
+
+        writeCachedKey(key);
+        state.push(GeneratorState.AFTER_KEY);
+        return this;
+    }
+
+
+
+    @Override
+    public void close() {
+        try {
+            if (currentState() != GeneratorState.END) {
+                throw new JsonGenerationException("Invalid json");
+            }
+        } finally {
+            flushBuffer();
+            try {
+                writer.close();
+            } catch (final IOException e) {
+                throw new JsonException(e.getMessage(), e);
+            } finally {
+                bufferProvider.release(buffer);
+            }
+        }
+    }
+
+    @Override
+    public void flush() {
+        flushBuffer();
+        try {
+            writer.flush();
+        } catch (final IOException e) {
+            throw new JsonException(e.getMessage(), e);
+        }
+    }
+
+    private void flushBuffer() {
+        if (bufferPos > 0) {
+            try {
+                writer.write(buffer, 0, bufferPos);
+                bufferPos = 0;
+            } catch (final IOException e) {
+                throw new JsonException(e.getMessage(), e);
+            }
+        }
+    }
+
+    private void writeEscaped0(final String value) {
+        int len = 0;
+        if (value == null || (len = value.length()) == 0) {
+            return;
+        }
+
+        for (int i = 0; i < len; i++) {
+            char c = value.charAt(i);
+
+            while (c != ESCAPE_CHAR && c != QUOTE_CHAR && c >= SPACE) {
+
+                //read fast
+                justWrite(c);
+
+                if (i >= len - 1) {
+                    return;
+                }
+
+                i++;
+                c = value.charAt(i);
+            }
+
+            switch (c) {
+                case QUOTE_CHAR:
+                case ESCAPE_CHAR:
+                    justWrite(ESCAPE_CHAR);
+                    justWrite(c);
+                    break;
+                default:
+                    if (c < SPACE) {
+                        switch (c) {
+                            case EOL:
+                                justWrite("\\n");
+                                break;
+                            case '\r':
+                                justWrite("\\r");
+                                break;
+                            case '\t':
+                                justWrite("\\t");
+                                break;
+                            case '\b':
+                                justWrite("\\b");
+                                break;
+                            case '\f':
+                                justWrite("\\f");
+                                break;
+                            default:
+                                justWrite(toUnicode(c));
+                        }
+                    } else if ((c >= '\u0080' && c < '\u00a0') || (c >= '\u2000' && c < '\u2100')) {
+                        justWrite(toUnicode(c));
+                    } else {
+                        justWrite(c);
+                    }
+            }
+        }
+    }
+
+    private static final String UNICODE_PREFIX = "\\u";
+    private static final String UNICODE_PREFIX_HELPER = "000";
+
+    private static String toUnicode(final char c) {
+        final String hex = UNICODE_PREFIX_HELPER + Integer.toHexString(c);
+        final String s = UNICODE_PREFIX + hex.substring(hex.length() - 4);
+        return s;
+    }
+
+    private void justWrite(final String value) {
+        final int valueLength = value.length();
+
+        if (bufferPos + valueLength >= buffer.length) {
+
+            int start = 0;
+            int len = buffer.length - bufferPos;
+
+            while (true) {
+                int end = start + len;
+                if (end > valueLength) {
+                    end = valueLength;
+                }
+
+                value.getChars(start, end, buffer, bufferPos);
+
+                bufferPos += (end - start);
+                start += (len);
+
+                if (start >= valueLength) {
+                    return;
+                }
+
+                if (bufferPos >= buffer.length) {
+                    flushBuffer();
+                    len = buffer.length;
+                }
+            }
+        } else {
+            //fits completely into the buffer
+            value.getChars(0, valueLength, buffer, bufferPos);
+            bufferPos += valueLength;
+        }
+    }
+
+    private void justWrite(final char value) {
+        if (bufferPos >= buffer.length) {
+            flushBuffer();
+        }
+        buffer[bufferPos++] = value;
+    }
+
+    private void checkObject(final boolean allowInitial) {
+        final GeneratorState currentState = currentState();
+        if (currentState != GeneratorState.IN_OBJECT && currentState != GeneratorState.START_OBJECT) {
+            if (!allowInitial || currentState != GeneratorState.INITIAL) {
+                throw new JsonGenerationException("write(name, param) is only valid in objects");
+            }
+        }
+    }
+
+    private void checkArray(final boolean allowInitial) {
+        final GeneratorState currentState = currentState();
+        if (currentState != GeneratorState.IN_ARRAY && currentState != GeneratorState.START_ARRAY) {
+            if (!allowInitial || currentState != GeneratorState.INITIAL) {
+                throw new JsonGenerationException("write(param) is only valid in arrays");
+            }
+        }
+    }
+
+    private void checkArrayOrObject(final boolean allowInitial) {
+        final GeneratorState currentState = currentState();
+        if (currentState != GeneratorState.IN_ARRAY && currentState != GeneratorState.START_ARRAY
+                && currentState != GeneratorState.IN_OBJECT && currentState != GeneratorState.START_OBJECT) {
+            if (!allowInitial || currentState != GeneratorState.INITIAL) {
+                throw new JsonGenerationException("only valid within array or object");
+            }
+        }
+    }
+
+    private static void checkDoubleRange(final double value) {
+        if (Double.isInfinite(value) || Double.isNaN(value)) {
+            throw new NumberFormatException("double can't be infinite or NaN");
+        }
+    }
+
+    private void prepareValue() {
+        final GeneratorState currentState = currentState();
+        if (!currentState.acceptsValue) {
+            throw new JsonGenerationException("state " + currentState + " does not accept a value");
+        }
+        if (currentState == GeneratorState.IN_ARRAY) {
+            justWrite(',');
+            writeEol();
+        }
+    }
+
+    private void alignState() {
+
+        if (currentState() == GeneratorState.AFTER_KEY) {
+            state.pop();
+        }
+        switch (currentState()) {
+            case START_ARRAY:
+                swapState(GeneratorState.IN_ARRAY);
+                break;
+            case START_OBJECT:
+                swapState(GeneratorState.IN_OBJECT);
+                break;
+            case INITIAL:
+                swapState(GeneratorState.END);
+                break;
+            default:
+        }
+    }
+
+    private void swapState(final GeneratorState newState) {
+        state.pop();
+        state.push(newState);
+    }
+
+    private GeneratorState currentState() {
+        return state.peek();
+    }
+
+    private void writeValueAsJsonString(final String value) {
+        prepareValue();
+        final GeneratorState peek = state.peek();
+        if (peek == GeneratorState.START_ARRAY || peek == GeneratorState.IN_ARRAY) {
+            writeIndent();
+        }
+        justWrite(QUOTE_CHAR);
+        writeEscaped0(value);
+        justWrite(QUOTE_CHAR);
+        alignState();
+    }
+
+    private void writeValue(final String value) {
+        prepareValue();
+        final GeneratorState peek = state.peek();
+        if (peek == GeneratorState.START_ARRAY || peek == GeneratorState.IN_ARRAY) {
+            writeIndent();
+        }
+        justWrite(String.valueOf(value));
+        alignState();
+    }
+
+    private void writeValue(final int value) {
+        prepareValue();
+        final GeneratorState peek = state.peek();
+        if (peek == GeneratorState.START_ARRAY || peek == GeneratorState.IN_ARRAY) {
+            writeIndent();
+        }
+        writeInt0(value);
+        alignState();
+    }
+
+    private void writeValue(final long value) {
+        prepareValue();
+        final GeneratorState peek = state.peek();
+        if (peek == GeneratorState.START_ARRAY || peek == GeneratorState.IN_ARRAY) {
+            writeIndent();
+        }
+        writeLong0(value);
+        alignState();
+    }
+
+    //unoptimized, see below
+    private void writeLong0(final long i) {
+        justWrite(String.valueOf(i));
+    }
+
+    //unoptimized, see below
+    private void writeInt0(final int i) {
+        justWrite(String.valueOf(i));
+    }
+
+    //optimized number optimizations
+    /*
+        private void writeLong0(final long i) {
+            if (i == Long.MIN_VALUE) {
+                justWrite("-9223372036854775808");
+                return;
+            }
+            final int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
+            final char[] buf = new char[size];
+            getChars(i, size, buf);
+            justWrite(buf);
+        }
+
+        private void writeInt0(final int i) {
+            if (i == Integer.MIN_VALUE) {
+                justWrite("-2147483648");
+                return;
+            }
+            final int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
+            final char[] buf = new char[size];
+            getChars(i, size, buf);
+            justWrite(buf);
+        }
+
+        private final static char[] DIGIT_TENS = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1', '1', '1', '1', '1', '1',
+                '1', '1', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', '4', '4', '4',
+                '4', '4', '4', '4', '4', '4', '4', '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', '6', '6', '6', '6', '6', '6', '6', '6',
+                '6', '6', '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', '9', '9', '9',
+                '9', '9', '9', '9', '9', '9', '9', };
+
+        private final static char[] DIGIT_ONES = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7',
+                '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
+                '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7',
+                '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
+                '3', '4', '5', '6', '7', '8', '9', };
+
+        private final static char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
+                'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
+
+        // Requires positive x
+        private static int stringSize(final long x) {
+            long p = 10;
+            for (int i = 1; i < 19; i++) {
+                if (x < p) {
+                    return i;
+                }
+                p = 10 * p;
+            }
+            return 19;
+        }
+
+        private static void getChars(long i, final int index, final char[] buf) {
+            long q;
+            int r;
+            int charPos = index;
+            char sign = 0;
+
+            if (i < 0) {
+                sign = '-';
+                i = -i;
+            }
+
+            // Get 2 digits/iteration using longs until quotient fits into an int
+            while (i > Integer.MAX_VALUE) {
+                q = i / 100;
+                // really: r = i - (q * 100);
+                r = (int) (i - ((q << 6) + (q << 5) + (q << 2)));
+                i = q;
+                buf[--charPos] = DIGIT_ONES[r];
+                buf[--charPos] = DIGIT_TENS[r];
+            }
+
+            // Get 2 digits/iteration using ints
+            int q2;
+            int i2 = (int) i;
+            while (i2 >= 65536) {
+                q2 = i2 / 100;
+                // really: r = i2 - (q * 100);
+                r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
+                i2 = q2;
+                buf[--charPos] = DIGIT_ONES[r];
+                buf[--charPos] = DIGIT_TENS[r];
+            }
+
+            // Fall thru to fast mode for smaller numbers
+            // assert(i2 <= 65536, i2);
+            for (;;) {
+                q2 = (i2 * 52429) >>> (16 + 3);
+                r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ...
+                buf[--charPos] = DIGITS[r];
+                i2 = q2;
+                if (i2 == 0) {
+                    break;
+                }
+            }
+            if (sign != 0) {
+                buf[--charPos] = sign;
+            }
+        }
+     */
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonInMemoryParser.java b/src/main/java/org/apache/johnzon/core/JsonInMemoryParser.java
new file mode 100644
index 0000000..1f2e33e
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonInMemoryParser.java
@@ -0,0 +1,314 @@
+/*
+ * 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.core;
+
+import java.math.BigDecimal;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.stream.Stream;
+
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import javax.json.JsonValue.ValueType;
+import javax.json.stream.JsonLocation;
+
+class JsonInMemoryParser implements JohnzonJsonParser {
+
+    private final SimpleStack<Iterator<Event>> stack = new SimpleStack<Iterator<Event>>();
+
+    private Event currentEvent;
+    private JsonValue currentValue;
+
+    private class ArrayIterator implements Iterator<Event> {
+
+        private final Iterator<JsonValue> aentries;
+        private Boolean end = null;
+
+        public ArrayIterator(final JsonArray ja) {
+            aentries = ja.iterator();
+
+        }
+
+        @Override
+        public boolean hasNext() {
+            return !Boolean.TRUE.equals(end);
+        }
+
+        @Override
+        public Event next() {
+
+            if (end == null) {
+                end = Boolean.FALSE;
+                return Event.START_ARRAY;
+            } else if (!aentries.hasNext()) {
+
+                if (!stack.isEmpty()) {
+                    stack.pop();
+                }
+
+                end = Boolean.TRUE;
+
+                return Event.END_ARRAY;
+            } else {
+
+                final JsonValue val = aentries.next();
+
+                final ValueType vt = val.getValueType();
+
+                if (vt == ValueType.OBJECT) {
+                    stack.push(new ObjectIterator((JsonObject) val));
+                    return stack.peek().next();
+
+                } else if (vt == ValueType.ARRAY) {
+                    stack.push(new ArrayIterator((JsonArray) val));
+                    return stack.peek().next();
+
+                } else {
+                    currentValue = val;
+                    return getEvent(vt);
+                }
+            }
+
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+
+        }
+
+    }
+
+    private class ObjectIterator implements Iterator<Event> {
+
+        private final Iterator<Map.Entry<String, JsonValue>> oentries;
+        private JsonValue jsonValue;
+        private Boolean end = null;
+
+        public ObjectIterator(final JsonObject jo) {
+            oentries = jo.entrySet().iterator();
+
+        }
+
+        @Override
+        public boolean hasNext() {
+            return !Boolean.TRUE.equals(end);
+        }
+
+        @Override
+        public Event next() {
+
+            if (end == null) {
+                end = Boolean.FALSE;
+                return Event.START_OBJECT;
+            } else if (jsonValue == null && !oentries.hasNext()) {
+
+                if (!stack.isEmpty()) {
+                    stack.pop();
+                }
+
+                end = Boolean.TRUE;
+
+                return Event.END_OBJECT;
+            } else if (jsonValue == null) {
+
+                final Map.Entry<String, JsonValue> tmp = oentries.next();
+                jsonValue = tmp.getValue();
+                currentValue = new JsonStringImpl(tmp.getKey());
+                return Event.KEY_NAME;
+
+            } else {
+
+                final ValueType vt = jsonValue.getValueType();
+
+                if (vt == ValueType.OBJECT) {
+                    stack.push(new ObjectIterator((JsonObject) jsonValue));
+                    jsonValue = null;
+                    return stack.peek().next();
+
+                } else if (vt == ValueType.ARRAY) {
+                    stack.push(new ArrayIterator((JsonArray) jsonValue));
+                    jsonValue = null;
+                    return stack.peek().next();
+
+                } else {
+
+                    final Event ret = getEvent(vt);
+                    currentValue = jsonValue;
+                    jsonValue = null;
+                    return ret;
+                }
+
+            }
+
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+
+        }
+
+    }
+
+    private static Event getEvent(final ValueType value) {
+
+        switch (value) {
+        case NUMBER:
+            return Event.VALUE_NUMBER;
+        case STRING:
+            return Event.VALUE_STRING;
+        case FALSE:
+            return Event.VALUE_FALSE;
+        case NULL:
+            return Event.VALUE_NULL;
+        case TRUE:
+            return Event.VALUE_TRUE;
+        default:
+            throw new IllegalArgumentException(value + " not supported");
+
+        }
+
+    }
+
+    JsonInMemoryParser(final JsonObject object) {
+        stack.push(new ObjectIterator(object));
+    }
+
+    JsonInMemoryParser(final JsonArray array) {
+        stack.push(new ArrayIterator(array));
+    }
+
+    @Override
+    public boolean hasNext() {
+        return !stack.isEmpty();
+    }
+
+    @Override
+    public Event next() {
+
+        if (!hasNext()) {
+            throw new NoSuchElementException();
+        }
+
+        currentEvent = stack.peek().next();
+
+        return currentEvent;
+    }
+
+    @Override
+    public String getString() {
+        if (currentEvent != Event.KEY_NAME && currentEvent != Event.VALUE_STRING) {
+            throw new IllegalStateException("String is for numbers and strings");
+        }
+        return JsonString.class.cast(currentValue).getString();
+    }
+
+    @Override
+    public boolean isIntegralNumber() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException("isIntegralNumber is for numbers");
+        }
+        return JsonNumber.class.cast(currentValue).isIntegral();
+    }
+
+    @Override
+    public boolean isNotTooLong() {
+        return true;
+    }
+
+    @Override
+    public int getInt() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException("getInt is for numbers");
+        }
+        return JsonNumber.class.cast(currentValue).intValue();
+    }
+
+    @Override
+    public long getLong() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException("getLong is for numbers");
+        }
+        return JsonNumber.class.cast(currentValue).longValue();
+    }
+
+    @Override
+    public BigDecimal getBigDecimal() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException("getBigDecimal is for numbers");
+        }
+        return JsonNumber.class.cast(currentValue).bigDecimalValue();
+    }
+
+    @Override
+    public JsonLocation getLocation() { // no location for in memory parsers
+        return JsonLocationImpl.UNKNOWN_LOCATION;
+    }
+
+    @Override
+    public void close() {
+        // no-op
+    }
+
+    @Override
+    public JsonObject getObject() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public JsonValue getValue() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public JsonArray getArray() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Stream<JsonValue> getArrayStream() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Stream<Map.Entry<String, JsonValue>> getObjectStream() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Stream<JsonValue> getValueStream() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void skipArray() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void skipObject() {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonLocationImpl.java b/src/main/java/org/apache/johnzon/core/JsonLocationImpl.java
new file mode 100644
index 0000000..11fd074
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonLocationImpl.java
@@ -0,0 +1,80 @@
+/*
+ * 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.core;
+
+import java.io.Serializable;
+
+import javax.json.stream.JsonLocation;
+
+final class JsonLocationImpl implements JsonLocation, Serializable {
+    
+    public static final JsonLocation UNKNOWN_LOCATION = new JsonLocationImpl(-1, -1, -1);
+    
+    private final long lineNumber;
+    private final long columnNumber;
+    private final long streamOffset;
+
+    JsonLocationImpl(final long lineNumber, final long columnNumber, final long streamOffset) {
+        this.lineNumber = lineNumber;
+        this.columnNumber = columnNumber;
+        this.streamOffset = streamOffset;
+    }
+
+    @Override
+    public long getLineNumber() {
+        return lineNumber;
+    }
+
+    @Override
+    public long getColumnNumber() {
+        return columnNumber;
+    }
+
+    @Override
+    public long getStreamOffset() {
+        return streamOffset;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final JsonLocationImpl that = JsonLocationImpl.class.cast(o);
+        return columnNumber == that.columnNumber && lineNumber == that.lineNumber && streamOffset == that.streamOffset;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = (int) (lineNumber ^ (lineNumber >>> 32));
+        result = 31 * result + (int) (columnNumber ^ (columnNumber >>> 32));
+        result = 31 * result + (int) (streamOffset ^ (streamOffset >>> 32));
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "[lineNumber=" + lineNumber + ", columnNumber=" + columnNumber + ", streamOffset=" + streamOffset + "]";
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonLongImpl.java b/src/main/java/org/apache/johnzon/core/JsonLongImpl.java
new file mode 100644
index 0000000..c9ae38f
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonLongImpl.java
@@ -0,0 +1,121 @@
+/*
+ * 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.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public final class JsonLongImpl implements JsonNumber, Serializable {
+    private final long value;
+    private Integer hashCode = null;
+
+    JsonLongImpl(final long value) {
+        this.value = value;
+    }
+
+    @Override
+    public Number numberValue() {
+        return value;
+    }
+
+    @Override
+    public boolean isIntegral() {
+        return true;
+    }
+
+    @Override
+    public int intValue() {
+        return (int) value;
+    }
+
+    @Override
+    public int intValueExact() {
+        return intValue();
+    }
+
+    @Override
+    public long longValue() {
+        return value;
+    }
+
+    @Override
+    public long longValueExact() {
+        return value;
+    }
+
+    @Override
+    public BigInteger bigIntegerValue() {
+        return new BigInteger(toString());
+    }
+
+    @Override
+    public BigInteger bigIntegerValueExact() {
+        return bigIntegerValue();
+    }
+
+    @Override
+    public double doubleValue() {
+        return value;
+    }
+
+    @Override
+    public BigDecimal bigDecimalValue() {
+        return new BigDecimal(toString());
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.NUMBER;
+    }
+
+    @Override
+    public String toString() {
+        return Long.toString(value);
+    }
+
+    @Override
+    public int hashCode() {
+        if (hashCode == null) {
+            hashCode = bigDecimalValue().hashCode();
+        }
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (JsonLongImpl.class.isInstance(obj)) {
+            return JsonLongImpl.class.cast(obj).value == value;
+        }
+        return JsonNumber.class.isInstance(obj) && JsonNumber.class.cast(obj).bigDecimalValue().equals(bigDecimalValue());
+    }
+
+    @Override
+    public JsonObject asJsonObject() {
+        return JsonObject.class.cast(this);
+    }
+
+    @Override
+    public JsonArray asJsonArray() {
+        return JsonArray.class.cast(this);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonMergePatchDiff.java b/src/main/java/org/apache/johnzon/core/JsonMergePatchDiff.java
new file mode 100644
index 0000000..5175e8d
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonMergePatchDiff.java
@@ -0,0 +1,81 @@
+/*
+ * 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.core;
+
+import java.util.Map;
+
+import javax.json.JsonMergePatch;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonValue;
+
+/**
+ * Creates a JsonMergePatch as diff between two JsonValues
+ */
+class JsonMergePatchDiff extends DiffBase {
+    private final JsonValue source;
+    private final JsonValue target;
+
+    public JsonMergePatchDiff(JsonValue source, JsonValue target) {
+        this.source = source;
+        this.target = target;
+    }
+
+    public JsonMergePatch calculateDiff() {
+        return new JsonMergePatchImpl(diff(source, target));
+    }
+
+    private JsonValue diff(JsonValue source, JsonValue target) {
+        JsonObjectBuilder builder = new JsonObjectBuilderImpl();
+
+        if (isJsonObject(source) && isJsonObject(target)) {
+            JsonObject srcObj = source.asJsonObject();
+            JsonObject targetObj = target.asJsonObject();
+            for (Map.Entry<String, JsonValue> sourceEntry : srcObj.entrySet()) {
+                String attributeName = sourceEntry.getKey();
+                if (targetObj.containsKey(attributeName)) {
+                    // compare the attribute values
+                    JsonValue attribDiff = diff(sourceEntry.getValue(), targetObj.get(attributeName));
+                    if (!JsonValue.EMPTY_JSON_OBJECT.equals(attribDiff)) {
+                        builder.add(attributeName, attribDiff);
+                    }
+                } else {
+                    // attribute got removed
+                    builder.add(attributeName, JsonValue.NULL);
+                }
+            }
+
+            for (Map.Entry<String, JsonValue> targetEntry : targetObj.entrySet()) {
+                String attributeName = targetEntry.getKey();
+                if (!srcObj.containsKey(attributeName)) {
+                    // add operation
+                    builder.add(attributeName, targetEntry.getValue());
+                }
+            }
+
+            return builder.build();
+        } else if (source.equals(target)) {
+            // if the two objects are identical, then return an empty patch
+            return JsonValue.EMPTY_JSON_OBJECT;
+        } else {
+            // as defined in the RFC anything else than comparing JsonObjects will result
+            // in completely replacing the source with the target
+            // That means our target is the patch.
+            return target;
+        }
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonMergePatchImpl.java b/src/main/java/org/apache/johnzon/core/JsonMergePatchImpl.java
new file mode 100644
index 0000000..b3f55c6
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonMergePatchImpl.java
@@ -0,0 +1,80 @@
+/*
+ * 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.core;
+
+
+import java.util.Map;
+
+import javax.json.JsonMergePatch;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonValue;
+
+/**
+ * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
+ */
+public class JsonMergePatchImpl implements JsonMergePatch {
+    private final JsonValue patch;
+
+    public JsonMergePatchImpl(JsonValue patch) {
+        this.patch = patch;
+    }
+
+    @Override
+    public JsonValue apply(JsonValue valueToApplyPatchOn) {
+        return applyPatch(valueToApplyPatchOn, patch);
+    }
+
+    private JsonValue applyPatch(JsonValue valueToApplyPatchOn, JsonValue patch) {
+        if (patch == null) {
+            return JsonValue.NULL;
+        } else if (patch instanceof JsonObject && valueToApplyPatchOn instanceof JsonObject) {
+            // we only apply an actual patch IF both sides are a JsonObject
+            JsonObject patchObject = patch.asJsonObject();
+
+            return applyJsonObjectPatch(valueToApplyPatchOn.asJsonObject(), patchObject);
+        } else {
+            // this must be a native JsonValue or JsonObject, so we just replace the
+            // the whole original valueToApplyPatchOn with the new jsonValue
+            return patch;
+        }
+    }
+
+    private JsonValue applyJsonObjectPatch(JsonObject jsonObject, JsonObject patch) {
+        JsonObjectBuilder builder = new JsonObjectBuilderImpl(jsonObject);
+
+        for (Map.Entry<String, JsonValue> patchAttrib : patch.entrySet()) {
+            String attribName = patchAttrib.getKey();
+            if (patchAttrib.getValue().equals(JsonValue.NULL)) {
+                builder.remove(attribName);
+            } else {
+                JsonValue originalAttrib = jsonObject.get(attribName);
+                if (originalAttrib == null) {
+                    builder.add(attribName, patchAttrib.getValue());
+                } else {
+                    builder.add(attribName, applyPatch(originalAttrib, patchAttrib.getValue()));
+                }
+            }
+        }
+        return builder.build();
+    }
+
+    @Override
+    public JsonValue toJsonValue() {
+        return patch;
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonNumberImpl.java b/src/main/java/org/apache/johnzon/core/JsonNumberImpl.java
new file mode 100644
index 0000000..7ffe348
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonNumberImpl.java
@@ -0,0 +1,132 @@
+/*
+ * 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.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+final class JsonNumberImpl implements JsonNumber, Serializable {
+    private final BigDecimal value;
+    private transient Integer hashCode = null;
+
+    JsonNumberImpl(final BigDecimal decimal) {
+        if (decimal == null) {
+            throw new NullPointerException("decimal must not be null");
+        }
+
+        this.value = decimal;
+    }
+
+    @Override
+    public Number numberValue() {
+        return value;
+    }
+
+    @Override
+    public boolean isIntegral() {
+        return value.scale() == 0;
+    }
+
+    @Override
+    public int intValue() {
+        return value.intValue();
+    }
+
+    @Override
+    public int intValueExact() {
+        checkFractionalPart();
+        return value.intValueExact();
+    }
+
+    @Override
+    public long longValue() {
+        return value.longValue();
+    }
+
+    @Override
+    public long longValueExact() {
+        checkFractionalPart();
+        return value.longValueExact();
+    }
+
+    @Override
+    public BigInteger bigIntegerValue() {
+        return value.toBigInteger();
+    }
+
+    @Override
+    public BigInteger bigIntegerValueExact() {
+        return value.toBigIntegerExact();
+    }
+
+    @Override
+    public double doubleValue() {
+        return value.doubleValue();
+    }
+
+    @Override
+    public BigDecimal bigDecimalValue() {
+        return value;
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.NUMBER;
+    }
+
+    @Override
+    public String toString() {
+        return value.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        Integer h = hashCode;
+        if (h == null) {
+            h = value.hashCode();
+            hashCode = h;
+        }
+        return h;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return JsonNumber.class.isInstance(obj) && JsonNumber.class.cast(obj).bigDecimalValue().equals(value);
+    }
+
+    private void checkFractionalPart() {
+        if (value.remainder(BigDecimal.ONE).doubleValue() != 0) {
+            throw new ArithmeticException("Not an int/long, use other value readers");
+        }
+    }
+
+    @Override
+    public JsonObject asJsonObject() {
+        return JsonObject.class.cast(this);
+    }
+
+    @Override
+    public JsonArray asJsonArray() {
+        return JsonArray.class.cast(this);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonObjectBuilderImpl.java b/src/main/java/org/apache/johnzon/core/JsonObjectBuilderImpl.java
new file mode 100644
index 0000000..69a2adb
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonObjectBuilderImpl.java
@@ -0,0 +1,178 @@
+/*
+ * 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.core;
+
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonValue;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+class JsonObjectBuilderImpl implements JsonObjectBuilder, Serializable {
+    private Map<String, JsonValue> attributeMap = new LinkedHashMap<>();
+
+    public JsonObjectBuilderImpl() {
+    }
+
+    public JsonObjectBuilderImpl(JsonObject initialData) {
+        attributeMap = new LinkedHashMap<>(initialData);
+    }
+
+    public JsonObjectBuilderImpl(Map<String, Object> initialValues) {
+        this();
+        for (Map.Entry<String, Object> entry : initialValues.entrySet()) {
+            add(entry.getKey(), entry.getValue());
+        }
+    }
+
+
+    /**
+     * Internal method to add a value where we do not yet know the type at compile time.
+     */
+    public void add(final String name, final Object value) {
+        if (value instanceof JsonValue) {
+            add(name, (JsonValue) value);
+        } else if (value instanceof BigDecimal) {
+            add(name, (BigDecimal) value);
+        } else if (value instanceof BigInteger) {
+            add(name, (BigInteger) value);
+        } else if (value instanceof Boolean) {
+            add(name, (boolean) value);
+        } else if (value instanceof Double) {
+            add(name, (double) value);
+        } else if (value instanceof Integer) {
+            add(name, (int) value);
+        } else if (value instanceof Long) {
+            add(name, (long) value);
+        } else if (value instanceof String) {
+            add(name, (String) value);
+        } else if (value == null) {
+            addNull(name);
+        } else {
+            throw new JsonException("Illegal JSON type! name=" + name + " type=" + value.getClass());
+        }
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final JsonValue value) {
+        putValue(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final String value) {
+        putValue(name, new JsonStringImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final BigInteger value) {
+        putValue(name, new JsonNumberImpl(new BigDecimal(value)));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final BigDecimal value) {
+        putValue(name, new JsonNumberImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final int value) {
+        putValue(name, new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final long value) {
+        putValue(name, new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final double value) {
+        putValue(name, new JsonDoubleImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final boolean value) {
+        putValue(name, value ? JsonValue.TRUE : JsonValue.FALSE);
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder addNull(final String name) {
+        putValue(name, JsonValue.NULL);
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final JsonObjectBuilder builder) {
+        putValue(name, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final JsonArrayBuilder builder) {
+        putValue(name, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder addAll(JsonObjectBuilder builder) {
+        attributeMap.putAll(builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder remove(String name) {
+        attributeMap.remove(name);
+        return this;
+    }
+
+    private void putValue(String name, JsonValue value){
+        if(name == null || value == null) {
+            throw new NullPointerException("name or value/builder must not be null");
+        }
+        
+        attributeMap.put(name, value);
+    }
+    
+
+    @Override
+    public JsonObject build() {
+        
+        if(attributeMap == null || attributeMap.isEmpty()) {
+            return new JsonObjectImpl(Collections.EMPTY_MAP);
+        } else {
+            Map<String, JsonValue> dump = (Collections.unmodifiableMap(attributeMap));
+            attributeMap =null;
+            return new JsonObjectImpl(dump);
+        }
+        
+        
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonObjectImpl.java b/src/main/java/org/apache/johnzon/core/JsonObjectImpl.java
new file mode 100644
index 0000000..8a81545
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonObjectImpl.java
@@ -0,0 +1,206 @@
+/*
+ * 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.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import javax.json.spi.JsonProvider;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.AbstractMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+
+final class JsonObjectImpl extends AbstractMap<String, JsonValue> implements JsonObject, Serializable {
+    private transient Integer hashCode = null;
+    private final Map<String, JsonValue> unmodifieableBackingMap;
+
+    private <T> T value(final String name, final Class<T> clazz) {
+        final JsonValue v = unmodifieableBackingMap.get(name);
+        if (v != null) {
+            return clazz.cast(v);
+        }
+        return null;
+    }
+
+    private <T> T valueOrExcpetion(final String name, final Class<T> clazz) {
+        T value = value(name, clazz);
+        if (value == null) {
+            throw new NullPointerException("no mapping for " + name);
+        }
+
+        return value;
+    }
+
+    JsonObjectImpl(final Map<String, JsonValue> backingMap) {
+        super();
+        this.unmodifieableBackingMap = backingMap;
+    }
+
+    @Override
+    public JsonArray getJsonArray(final String name) {
+        return value(name, JsonArray.class);
+    }
+
+    @Override
+    public JsonObject getJsonObject(final String name) {
+        return value(name, JsonObject.class);
+    }
+
+    @Override
+    public JsonNumber getJsonNumber(final String name) {
+        return value(name, JsonNumber.class);
+    }
+
+    @Override
+    public JsonString getJsonString(final String name) {
+        return value(name, JsonString.class);
+    }
+
+    @Override
+    public String getString(final String name) {
+        return valueOrExcpetion(name, JsonString.class).getString();
+    }
+
+    @Override
+    public String getString(final String name, final String defaultValue) {
+        final Object v = value(name, JsonValue.class);
+        if (v != null && v instanceof JsonString) {
+            return JsonString.class.cast(v).getString();
+        }
+
+        return defaultValue;
+    }
+
+    @Override
+    public int getInt(final String name) {
+        return valueOrExcpetion(name, JsonNumber.class).intValue();
+    }
+
+    @Override
+    public int getInt(final String name, final int defaultValue) {
+        final Object v = value(name, JsonValue.class);
+        if (v != null && v instanceof JsonNumber) {
+            return JsonNumber.class.cast(v).intValue();
+        }
+
+        return defaultValue;
+    }
+
+    @Override
+    public boolean getBoolean(final String name) {
+        final JsonValue obj = valueOrExcpetion(name, JsonValue.class);
+        if (JsonValue.TRUE == obj) {
+            return true;
+        }
+        if (JsonValue.FALSE == obj) {
+            return false;
+        }
+        throw new ClassCastException("Wrong value for a boolean: " + obj);
+    }
+
+    @Override
+    public boolean getBoolean(final String name, final boolean defaultValue) {
+        final Object v = value(name, JsonValue.class);
+        if (v != null) {
+            return JsonValue.TRUE.equals(v) || !JsonValue.FALSE.equals(v) && defaultValue;
+        }
+        return defaultValue;
+    }
+
+    @Override
+    public boolean isNull(final String name) {
+        return JsonValue.NULL.equals(valueOrExcpetion(name, JsonValue.class));
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.OBJECT;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder("{");
+        final Iterator<Map.Entry<String, JsonValue>> it = unmodifieableBackingMap.entrySet().iterator();
+        boolean hasNext = it.hasNext();
+        while (hasNext) {
+            final Map.Entry<String, JsonValue> entry = it.next();
+
+            builder.append('"').append(entry.getKey()).append("\":");
+
+            final JsonValue value = entry.getValue();
+            if (JsonString.class.isInstance(value)) {
+                builder.append(value.toString());
+            } else {
+                builder.append(value != JsonValue.NULL ? value.toString() : JsonChars.NULL);
+            }
+
+            hasNext = it.hasNext();
+            if (hasNext) {
+                builder.append(",");
+            }
+        }
+        return builder.append('}').toString();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return JsonObjectImpl.class.isInstance(obj)
+                && unmodifieableBackingMap.equals(JsonObjectImpl.class.cast(obj).unmodifieableBackingMap);
+    }
+
+    @Override
+    public int hashCode() {
+        Integer h = hashCode;
+        if (h == null) {
+            h = unmodifieableBackingMap.hashCode();
+            hashCode = h;
+        }
+        return h;
+    }
+
+    @Override
+    public Set<java.util.Map.Entry<String, JsonValue>> entrySet() {
+        return unmodifieableBackingMap.entrySet();
+    }
+
+    private Object writeReplace() throws ObjectStreamException {
+        return new SerializableValue(toString());
+    }
+
+    @Override
+    public JsonValue getValue(String jsonPointer) {
+        return JsonProvider.provider().createPointer(jsonPointer).getValue(this);
+    }
+
+    @Override
+    public JsonObject asJsonObject() {
+        return JsonObject.class.cast(this);
+    }
+
+    @Override
+    public JsonArray asJsonArray() {
+        return JsonArray.class.cast(this);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java b/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java
new file mode 100644
index 0000000..440b4cb
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java
@@ -0,0 +1,137 @@
+/*
+ * 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.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParserFactory;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import static java.util.Arrays.asList;
+
+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, 256 * 1024); //256kB
+    
+    public static final String AUTO_ADJUST_STRING_BUFFER = "org.apache.johnzon.auto-adjust-buffer";
+    public static final String BUFFER_LENGTH = "org.apache.johnzon.default-char-buffer";
+    public static final int DEFAULT_BUFFER_LENGTH = Integer.getInteger(BUFFER_LENGTH, 64 * 1024); //64k
+    
+    public static final String SUPPORTS_COMMENTS = "org.apache.johnzon.supports-comments";
+    public static final boolean DEFAULT_SUPPORTS_COMMENT = Boolean.getBoolean(SUPPORTS_COMMENTS); //default is false;
+
+    static final Collection<String> SUPPORTED_CONFIG_KEYS = asList(
+        BUFFER_STRATEGY, MAX_STRING_LENGTH, BUFFER_LENGTH, SUPPORTS_COMMENTS, AUTO_ADJUST_STRING_BUFFER
+    );
+      
+    private final int maxSize;
+    private final BufferStrategy.BufferProvider<char[]> bufferProvider;
+    private final BufferStrategy.BufferProvider<char[]> valueBufferProvider;
+    private final boolean supportsComments;
+    private final boolean autoAdjustBuffers;
+
+    JsonParserFactoryImpl(final Map<String, ?> config) {
+        super(config, SUPPORTED_CONFIG_KEYS, null);
+
+        final int bufferSize = getInt(BUFFER_LENGTH, DEFAULT_BUFFER_LENGTH);
+        if (bufferSize <= 0) {
+            throw new IllegalArgumentException("buffer length must be greater than zero");
+        }
+
+        this.maxSize = getInt(MAX_STRING_LENGTH, DEFAULT_MAX_STRING_LENGTH);
+        this.bufferProvider = getBufferProvider().newCharProvider(bufferSize);
+        this.valueBufferProvider = getBufferProvider().newCharProvider(maxSize);
+        this.supportsComments = getBool(SUPPORTS_COMMENTS, DEFAULT_SUPPORTS_COMMENT);
+        this.autoAdjustBuffers = getBool(AUTO_ADJUST_STRING_BUFFER, true);
+    }
+
+    private JsonStreamParserImpl getDefaultJsonParserImpl(final InputStream in) {
+        if (supportsComments) {
+            return new CommentsJsonStreamParserImpl(in, maxSize, bufferProvider, valueBufferProvider, autoAdjustBuffers);
+        }
+        //UTF Auto detection RFC 4627
+        return new JsonStreamParserImpl(in, maxSize, bufferProvider, valueBufferProvider, autoAdjustBuffers);
+    }
+
+    private JsonStreamParserImpl getDefaultJsonParserImpl(final InputStream in, final Charset charset) {
+        if (supportsComments) {
+            return new CommentsJsonStreamParserImpl(in, charset, maxSize, bufferProvider, valueBufferProvider, autoAdjustBuffers);
+        }
+        //use provided charset
+        return new JsonStreamParserImpl(in, charset, maxSize, bufferProvider, valueBufferProvider, autoAdjustBuffers);
+    }
+
+    private JsonStreamParserImpl getDefaultJsonParserImpl(final Reader in) {
+        if (supportsComments) {
+            return new CommentsJsonStreamParserImpl(in, maxSize, bufferProvider, valueBufferProvider, autoAdjustBuffers);
+        }
+        //no charset necessary
+        return new JsonStreamParserImpl(in, maxSize, bufferProvider, valueBufferProvider, autoAdjustBuffers);
+    }
+
+    @Override
+    public JsonParser createParser(final Reader reader) {
+        return getDefaultJsonParserImpl(reader);
+    }
+
+    @Override
+    public JsonParser createParser(final InputStream in) {
+        return getDefaultJsonParserImpl(in);
+    }
+
+    @Override
+    public JsonParser createParser(final InputStream in, final Charset charset) {
+        return getDefaultJsonParserImpl(in, charset);
+    }
+
+    @Override
+    public JsonParser createParser(final JsonObject obj) {
+        // no need of a comment version since JsonObject has no comment event
+        return new JsonInMemoryParser(obj);
+    }
+
+    @Override
+    public JsonParser createParser(final JsonArray array) {
+        // no need of a comment version since JsonObject has no comment event
+        return new JsonInMemoryParser(array);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return Collections.unmodifiableMap(internalConfig);
+    }
+
+    public JsonStreamParserImpl createInternalParser(final InputStream in) {
+        return getDefaultJsonParserImpl(in);
+    }
+    
+    public JsonStreamParserImpl createInternalParser(final InputStream in, final Charset charset) {
+        return getDefaultJsonParserImpl(in, charset);
+    }
+
+    public JsonStreamParserImpl createInternalParser(final Reader reader) {
+        return getDefaultJsonParserImpl(reader);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonPatchBuilderImpl.java b/src/main/java/org/apache/johnzon/core/JsonPatchBuilderImpl.java
new file mode 100644
index 0000000..ada2db7
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonPatchBuilderImpl.java
@@ -0,0 +1,181 @@
+/*
+ * 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.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonPatch;
+import javax.json.JsonPatchBuilder;
+import javax.json.JsonValue;
+import java.util.ArrayList;
+import java.util.List;
+
+class JsonPatchBuilderImpl implements JsonPatchBuilder {
+
+    private final List<JsonPatchImpl.PatchValue> operations;
+
+
+    JsonPatchBuilderImpl() {
+        operations = new ArrayList<>();
+    }
+
+    JsonPatchBuilderImpl(JsonArray initialData) {
+        operations = new ArrayList<>(initialData.size());
+
+        for (JsonValue value : initialData) {
+
+            JsonObject operation = (JsonObject) value;
+
+            JsonPatch.Operation op = JsonPatch.Operation.fromOperationName(operation.getString("op"));
+            String path = operation.getString("path");
+            String from = operation.getString("from", null);
+            JsonValue jsonValue = operation.get("value");
+
+            operations.add(new JsonPatchImpl.PatchValue(op,
+                                                        path,
+                                                        from,
+                                                        jsonValue));
+        }
+    }
+
+
+    @Override
+    public JsonPatchBuilder add(String path, JsonValue value) {
+        return addOperation(new JsonPatchImpl.PatchValue(JsonPatch.Operation.ADD,
+                                                         path,
+                                                         null,
+                                                         value));
+    }
+
+    @Override
+    public JsonPatchBuilder add(String path, String value) {
+        return add(path, toJsonString(value));
+    }
+
+    @Override
+    public JsonPatchBuilder add(String path, int value) {
+        return add(path, toJsonNumber(value));
+    }
+
+    @Override
+    public JsonPatchBuilder add(String path, boolean value) {
+        return add(path, toJsonBoolean(value));
+    }
+
+
+    @Override
+    public JsonPatchBuilder remove(String path) {
+        return addOperation(new JsonPatchImpl.PatchValue(JsonPatch.Operation.REMOVE,
+                                                         path,
+                                                         null,
+                                                         null));
+    }
+
+
+    @Override
+    public JsonPatchBuilder replace(String path, JsonValue value) {
+        return addOperation(new JsonPatchImpl.PatchValue(JsonPatch.Operation.REPLACE,
+                                                         path,
+                                                         null,
+                                                         value));
+    }
+
+    @Override
+    public JsonPatchBuilder replace(String path, String value) {
+        return replace(path, toJsonString(value));
+    }
+
+    @Override
+    public JsonPatchBuilder replace(String path, int value) {
+        return replace(path, toJsonNumber(value));
+    }
+
+    @Override
+    public JsonPatchBuilder replace(String path, boolean value) {
+        return replace(path, toJsonBoolean(value));
+    }
+
+
+    @Override
+    public JsonPatchBuilder move(String path, String from) {
+        return addOperation(new JsonPatchImpl.PatchValue(JsonPatch.Operation.MOVE,
+                                                         path,
+                                                         from,
+                                                         null));
+    }
+
+
+    @Override
+    public JsonPatchBuilder copy(String path, String from) {
+        return addOperation(new JsonPatchImpl.PatchValue(JsonPatch.Operation.COPY,
+                                                         path,
+                                                         from,
+                                                         null));
+    }
+
+
+    @Override
+    public JsonPatchBuilder test(String path, JsonValue value) {
+        return addOperation(new JsonPatchImpl.PatchValue(JsonPatch.Operation.TEST,
+                                                         path,
+                                                         null,
+                                                         value));
+    }
+
+    @Override
+    public JsonPatchBuilder test(String path, String value) {
+        return test(path, toJsonString(value));
+    }
+
+    @Override
+    public JsonPatchBuilder test(String path, int value) {
+        return test(path, toJsonNumber(value));
+    }
+
+    @Override
+    public JsonPatchBuilder test(String path, boolean value) {
+        return test(path, toJsonBoolean(value));
+    }
+
+
+    @Override
+    public JsonPatch build() {
+        JsonPatchImpl patch = new JsonPatchImpl(new ArrayList<>(operations));
+
+        return patch;
+
+    }
+
+
+    private JsonPatchBuilder addOperation(JsonPatchImpl.PatchValue operation) {
+        operations.add(operation);
+        return this;
+    }
+
+    private static JsonValue toJsonBoolean(boolean value) {
+        return value ? JsonValue.TRUE : JsonValue.FALSE;
+    }
+
+    private static JsonValue toJsonString(String value) {
+        return value == null ? JsonValue.NULL : new JsonStringImpl(value);
+    }
+
+    private static JsonValue toJsonNumber(int value) {
+        return new JsonLongImpl(value);
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonPatchDiff.java b/src/main/java/org/apache/johnzon/core/JsonPatchDiff.java
new file mode 100644
index 0000000..301a775
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonPatchDiff.java
@@ -0,0 +1,102 @@
+/*
+ * 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.core;
+
+import java.util.Map;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonPatch;
+import javax.json.JsonPatchBuilder;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+
+/**
+ * Create a diff from a source and target JsonStructure
+ */
+class JsonPatchDiff extends DiffBase {
+
+    private final JsonStructure source;
+    private final JsonStructure target;
+
+    JsonPatchDiff(JsonStructure source, JsonStructure target) {
+        this.source = source;
+        this.target = target;
+    }
+
+    JsonPatch calculateDiff() {
+        JsonPatchBuilder patchBuilder = new JsonPatchBuilderImpl();
+
+        diff(patchBuilder, "", source, target);
+
+        return patchBuilder.build();
+    }
+
+    private void diff(JsonPatchBuilder patchBuilder, String basePath, JsonValue source, JsonValue target) {
+        if (isJsonObject(source) && isJsonObject(target)) {
+            diffJsonObjects(patchBuilder, basePath + "/", (JsonObject) source, (JsonObject) target);
+        } else if (isJsonArray(source) && isJsonArray(target)) {
+            diffJsonArray(patchBuilder, basePath + "/", (JsonArray) source, (JsonArray) target);
+        } else if (!source.equals(target)){
+            patchBuilder.replace(basePath, target);
+        }
+    }
+
+    private void diffJsonArray(JsonPatchBuilder patchBuilder, String basePath, JsonArray source, JsonArray target) {
+        for (int i = 0; i < source.size(); i++) {
+            JsonValue sourceValue = source.get(i);
+
+            if (target.size() <= i) {
+                patchBuilder.remove(basePath + i);
+                continue;
+            }
+
+            diff(patchBuilder, basePath + i, sourceValue, target.get(i));
+        }
+
+        if (target.size() > source.size()) {
+
+            for (int i = target.size() - source.size(); i < target.size(); i++) {
+                patchBuilder.add(basePath + i, target.get(i));
+            }
+        }
+
+    }
+
+    private void diffJsonObjects(JsonPatchBuilder patchBuilder, String basePath, JsonObject source, JsonObject target) {
+
+        for (Map.Entry<String, JsonValue> sourceEntry : source.entrySet()) {
+            String attributeName = sourceEntry.getKey();
+
+            if (target.containsKey(attributeName)) {
+                diff(patchBuilder, basePath + JsonPointerUtil.encode(attributeName), sourceEntry.getValue(), target.get(attributeName));
+            } else {
+                // the value got removed
+                patchBuilder.remove(basePath + JsonPointerUtil.encode(attributeName));
+            }
+        }
+
+        for (Map.Entry<String, JsonValue> targetEntry : target.entrySet()) {
+            if (!source.containsKey(targetEntry.getKey())) {
+                patchBuilder.add(basePath + JsonPointerUtil.encode(targetEntry.getKey()), targetEntry.getValue());
+            }
+        }
+
+    }
+
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonPatchImpl.java b/src/main/java/org/apache/johnzon/core/JsonPatchImpl.java
new file mode 100644
index 0000000..257ab40
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonPatchImpl.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonPatch;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+
+class JsonPatchImpl implements JsonPatch {
+
+    private final List<PatchValue> patches;
+
+
+    JsonPatchImpl(PatchValue... patches) {
+        this.patches = Arrays.asList(patches);
+    }
+
+    JsonPatchImpl(List<PatchValue> patches) {
+        if (patches == null) {
+            this.patches = Collections.emptyList();
+        } else {
+            this.patches = Collections.unmodifiableList(patches);
+        }
+    }
+
+
+    @Override
+    public <T extends JsonStructure> T apply(T target) {
+
+        //X TODO JsonPointer should use generics like JsonPatch
+        JsonStructure patched = target;
+
+        for (PatchValue patch : patches) {
+
+            switch (patch.operation) {
+                case ADD:
+                    patched = patch.path.add(patched, patch.value);
+                    break;
+                case REMOVE:
+                    patched = patch.path.remove(patched);
+                    break;
+                case REPLACE:
+                    // first remove the existing element and then add the new value
+                    patched = patch.path.add(patch.path.remove(patched), patch.value);
+                    break;
+                case MOVE:
+                    JsonValue valueToMove = patch.from.getValue(patched);
+                    patched = patch.path.add(patch.from.remove(patched), valueToMove);
+                    break;
+                case COPY:
+                    JsonValue toCopy = patch.from.getValue(patched);
+                    patched = patch.path.add(patched, toCopy);
+                    break;
+                case TEST:
+                    JsonValue toTest = patch.path.getValue(patched);
+                    if (!toTest.equals(patch.value)) {
+                        throw new JsonException("JsonPatch.Operation.TEST fails! Values are not equal");
+                    }
+                    break;
+                default:
+                    throw new IllegalStateException("unsupported operation: " + patch.operation);
+            }
+        }
+
+        //X TODO dirty cast can be removed after JsonPointer uses generics like JsonPatch
+        return (T) patched;
+    }
+
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        JsonPatchImpl jsonPatch = (JsonPatchImpl) o;
+
+        return patches.equals(jsonPatch.patches);
+    }
+
+    @Override
+    public int hashCode() {
+        return patches.hashCode();
+    }
+
+
+    @Override
+    public JsonArray toJsonArray() {
+
+        JsonArrayBuilder builder = Json.createArrayBuilder();
+        for (PatchValue patch : patches) {
+            builder.add(patch.toJson());
+        }
+
+        return builder.build();
+    }
+
+
+
+    static class PatchValue {
+        private final JsonPatch.Operation operation;
+        private final JsonPointerImpl path;
+        private final JsonPointerImpl from;
+        private final JsonValue value;
+
+        PatchValue(JsonPatch.Operation operation,
+                   String path,
+                   String from,
+                   JsonValue value) {
+            this.operation = operation;
+            this.path = new JsonPointerImpl(path);
+
+            // ignore from if we do not need it
+            if (operation == JsonPatch.Operation.MOVE || operation == JsonPatch.Operation.COPY) {
+                this.from = new JsonPointerImpl(from);
+            } else {
+                this.from = null;
+            }
+
+            this.value = value;
+        }
+
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            PatchValue that = (PatchValue) o;
+
+            if (operation != that.operation) {
+                return false;
+            }
+            if (!path.equals(that.path)) {
+                return false;
+            }
+            if (from != null ? !from.equals(that.from) : that.from != null) {
+                return false;
+            }
+            return value != null ? value.equals(that.value) : that.value == null;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = operation.hashCode();
+            result = 31 * result + path.hashCode();
+            result = 31 * result + (from != null ? from.hashCode() : 0);
+            result = 31 * result + (value != null ? value.hashCode() : 0);
+            return result;
+        }
+
+
+        @Override
+        public String toString() {
+            return "{" +
+                   "op: " + operation +
+                   ", path: " + path +
+                   ", from: " + from +
+                   ", value: " + value +
+                   '}';
+        }
+
+        JsonObject toJson() {
+            JsonObjectBuilder builder = Json.createObjectBuilder()
+                                            .add("op", operation.name().toLowerCase())
+                                            .add("path", path.getJsonPointer());
+
+            if (from != null) {
+                builder.add("from", from.getJsonPointer());
+            }
+
+            if (value != null) {
+                builder.add("value", value);
+            }
+
+            return builder.build();
+        }
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonPointerImpl.java b/src/main/java/org/apache/johnzon/core/JsonPointerImpl.java
new file mode 100644
index 0000000..b674ad9
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonPointerImpl.java
@@ -0,0 +1,484 @@
+/*
+ * 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.core;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonPointer;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+
+public class JsonPointerImpl implements JsonPointer {
+
+    private final String jsonPointer;
+    private final List<String> referenceTokens = new ArrayList<>();
+    private final String lastReferenceToken;
+
+    /**
+     * Constructs and initializes a JsonPointer.
+     *
+     * @param jsonPointer the JSON Pointer string
+     * @throws NullPointerException if {@code jsonPointer} is {@code null}
+     * @throws JsonException        if {@code jsonPointer} is not a valid JSON Pointer
+     */
+    public JsonPointerImpl(String jsonPointer) {
+        if (jsonPointer == null) {
+            throw new NullPointerException("jsonPointer must not be null");
+        }
+        if (!jsonPointer.equals("") && !jsonPointer.startsWith("/")) {
+            throw new JsonException("A non-empty JsonPointer string must begin with a '/'");
+        }
+
+        this.jsonPointer = jsonPointer;
+        String[] encodedReferenceTokens = jsonPointer.split("/", -1);
+
+        for (String encodedReferenceToken : encodedReferenceTokens) {
+            referenceTokens.add(JsonPointerUtil.decode(encodedReferenceToken));
+        }
+        lastReferenceToken = referenceTokens.get(referenceTokens.size() - 1);
+    }
+
+    /**
+     * Compares this {@code JsonPointer} with another object.
+     *
+     * @param obj the object to compare this {@code JsonPointer} against
+     * @return true if the given object is a {@code JsonPointer} with the same
+     * reference tokens as this one, false otherwise.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        JsonPointerImpl that = (JsonPointerImpl) obj;
+        return jsonPointer.equals(that.jsonPointer);
+    }
+
+    /**
+     * Returns the hash code value for this {@code JsonPointer} object.
+     * The hash code of this object is defined by the hash codes of it's reference tokens.
+     *
+     * @return the hash code value for this {@code JsonPointer} object
+     */
+    @Override
+    public int hashCode() {
+        return jsonPointer.hashCode();
+    }
+
+    /**
+     * Returns the value at the referenced location in the specified {@code target}
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the referenced value in the target.
+     * @throws NullPointerException if {@code target} is null
+     * @throws JsonException        if the referenced value does not exist
+     */
+    public JsonValue getValue(JsonStructure target) {
+        if (target == null) {
+            throw new NullPointerException("target must not be null");
+        }
+        if (isEmptyJsonPointer()) {
+            return target;
+        }
+
+        JsonValue jsonValue = target;
+        for (int i = 1; i < referenceTokens.size(); i++) {
+            jsonValue = getValue(jsonValue, referenceTokens.get(i), i, referenceTokens.size() - 1);
+        }
+        return jsonValue;
+    }
+
+    @Override
+    public boolean containsValue(JsonStructure target) {
+        try {
+            getValue(target);
+            return true;
+        } catch (JsonException je) {
+            return false;
+        }
+    }
+
+    /**
+     * Adds or replaces a value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     * <ol>
+     * <li>If the reference is the target (empty JSON Pointer string),
+     * the specified {@code value}, which must be the same type as
+     * specified {@code target}, is returned.</li>
+     * <li>If the reference is an array element, the specified {@code value} is inserted
+     * into the array, at the referenced index. The value currently at that location, and
+     * any subsequent values, are shifted to the right (adds one to the indices).
+     * Index starts with 0. If the reference is specified with a "-", or if the
+     * index is equal to the size of the array, the value is appended to the array.</li>
+     * <li>If the reference is a name/value pair of a {@code JsonObject}, and the
+     * referenced value exists, the value is replaced by the specified {@code value}.
+     * If the value does not exist, a new name/value pair is added to the object.</li>
+     * </ol>
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be added
+     * @return the transformed {@code target} after the value is added.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the reference is an array element and
+     *                              the index is out of range ({@code index < 0 || index > array size}),
+     *                              or if the pointer contains references to non-existing objects or arrays.
+     */
+    public JsonStructure add(JsonStructure target, JsonValue value) {
+        validateAdd(target);
+        if (isEmptyJsonPointer()) {
+            if (value.getClass() != target.getClass()) {
+                throw new JsonException("The value must have the same type as the target");
+            }
+            return (JsonStructure) value;
+        }
+
+        return addInternal(target, value);
+    }
+
+    /**
+     * Adds or replaces a value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be added
+     * @return the transformed {@code target} after the value is added.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the reference is an array element and
+     *                              the index is out of range ({@code index < 0 || index > array size}),
+     *                              or if the pointer contains references to non-existing objects or arrays.
+     * @see #add(JsonStructure, JsonValue)
+     */
+    public JsonObject add(JsonObject target, JsonValue value) {
+        validateAdd(target);
+
+        return addInternal(target, value);
+    }
+
+    /**
+     * Adds or replaces a value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be added
+     * @return the transformed {@code target} after the value is added.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the reference is an array element and
+     *                              the index is out of range ({@code index < 0 || index > array size}),
+     *                              or if the pointer contains references to non-existing objects or arrays.
+     * @see #add(JsonStructure, JsonValue)
+     */
+    public JsonArray add(JsonArray target, JsonValue value) {
+        validateAdd(target);
+
+        return addInternal(target, value);
+    }
+
+    /**
+     * Replaces the value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be stored at the referenced location
+     * @return the transformed {@code target} after the value is replaced.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     */
+    public JsonStructure replace(JsonStructure target, JsonValue value) {
+        if (target instanceof JsonObject) {
+            return replace((JsonObject) target, value);
+        } else {
+            return replace((JsonArray) target, value);
+        }
+    }
+
+    /**
+     * Replaces the value at the referenced location in the specified
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be stored at the referenced location
+     * @return the transformed {@code target} after the value is replaced.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     * @see #replace(JsonStructure, JsonValue)
+     */
+    public JsonObject replace(JsonObject target, JsonValue value) {
+        return add(remove(target), value);
+    }
+
+    /**
+     * Replaces the value at the referenced location in the specified
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be stored at the referenced location
+     * @return the transformed {@code target} after the value is replaced.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     * @see #replace(JsonStructure, JsonValue)
+     */
+    public JsonArray replace(JsonArray target, JsonValue value) {
+        return add(remove(target), value);
+    }
+
+    /**
+     * Removes the value at the reference location in the specified {@code target}
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the transformed {@code target} after the value is removed.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     */
+    public JsonStructure remove(JsonStructure target) {
+        if (target instanceof JsonObject) {
+            return remove((JsonObject) target);
+        } else {
+            return remove((JsonArray) target);
+        }
+    }
+
+    /**
+     * Removes the value at the reference location in the specified {@code target}
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the transformed {@code target} after the value is removed.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     * @see #remove(JsonStructure)
+     */
+    public JsonObject remove(JsonObject target) {
+        validateRemove(target);
+
+        return (JsonObject) remove(target, 1, referenceTokens.size() - 1);
+    }
+
+    /**
+     * Removes the value at the reference location in the specified {@code target}
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the transformed {@code target} after the value is removed.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     * @see #remove(JsonStructure)
+     */
+    public JsonArray remove(JsonArray target) {
+        validateRemove(target);
+
+        return (JsonArray) remove(target, 1, referenceTokens.size() - 1);
+    }
+
+    String getJsonPointer() {
+        return jsonPointer;
+    }
+
+    private void validateAdd(JsonValue target) {
+        validateJsonPointer(target, referenceTokens.size() - 1);
+    }
+
+    private void validateRemove(JsonValue target) {
+        validateJsonPointer(target, referenceTokens.size());
+        if (isEmptyJsonPointer()) {
+            throw new JsonException("The reference must not be the target");
+        }
+    }
+
+    private boolean isEmptyJsonPointer() {
+        return jsonPointer.equals("");
+    }
+
+    private JsonValue getValue(JsonValue jsonValue, String referenceToken, int currentPosition, int referencePosition) {
+        if (jsonValue instanceof JsonObject) {
+            JsonObject jsonObject = (JsonObject) jsonValue;
+            jsonValue = jsonObject.get(referenceToken);
+
+            if (jsonValue != null) {
+                return jsonValue;
+            }
+            throw new JsonException("'" + jsonObject + "' contains no value for name '" + referenceToken + "'");
+        } else if (jsonValue instanceof JsonArray) {
+            validateArrayIndex(referenceToken);
+
+            try {
+                JsonArray jsonArray = (JsonArray) jsonValue;
+                int arrayIndex = Integer.parseInt(referenceToken);
+                validateArraySize(jsonArray, arrayIndex, jsonArray.size());
+                return jsonArray.get(arrayIndex);
+            } catch (NumberFormatException e) {
+                throw new JsonException("'" + referenceToken + "' is no valid array index", e);
+            }
+        } else {
+            if (currentPosition != referencePosition) {
+                return jsonValue;
+            }
+            throw new JsonException("'" + jsonValue + "' contains no element for '" + referenceToken + "'");
+        }
+    }
+
+    private <T extends JsonStructure> T addInternal(T jsonValue, JsonValue newValue) {
+        List<String> currentPath = new ArrayList<>();
+        currentPath.add("");
+
+        return (T) addInternal(jsonValue, newValue, currentPath);
+    }
+
+    private JsonValue addInternal(JsonValue jsonValue, JsonValue newValue, List<String> currentPath) {
+        if (jsonValue instanceof JsonObject) {
+            JsonObject jsonObject = (JsonObject) jsonValue;
+            JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
+
+            if (jsonObject.isEmpty()) {
+                objectBuilder.add(lastReferenceToken, newValue);
+            } else {
+                for (Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
+
+                    currentPath.add(entry.getKey());
+                    objectBuilder.add(entry.getKey(), addInternal(entry.getValue(), newValue, currentPath));
+                    currentPath.remove(entry.getKey());
+
+                    if (currentPath.size() == referenceTokens.size() - 1 &&
+                        currentPath.get(currentPath.size() - 1).equals(referenceTokens.get(referenceTokens.size() - 2))) {
+                        objectBuilder.add(lastReferenceToken, newValue);
+                    }
+                }
+            }
+            return objectBuilder.build();
+        } else if (jsonValue instanceof JsonArray) {
+            JsonArray jsonArray = (JsonArray) jsonValue;
+            JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
+
+            int arrayIndex = -1;
+            if (currentPath.size() == referenceTokens.size() - 1 &&
+                currentPath.get(currentPath.size() - 1).equals(referenceTokens.get(referenceTokens.size() - 2))) {
+
+                arrayIndex = getArrayIndex(lastReferenceToken, jsonArray, true);
+            }
+
+            int jsonArraySize = jsonArray.size();
+            for (int i = 0; i <= jsonArraySize; i++) {
+                if (i == arrayIndex) {
+                    arrayBuilder.add(newValue);
+                }
+                if (i == jsonArraySize) {
+                    break;
+                }
+
+                String path = String.valueOf(i);
+                currentPath.add(path);
+                arrayBuilder.add(addInternal(jsonArray.get(i), newValue, currentPath));
+                currentPath.remove(path);
+            }
+            return arrayBuilder.build();
+        }
+        return jsonValue;
+    }
+
+    private JsonValue remove(JsonValue jsonValue, int currentPosition, int referencePosition) {
+        if (jsonValue instanceof JsonObject) {
+            JsonObject jsonObject = (JsonObject) jsonValue;
+            JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
+
+            for (Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
+                if (currentPosition == referencePosition
+                        && lastReferenceToken.equals(entry.getKey())) {
+                    continue;
+                }
+                objectBuilder.add(entry.getKey(), remove(entry.getValue(), currentPosition + 1, referencePosition));
+            }
+            return objectBuilder.build();
+        } else if (jsonValue instanceof JsonArray) {
+            JsonArray jsonArray = (JsonArray) jsonValue;
+            JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
+
+            int arrayIndex = -1;
+            if (currentPosition == referencePosition) {
+                arrayIndex = getArrayIndex(lastReferenceToken, jsonArray, false);
+            }
+
+            int jsonArraySize = jsonArray.size();
+            for (int i = 0; i < jsonArraySize; i++) {
+                if (i == arrayIndex) {
+                    continue;
+                }
+                arrayBuilder.add(remove(jsonArray.get(i), currentPosition + 1, referencePosition));
+            }
+            return arrayBuilder.build();
+        }
+        return jsonValue;
+    }
+
+    private int getArrayIndex(String referenceToken, JsonArray jsonArray, boolean addOperation) {
+        if (addOperation && referenceToken.equals("-")) {
+            return jsonArray.size();
+        }
+
+        validateArrayIndex(referenceToken);
+
+        try {
+            int arrayIndex = Integer.parseInt(referenceToken);
+            int arraySize = addOperation ? jsonArray.size() + 1 : jsonArray.size();
+            validateArraySize(jsonArray, arrayIndex, arraySize);
+            return arrayIndex;
+        } catch (NumberFormatException e) {
+            throw new JsonException("'" + referenceToken + "' is no valid array index", e);
+        }
+    }
+
+    private void validateJsonPointer(JsonValue target, int size) throws NullPointerException, JsonException {
+        if (target == null) {
+            throw new NullPointerException("target must not be null");
+        }
+
+        JsonValue jsonValue = target;
+        for (int i = 1; i < size; i++) {
+            jsonValue = getValue(jsonValue, referenceTokens.get(i), i, referenceTokens.size() - 1);
+        }
+    }
+
+    private void validateArrayIndex(String referenceToken) throws JsonException {
+        if (referenceToken.startsWith("+") || referenceToken.startsWith("-")) {
+            throw new JsonException("An array index must not start with '" + referenceToken.charAt(0) + "'");
+        }
+        if (referenceToken.startsWith("0") && referenceToken.length() > 1) {
+            throw new JsonException("An array index must not start with a leading '0'");
+        }
+    }
+
+    private void validateArraySize(JsonArray jsonArray, int arrayIndex, int arraySize) throws JsonException {
+        if (arrayIndex >= arraySize) {
+            throw new JsonException("'" + jsonArray + "' contains no element for index " + arrayIndex);
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonPointerUtil.java b/src/main/java/org/apache/johnzon/core/JsonPointerUtil.java
new file mode 100644
index 0000000..91bc9c2
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonPointerUtil.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.core;
+
+public class JsonPointerUtil {
+
+    private JsonPointerUtil() {
+
+    }
+
+    /**
+     * Transforms "~" to "~0" and then "/" to "~1"
+     */
+    public static String encode(String s) {
+        if (s == null || s.length() == 0) {
+            return s;
+        }
+
+        return s.replace("~", "~0").replace("/", "~1");
+    }
+
+    /**
+     * Transforms "~1" to "/" and then "~0" to "~",
+     */
+    public static String decode(String s) {
+        if (s == null || s.length() == 0) {
+            return s;
+        }
+
+        return s.replace("~1", "/").replace("~0", "~");
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonProviderImpl.java b/src/main/java/org/apache/johnzon/core/JsonProviderImpl.java
new file mode 100644
index 0000000..afc1da2
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonProviderImpl.java
@@ -0,0 +1,384 @@
+/*
+ * 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.core;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Serializable;
+import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Map;
+
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonBuilderFactory;
+import javax.json.JsonMergePatch;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonPatch;
+import javax.json.JsonPatchBuilder;
+import javax.json.JsonPointer;
+import javax.json.JsonReader;
+import javax.json.JsonReaderFactory;
+import javax.json.JsonString;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import javax.json.JsonWriter;
+import javax.json.JsonWriterFactory;
+import javax.json.spi.JsonProvider;
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonGeneratorFactory;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParserFactory;
+
+@ProviderType
+public class JsonProviderImpl extends JsonProvider implements Serializable {
+    private static final JsonProvider DELEGATE = new JsonProviderDelegate();
+
+    @Override
+    public JsonParser createParser(final Reader reader) {
+        return DELEGATE.createParser(reader);
+    }
+
+    @Override
+    public JsonParser createParser(final InputStream inputStream) {
+        return DELEGATE.createParser(inputStream);
+    }
+
+    @Override
+    public JsonParserFactory createParserFactory(final Map<String, ?> stringMap) {
+        return DELEGATE.createParserFactory(stringMap);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(final Writer writer) {
+        return DELEGATE.createGenerator(writer);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(final OutputStream outputStream) {
+        return DELEGATE.createGenerator(outputStream);
+    }
+
+    @Override
+    public JsonGeneratorFactory createGeneratorFactory(final Map<String, ?> stringMap) {
+        return DELEGATE.createGeneratorFactory(stringMap);
+    }
+
+    @Override
+    public JsonReader createReader(final Reader reader) {
+        return DELEGATE.createReader(reader);
+    }
+
+    @Override
+    public JsonReader createReader(final InputStream inputStream) {
+        return DELEGATE.createReader(inputStream);
+    }
+
+    @Override
+    public JsonWriter createWriter(final Writer writer) {
+        return DELEGATE.createWriter(writer);
+    }
+
+    @Override
+    public JsonWriter createWriter(final OutputStream outputStream) {
+        return DELEGATE.createWriter(outputStream);
+    }
+
+    @Override
+    public JsonWriterFactory createWriterFactory(final Map<String, ?> stringMap) {
+        return DELEGATE.createWriterFactory(stringMap);
+    }
+
+    @Override
+    public JsonReaderFactory createReaderFactory(final Map<String, ?> stringMap) {
+        return DELEGATE.createReaderFactory(stringMap);
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder() {
+        return DELEGATE.createObjectBuilder();
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder() {
+        return DELEGATE.createArrayBuilder();
+    }
+
+    @Override
+    public JsonBuilderFactory createBuilderFactory(final Map<String, ?> stringMap) {
+        return DELEGATE.createBuilderFactory(stringMap);
+    }
+    
+    @Override
+    public JsonPatchBuilder createPatchBuilder() {
+        return DELEGATE.createPatchBuilder();
+    }
+
+    @Override
+    public JsonPatchBuilder createPatchBuilder(JsonArray initialData) {
+        return DELEGATE.createPatchBuilder(initialData);
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder(JsonObject jsonObject) {
+        return DELEGATE.createObjectBuilder(jsonObject);
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder(Map<String, Object> map) {
+        return DELEGATE.createObjectBuilder(map);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder(JsonArray initialData) {
+        return DELEGATE.createArrayBuilder(initialData);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder(Collection<?> initialData) {
+        return DELEGATE.createArrayBuilder(initialData);
+    }
+
+    @Override
+    public JsonPointer createPointer(String path) {
+        return DELEGATE.createPointer(path);
+    }
+
+    @Override
+    public JsonString createValue(String value) {
+        return DELEGATE.createValue(value);
+    }
+
+    @Override
+    public JsonNumber createValue(int value) {
+        return DELEGATE.createValue(value);
+    }
+
+    @Override
+    public JsonNumber createValue(long value) {
+        return DELEGATE.createValue(value);
+    }
+
+    @Override
+    public JsonNumber createValue(double value) {
+        return DELEGATE.createValue(value);
+    }
+
+    @Override
+    public JsonNumber createValue(BigDecimal value) {
+        return DELEGATE.createValue(value);
+    }
+
+    @Override
+    public JsonNumber createValue(BigInteger value) {
+        return DELEGATE.createValue(value);
+    }
+
+    @Override
+    public JsonPatch createPatch(JsonArray array) {
+        return DELEGATE.createPatch(array);
+    }
+
+    @Override
+    public JsonPatch createDiff(JsonStructure source, JsonStructure target) {
+        return DELEGATE.createDiff(source, target);
+    }
+
+    @Override
+    public JsonMergePatch createMergePatch(JsonValue patch) {
+        return DELEGATE.createMergePatch(patch);
+    }
+
+    @Override
+    public JsonMergePatch createMergeDiff(JsonValue source, JsonValue target) {
+        return DELEGATE.createMergeDiff(source, target);
+    }
+
+    static class JsonProviderDelegate extends JsonProvider {
+        private final JsonReaderFactory readerFactory = new JsonReaderFactoryImpl(null);
+        private final JsonParserFactory parserFactory = new JsonParserFactoryImpl(null);
+        private final JsonGeneratorFactory generatorFactory = new JsonGeneratorFactoryImpl(null);
+        private final JsonWriterFactory writerFactory = new JsonWriterFactoryImpl(null);
+        private final JsonBuilderFactoryImpl builderFactory = new JsonBuilderFactoryImpl(null);
+
+        @Override
+        public JsonParser createParser(final InputStream in) {
+            return parserFactory.createParser(in);
+        }
+
+        @Override
+        public JsonParser createParser(final Reader reader) {
+            return parserFactory.createParser(reader);
+        }
+
+        @Override
+        public JsonReader createReader(final InputStream in) {
+            return readerFactory.createReader(in);
+        }
+
+        @Override
+        public JsonReader createReader(final Reader reader) {
+            return readerFactory.createReader(reader);
+        }
+
+        @Override
+        public JsonParserFactory createParserFactory(final Map<String, ?> config) {
+            return (config == null || config.isEmpty()) ? parserFactory : new JsonParserFactoryImpl(config);
+        }
+
+        @Override
+        public JsonReaderFactory createReaderFactory(final Map<String, ?> config) {
+            return (config == null || config.isEmpty()) ? readerFactory : new JsonReaderFactoryImpl(config);
+        }
+
+        @Override
+        public JsonGenerator createGenerator(final Writer writer) {
+            return generatorFactory.createGenerator(writer);
+        }
+
+        @Override
+        public JsonGenerator createGenerator(final OutputStream out) {
+            return generatorFactory.createGenerator(out);
+        }
+
+        @Override
+        public JsonGeneratorFactory createGeneratorFactory(final Map<String, ?> config) {
+            return (config == null || config.isEmpty()) ? generatorFactory : new JsonGeneratorFactoryImpl(config);
+        }
+
+        @Override
+        public JsonWriter createWriter(final Writer writer) {
+            return writerFactory.createWriter(writer);
+        }
+
+        @Override
+        public JsonWriter createWriter(final OutputStream out) {
+            return writerFactory.createWriter(out);
+        }
+
+        @Override
+        public JsonWriterFactory createWriterFactory(final Map<String, ?> config) {
+            return (config == null || config.isEmpty()) ? writerFactory : new JsonWriterFactoryImpl(config);
+        }
+
+        @Override
+        public JsonObjectBuilder createObjectBuilder() {
+            return builderFactory.createObjectBuilder();
+        }
+
+        @Override
+        public JsonObjectBuilder createObjectBuilder(JsonObject jsonObject) {
+            return builderFactory.createObjectBuilder(jsonObject);
+        }
+
+        @Override
+        public JsonObjectBuilder createObjectBuilder(Map<String, Object> initialValues) {
+            return builderFactory.createObjectBuilder(initialValues);
+        }
+
+        @Override
+        public JsonArrayBuilder createArrayBuilder() {
+            return builderFactory.createArrayBuilder();
+        }
+
+        @Override
+        public JsonArrayBuilder createArrayBuilder(JsonArray initialData) {
+            return builderFactory.createArrayBuilder(initialData);
+        }
+
+        public JsonArrayBuilder createArrayBuilder(Collection<?> initialData) {
+            return builderFactory.createArrayBuilder(initialData);
+        }
+
+        @Override
+        public JsonString createValue(String value) {
+            return new JsonStringImpl(value);
+        }
+
+        @Override
+        public JsonNumber createValue(int value) {
+            return new JsonLongImpl(value);
+        }
+
+        @Override
+        public JsonNumber createValue(long value) {
+            return new JsonLongImpl(value);
+        }
+
+        @Override
+        public JsonNumber createValue(double value) {
+            return new JsonDoubleImpl(value);
+        }
+
+        @Override
+        public JsonNumber createValue(BigDecimal value) {
+            return new JsonNumberImpl(value);
+        }
+
+        @Override
+        public JsonNumber createValue(BigInteger value) {
+            return new JsonLongImpl(value.longValue());
+        }
+
+        @Override
+        public JsonBuilderFactory createBuilderFactory(final Map<String, ?> config) {
+            return (config == null || config.isEmpty()) ? builderFactory : new JsonBuilderFactoryImpl(config);
+        }
+
+        @Override
+        public JsonPatchBuilder createPatchBuilder() {
+            return new JsonPatchBuilderImpl();
+        }
+
+        @Override
+        public JsonPatchBuilder createPatchBuilder(JsonArray initialData) {
+            return new JsonPatchBuilderImpl(initialData);
+        }
+
+        @Override
+        public JsonPointer createPointer(String path) {
+            return new JsonPointerImpl(path);
+        }
+
+        public JsonPatch createPatch(JsonArray array) {
+            return createPatchBuilder(array).build();
+        }
+
+        @Override
+        public JsonPatch createDiff(JsonStructure source, JsonStructure target) {
+            return new JsonPatchDiff(source, target).calculateDiff();
+        }
+
+        public JsonMergePatch createMergePatch(JsonValue patch) {
+            return new JsonMergePatchImpl(patch);
+        }
+
+        @Override
+        public JsonMergePatch createMergeDiff(JsonValue source, JsonValue target) {
+            return new JsonMergePatchDiff(source, target).calculateDiff();
+        }
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonReaderFactoryImpl.java b/src/main/java/org/apache/johnzon/core/JsonReaderFactoryImpl.java
new file mode 100644
index 0000000..98484b1
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonReaderFactoryImpl.java
@@ -0,0 +1,63 @@
+/*
+ * 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.core;
+
+import static java.util.Arrays.asList;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import javax.json.JsonReader;
+import javax.json.JsonReaderFactory;
+
+class JsonReaderFactoryImpl extends AbstractJsonFactory implements JsonReaderFactory {
+    static final Collection<String> SUPPORTED_CONFIG_KEYS = asList(
+
+    );
+    private final JsonParserFactoryImpl parserFactory;
+
+    JsonReaderFactoryImpl(final Map<String, ?> config) {
+        super(config, SUPPORTED_CONFIG_KEYS, JsonParserFactoryImpl.SUPPORTED_CONFIG_KEYS);
+        this.parserFactory = new JsonParserFactoryImpl(internalConfig);
+    }
+
+    @Override
+    public JsonReader createReader(final Reader reader) {
+        return new JsonReaderImpl(parserFactory.createInternalParser(reader));
+    }
+
+    @Override
+    public JsonReader createReader(final InputStream in) {
+        return new JsonReaderImpl(parserFactory.createInternalParser(in));
+    }
+
+    @Override
+    public JsonReader createReader(final InputStream in, final Charset charset) {
+        return new JsonReaderImpl(parserFactory.createInternalParser(in, charset));
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return Collections.unmodifiableMap(internalConfig);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java b/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
new file mode 100644
index 0000000..e3af4af
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
@@ -0,0 +1,261 @@
+/*
+ * 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.core;
+
+
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonReader;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParsingException;
+
+public class JsonReaderImpl implements JsonReader {
+    private final JohnzonJsonParser parser;
+    private boolean closed = false;
+
+    public JsonReaderImpl(final JsonParser parser) {
+        if (parser instanceof JohnzonJsonParser) {
+            this.parser = (JohnzonJsonParser) parser;
+        } else {
+            this.parser = new JohnzonJsonParser.JohnzonJsonParserWrapper(parser);
+        }
+    }
+
+    @Override
+    public JsonStructure read() {
+        return JsonStructure.class.cast(readValue());
+    }
+
+    @Override
+    public JsonValue readValue() {
+        checkClosed();
+
+        if (!parser.hasNext()) {
+            throw new IllegalStateException("Nothing to read");
+        }
+        final JsonParser.Event next = parser.next();
+        switch (next) {
+            case START_OBJECT:
+                final JsonObjectBuilder objectBuilder = new JsonObjectBuilderImpl();
+                parseObject(objectBuilder);
+                if (parser.hasNext()) {
+                    throw new JsonParsingException("Expected end of file", parser.getLocation());
+                }
+                close();
+                return objectBuilder.build();
+            case START_ARRAY:
+                final JsonArrayBuilder arrayBuilder = new JsonArrayBuilderImpl();
+                parseArray(arrayBuilder);
+                if (parser.hasNext()) {
+                    throw new JsonParsingException("Expected end of file", parser.getLocation());
+                }
+                close();
+                return arrayBuilder.build();
+            case VALUE_STRING:
+                if (parser.hasNext()) {
+                    throw new JsonParsingException("Expected end of file", parser.getLocation());
+                }
+                final JsonStringImpl string = new JsonStringImpl(parser.getString());
+                close();
+                return string;
+            case VALUE_FALSE:
+                if (parser.hasNext()) {
+                    throw new JsonParsingException("Expected end of file", parser.getLocation());
+                }
+                close();
+                return JsonValue.FALSE;
+            case VALUE_TRUE:
+                if (parser.hasNext()) {
+                    throw new JsonParsingException("Expected end of file", parser.getLocation());
+                }
+                close();
+                return JsonValue.TRUE;
+            case VALUE_NULL:
+                if (parser.hasNext()) {
+                    throw new JsonParsingException("Expected end of file", parser.getLocation());
+                }
+                close();
+                return JsonValue.NULL;
+            case VALUE_NUMBER:
+                if (parser.hasNext()) {
+                    throw new JsonParsingException("Expected end of file", parser.getLocation());
+                }
+                final JsonNumber number = new JsonNumberImpl(parser.getBigDecimal());
+                close();
+                return number;
+            default:
+                close();
+                throw new JsonParsingException("Unknown structure: " + next, parser.getLocation());
+        }
+    }
+
+    @Override
+    public JsonObject readObject() {
+        final JsonStructure read = read();
+        checkType(JsonObject.class, read);
+        return JsonObject.class.cast(read);
+    }
+
+    @Override
+    public JsonArray readArray() {
+        final JsonStructure read = read();
+        checkType(JsonArray.class, read);
+        return JsonArray.class.cast(read);
+    }
+
+    private void checkType(final Class<?> expected, final JsonStructure read) {
+        if (!expected.isInstance(read)) {
+            throw new JsonParsingException("Expecting " + expected + " but got " + read, parser.getLocation());
+        }
+    }
+
+    @Override
+    public void close() {
+
+        if (!closed) {
+            closed = true;
+            parser.close();
+        }
+
+    }
+
+    private void parseObject(final JsonObjectBuilder builder) {
+        String key = null;
+        while (parser.hasNext()) {
+            final JsonParser.Event next = parser.next();
+            switch (next) {
+                case KEY_NAME:
+                    key = parser.getString();
+                    break;
+
+                case VALUE_STRING:
+                    builder.add(key, new JsonStringImpl(parser.getString()));
+                    break;
+
+                case START_OBJECT:
+                    JsonObjectBuilder subObject = null;
+                    parseObject(subObject = new JsonObjectBuilderImpl());
+                    builder.add(key, subObject);
+                    break;
+
+                case START_ARRAY:
+                    JsonArrayBuilder subArray = null;
+                    parseArray(subArray = new JsonArrayBuilderImpl());
+                    builder.add(key, subArray);
+                    break;
+
+                case VALUE_NUMBER:
+                    if (parser.isIntegralNumber() && parser.isNotTooLong()) {
+                        builder.add(key, new JsonLongImpl(parser.getLong()));
+                    } else {
+                        builder.add(key, new JsonNumberImpl(parser.getBigDecimal()));
+                    }
+                    break;
+
+                case VALUE_NULL:
+                    builder.addNull(key);
+                    break;
+
+                case VALUE_TRUE:
+                    builder.add(key, true);
+                    break;
+
+                case VALUE_FALSE:
+                    builder.add(key, false);
+                    break;
+
+                case END_OBJECT:
+                    return;
+
+                case END_ARRAY:
+                    throw new JsonParsingException("']', shouldn't occur", parser.getLocation());
+
+                default:
+                    throw new JsonParsingException(next.name() + ", shouldn't occur", parser.getLocation());
+            }
+        }
+    }
+
+    private void parseArray(final JsonArrayBuilder builder) {
+        while (parser.hasNext()) {
+            final JsonParser.Event next = parser.next();
+            switch (next) {
+                case VALUE_STRING:
+                    builder.add(new JsonStringImpl(parser.getString()));
+                    break;
+
+                case VALUE_NUMBER:
+                    if (parser.isIntegralNumber()) {
+                        builder.add(new JsonLongImpl(parser.getLong()));
+                    } else {
+                        builder.add(new JsonNumberImpl(parser.getBigDecimal()));
+                    }
+                    break;
+
+                case START_OBJECT:
+                    JsonObjectBuilder subObject = null;
+                    parseObject(subObject = new JsonObjectBuilderImpl());
+                    builder.add(subObject);
+                    break;
+
+                case START_ARRAY:
+                    JsonArrayBuilder subArray = null;
+                    parseArray(subArray = new JsonArrayBuilderImpl());
+                    builder.add(subArray);
+                    break;
+
+                case END_ARRAY:
+                    return;
+
+                case VALUE_NULL:
+                    builder.addNull();
+                    break;
+
+                case VALUE_TRUE:
+                    builder.add(true);
+                    break;
+
+                case VALUE_FALSE:
+                    builder.add(false);
+                    break;
+
+                case KEY_NAME:
+                    throw new JsonParsingException("array doesn't have keys", parser.getLocation());
+
+                case END_OBJECT:
+                    throw new JsonParsingException("'}', shouldn't occur", parser.getLocation());
+
+                default:
+                    throw new JsonParsingException(next.name() + ", shouldn't occur", parser.getLocation());
+            }
+        }
+    }
+
+    private void checkClosed() {
+        if (closed) {
+            throw new IllegalStateException("read(), readObject(), readArray() or close() method was already called");
+        }
+
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java b/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java
new file mode 100644
index 0000000..b8a0005
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java
@@ -0,0 +1,1071 @@
+/*
+ * 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.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+import javax.json.stream.JsonLocation;
+import javax.json.stream.JsonParsingException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.nio.charset.Charset;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.stream.Stream;
+
+//This class represents either the Json tokenizer and the Json parser.
+public class JsonStreamParserImpl implements JsonChars, JohnzonJsonParser {
+    private final boolean autoAdjust;
+
+    //the main buffer where the stream will be buffered
+    private final char[] buffer;
+
+    //current parser position within the buffer
+    //Initial MIN_VALUE will trigger buffer refill, normally bufferPos is >= -1
+    //-1 would cause a re-read of the first character in the buffer (which is at zero index)
+    private int bufferPos = Integer.MIN_VALUE;
+
+    //available character in the buffer. It might be <= "buffer.length".
+    private int availableCharsInBuffer;
+
+    //start and end position of values in the buffer
+    //may cross boundaries, then value is in fallBackCopyBuffer
+    private int startOfValueInBuffer = -1;
+    private int endOfValueInBuffer = -1;
+
+    private final Reader in;
+
+    //do we read from a character stream or a byte stream
+    //not used at the moment but maybe relevant in future to calculate the JsonLocation offset
+    @SuppressWarnings("unused")
+    private final BufferStrategy.BufferProvider<char[]> bufferProvider;
+    private final BufferStrategy.BufferProvider<char[]> valueProvider;
+
+    //max length for strings and numbers (max count of characters)
+    private final int maxValueLength;
+
+    //we use a byte here, because comparing bytes
+    //is more efficient than comparing enums
+    //Additionally we handle internally two more event: COMMA_EVENT and KEY_SEPARATOR_EVENT
+    private byte previousEvent;
+
+    //this buffer is used to store current String or Number value in case that
+    //within the value a buffer boundary is crossed or the string contains escaped characters
+    private char[] fallBackCopyBuffer;
+    private boolean releaseFallBackCopyBufferLength = true;
+    private int fallBackCopyBufferLength;
+
+    // location (line, column, offset)
+    // We try to calculate this efficiently so we do not just increment the values per char read
+    // Instead we calculate the column and offset relative to the pastBufferReadCount and/or lastLineBreakPosition.
+    private long currentLine = 1;
+    private long lastLineBreakPosition;
+    private long pastBufferReadCount;
+
+    //cache (if current value is a number) integral state and the number itself if its only one digit    
+    private boolean isCurrentNumberIntegral = true;
+    private int currentIntegralNumber = Integer.MIN_VALUE; //for number from 0 - 9
+
+    //maybe we want also cache BigDecimals
+    //private BigDecimal currentBigDecimalNumber = null;
+
+    //We need a stack if we want detect bad formatted Json do determine if we are within an array or not
+    //example
+    //     Streamparser sees: ],1  <-- we look from here
+    //the 1 is only allowed if we are within an array
+    //This can only be determined by build up a stack which tracks the trail of Json objects and arrays
+    //This stack here is only needed for validating the above mentioned case, if we want to be lenient we can skip suing the stack.
+    //Stack can cause out of memory issues when the nesting depth of a Json stream is too deep.
+    private StructureElement currentStructureElement = null;
+
+    //minimal stack implementation
+    private static final class StructureElement {
+        private final StructureElement previous;
+        private final boolean isArray;
+
+        StructureElement(final StructureElement previous, final boolean isArray) {
+            super();
+            this.previous = previous;
+            this.isArray = isArray;
+        }
+    }
+
+    //detect charset according to RFC 4627
+    public JsonStreamParserImpl(final InputStream inputStream, final int maxStringLength,
+                                final BufferStrategy.BufferProvider<char[]> bufferProvider, final BufferStrategy.BufferProvider<char[]> valueBuffer,
+                                final boolean autoAdjust) {
+
+        this(inputStream, null, null, maxStringLength, bufferProvider, valueBuffer, autoAdjust);
+    }
+
+    //use charset provided
+    public JsonStreamParserImpl(final InputStream inputStream, final Charset encoding, final int maxStringLength,
+                                final BufferStrategy.BufferProvider<char[]> bufferProvider, final BufferStrategy.BufferProvider<char[]> valueBuffer,
+                                final boolean autoAdjust) {
+
+        this(inputStream, null, encoding, maxStringLength, bufferProvider, valueBuffer, autoAdjust);
+    }
+
+    public JsonStreamParserImpl(final Reader reader, final int maxStringLength, final BufferStrategy.BufferProvider<char[]> bufferProvider,
+                                final BufferStrategy.BufferProvider<char[]> valueBuffer, final boolean autoAdjust) {
+
+        this(null, reader, null, maxStringLength, bufferProvider, valueBuffer, autoAdjust);
+    }
+
+    private JsonStreamParserImpl(final InputStream inputStream, final Reader reader, final Charset encoding, final int maxStringLength,
+                                 final BufferStrategy.BufferProvider<char[]> bufferProvider, final BufferStrategy.BufferProvider<char[]> valueBuffer,
+                                 final boolean autoAdjust) {
+
+        this.autoAdjust = autoAdjust;
+        this.maxValueLength = maxStringLength <= 0 ? 8192 : maxStringLength;
+        this.fallBackCopyBuffer = valueBuffer.newBuffer();
+        this.buffer = bufferProvider.newBuffer();
+        this.bufferProvider = bufferProvider;
+        this.valueProvider = valueBuffer;
+
+        if (fallBackCopyBuffer.length < maxStringLength) {
+            throw cust("Size of value buffer cannot be smaller than maximum string length");
+        }
+
+        if (reader != null) {
+            this.in = reader;
+        } else if (encoding == null) {
+            this.in = new RFC4627AwareInputStreamReader(inputStream);
+
+        } else {
+            this.in = new InputStreamReader(inputStream, encoding.newDecoder());
+        }
+    }
+
+    //append a single char to the value buffer
+    private void appendToCopyBuffer(final char c) {
+        fallBackCopyBuffer[fallBackCopyBufferLength++] = c;
+    }
+
+    //copy content between "start" and "end" from buffer to value buffer 
+    private void copyCurrentValue() {
+        final int length = endOfValueInBuffer - startOfValueInBuffer;
+        if (length > 0) {
+
+            if (length > maxValueLength) {
+                throw tmc();
+            }
+
+            if (fallBackCopyBufferLength >= fallBackCopyBuffer.length - length) { // not good at runtime but handled
+                if (!autoAdjust) {
+                    throw new ArrayIndexOutOfBoundsException("Buffer too small for such a long string");
+                }
+
+                final char[] newArray = new char[fallBackCopyBuffer.length + getBufferExtends(fallBackCopyBuffer.length)];
+                // TODO: log to adjust size once?
+                System.arraycopy(fallBackCopyBuffer, 0, newArray, 0, fallBackCopyBufferLength);
+                System.arraycopy(buffer, startOfValueInBuffer, newArray, fallBackCopyBufferLength, length);
+                if (releaseFallBackCopyBufferLength) {
+                    bufferProvider.release(fallBackCopyBuffer);
+                    releaseFallBackCopyBufferLength = false;
+                }
+                fallBackCopyBuffer = newArray;
+            } else {
+                System.arraycopy(buffer, startOfValueInBuffer, fallBackCopyBuffer, fallBackCopyBufferLength, length);
+            }
+            fallBackCopyBufferLength += length;
+        }
+
+        startOfValueInBuffer = endOfValueInBuffer = -1;
+    }
+
+    /**
+     * @return the amount of bytes the current buffer should get extended with
+     */
+    protected int getBufferExtends(int currentLength) {
+        return currentLength/4;
+    }
+
+
+    @Override
+    public final boolean hasNext() {
+
+        if (currentStructureElement != null ||
+                (previousEvent != END_ARRAY && previousEvent != END_OBJECT &&
+                        previousEvent != VALUE_STRING && previousEvent != VALUE_FALSE && previousEvent != VALUE_TRUE &&
+                        previousEvent != VALUE_NULL && previousEvent != VALUE_NUMBER) ||
+                previousEvent == 0) {
+
+            return true;
+        }
+
+        //detect garbage at the end of the file after last object or array is closed
+        if (bufferPos < availableCharsInBuffer) {
+
+            final char c = readNextNonWhitespaceChar(readNextChar());
+
+            if (c == EOF) {
+                return false;
+            }
+
+            if (bufferPos < availableCharsInBuffer) {
+                throw uexc("EOF expected");
+            }
+
+        }
+
+        return false;
+
+    }
+
+    private static boolean isAsciiDigit(final char value) {
+        return value <= NINE && value >= ZERO;
+    }
+
+    //check if value is a valid hex digit and return the numeric value
+    private int parseHexDigit(final char value) {
+
+        if (isAsciiDigit(value)) {
+            return value - 48;
+        } else if (value <= 'f' && value >= 'a') {
+            return (value) - 87;
+        } else if ((value <= 'F' && value >= 'A')) {
+            return (value) - 55;
+        } else {
+            throw uexc("Invalid hex character");
+        }
+    }
+
+    private JsonLocation createLocation() {
+
+        //we start with column = 1, so column is always >= 1
+        //APi is not clear in this, but starting column with 1 is convenient
+        long column = 1;
+        long charOffset = 0;
+
+        if (bufferPos >= -1) {
+
+            charOffset = pastBufferReadCount + bufferPos + 1;
+            column = lastLineBreakPosition == 0 ? charOffset + 1 : charOffset - lastLineBreakPosition;
+        }
+
+        //For now its unclear how to calculate offset for (byte) inputsream.
+        //API says count bytes but thats dependent on encoding and not efficient
+        //skip this for now, count always bytes and defer this until the JSR TCK arrives.
+
+        return new JsonLocationImpl(currentLine, column, charOffset);
+    }
+
+    //read the next char from the stream and set/increment the bufferPos
+    //will also refill buffer if necessary
+    //if we are currently processing a value (string or number) and buffer 
+    //refill is necessary copy the already read value part into the value buffer
+    protected final char readNextChar() {
+
+        if ((availableCharsInBuffer - bufferPos) <= 1) {
+            //fillbuffer
+
+            //copy content from old buffer to valuebuffer
+            //correct start end mark
+            if (startOfValueInBuffer > -1 && endOfValueInBuffer == -1) {
+                endOfValueInBuffer = availableCharsInBuffer;
+                copyCurrentValue();
+
+                startOfValueInBuffer = 0;
+            }
+
+            if (bufferPos >= -1) {
+                pastBufferReadCount += availableCharsInBuffer;
+            }
+
+            try {
+                availableCharsInBuffer = in.read(buffer, 0, buffer.length);
+                if (availableCharsInBuffer <= 0) {
+                    return EOF;
+                }
+
+            } catch (final IOException e) {
+                close();
+                throw uexio(e);
+            }
+
+            bufferPos = 0;
+            //end fillbuffer
+        } else {
+
+            //since JOHNZON-18 not longer necessary
+            //prevent "bufferoverflow
+            //if(bufferPos + 1 >= availableCharsInBuffer) {
+            //    return EOF;
+            //}
+
+            bufferPos++;
+        }
+
+        return buffer[bufferPos];
+    }
+
+    //skip whitespaces
+    //tracks location informations (line, column)
+    //returns the first non whitespace character
+    protected final char readNextNonWhitespaceChar(char c) {
+
+        int dosCount = 0;
+
+        while (c == SPACE || c == TAB || c == CR || c == EOL) {
+
+            if (c == EOL) {
+                currentLine++;
+                lastLineBreakPosition = pastBufferReadCount + bufferPos;
+            }
+
+            //prevent DOS (denial of service) attack
+            if (dosCount >= maxValueLength) {
+                throw tmc();
+            }
+            dosCount++;
+
+            //read next character
+            c = readNextChar();
+
+        }
+
+        return c;
+    }
+
+    @Override
+    public final Event next() {
+        //main entry, make decision how to handle the current character in the stream
+
+        if (!hasNext()) {
+            throw new NoSuchElementException();
+        }
+
+        if (previousEvent != 0 && currentStructureElement == null) {
+            throw uexc("Unexpected end of structure");
+        }
+
+        final char c = readNextNonWhitespaceChar(readNextChar());
+
+        if (c == COMMA_CHAR) {
+            //last event must one of the following-> " ] } LITERAL
+            if (previousEvent == KEY_SEPARATOR_EVENT || previousEvent == START_ARRAY
+                    || previousEvent == START_OBJECT || previousEvent == COMMA_EVENT
+                    || previousEvent == KEY_NAME) {
+                throw uexc("Expected \" ] } LITERAL");
+            }
+
+            previousEvent = COMMA_EVENT;
+            return next();
+
+        }
+
+        if (c == KEY_SEPARATOR) {
+
+            if (previousEvent != KEY_NAME) {
+                throw uexc("A : can only follow a key name");
+            }
+
+            previousEvent = KEY_SEPARATOR_EVENT;
+            return next();
+
+        }
+
+        if (!isCurrentNumberIntegral) {
+            isCurrentNumberIntegral = true;
+        }
+        //        if (currentBigDecimalNumber != null) {
+        //            currentBigDecimalNumber = null;
+        //        }
+        if (currentIntegralNumber != Integer.MIN_VALUE) {
+            currentIntegralNumber = Integer.MIN_VALUE;
+        }
+
+        if (fallBackCopyBufferLength != 0) {
+            fallBackCopyBufferLength = 0;
+        }
+
+        startOfValueInBuffer = endOfValueInBuffer = -1;
+
+        switch (c) {
+
+            case START_OBJECT_CHAR:
+
+                return handleStartObject();
+
+            case END_OBJECT_CHAR:
+
+                return handleEndObject();
+
+            case START_ARRAY_CHAR:
+
+                return handleStartArray();
+
+            case END_ARRAY_CHAR:
+
+                return handleEndArray();
+
+            case QUOTE_CHAR:
+
+                return handleQuote();
+
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            case MINUS:
+            case FALSE_F: // false
+            case TRUE_T: // true
+            case NULL_N: // null
+
+                return handleLiteral();
+
+            default:
+
+                return defaultHandling(c);
+        }
+    }
+
+    protected Event defaultHandling(char c) {
+        if (c == EOF) {
+            throw uexc("End of file hit too early");
+        }
+        throw uexc("Expected structural character or digit or 't' or 'n' or 'f' or '-'");
+    }
+
+    private Event handleStartObject() {
+
+        //last event must one of the following-> : , [
+        if (previousEvent != 0 && previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_ARRAY && previousEvent != COMMA_EVENT) {
+            throw uexc("Expected : , [");
+        }
+
+        //push upon the stack
+        if (currentStructureElement == null) {
+            currentStructureElement = new StructureElement(null, false);
+        } else {
+            if (!currentStructureElement.isArray && previousEvent != KEY_SEPARATOR_EVENT) {
+                throw uexc("Expected :");
+            }
+            final StructureElement localStructureElement = new StructureElement(currentStructureElement, false);
+            currentStructureElement = localStructureElement;
+        }
+
+        return EVT_MAP[previousEvent = START_OBJECT];
+
+    }
+
+    private Event handleEndObject() {
+
+        //last event must one of the following-> " ] { } LITERAL
+        if (previousEvent == START_ARRAY || previousEvent == COMMA_EVENT || previousEvent == KEY_NAME
+                || previousEvent == KEY_SEPARATOR_EVENT || currentStructureElement == null) {
+            throw uexc("Expected \" ] { } LITERAL");
+        }
+
+        if (currentStructureElement.isArray) {
+            throw uexc("Expected : ]");
+        }
+
+        //pop from stack
+        currentStructureElement = currentStructureElement.previous;
+
+        return EVT_MAP[previousEvent = END_OBJECT];
+    }
+
+    private Event handleStartArray() {
+
+        //last event must one of the following-> : , [
+        if (previousEvent != 0 && previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_ARRAY && previousEvent != COMMA_EVENT) {
+            throw uexc("Expected : , [");
+        }
+
+        //push upon the stack
+        if (currentStructureElement == null) {
+            currentStructureElement = new StructureElement(null, true);
+        } else {
+            if (!currentStructureElement.isArray && previousEvent != KEY_SEPARATOR_EVENT) {
+                throw uexc("Expected \"");
+            }
+            final StructureElement localStructureElement = new StructureElement(currentStructureElement, true);
+            currentStructureElement = localStructureElement;
+        }
+
+        return EVT_MAP[previousEvent = START_ARRAY];
+    }
+
+    private Event handleEndArray() {
+
+        //last event must one of the following-> [ ] } " LITERAL
+        if (previousEvent == START_OBJECT || previousEvent == COMMA_EVENT || previousEvent == KEY_SEPARATOR_EVENT
+                || currentStructureElement == null) {
+            throw uexc("Expected [ ] } \" LITERAL");
+        }
+
+        if (!currentStructureElement.isArray) {
+            throw uexc("Expected : }");
+        }
+
+        //pop from stack
+        currentStructureElement = currentStructureElement.previous;
+
+        return EVT_MAP[previousEvent = END_ARRAY];
+    }
+
+    //read a string, gets called recursively
+    //Handles escape/d characters
+    //if string contains escape chars and/or cross buffer boundary then copy in the value buffer
+    //if not then denote string start and end in startOfValueInBuffer and endOfValueInBuffer and read directly from buffer
+    private void readString() {
+
+        do {
+            char n = readNextChar();
+            //when first called n its first char after the starting quote
+            //after that its the next character after the while loop below
+
+            if (n == QUOTE_CHAR) {
+                endOfValueInBuffer = startOfValueInBuffer = bufferPos; //->"" case
+                return;
+            } else if (n == EOL) {
+                throw uexc("Unexpected linebreak");
+
+            } else if (/* n >= '\u0000' && */ n <= '\u001F') {
+                throw uexc("Unescaped control character");
+
+            } else if (n == ESCAPE_CHAR) {
+
+                n = readNextChar();
+
+                //  \ u XXXX -> unicode char
+                if (n == 'u') {
+                    n = parseUnicodeHexChars();
+                    appendToCopyBuffer(n);
+
+                    // \\ -> \
+                } else if (n == ESCAPE_CHAR) {
+                    appendToCopyBuffer(n);
+
+                    //another escape chars, for example \t
+                } else {
+                    appendToCopyBuffer(Strings.asEscapedChar(n));
+
+                }
+
+            } else {
+
+                startOfValueInBuffer = bufferPos;
+                endOfValueInBuffer = -1;
+
+                while ((n = readNextChar()) > '\u001F' && n != ESCAPE_CHAR && n != EOL && n != QUOTE_CHAR) {
+                    //read fast
+                }
+
+                endOfValueInBuffer = bufferPos;
+
+                if (n == QUOTE_CHAR) {
+
+                    if (fallBackCopyBufferLength > 0) {
+                        copyCurrentValue();
+                    } else {
+                        if ((endOfValueInBuffer - startOfValueInBuffer) > maxValueLength) {
+                            throw tmc();
+                        }
+
+                    }
+
+                    return;
+                } else if (n == EOL) {
+                    throw uexc("Unexpected linebreak");
+
+                } else if (n >= '\u0000' && n <= '\u001F') {
+                    throw uexc("Unescaped control character");
+                }
+
+                copyCurrentValue();
+
+                //current n is one of < '\u001F' -OR- ESCAPE_CHAR -OR- EOL -OR- QUOTE
+
+                bufferPos--; //unread one char
+
+            }
+        } while (true);
+
+        // before this do while(true) it was:
+        //
+        //recurse until string is terminated by a non escaped quote
+        //readString();
+        //
+        //
+        // but recursive = can't read big strings
+
+    }
+
+    //maybe we want to check invalid utf encoding
+    //not clear yet if the InputStreamReader is doing that
+
+    /*
+    private char checkSurrogates(char n, char highSurrogate) {
+        //check for invalid surrogates
+        //high followed by low       
+        if (Character.isHighSurrogate(n)) {
+
+            if (highSurrogate != 0) {
+                throw uexc("Unexpected high surrogate");
+            }
+            return n;
+        } else if (Character.isLowSurrogate(n)) {
+
+            if (highSurrogate == 0) {
+                throw uexc("Unexpected low surrogate");
+            } else if (!Character.isSurrogatePair(highSurrogate, n)) {
+                throw uexc("Invalid surrogate pair");
+            }
+            return 0;
+        } else if (highSurrogate != 0 && !Character.isLowSurrogate(n)) {
+            throw uexc("Expected low surrogate");
+        }
+        
+        return highSurrogate;
+    }*/
+
+    //read the next four chars, check them and treat them as an single unicode char
+    private char parseUnicodeHexChars() {
+        // \u08Ac etc       
+        return (char) (((parseHexDigit(readNextChar())) * 4096) + ((parseHexDigit(readNextChar())) * 256)
+                + ((parseHexDigit(readNextChar())) * 16) + ((parseHexDigit(readNextChar()))));
+
+    }
+
+    private Event handleQuote() {
+
+        //always the beginning quote of a key or value  
+
+        //last event must one of the following-> : { [ ,
+        if (previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_OBJECT && previousEvent != START_ARRAY
+                && previousEvent != COMMA_EVENT) {
+            throw uexc("Expected : { [ ,");
+        }
+        //starting quote already consumed
+        readString();
+        //end quote already consumed
+
+        //make the decision if its an key or value
+        if (previousEvent == KEY_SEPARATOR_EVENT) {
+            //must be value
+
+            if (currentStructureElement != null && currentStructureElement.isArray) {
+                //not in array, only allowed within array
+                throw uexc("Key value pair not allowed in an array");
+            }
+
+            return EVT_MAP[previousEvent = VALUE_STRING];
+
+        } else { //Event is  START_OBJECT  OR START_ARRAY OR COMMA_EVENT
+            //must be a key if we are in an object, if not its a value 
+
+            if ((currentStructureElement != null && currentStructureElement.isArray) || currentStructureElement == null) {
+                return EVT_MAP[previousEvent = VALUE_STRING];
+            }
+
+            return EVT_MAP[previousEvent = KEY_NAME];
+        }
+
+    }
+
+    //read a number
+    //if a number cross buffer boundary then copy in the value buffer
+    //if not then denote string start and end in startOfValueInBuffer and endOfValueInBuffer and read directly from buffer
+    private void readNumber() {
+
+        char c = buffer[bufferPos];
+
+        //start can change on any read() if we cross buffer boundary
+        startOfValueInBuffer = bufferPos;
+        endOfValueInBuffer = -1;
+
+        char y = EOF;
+
+        //sum up the digit values 
+        int cumulatedDigitValue = 0;
+        while (isAsciiDigit(y = readNextChar())) {
+
+            if (c == ZERO) {
+                throw uexc("Leading zeros not allowed");
+            }
+
+            if (c == MINUS && cumulatedDigitValue == 48) {
+                throw uexc("Leading zeros after minus not allowed");
+            }
+
+            cumulatedDigitValue += y;
+
+        }
+
+        if (c == MINUS && cumulatedDigitValue == 0) {
+
+            throw uexc("Unexpected premature end of number");
+        }
+
+        if (y == DOT) {
+            isCurrentNumberIntegral = false;
+            cumulatedDigitValue = 0;
+            while (isAsciiDigit(y = readNextChar())) {
+                cumulatedDigitValue++;
+            }
+
+            if (cumulatedDigitValue == 0) {
+
+                throw uexc("Unexpected premature end of number");
+            }
+
+        }
+
+        if (y == EXP_LOWERCASE || y == EXP_UPPERCASE) {
+            isCurrentNumberIntegral = false;
+
+            y = readNextChar(); //+ or - or digit
+
+            if (!isAsciiDigit(y) && y != MINUS && y != PLUS) {
+                throw uexc("Expected DIGIT or + or -");
+            }
+
+            if (y == MINUS || y == PLUS) {
+                y = readNextChar();
+                if (!isAsciiDigit(y)) {
+                    throw uexc("Unexpected premature end of number");
+                }
+
+            }
+
+            while (isAsciiDigit(y = readNextChar())) {
+                //no-op
+            }
+
+        }
+
+        endOfValueInBuffer = y == EOF && endOfValueInBuffer < 0 ? -1 : bufferPos;
+
+        if (y == COMMA_CHAR || y == END_ARRAY_CHAR || y == END_OBJECT_CHAR || y == EOL || y == SPACE || y == TAB || y == CR || y == EOF) {
+
+            bufferPos--;//unread one char
+
+            //['-', DIGIT]
+            if (isCurrentNumberIntegral && c == MINUS && cumulatedDigitValue >= 48 && cumulatedDigitValue <= 57) {
+
+                currentIntegralNumber = -(cumulatedDigitValue - 48); //optimize -0 till -9
+                return;
+            }
+
+            //[DIGIT]
+            if (isCurrentNumberIntegral && c != MINUS && cumulatedDigitValue == 0) {
+
+                currentIntegralNumber = (c - 48); //optimize 0 till 9
+                return;
+            }
+
+            if (fallBackCopyBufferLength > 0) {
+
+                //we crossed a buffer boundary, use value buffer
+                copyCurrentValue();
+
+            } else {
+                if ((endOfValueInBuffer - startOfValueInBuffer) >= maxValueLength) {
+                    throw tmc();
+                }
+            }
+
+            return;
+
+        }
+
+        throw uexc("Unexpected premature end of number");
+
+    }
+
+    //handles false, true, null and numbers
+    private Event handleLiteral() {
+
+        //last event must one of the following-> : , [
+        if (previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_ARRAY && previousEvent != COMMA_EVENT) {
+            throw uexc("Expected : , [");
+        }
+
+        if (previousEvent == COMMA_EVENT && !currentStructureElement.isArray) {
+            //only allowed within array
+            throw uexc("Not in an array context");
+        }
+
+        char c = buffer[bufferPos];
+
+        // probe literals
+        switch (c) {
+            case TRUE_T:
+
+                if (readNextChar() != TRUE_R || readNextChar() != TRUE_U || readNextChar() != TRUE_E) {
+                    throw uexc("Expected LITERAL: true");
+                }
+                return EVT_MAP[previousEvent = VALUE_TRUE];
+            case FALSE_F:
+
+                if (readNextChar() != FALSE_A || readNextChar() != FALSE_L || readNextChar() != FALSE_S || readNextChar() != FALSE_E) {
+                    throw uexc("Expected LITERAL: false");
+                }
+
+                return EVT_MAP[previousEvent = VALUE_FALSE];
+
+            case NULL_N:
+
+                if (readNextChar() != NULL_U || readNextChar() != NULL_L || readNextChar() != NULL_L) {
+                    throw uexc("Expected LITERAL: null");
+                }
+                return EVT_MAP[previousEvent = VALUE_NULL];
+
+            default:
+                readNumber();
+                return EVT_MAP[previousEvent = VALUE_NUMBER];
+        }
+
+    }
+
+    @Override
+    public String getString() {
+        if (previousEvent == KEY_NAME || previousEvent == VALUE_STRING || previousEvent == VALUE_NUMBER) {
+
+            //if there a content in the value buffer read from them, if not use main buffer
+            return fallBackCopyBufferLength > 0 ? new String(fallBackCopyBuffer, 0, fallBackCopyBufferLength) : new String(buffer,
+                    startOfValueInBuffer, endOfValueInBuffer - startOfValueInBuffer);
+        } else {
+            throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getString()");
+        }
+    }
+
+    @Override
+    public boolean isIntegralNumber() {
+
+        if (previousEvent != VALUE_NUMBER) {
+            throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support isIntegralNumber()");
+        } else {
+            return isCurrentNumberIntegral;
+        }
+    }
+
+    @Override
+    public boolean isNotTooLong() {
+        return (endOfValueInBuffer - startOfValueInBuffer) < 19;
+    }
+
+    @Override
+    public int getInt() {
+        if (previousEvent != VALUE_NUMBER) {
+            throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getInt()");
+        } else if (isCurrentNumberIntegral && currentIntegralNumber != Integer.MIN_VALUE) {
+            return currentIntegralNumber;
+        } else if (isCurrentNumberIntegral) {
+            //if there a content in the value buffer read from them, if not use main buffer
+            final Integer retVal = fallBackCopyBufferLength > 0 ? parseIntegerFromChars(fallBackCopyBuffer, 0, fallBackCopyBufferLength)
+                    : parseIntegerFromChars(buffer, startOfValueInBuffer, endOfValueInBuffer);
+            if (retVal == null) {
+                return getBigDecimal().intValue();
+            } else {
+                return retVal.intValue();
+            }
+        } else {
+            return getBigDecimal().intValue();
+        }
+    }
+
+    @Override
+    public long getLong() {
+        if (previousEvent != VALUE_NUMBER) {
+            throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getLong()");
+        } else if (isCurrentNumberIntegral && currentIntegralNumber != Integer.MIN_VALUE) {
+            return currentIntegralNumber;
+        } else if (isCurrentNumberIntegral) {
+            //if there a content in the value buffer read from them, if not use main buffer
+            final Long retVal = fallBackCopyBufferLength > 0 ? parseLongFromChars(fallBackCopyBuffer, 0, fallBackCopyBufferLength)
+                    : parseLongFromChars(buffer, startOfValueInBuffer, endOfValueInBuffer);
+            if (retVal == null) {
+                return getBigDecimal().longValue();
+            } else {
+                return retVal.longValue();
+            }
+        } else {
+            return getBigDecimal().longValue();
+        }
+
+    }
+
+    @Override
+    public BigDecimal getBigDecimal() {
+        if (previousEvent != VALUE_NUMBER) {
+            throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getBigDecimal()");
+            //        } else if (currentBigDecimalNumber != null) {
+            //            return currentBigDecimalNumber;
+        } else if (isCurrentNumberIntegral && currentIntegralNumber != Integer.MIN_VALUE) {
+            return new BigDecimal(currentIntegralNumber);
+        } else if (isCurrentNumberIntegral) {
+            //if there a content in the value buffer read from them, if not use main buffer
+            final Long retVal = fallBackCopyBufferLength > 0 ? parseLongFromChars(fallBackCopyBuffer, 0, fallBackCopyBufferLength)
+                    : parseLongFromChars(buffer, startOfValueInBuffer, endOfValueInBuffer);
+            if (retVal == null) {
+                return (/*currentBigDecimalNumber = */fallBackCopyBufferLength > 0 ? new BigDecimal(fallBackCopyBuffer, 0,
+                        fallBackCopyBufferLength) : new BigDecimal(buffer, startOfValueInBuffer,
+                        (endOfValueInBuffer - startOfValueInBuffer)));
+            } else {
+                return (/*currentBigDecimalNumber = */new BigDecimal(retVal.longValue()));
+            }
+        } else {
+            //if there a content in the value buffer read from them, if not use main buffer
+            return (/*currentBigDecimalNumber = */fallBackCopyBufferLength > 0 ? new BigDecimal(fallBackCopyBuffer, 0,
+                    fallBackCopyBufferLength) : new BigDecimal(buffer, startOfValueInBuffer, (endOfValueInBuffer - startOfValueInBuffer)));
+        }
+
+    }
+
+    @Override
+    public JsonLocation getLocation() {
+        return createLocation();
+    }
+
+    @Override
+    public void close() {
+        bufferProvider.release(buffer);
+        if (releaseFallBackCopyBufferLength) {
+            valueProvider.release(fallBackCopyBuffer);
+        }
+
+        try {
+            in.close();
+        } catch (final IOException e) {
+            throw new JsonException("Unexpected IO exception " + e.getMessage(), e);
+        }
+    }
+
+    //parse a char[] to long while checking overflow
+    //if overflowed return null
+    //no additional checks since we are sure here that there are no non digits in the array
+    private static Long parseLongFromChars(final char[] chars, final int start, final int end) {
+
+        long retVal = 0;
+        final boolean negative = chars[start] == MINUS;
+        for (int i = negative ? start + 1 : start; i < end; i++) {
+            final long tmp = retVal * 10 + (chars[i] - ZERO);
+            if (tmp < retVal) { //check overflow
+                return null;
+            } else {
+                retVal = tmp;
+            }
+        }
+
+        return negative ? -retVal : retVal;
+    }
+
+    //parse a char[] to int while checking overflow
+    //if overflowed return null
+    //no additional checks since we are sure here that there are no non digits in the array
+    private static Integer parseIntegerFromChars(final char[] chars, final int start, final int end) {
+
+        int retVal = 0;
+        final boolean negative = chars[start] == MINUS;
+        for (int i = negative ? start + 1 : start; i < end; i++) {
+            final int tmp = retVal * 10 + (chars[i] - ZERO);
+            if (tmp < retVal) { //check overflow
+                return null;
+            } else {
+                retVal = tmp;
+            }
+        }
+
+        return negative ? -retVal : retVal;
+    }
+
+    @Override
+    public JsonObject getObject() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public JsonValue getValue() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public JsonArray getArray() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Stream<JsonValue> getArrayStream() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Stream<Map.Entry<String, JsonValue>> getObjectStream() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Stream<JsonValue> getValueStream() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void skipArray() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void skipObject() {
+        throw new UnsupportedOperationException();
+    }
+
+    private JsonParsingException uexc(final char c, final String message) {
+        final JsonLocation location = createLocation();
+        return new JsonParsingException("Unexpected character '" + c + "' (Codepoint: " + String.valueOf(c).codePointAt(0) + ") on "
+                + location + ". Reason is [[" + message + "]]", location);
+    }
+
+    private JsonParsingException uexc(final String message) {
+        final char c = bufferPos < 0 ? 0 : buffer[bufferPos];
+        return uexc(c, message);
+    }
+
+    private JsonParsingException tmc() {
+        final JsonLocation location = createLocation();
+        return new JsonParsingException("Too many characters. Maximum string/number length of " + maxValueLength + " exceeded on "
+                + location + ". Maybe increase org.apache.johnzon.max-string-length in jsonp factory properties or system properties.", location);
+    }
+
+    private JsonParsingException uexio(final IOException e) {
+        final JsonLocation location = createLocation();
+        return new JsonParsingException("Unexpected IO exception on " + location, e, location);
+    }
+
+    private JsonParsingException cust(final String message) {
+        final JsonLocation location = createLocation();
+        return new JsonParsingException("General exception on " + location + ". Reason is [[" + message + "]]", location);
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/johnzon/core/JsonStringImpl.java b/src/main/java/org/apache/johnzon/core/JsonStringImpl.java
new file mode 100644
index 0000000..43b62fa
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonStringImpl.java
@@ -0,0 +1,89 @@
+/*
+ * 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.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import java.io.Serializable;
+
+final class JsonStringImpl implements JsonString, Serializable {
+    private final String value;
+    private String escape;
+    private transient Integer hashCode = null;
+
+
+    JsonStringImpl(final String value) {
+        if(value == null) {
+            throw new NullPointerException("value must not be null");
+        }
+
+        this.value = value;
+    }
+
+    @Override
+    public String getString() {
+        return value;
+    }
+
+    @Override
+    public CharSequence getChars() {
+        return value;
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.STRING;
+    }
+
+    @Override
+    public String toString() {
+        String s = escape;
+        if (s == null) {
+            s =  JsonChars.QUOTE_CHAR+Strings.escape(value)+JsonChars.QUOTE_CHAR;
+            escape=s;
+        }
+        return s;
+    }
+
+    @Override
+    public int hashCode() {
+        Integer h = hashCode;
+        if (h == null) {
+            h = value.hashCode();
+            hashCode=h;
+        }
+        return h;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return JsonString.class.isInstance(obj) && JsonString.class.cast(obj).getString().equals(value);
+    }
+
+    @Override
+    public JsonObject asJsonObject() {
+        return JsonObject.class.cast(this);
+    }
+
+    @Override
+    public JsonArray asJsonArray() {
+        return JsonArray.class.cast(this);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonWriterFactoryImpl.java b/src/main/java/org/apache/johnzon/core/JsonWriterFactoryImpl.java
new file mode 100644
index 0000000..84f3df2
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonWriterFactoryImpl.java
@@ -0,0 +1,66 @@
+/*
+ * 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.core;
+
+import static java.util.Arrays.asList;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import javax.json.JsonWriter;
+import javax.json.JsonWriterFactory;
+import javax.json.stream.JsonGeneratorFactory;
+
+class JsonWriterFactoryImpl extends AbstractJsonFactory implements JsonWriterFactory{
+    private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
+    static final Collection<String> SUPPORTED_CONFIG_KEYS = asList(
+
+    );
+    private final JsonGeneratorFactory factory;
+
+    JsonWriterFactoryImpl(final Map<String, ?> config) {
+        super(config, SUPPORTED_CONFIG_KEYS, JsonGeneratorFactoryImpl.SUPPORTED_CONFIG_KEYS);
+        this.factory = new JsonGeneratorFactoryImpl(internalConfig);
+    }
+
+    @Override
+    public JsonWriter createWriter(final Writer writer) {
+        return new JsonWriterImpl(factory.createGenerator(writer));
+    }
+
+    @Override
+    public JsonWriter createWriter(final OutputStream out) {
+        return createWriter(new OutputStreamWriter(out, UTF8_CHARSET));
+    }
+
+    @Override
+    public JsonWriter createWriter(final OutputStream out, final Charset charset) {
+        return createWriter(new OutputStreamWriter(out, charset));
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return Collections.unmodifiableMap(internalConfig);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonWriterImpl.java b/src/main/java/org/apache/johnzon/core/JsonWriterImpl.java
new file mode 100644
index 0000000..2577f1f
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonWriterImpl.java
@@ -0,0 +1,92 @@
+/*
+ * 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.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import javax.json.JsonWriter;
+import javax.json.stream.JsonGenerator;
+import java.io.Serializable;
+
+class JsonWriterImpl implements JsonWriter, Serializable {
+    private final JsonGenerator generator;
+    private boolean closed = false;
+
+    JsonWriterImpl(final JsonGenerator generator) {
+        this.generator = generator;
+    }
+
+    @Override
+    public void writeArray(final JsonArray array) {
+        checkClosed();
+        try {
+            generator.write(array);
+        } finally {
+            close();
+        }
+    }
+
+    @Override
+    public void writeObject(final JsonObject object) {
+        checkClosed();
+        try {
+            generator.write(object);
+        } finally {
+            close();
+        }
+    }
+
+    @Override
+    public void write(final JsonValue value) {
+        checkClosed();
+        try {
+            generator.write(value);
+        } finally {
+            close();
+        }
+    }
+
+    @Override
+    public void write(final JsonStructure value) {
+        checkClosed();
+        try {
+            generator.write(value);
+        } finally {
+            close();
+        }
+    }
+
+    @Override
+    public void close() {
+
+        if (!closed) {
+            closed = true;
+            generator.close();
+        }
+    }
+
+    private void checkClosed() {
+        if (closed) {
+            throw new IllegalStateException("writeArray(), writeObject(), write() or close() method was already called");
+        }
+
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/RFC4627AwareInputStreamReader.java b/src/main/java/org/apache/johnzon/core/RFC4627AwareInputStreamReader.java
new file mode 100644
index 0000000..645cf0e
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/RFC4627AwareInputStreamReader.java
@@ -0,0 +1,141 @@
+/*
+ * 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.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PushbackInputStream;
+import java.nio.charset.Charset;
+
+import javax.json.JsonException;
+
+final class RFC4627AwareInputStreamReader extends InputStreamReader {
+
+    RFC4627AwareInputStreamReader(final InputStream in) {
+        this(new PushbackInputStream(in,4));
+    }
+    
+    private RFC4627AwareInputStreamReader(final PushbackInputStream in) {
+        super(in, getCharset(in).newDecoder());
+       
+    }
+
+    /**
+     * According to the Java API "An attempt is made to read as many as len bytes, but a smaller number may be read".
+     * [http://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#read(byte[],%20int,%20int)]
+     * For this reason we need to ensure that we've read all the bytes that we need out of this stream.
+     */
+    private static byte[] readAllBytes(final PushbackInputStream inputStream) throws IOException {
+        final int first = inputStream.read();
+        final int second = inputStream.read();
+        if(first == -1|| second == -1) {
+            throw new JsonException("Invalid Json. Valid Json has at least 2 bytes");
+        }
+        final int third = inputStream.read();
+        final int fourth = inputStream.read();
+        if(third == -1) {
+            return new byte[] { (byte) first, (byte) second };
+        } else if(fourth == -1) {
+            return new byte[] { (byte) first, (byte) second, (byte) third };
+        } else {
+            return new byte[] { (byte) first, (byte) second, (byte) third, (byte) fourth };
+        }
+    }
+
+    /*
+        * RFC 4627
+
+          JSON text SHALL be encoded in Unicode.  The default encoding is
+          UTF-8.
+       
+          Since the first two characters of a JSON text will always be ASCII
+          characters [RFC0020], it is possible to determine whether an octet
+          stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
+          at the pattern of nulls in the first four octets.
+
+          00 00 00 xx  UTF-32BE
+          00 xx 00 xx  UTF-16BE
+          xx 00 00 00  UTF-32LE
+          xx 00 xx 00  UTF-16LE
+          xx xx xx xx  UTF-8
+
+        */
+
+    private static Charset getCharset(final PushbackInputStream inputStream) {
+        Charset charset = Charset.forName("UTF-8");
+        int bomLength=0;
+        try {
+            final byte[] utfBytes = readAllBytes(inputStream);
+            int first = (utfBytes[0] & 0xFF);
+            int second = (utfBytes[1] & 0xFF);
+            if (first == 0x00) {
+                charset = (second == 0x00) ? Charset.forName("UTF-32BE") : Charset.forName("UTF-16BE");
+            } else if (utfBytes.length > 2 && second == 0x00) {
+                int third = (utfBytes[2] & 0xFF);
+                charset = (third  == 0x00) ? Charset.forName("UTF-32LE") : Charset.forName("UTF-16LE");
+            } else {
+
+                    /*check BOM
+
+                    Encoding       hex byte order mark
+                    UTF-8          EF BB BF
+                    UTF-16 (BE)    FE FF
+                    UTF-16 (LE)    FF FE
+                    UTF-32 (BE)    00 00 FE FF
+                    UTF-32 (LE)    FF FE 00 00
+                    */
+
+                //We do not check for UTF-32BE because that is already covered above and we
+                //do not to unread anything.
+
+                if(first == 0xFE && second == 0xFF) {
+                    charset = Charset.forName("UTF-16BE");
+                    bomLength=2;
+                } else if(first == 0xFF && second == 0xFE) {
+                    if(utfBytes.length > 3 && (utfBytes[2]&0xff) == 0x00 && (utfBytes[3]&0xff) == 0x00) {
+                        charset = Charset.forName("UTF-32LE");
+                        bomLength=4;
+                    }else {
+                        charset = Charset.forName("UTF-16LE");
+                        bomLength=2;
+                    }
+                } else if (utfBytes.length > 2 && first == 0xEF && second == 0xBB && (utfBytes[2]&0xff) == 0xBF) {
+                    //UTF-8 with BOM
+                    bomLength=3;
+                }
+            }
+            //assume UTF8
+            if(bomLength > 0 && bomLength < 4) {             
+                //do not unread BOM, only bytes after BOM        
+                inputStream.unread(utfBytes,bomLength,utfBytes.length - bomLength);
+            } else {             
+                //no BOM, unread all read bytes
+                inputStream.unread(utfBytes);
+            }
+          
+
+        } catch (final IOException e) {
+            throw new JsonException("Unable to detect charset due to "+e.getMessage(), e);
+        }
+
+        return charset;
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/SerializableValue.java b/src/main/java/org/apache/johnzon/core/SerializableValue.java
new file mode 100644
index 0000000..76b3ec6
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/SerializableValue.java
@@ -0,0 +1,56 @@
+/*
+ * 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.core;
+
+import javax.json.JsonReader;
+import javax.json.JsonReaderFactory;
+import javax.json.spi.JsonProvider;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.io.StringReader;
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class SerializableValue implements Serializable {
+    private static final AtomicReference<JsonReaderFactory> FACTORY_ATOMIC_REFERENCE = new AtomicReference<JsonReaderFactory>();
+
+    private final String value;
+
+    SerializableValue(final String value) {
+        this.value = value;
+    }
+
+    private Object readResolve() throws ObjectStreamException {
+        final JsonReader parser = factory().createReader(new StringReader(value));
+        try {
+            return parser.read();
+        } finally {
+            parser.close();
+        }
+    }
+
+    private static JsonReaderFactory factory() { // avoid to create too much instances of provider or factories, not needed
+        JsonReaderFactory factory = FACTORY_ATOMIC_REFERENCE.get();
+        if (factory == null) {
+            FACTORY_ATOMIC_REFERENCE.compareAndSet(null, JsonProvider.provider().createReaderFactory(Collections.<String, Object>emptyMap()));
+            factory = FACTORY_ATOMIC_REFERENCE.get();
+        }
+        return factory;
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/SimpleStack.java b/src/main/java/org/apache/johnzon/core/SimpleStack.java
new file mode 100644
index 0000000..74bd14f
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/SimpleStack.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.core;
+
+class SimpleStack<T> {
+
+    private Element<T> head;
+
+    void push(final T element) {
+
+        final Element<T> tmp = new Element<T>();
+        tmp.payload = element;
+        tmp.previous = head;
+        head = tmp;
+
+    }
+
+    T pop() {
+
+        final T tmp = head.payload;
+        head = head.previous;
+        return tmp;
+
+    }
+
+    T peek() {
+        return head.payload;
+    }
+
+    boolean isEmpty() {
+        return head == null;
+    }
+
+    private static class Element<T> {
+
+        public Element() {
+
+        }
+
+        private Element<T> previous;
+        private T payload;
+
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/Strings.java b/src/main/java/org/apache/johnzon/core/Strings.java
new file mode 100644
index 0000000..412f6ef
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/Strings.java
@@ -0,0 +1,127 @@
+/*
+ * 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.core;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.json.stream.JsonParsingException;
+
+class Strings implements JsonChars {
+    private static final BufferStrategy.BufferProvider<StringBuilder> BUILDER_CACHE =
+        BufferStrategy.valueOf(System.getProperty("johnzon.string-builder.strategy", "QUEUE"))
+            .newStringBuilderProvider(Integer.getInteger("org.apache.johnzon.default-string-builder", 1024));
+
+    private static final String UNICODE_PREFIX = "\\u";
+    private static final String UNICODE_PREFIX_HELPER = "000";
+    private static final ConcurrentMap<Character, String> UNICODE_CACHE = new ConcurrentHashMap<Character, String>();
+
+    static char asEscapedChar(final char current) {
+        switch (current) {
+            case 'r':
+                return '\r';
+            case 't':
+                return '\t';
+            case 'b':
+                return '\b';
+            case 'f':
+                return '\f';
+            case 'n':
+                return '\n';
+            case '"':
+                return '\"';
+            case '\\':
+                return '\\';  
+            case '/':
+                return '/';  
+            default:
+                if(Character.isHighSurrogate(current) || Character.isLowSurrogate(current)) {
+                    return current;
+                }
+                throw new JsonParsingException("Invalid escape sequence '"+current +"' (Codepoint: "+String.valueOf(current).
+                        codePointAt(0),JsonLocationImpl.UNKNOWN_LOCATION);
+        }
+
+    }
+
+    static String escape(final String value) {
+        
+        if(value == null || value.length()==0) {
+            return value;
+        }
+        
+        final StringBuilder builder = BUILDER_CACHE.newBuffer();
+        try {
+            for (int i = 0; i < value.length(); i++) {
+                final char c = value.charAt(i);
+                switch (c) {
+                    case QUOTE_CHAR:
+                    case ESCAPE_CHAR:
+                        builder.append(ESCAPE_CHAR).append(c);
+                        break;
+                    default:
+                        if (c < SPACE) { // we could do a single switch but actually we should rarely enter this if so no need to pay it
+                            switch (c) {
+                                case EOL:
+                                    builder.append("\\n");
+                                    break;
+                                case '\r':
+                                    builder.append("\\r");
+                                    break;
+                                case '\t':
+                                    builder.append("\\t");
+                                    break;
+                                case '\b':
+                                    builder.append("\\b");
+                                    break;
+                                case '\f':
+                                    builder.append("\\f");
+                                    break;
+                                default:
+                                    builder.append(toUnicode(c));
+                            }
+                        } else if ((c >= '\u0080' && c < '\u00a0') || (c >= '\u2000' && c < '\u2100')) {
+                            builder.append(toUnicode(c));
+                        } else {
+                            builder.append(c);
+                        }
+                }
+            }
+            return builder.toString();
+        } finally {
+            BUILDER_CACHE.release(builder);
+        }
+    }
+
+    private static String toUnicode(final char c) {
+        final String found = UNICODE_CACHE.get(c);
+        if (found != null) {
+            return found;
+        }
+
+        final String hex = UNICODE_PREFIX_HELPER + Integer.toHexString(c);
+        final String s = UNICODE_PREFIX + hex.substring(hex.length() - 4);
+        UNICODE_CACHE.putIfAbsent(c, s);
+        return s;
+    }
+
+    private Strings() {
+        // no-op
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/ThreadLocalBufferCache.java b/src/main/java/org/apache/johnzon/core/ThreadLocalBufferCache.java
new file mode 100644
index 0000000..f973fcd
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/ThreadLocalBufferCache.java
@@ -0,0 +1,53 @@
+/*
+ * 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.core;
+
+import java.io.Serializable;
+
+public abstract class ThreadLocalBufferCache<T> implements Serializable {
+    // alloc are expensive so using a very trivial buffer, we remove from TL to say we use it and reset it when we are done (close)
+    private final ThreadLocal<T> buffers;
+    private final int defaultSize;
+
+    public ThreadLocalBufferCache(final int defaultSize) {
+        this.buffers = new ThreadLocal<T>() {
+            @Override
+            protected T initialValue() {
+                return ThreadLocalBufferCache.this.newValue(defaultSize);
+            }
+        };
+        this.defaultSize = defaultSize;
+    }
+
+    protected abstract T newValue(final int defaultSize);
+
+    public T getCache() {
+        final T cachedLoadedChars = buffers.get();
+        if (cachedLoadedChars == null) {
+            return newValue(defaultSize);
+        }
+        buffers.set(null); // remove just kills the buffers
+        return cachedLoadedChars;
+    }
+
+
+    public void release(final T buffer) {
+        buffers.set(buffer);
+    }
+}

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.