Skip to content

Commit bc9b6b0

Browse files
committed
json-iterator#7 support type variables
1 parent 66675e4 commit bc9b6b0

11 files changed

Lines changed: 277 additions & 63 deletions

src/main/java/com/jsoniter/annotation/JsoniterAnnotationSupport.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,8 @@ public void updateClassDescriptor(ClassDescriptor desc) {
4747
if (jsonProperty == null) {
4848
throw new JsonException("must mark all parameters using @JsonProperty: " + ctor);
4949
}
50-
Binding binding = new Binding();
50+
Binding binding = new Binding(desc.clazz, desc.lookup, ctor.getGenericParameterTypes()[i]);
5151
binding.name = jsonProperty.value();
52-
binding.valueType = ctor.getParameterTypes()[i];
5352
binding.annotations = paramAnnotations;
5453
desc.ctor.parameters.add(binding);
5554
}
@@ -78,9 +77,8 @@ public void updateClassDescriptor(ClassDescriptor desc) {
7877
if (jsonProperty == null) {
7978
throw new JsonException("must mark all parameters using @JsonProperty: " + method);
8079
}
81-
Binding binding = new Binding();
80+
Binding binding = new Binding(desc.clazz, desc.lookup, method.getGenericParameterTypes()[i]);
8281
binding.name = jsonProperty.value();
83-
binding.valueType = method.getParameterTypes()[i];
8482
binding.annotations = paramAnnotations;
8583
desc.ctor.parameters.add(binding);
8684
}
@@ -101,9 +99,8 @@ public void updateClassDescriptor(ClassDescriptor desc) {
10199
if (jsonProperty == null) {
102100
throw new JsonException("must mark all parameters using @JsonProperty: " + method);
103101
}
104-
Binding binding = new Binding();
102+
Binding binding = new Binding(desc.clazz, desc.lookup, method.getGenericParameterTypes()[i]);
105103
binding.name = jsonProperty.value();
106-
binding.valueType = method.getParameterTypes()[i];
107104
binding.annotations = paramAnnotations;
108105
setter.parameters.add(binding);
109106
}

src/main/java/com/jsoniter/spi/Binding.java

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package com.jsoniter.spi;
22

33
import java.lang.annotation.Annotation;
4-
import java.lang.reflect.Field;
5-
import java.lang.reflect.Type;
4+
import java.lang.reflect.*;
5+
import java.util.Map;
66

