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 2018/02/04 16:16:51 UTC
johnzon git commit: JOHNZON-155 better support of TypeVariables
Repository: johnzon
Updated Branches:
refs/heads/master a25adfaee -> fea9ac9f0
JOHNZON-155 better support of TypeVariables
Project: http://git-wip-us.apache.org/repos/asf/johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/johnzon/commit/fea9ac9f
Tree: http://git-wip-us.apache.org/repos/asf/johnzon/tree/fea9ac9f
Diff: http://git-wip-us.apache.org/repos/asf/johnzon/diff/fea9ac9f
Branch: refs/heads/master
Commit: fea9ac9f0b4e017378f67f57985a9621ce94457a
Parents: a25adfa
Author: Romain Manni-Bucau <rm...@apache.org>
Authored: Sun Feb 4 17:15:12 2018 +0100
Committer: Romain Manni-Bucau <rm...@apache.org>
Committed: Sun Feb 4 17:15:31 2018 +0100
----------------------------------------------------------------------
johnzon-mapper/pom.xml | 5 +
.../org/apache/johnzon/mapper/Mappings.java | 28 +++---
.../johnzon/mapper/reflection/Generics.java | 98 ++++++++++++++++++++
.../org/apache/johnzon/mapper/GenericsTest.java | 43 +++++++++
.../src/test/java/org/superbiz/Model.java | 29 ++++++
.../src/test/java/org/superbiz/ModelBase.java | 34 +++++++
.../test/java/org/superbiz/ModelSuperBase.java | 53 +++++++++++
.../java/org/superbiz/ModelSuperSuperBase.java | 48 ++++++++++
8 files changed, 326 insertions(+), 12 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/pom.xml
----------------------------------------------------------------------
diff --git a/johnzon-mapper/pom.xml b/johnzon-mapper/pom.xml
index 2104a3f..d4f0391 100644
--- a/johnzon-mapper/pom.xml
+++ b/johnzon-mapper/pom.xml
@@ -34,6 +34,11 @@
<artifactId>johnzon-core</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.6</version>
+ </dependency>
<dependency>
<groupId>com.github.stefanbirkner</groupId>
http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
index 44e13f1..8a04515 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
@@ -52,6 +52,7 @@ import java.util.concurrent.ConcurrentMap;
import static java.util.Arrays.asList;
import static org.apache.johnzon.mapper.reflection.Converters.matches;
+import static org.apache.johnzon.mapper.reflection.Generics.resolve;
public class Mappings {
public static class ClassMapping {
@@ -361,8 +362,8 @@ public class Mappings {
Comparator<String> fieldComparator = accessMode.fieldComparator(inClazz);
fieldComparator = fieldComparator == null ? config.getAttributeOrder() : fieldComparator;
- final Map<String, Getter> getters = fieldComparator == null ? newOrderedMap(Getter.class) : new TreeMap<String, Getter>(fieldComparator);
- final Map<String, Setter> setters = fieldComparator == null ? newOrderedMap(Setter.class) : new TreeMap<String, Setter>(fieldComparator);
+ final Map<String, Getter> getters = fieldComparator == null ? newOrderedMap(Getter.class) : new TreeMap<>(fieldComparator);
+ final Map<String, Setter> setters = fieldComparator == null ? newOrderedMap(Setter.class) : new TreeMap<>(fieldComparator);
final Map<String, AccessMode.Reader> readers = accessMode.findReaders(clazz);
final Map<String, AccessMode.Writer> writers = accessMode.findWriters(clazz);
@@ -372,13 +373,13 @@ public class Mappings {
final JohnzonVirtualObjects virtualObjects = clazz.getAnnotation(JohnzonVirtualObjects.class);
if (virtualObjects != null) {
for (final JohnzonVirtualObject virtualObject : virtualObjects.value()) {
- handleVirtualObject(virtualFields, virtualObject, getters, setters, readers, writers, copyDate);
+ handleVirtualObject(virtualFields, virtualObject, getters, setters, readers, writers, copyDate, clazz);
}
}
final JohnzonVirtualObject virtualObject = clazz.getAnnotation(JohnzonVirtualObject.class);
if (virtualObject != null) {
- handleVirtualObject(virtualFields, virtualObject, getters, setters, readers, writers, copyDate);
+ handleVirtualObject(virtualFields, virtualObject, getters, setters, readers, writers, copyDate, clazz);
}
}
@@ -387,7 +388,7 @@ public class Mappings {
if (virtualFields.contains(key)) {
continue;
}
- addGetterIfNeeded(getters, key, reader.getValue(), copyDate);
+ addGetterIfNeeded(getters, key, reader.getValue(), copyDate, clazz);
}
for (final Map.Entry<String, AccessMode.Writer> writer : writers.entrySet()) {
@@ -395,7 +396,7 @@ public class Mappings {
if (virtualFields.contains(key)) {
continue;
}
- addSetterIfNeeded(setters, key, writer.getValue(), copyDate);
+ addSetterIfNeeded(setters, key, writer.getValue(), copyDate, clazz);
}
final Method anyGetter = accessMode.findAnyGetter(clazz);
@@ -435,7 +436,8 @@ public class Mappings {
private void addSetterIfNeeded(final Map<String, Setter> setters,
final String key,
final AccessMode.Writer value,
- final boolean copyDate) {
+ final boolean copyDate,
+ final Class<?> rootClass) {
final JohnzonIgnore writeIgnore = value.getAnnotation(JohnzonIgnore.class);
if (writeIgnore == null || writeIgnore.minVersion() >= 0) {
if (key.equals("metaClass")) {
@@ -444,7 +446,7 @@ public class Mappings {
final Type param = value.getType();
final Class<?> returnType = Class.class.isInstance(param) ? Class.class.cast(param) : null;
final Setter setter = new Setter(
- value, isPrimitive(param), returnType != null && returnType.isArray(), param,
+ value, isPrimitive(param), returnType != null && returnType.isArray(), resolve(param, rootClass),
findConverter(copyDate, value), value.findObjectConverterReader(),
writeIgnore != null ? writeIgnore.minVersion() : -1);
setters.put(key, setter);
@@ -454,7 +456,8 @@ public class Mappings {
private void addGetterIfNeeded(final Map<String, Getter> getters,
final String key,
final AccessMode.Reader value,
- final boolean copyDate) {
+ final boolean copyDate,
+ final Class<?> rootClass) {
final JohnzonIgnore readIgnore = value.getAnnotation(JohnzonIgnore.class);
final JohnzonIgnoreNested ignoreNested = value.getAnnotation(JohnzonIgnoreNested.class);
if (readIgnore == null || readIgnore.minVersion() >= 0) {
@@ -480,7 +483,8 @@ public class Mappings {
final Map<String, Setter> setters,
final Map<String, AccessMode.Reader> readers,
final Map<String, AccessMode.Writer> writers,
- final boolean copyDate) {
+ final boolean copyDate,
+ final Class<?> rootClazz) {
final String[] path = o.path();
if (path.length < 1) {
throw new IllegalArgumentException("@JohnzonVirtualObject need a path");
@@ -500,13 +504,13 @@ public class Mappings {
if (f.read()) {
final AccessMode.Reader reader = readers.get(name);
if (reader != null) {
- addGetterIfNeeded(objectGetters, name, reader, copyDate);
+ addGetterIfNeeded(objectGetters, name, reader, copyDate, rootClazz);
}
}
if (f.write()) {
final AccessMode.Writer writer = writers.get(name);
if (writer != null) {
- addSetterIfNeeded(objectSetters, name, writer, copyDate);
+ addSetterIfNeeded(objectSetters, name, writer, copyDate, rootClazz);
}
}
}
http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Generics.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Generics.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Generics.java
new file mode 100644
index 0000000..31ad7f2
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Generics.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.mapper.reflection;
+
+import static java.util.Arrays.asList;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.ArrayList;
+import java.util.Collection;
+
+public final class Generics {
+ private Generics() {
+ // no-op
+ }
+
+ // todo: this piece of code needs to be enhanced a lot:
+ // - better handling of the hierarchy
+ // - wildcard support?
+ // - cycle handling (Foo<Foo>)
+ // - ....
+ public static Type resolve(final Type value, final Class<?> rootClass) {
+ if (TypeVariable.class.isInstance(value)) {
+ return resolveTypeVariable(value, rootClass);
+ }
+ if (ParameterizedType.class.isInstance(value)) {
+ return resolveParameterizedType(value, rootClass);
+ }
+ return value;
+ }
+
+ private static Type resolveParameterizedType(final Type value, final Class<?> rootClass) {
+ Collection<Type> args = null;
+ final ParameterizedType parameterizedType = ParameterizedType.class.cast(value);
+ int index = 0;
+ for (final Type arg : parameterizedType.getActualTypeArguments()) {
+ final Type type = resolve(arg, rootClass);
+ if (type != arg) {
+ if (args == null) {
+ args = new ArrayList<>();
+ if (index > 0) {
+ args.addAll(asList(parameterizedType.getActualTypeArguments()).subList(0, index + 1));
+ }
+ }
+ }
+ if (args != null) {
+ args.add(arg);
+ }
+ index++;
+ }
+ if (args != null) {
+ return new JohnzonParameterizedType(parameterizedType.getRawType(), args.toArray(new Type[args.size()]));
+ }
+ return value;
+ }
+
+ // for now the level is hardcoded to 2 with generic > concrete
+ private static Type resolveTypeVariable(final Type value, final Class<?> rootClass) {
+ final TypeVariable<?> tv = TypeVariable.class.cast(value);
+ final Type parent = rootClass.getGenericSuperclass();
+ if (ParameterizedType.class.isInstance(parent)) {
+ final ParameterizedType parentPt = ParameterizedType.class.cast(parent);
+ if (Class.class.isInstance(parentPt.getRawType())) {
+ final Type grandParent = Class.class.cast(parentPt.getRawType()).getGenericSuperclass();
+ if (ParameterizedType.class.isInstance(grandParent)) {
+ final ParameterizedType grandParentPt = ParameterizedType.class.cast(grandParent);
+ final Type[] grandParentArgs = grandParentPt.getActualTypeArguments();
+ int index = 0;
+ final String name = tv.getName();
+ for (final Type t : grandParentArgs) {
+ if (TypeVariable.class.isInstance(t) && TypeVariable.class.cast(t).getName().equals(name)) {
+ return parentPt.getActualTypeArguments()[index];
+ }
+ index++;
+ }
+ }
+ }
+ }
+ return value;
+ }
+}
http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/GenericsTest.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/GenericsTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/GenericsTest.java
new file mode 100644
index 0000000..a5b0577
--- /dev/null
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/GenericsTest.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.mapper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.superbiz.Model;
+
+public class GenericsTest {
+ @Test
+ public void typeVariableMultiLevel() {
+ final String input = "{\"aalist\":[{\"detail\":\"something2\",\"name\":\"Na2\"}]," +
+ "\"childA\":{\"detail\":\"something\",\"name\":\"Na\"},\"childB\":{}}";
+ final Mapper mapper = new MapperBuilder().setAttributeOrder(String::compareTo).build();
+ final Model model = mapper.readObject(input, Model.class);
+ assertNotNull(model.getChildA());
+ assertNotNull(model.getChildB());
+ assertNotNull(model.getAalist());
+ assertEquals("something", model.getChildA().detail);
+ assertEquals("Na", model.getChildA().name);
+ assertEquals(1, model.getAalist().size());
+ assertEquals("something2", model.getAalist().iterator().next().detail);
+ assertEquals(input, mapper.writeObjectAsString(model));
+ }
+}
http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/src/test/java/org/superbiz/Model.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/superbiz/Model.java b/johnzon-mapper/src/test/java/org/superbiz/Model.java
new file mode 100644
index 0000000..1b3a077
--- /dev/null
+++ b/johnzon-mapper/src/test/java/org/superbiz/Model.java
@@ -0,0 +1,29 @@
+/*
+ * 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.superbiz;
+
+public class Model extends ModelBase<Model.ChildA, Model.ChildB> {
+ public static class ChildA extends ModelSuperBase.ChildA {
+ public String detail;
+ }
+
+ public static class ChildB extends ModelSuperBase.ChildB {
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/src/test/java/org/superbiz/ModelBase.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/superbiz/ModelBase.java b/johnzon-mapper/src/test/java/org/superbiz/ModelBase.java
new file mode 100644
index 0000000..d7668df
--- /dev/null
+++ b/johnzon-mapper/src/test/java/org/superbiz/ModelBase.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.superbiz;
+
+import java.util.Collection;
+
+public abstract class ModelBase<A extends ModelSuperBase.ChildA, B extends ModelSuperBase.ChildB>
+ extends ModelSuperBase<A, B> {
+ private Collection<A> aalist;
+
+ public Collection<A> getAalist() {
+ return aalist;
+ }
+
+ public void setAalist(final Collection<A> aalist) {
+ this.aalist = aalist;
+ }
+}
http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/src/test/java/org/superbiz/ModelSuperBase.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/superbiz/ModelSuperBase.java b/johnzon-mapper/src/test/java/org/superbiz/ModelSuperBase.java
new file mode 100644
index 0000000..c39d80b
--- /dev/null
+++ b/johnzon-mapper/src/test/java/org/superbiz/ModelSuperBase.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.superbiz;
+
+public abstract class ModelSuperBase<A extends ModelSuperBase.ChildA, B extends ModelSuperBase.ChildB>
+ extends ModelSuperSuperBase<A, B> {
+ public static class ChildA extends ModelSuperSuperBase.ChildA {
+
+ }
+
+ public static class ChildB extends ModelSuperSuperBase.ChildB {
+
+ }
+
+ private A childA;
+ private B childB;
+
+ @Override
+ public A getChildA() {
+ return childA;
+ }
+
+ @Override
+ public void setChildA(A childA) {
+ this.childA = childA;
+ }
+
+ @Override
+ public B getChildB() {
+ return childB;
+ }
+
+ @Override
+ public void setChildB(B childB) {
+ this.childB = childB;
+ }
+}
http://git-wip-us.apache.org/repos/asf/johnzon/blob/fea9ac9f/johnzon-mapper/src/test/java/org/superbiz/ModelSuperSuperBase.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/superbiz/ModelSuperSuperBase.java b/johnzon-mapper/src/test/java/org/superbiz/ModelSuperSuperBase.java
new file mode 100644
index 0000000..72980b6
--- /dev/null
+++ b/johnzon-mapper/src/test/java/org/superbiz/ModelSuperSuperBase.java
@@ -0,0 +1,48 @@
+/*
+ * 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.superbiz;
+
+public abstract class ModelSuperSuperBase<A extends ModelSuperSuperBase.ChildA, B extends ModelSuperSuperBase.ChildB> {
+ public static class ChildA {
+ public String name;
+ }
+
+ public static class ChildB {
+
+ }
+
+ private A childA;
+ private B childB;
+
+ public A getChildA() {
+ return childA;
+ }
+
+ public void setChildA(A childA) {
+ this.childA = childA;
+ }
+
+ public B getChildB() {
+ return childB;
+ }
+
+ public void setChildB(B childB) {
+ this.childB = childB;
+ }
+}