You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@johnzon.apache.org by rm...@apache.org on 2019/10/01 17:04:07 UTC

[johnzon] branch master updated: JOHNZON-285 enable to support a fastpath for a reader implementing Supplier and a writer implementing a Consumer

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

rmannibucau pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/johnzon.git


The following commit(s) were added to refs/heads/master by this push:
     new d721074  JOHNZON-285 enable to support a fastpath for a reader implementing Supplier<JsonObject> and a writer implementing a Consumer<JsonValue>
d721074 is described below

commit d721074adc406c2e5e3112e939379c5989f247a3
Author: Romain Manni-Bucau <rm...@apache.org>
AuthorDate: Tue Oct 1 19:03:45 2019 +0200

    JOHNZON-285 enable to support a fastpath for a reader implementing Supplier<JsonObject> and a writer implementing a Consumer<JsonValue>
---
 .../org/apache/johnzon/jsonb/JohnzonJsonb.java     | 69 ++++++++++++++++++----
 .../johnzon/jsonb/extension/JsonValueReader.java   | 26 ++++++--
 .../johnzon/jsonb/extension/JsonValueWriter.java   | 27 +++++++--
 .../java/org/apache/johnzon/mapper/Mapper.java     | 13 +++-
 src/site/markdown/index.md                         |  2 +-
 5 files changed, 113 insertions(+), 24 deletions(-)

diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java
index 7eae65e..ba2d8b2 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java
@@ -20,8 +20,6 @@ package org.apache.johnzon.jsonb;
 
 import org.apache.johnzon.mapper.util.ArrayUtil;
 import org.apache.johnzon.jsonb.api.experimental.JsonbExtension;
-import org.apache.johnzon.jsonb.extension.JsonValueReader;
-import org.apache.johnzon.jsonb.extension.JsonValueWriter;
 import org.apache.johnzon.mapper.JsonObjectGenerator;
 import org.apache.johnzon.mapper.Mapper;
 import org.apache.johnzon.mapper.MapperException;
@@ -43,17 +41,21 @@ import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.math.BigInteger;
 import java.util.Collection;
+import java.util.Map;
 import java.util.Optional;
 import java.util.OptionalDouble;
 import java.util.OptionalInt;
 import java.util.OptionalLong;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
 