77
public class Binding {
88
// input
@@ -22,6 +22,50 @@ public class Binding {
2222
public Field field;
2323
public int idx;
2424

25+
public Binding(Class clazz, Map<String, Type> lookup, Type valueType) {
26+
this.clazz = clazz;
27+
this.valueType = substituteTypeVariables(lookup, valueType);
28+
this.valueTypeLiteral = TypeLiteral.create(this.valueType);
29+
}
30+
31+
private static Type substituteTypeVariables(Map<String, Type> lookup, Type type) {
32+
if (type instanceof TypeVariable) {
33+
return translateTypeVariable(lookup, (TypeVariable) type);
34+
}
35+
if (type instanceof ParameterizedType) {
36+
ParameterizedType pType = (ParameterizedType) type;
37+
Type[] args = pType.getActualTypeArguments();
38+
for (int i = 0; i < args.length; i++) {
39+
args[i] = substituteTypeVariables(lookup, args[i]);
40+
}
41+
return new ParameterizedTypeImpl(args, pType.getOwnerType(), pType.getRawType());
42+
}
43+
if (type instanceof GenericArrayType) {
44+
GenericArrayType gaType = (GenericArrayType) type;
45+
return new GenericArrayTypeImpl(substituteTypeVariables(lookup, gaType.getGenericComponentType()));
46+
}
47+
return type;
48+
}
49+
50+
private static Type translateTypeVariable(Map<String, Type> lookup, TypeVariable var) {
51+
GenericDeclaration declaredBy = var.getGenericDeclaration();
52+
if (!(declaredBy instanceof Class)) {
53+
// if the <T> is not defined by class, there is no way to get the actual type
54+
return Object.class;
55+
}
56+
Class clazz = (Class) declaredBy;
57+
Type actualType = lookup.get(var.getName() + "@" + clazz.getCanonicalName());
58+
if (actualType == null) {
59+
// should not happen
60+
return Object.class;
61+
}
62+
if (actualType instanceof TypeVariable) {
63+
// translate to another variable, try again
64+
return translateTypeVariable(lookup, (TypeVariable) actualType);
65+
}
66+
return actualType;
67+
}
68+
2569
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
2670
if (annotations == null) {
2771
return null;

src/main/java/com/jsoniter/spi/ClassDescriptor.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package com.jsoniter.spi;
22

3+
import java.lang.reflect.Type;
34
import java.util.ArrayList;
45
import java.util.List;
6+
import java.util.Map;
57

68
public class ClassDescriptor {
79
public Class clazz;
10+
public Map<String, Type> lookup;
811
public ConstructorDescriptor ctor;
912
public List<Binding> fields;
1013
public List<SetterDescriptor> setters;

src/main/java/com/jsoniter/spi/ExtensionManager.java

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package com.jsoniter.spi;
22

3-
import java.lang.reflect.Field;
4-
import java.lang.reflect.Method;
5-
import java.lang.reflect.Modifier;
6-
import java.lang.reflect.Type;
3+
import com.jsoniter.JsonException;
4+
import sun.net.www.content.text.Generic;
5+
6+
import java.lang.reflect.*;
77
import java.util.*;
88

99
public class ExtensionManager {
@@ -76,12 +76,14 @@ public synchronized static void addNewEncoder(String cacheKey, Encoder encoder)
7676
}
7777

7878
public static ClassDescriptor getClassDescriptor(Class clazz, boolean includingPrivate) {
79+
Map<String, Type> lookup = collectTypeVariableLookup(clazz);
7980
ClassDescriptor desc = new ClassDescriptor();
8081
desc.clazz = clazz;
82+
desc.lookup = lookup;
8183
desc.ctor = getCtor(clazz);
82-
desc.fields = getFields(clazz, includingPrivate);
83-
desc.setters = getSetters(clazz, includingPrivate);
84-
desc.getters = getGetters(clazz, includingPrivate);
84+
desc.fields = getFields(lookup, clazz, includingPrivate);
85+
desc.setters = getSetters(lookup, clazz, includingPrivate);
86+
desc.getters = getGetters(lookup, clazz, includingPrivate);
8587
for (Extension extension : extensions) {
8688
extension.updateClassDescriptor(desc);
8789
}
@@ -103,8 +105,6 @@ public static ClassDescriptor getClassDescriptor(Class clazz, boolean includingP
103105
if (binding.field != null && includingPrivate) {
104106
binding.field.setAccessible(true);
105107
}
106-
binding.clazz = clazz;
107-
binding.valueTypeLiteral = TypeLiteral.create(binding.valueType);
108108
}
109109
for (Binding binding : desc.allEncoderBindings()) {
110110
if (binding.toNames == null) {
@@ -113,8 +113,6 @@ public static ClassDescriptor getClassDescriptor(Class clazz, boolean includingP
113113
if (binding.field != null && includingPrivate) {
114114
binding.field.setAccessible(true);
115115
}
116-
binding.clazz = clazz;
117-
binding.valueTypeLiteral = TypeLiteral.create(binding.valueType);
118116
}
119117
return desc;
120118
}
@@ -129,7 +127,7 @@ private static ConstructorDescriptor getCtor(Class clazz) {
129127
return cctor;
130128
}
131129

132-
private static List<Binding> getFields(Class clazz, boolean includingPrivate) {
130+
private static List<Binding> getFields(Map<String, Type> lookup, Class clazz, boolean includingPrivate) {
133131
ArrayList<Binding> bindings = new ArrayList<Binding>();
134132
for (Field field : getAllFields(clazz, includingPrivate)) {
135133
if (Modifier.isStatic(field.getModifiers())) {
@@ -138,23 +136,16 @@ private static List<Binding> getFields(Class clazz, boolean includingPrivate) {
138136
if (includingPrivate) {
139137
field.setAccessible(true);
140138
}
141-
Binding binding = createBindingFromField(clazz, field);
139+
Binding binding = createBindingFromField(lookup, clazz, field);
142140
bindings.add(binding);
143141
}
144-
Binding binding = new Binding();
145-
binding.fromNames = new String[0];
146-
binding.name = "*";
147-
binding.clazz = clazz;
148142
return bindings;
149143
}
150144

151-
private static Binding createBindingFromField(Class clazz, Field field) {
152-
Binding binding = new Binding();
145+
private static Binding createBindingFromField(Map<String, Type> lookup, Class clazz, Field field) {
146+
Binding binding = new Binding(clazz, lookup, field.getGenericType());
153147
binding.fromNames = new String[]{field.getName()};
154148
binding.name = field.getName();
155-
binding.valueType = field.getType();
156-
binding.valueTypeLiteral = TypeLiteral.create(binding.valueType);
157-
binding.clazz = clazz;
158149
binding.annotations = field.getAnnotations();
159150
binding.field = field;
160151
return binding;
@@ -173,7 +164,7 @@ private static List<Field> getAllFields(Class clazz, boolean includingPrivate) {
173164
return allFields;
174165
}
175166

176-
private static List<SetterDescriptor> getSetters(Class clazz, boolean includingPrivate) {
167+
private static List<SetterDescriptor> getSetters(Map<String, Type> lookup, Class clazz, boolean includingPrivate) {
177168
ArrayList<SetterDescriptor> setters = new ArrayList<SetterDescriptor>();
178169
List<Method> allMethods = Arrays.asList(clazz.getMethods());
179170
if (includingPrivate) {
@@ -209,19 +200,17 @@ private static List<SetterDescriptor> getSetters(Class clazz, boolean includingP
209200
SetterDescriptor setter = new SetterDescriptor();
210201
setter.method = method;
211202
setter.methodName = methodName;
212-
Binding param = new Binding();
203+
Binding param = new Binding(clazz, lookup, paramTypes[0]);
213204
param.fromNames = new String[]{fromName};
214205
param.name = fromName;
215-
param.valueType = paramTypes[0];
216-
param.valueTypeLiteral = TypeLiteral.create(param.valueType);
217206
param.clazz = clazz;
218207
setter.parameters.add(param);
219208
setters.add(setter);
220209
}
221210
return setters;
222211
}
223212

224-
private static List<Binding> getGetters(Class clazz, boolean includingPrivate) {
213+
private static List<Binding> getGetters(Map<String, Type> lookup, Class clazz, boolean includingPrivate) {
225214
ArrayList<Binding> getters = new ArrayList<Binding>();
226215
for (Method method : clazz.getMethods()) {
227216
if (Modifier.isStatic(method.getModifiers())) {
@@ -244,11 +233,9 @@ private static List<Binding> getGetters(Class clazz, boolean includingPrivate) {
244233
char[] fromNameChars = toName.toCharArray();
245234
fromNameChars[0] = Character.toLowerCase(fromNameChars[0]);
246235
toName = new String(fromNameChars);
247-
Binding getter = new Binding();
236+
Binding getter = new Binding(clazz, lookup, method.getGenericReturnType());
248237
getter.toNames = new String[]{toName};
249238
getter.name = methodName + "()";
250-
getter.valueType = method.getGenericReturnType();
251-
getter.clazz = clazz;
252239
getters.add(getter);
253240
}
254241
return getters;
@@ -262,4 +249,28 @@ public static void dump() {
262249
System.err.println(cacheKey);
263250
}
264251
}
252+
253+
private static Map<String, Type> collectTypeVariableLookup(Type type) {
254+
HashMap<String, Type> vars = new HashMap<String, Type>();
255+
if (null == type) {
256+
return vars;
257+
}
258+
if (type instanceof ParameterizedType) {
259+
ParameterizedType pType = (ParameterizedType) type;
260+
Type[] actualTypeArguments = pType.getActualTypeArguments();
261+
Class clazz = (Class) pType.getRawType();
262+
for (int i = 0; i < clazz.getTypeParameters().length; i++) {
263+
TypeVariable variable = clazz.getTypeParameters()[i];
264+
vars.put(variable.getName() + "@" + clazz.getCanonicalName(), actualTypeArguments[i]);
265+
}
266+
vars.putAll(collectTypeVariableLookup(clazz.getGenericSuperclass()));
267+
return vars;
268+
}
269+
if (type instanceof Class) {
270+
Class clazz = (Class) type;
271+
vars.putAll(collectTypeVariableLookup(clazz.getGenericSuperclass()));
272+
return vars;
273+
}
274+
throw new JsonException("unexpected type: " + type);
275+
}
265276
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.jsoniter.spi;
2+
3+
import java.lang.reflect.GenericArrayType;
4+
import java.lang.reflect.Type;
5+
6+
class GenericArrayTypeImpl implements GenericArrayType {
7+
8+
private final Type componentType;
9+
10+
GenericArrayTypeImpl(Type componentType) {
11+
this.componentType = componentType;
12+
}
13+
14+
@Override
15+
public Type getGenericComponentType() {
16+
return componentType;
17+
}
18+
19+
@Override
20+
public boolean equals(Object o) {
21+
if (this == o) return true;
22+
if (o == null || getClass() != o.getClass()) return false;
23+
24+
GenericArrayTypeImpl that = (GenericArrayTypeImpl) o;
25+
26+
return componentType != null ? componentType.equals(that.componentType) : that.componentType == null;
27+
28+
}
29+
30+
@Override
31+
public int hashCode() {
32+
return componentType != null ? componentType.hashCode() : 0;
33+
}
34+
35+
@Override
36+
public String toString() {
37+
return "GenericArrayTypeImpl{" +
38+
"componentType=" + componentType +
39+
'}';
40+
}
41+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.jsoniter.spi;
2+
3+
import java.lang.reflect.ParameterizedType;
4+
import java.lang.reflect.Type;
5+
import java.util.Arrays;
6+
7+
class ParameterizedTypeImpl implements ParameterizedType {
8+
private final Type[] actualTypeArguments;
9+
private final Type ownerType;
10+
private final Type rawType;
11+
12+
public ParameterizedTypeImpl(Type[] actualTypeArguments, Type ownerType, Type rawType){
13+
this.actualTypeArguments = actualTypeArguments;
14+
this.ownerType = ownerType;
15+
this.rawType = rawType;
16+
}
17+
18+
public Type[] getActualTypeArguments() {
19+
return actualTypeArguments;
20+
}
21+
22+
public Type getOwnerType() {
23+
return ownerType;
24+
}
25+
26+
public Type getRawType() {
27+
return rawType;
28+
}
29+
30+
@Override
31+
public boolean equals(Object o) {
32+
if (this == o) return true;
33+
if (o == null || getClass() != o.getClass()) return false;
34+
35+
ParameterizedTypeImpl that = (ParameterizedTypeImpl) o;
36+
37+
// Probably incorrect - comparing Object[] arrays with Arrays.equals
38+
if (!Arrays.equals(actualTypeArguments, that.actualTypeArguments)) return false;
39+
if (ownerType != null ? !ownerType.equals(that.ownerType) : that.ownerType != null) return false;
40+
return rawType != null ? rawType.equals(that.rawType) : that.rawType == null;
41+
42+
}
43+
44+
@Override
45+
public int hashCode() {
46+
int result = Arrays.hashCode(actualTypeArguments);
47+
result = 31 * result + (ownerType != null ? ownerType.hashCode() : 0);
48+
result = 31 * result + (rawType != null ? rawType.hashCode() : 0);
49+
return result;
50+
}
51+
52+
@Override
53+
public String toString() {
54+
return "ParameterizedTypeImpl{" +
55+
"actualTypeArguments=" + Arrays.toString(actualTypeArguments) +
56+
", ownerType=" + ownerType +
57+
", rawType=" + rawType +
58+
'}';
59+
}
60+
}

0 commit comments

Comments
 (0)