Merge pull request #92 from bmarwell/JOHNZON-386_inherited_jsonbadapter

[JOHNZON-386] Make JsonbAdapter inheritable
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
index 2e2f2a4..bbeb2d4 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
@@ -55,13 +55,9 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
 import java.nio.charset.StandardCharsets;
-import java.util.Base64;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Optional;
+import java.util.*;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
@@ -238,10 +234,10 @@
                 .ifPresent(builder::setSnippetMaxLength);
 
         // user adapters
+        final Types types = new Types();
+
         config.getProperty(JsonbConfig.ADAPTERS).ifPresent(adapters -> Stream.of(JsonbAdapter[].class.cast(adapters)).forEach(adapter -> {
-            final ParameterizedType pt = ParameterizedType.class.cast(
-                    Stream.of(adapter.getClass().getGenericInterfaces())
-                          .filter(i -> ParameterizedType.class.isInstance(i) && ParameterizedType.class.cast(i).getRawType() == JsonbAdapter.class).findFirst().orElse(null));
+            final ParameterizedType pt = types.findParameterizedType(adapter.getClass(), JsonbAdapter.class);
             if (pt == null) {
                 throw new IllegalArgumentException(adapter + " doesn't implement JsonbAdapter");
             }
@@ -286,7 +282,6 @@
             getBeanManager(); // force detection
         }
 
-        final Types types = new Types();
         builder.setReadAttributeBeforeWrite(
                 config.getProperty("johnzon.readAttributeBeforeWrite").map(Boolean.class::cast).orElse(false));
         builder.setAutoAdjustStringBuffers(
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbAdapterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbAdapterTest.java
new file mode 100644
index 0000000..e690f21
--- /dev/null
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbAdapterTest.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.jsonb;
+
+import org.apache.johnzon.jsonb.test.JsonbRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+import javax.json.bind.adapter.JsonbAdapter;
+
+import static org.junit.Assert.assertEquals;
+
+public class JsonbAdapterTest {
+
+    @Rule
+    public JsonbRule jsonbRule = new JsonbRule().withTypeAdapter(new ColorIdAdapter());
+
+    @Test
+    public void test_inherited_adapter_recognized() {
+        // given
+        ColorId colorId = new ColorId("#336699");
+
+        // when
+        String json = jsonbRule.toJson(colorId);
+
+        // then
+        assertEquals("\"#336699\"", json);
+    }
+
+    interface ValueType<T> {
+        T value();
+    }
+
+    static class ColorId implements ValueType<String> {
+
+        private final String value;
+
+        public ColorId(String value) {
+            this.value = value;
+        }
+
+        @Override
+        public String value() {
+            return this.value;
+        }
+    }
+
+    static abstract class AbstractValueTypeAdapter<T extends ValueType<String>> implements JsonbAdapter<T, String> {
+
+        @Override
+        public String adaptToJson(T t) throws Exception {
+            return t.value();
+        }
+    }
+
+    static class ColorIdAdapter extends AbstractValueTypeAdapter<ColorId> /*IMPLICIT: implements JsonbAdapter<ValueType<String>, JsonString>*/ {
+
+        @Override
+        public ColorId adaptFromJson(String s) throws Exception {
+            return new ColorId(s);
+        }
+    }
+}
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/test/JsonbRule.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/test/JsonbRule.java
index 2bb0a58..43575ed 100644
--- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/test/JsonbRule.java
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/test/JsonbRule.java
@@ -18,6 +18,7 @@
  */
 package org.apache.johnzon.jsonb.test;
 
+import org.apache.johnzon.jsonb.JsonbAdapterTest;
 import org.apache.johnzon.jsonb.api.experimental.JsonbExtension;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
@@ -28,6 +29,7 @@
 import javax.json.bind.JsonbBuilder;
 import javax.json.bind.JsonbConfig;
 import javax.json.bind.JsonbException;
+import javax.json.bind.adapter.JsonbAdapter;
 import javax.json.stream.JsonGenerator;
 import javax.json.stream.JsonParser;
 import java.io.InputStream;
@@ -51,6 +53,11 @@
         return this;
     }
 
+    public JsonbRule withTypeAdapter(JsonbAdapter<?, ?>... jsonbAdapters) {
+        config.withAdapters(jsonbAdapters);
+        return this;
+    }
+
     @Override
     public Statement apply(final Statement statement, final Description description) {
         return new Statement() {
@@ -170,4 +177,5 @@
     public void toJson(final Object object, final Type runtimeType, final JsonGenerator jsonGenerator) {
         JsonbExtension.class.cast(jsonb).toJson(object, runtimeType, jsonGenerator);
     }
+
 }