-// TODO: Optional handling for lists (and arrays)?
 public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension {
     private final Mapper delegate;
     private final boolean ijson;
     private final Consumer<JohnzonJsonb> onClose;
+    private final Map<Class<?>, Boolean> structureAwareIo = new ConcurrentHashMap<>();
 
     public JohnzonJsonb(final Mapper build, final boolean ijson, final Consumer<JohnzonJsonb> onClose) {
         this.delegate = build;
@@ -143,18 +145,28 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension {
 
     @Override
     public <T> T fromJson(final Reader reader, final Class<T> type) throws JsonbException {
-        if (JsonValueReader.class.isInstance(reader)) {
-            return delegate.readObject(JsonValueReader.class.cast(reader).getInput(), type);
-        }
-
+        final boolean valueProvider = isValueProvider(reader);
         try {
             if (isArray(type)) {
+                if (valueProvider) {
+                    return delegate.readObject(((Supplier<JsonValue>) reader).get(), type);
+                }
                 return delegate.readTypedArray(reader, type.getComponentType(), type);
             } else if (JsonArray.class == type) {
+                if (valueProvider) {
+                    return delegate.readObject(((Supplier<JsonValue>) reader).get(), type);
+                }
                 return (T) delegate.readJsonArray(reader);
             } else if (Collection.class.isAssignableFrom(type)) {
+                if (valueProvider) {
+                    return delegate.readObject(((Supplier<JsonValue>) reader).get(), type);
+                }
                 return (T) delegate.readCollection(reader, new JohnzonParameterizedType(type, Object.class));
             }
+            if (valueProvider) {
+                return delegate.readObject(((Supplier<JsonValue>) reader).get(), type);
+            }
+
             final Type mappingType = unwrapPrimitiveOptional(type);
             final Object object = delegate.readObject(reader, mappingType);
             if (mappingType != type) {
@@ -168,8 +180,8 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension {
 
     @Override
     public <T> T fromJson(final Reader reader, final Type runtimeType) throws JsonbException {
-        if (JsonValueReader.class.isInstance(reader)) {
-            return delegate.readObject(JsonValueReader.class.cast(reader).getInput(), runtimeType);
+        if (isValueProvider(reader)) {
+            return delegate.readObject(((Supplier<JsonStructure>) reader).get(), runtimeType);
         }
 
         try {
@@ -323,8 +335,8 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension {
 
     @Override
     public void toJson(final Object inObject, final Writer writer) throws JsonbException {
-        if (JsonValueWriter.class.isInstance(writer)) {
-            JsonValueWriter.class.cast(writer).setResult(delegate.toStructure(inObject));
+        if (isValueConsumer(writer)) {
+            Consumer.class.cast(writer).accept(delegate.toStructure(inObject));
             return;
         }
 
@@ -342,8 +354,8 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension {
 
     @Override
     public void toJson(final Object inObject, final Type runtimeType, final Writer writer) throws JsonbException {
-        if (JsonValueWriter.class.isInstance(writer)) {
-            JsonValueWriter.class.cast(writer).setResult(delegate.toStructure(inObject));
+        if (isValueConsumer(writer)) {
+            Consumer.class.cast(writer).accept(delegate.toStructure(inObject));
             return;
         }
 
@@ -520,4 +532,35 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension {
             return jsonObjectGenerator.getResult();
         }
     }
+
+    private boolean isValueProvider(final Reader reader) {
+        final Class<? extends Reader> key = reader.getClass();
+        Boolean exists = structureAwareIo.get(key);
+        if (exists == null) {
+            exists = matchesType(key, Supplier.class);
+            structureAwareIo.putIfAbsent(key, exists);
+        }
+        return exists;
+    }
+
+    private boolean isValueConsumer(final Writer writer) {
+        final Class<? extends Writer> key = writer.getClass();
+        Boolean exists = structureAwareIo.get(key);
+        if (exists == null) {
+            exists = matchesType(writer.getClass(), Consumer.class);
+            structureAwareIo.putIfAbsent(key, exists);
+        }
+        return exists;
+    }
+
+    private boolean matchesType(final Class<?> type, final Class<?> rawType) {
+        return rawType.isAssignableFrom(type) &&
+                Stream.of(type.getGenericInterfaces())
+                        .filter(ParameterizedType.class::isInstance)
+                        .map(ParameterizedType.class::cast)
+                        .anyMatch(pt -> pt.getRawType() == rawType &&
+                                pt.getActualTypeArguments().length == 1 &&
+                                Class.class.isInstance(pt.getActualTypeArguments()[0]) &&
+                                JsonValue.class.isAssignableFrom(Class.class.cast(pt.getActualTypeArguments()[0])));
+    }
 }
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueReader.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueReader.java
index 222a6e0..c4899d3 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueReader.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueReader.java
@@ -18,12 +18,17 @@
  */
 package org.apache.johnzon.jsonb.extension;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.util.function.Supplier;
 
 import javax.json.JsonStructure;
 
-public class JsonValueReader<T> extends Reader {
+public class JsonValueReader<T> extends Reader implements Supplier<JsonStructure> {
     private final JsonStructure input;
+    private ByteArrayInputStream fallbackDelegate;
     private T result;
 
     public JsonValueReader(final JsonStructure input) {
@@ -32,22 +37,35 @@ public class JsonValueReader<T> extends Reader {
 
     @Override
     public int read(final char[] cbuf, final int off, final int len) {
-        throw new UnsupportedOperationException();
+        if (fallbackDelegate == null) {
+            fallbackDelegate = new ByteArrayInputStream(input.toString().getBytes(StandardCharsets.UTF_8));
+        }
+        return fallbackDelegate.read();
     }
 
     @Override
-    public void close() {
-        // no-op
+    public void close() throws IOException {
+        if (fallbackDelegate != null) {
+            fallbackDelegate.close();
+        }
     }
 
+    @Override
+    public JsonStructure get() {
+        return input;
+    }
+
+    @Deprecated
     public JsonStructure getInput() {
         return input;
     }
 
+    @Deprecated
     public T getResult() {
         return result;
     }
 
+    @Deprecated
     public void setResult(final T result) {
         this.result = result;
     }
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueWriter.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueWriter.java
index a861658..fbb9404 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueWriter.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueWriter.java
@@ -18,18 +18,26 @@
  */
 package org.apache.johnzon.jsonb.extension;
 
+import java.io.StringReader;
 import java.io.Writer;
+import java.util.function.Consumer;
 
+import javax.json.Json;
 import javax.json.JsonArray;
 import javax.json.JsonObject;
+import javax.json.JsonReader;
 import javax.json.JsonValue;
 
-public class JsonValueWriter extends Writer {
+public class JsonValueWriter extends Writer implements Consumer<JsonValue> {
     private JsonValue result;
+    private StringBuilder fallbackOutput;
 
     @Override
     public void write(final char[] cbuf, final int off, final int len) {
-        throw new UnsupportedOperationException();
+        if (fallbackOutput == null) {
+            fallbackOutput = new StringBuilder();
+        }
+        fallbackOutput.append(cbuf, off, len);
     }
 
     @Override
@@ -42,19 +50,30 @@ public class JsonValueWriter extends Writer {
         flush();
     }
 
+    @Deprecated
     public void setResult(final JsonValue result) {
         this.result = result;
     }
 
     public JsonValue getResult() {
+        if (result == null && fallbackOutput != null) {
+            try (final JsonReader reader = Json.createReader(new StringReader(fallbackOutput.toString()))) {
+                result = reader.readValue();
+            }
+        }
         return result;
     }
 
     public JsonObject getObject() {
-        return result.asJsonObject();
+        return getResult().asJsonObject();
     }
 
     public JsonArray getArray() {
-        return result.asJsonArray();
+        return getResult().asJsonArray();
+    }
+
+    @Override
+    public void accept(final JsonValue jsonValue) {
+        this.result = jsonValue;
     }
 }
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
index e8ae5cc..4a9639c 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
@@ -250,11 +250,20 @@ public class Mapper implements Closeable {
         return writer.toString();
     }
 
-    public <T> T readObject(final JsonStructure value, final Type clazz) {
+    public <T> T readObject(final JsonValue value, final Type clazz) {
         return new MappingParserImpl(config, mappings, new JsonReader() {
             @Override
             public JsonStructure read() {
-                return value;
+                switch (value.getValueType()) {
+                    case STRING:
+                    case FALSE:
+                    case TRUE:
+                    case NULL:
+                    case NUMBER:
+                        throw new UnsupportedOperationException("use readValue()");
+                    default:
+                        return JsonStructure.class.cast(readValue());
+                }
             }
 
             @Override
diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md
index 91f3f9f..764c722 100644
--- a/src/site/markdown/index.md
+++ b/src/site/markdown/index.md
@@ -326,7 +326,7 @@ Default will be picked from the current available API. Finally, this behavior on
 
 #### Integration with `JsonValue`
 
-You can use some optimization to map a `JsonObject` to a POJO using Johnzon `JsonValueReader` and `JsonValueWriter`:
+You can use some optimization to map a `JsonObject` to a POJO using Johnzon `JsonValueReader` - or any implementation of  `Reader` implementing `Supplier<JsonStructure>` - and `JsonValueWriter` - or any implementation of  `Writer` implementing `Consumer<JsonValue>` -:
 
 <pre class="prettyprint linenums"><![CDATA[
 final JsonValueReader<Simple> reader = new JsonValueReader<>(Json.createObjectBuilder().add("value", "simple").build());