Skip to content

Commit c1f6932

Browse files
committed
Made Gson type adapters better extensible
1 parent 9bd814b commit c1f6932

File tree

10 files changed

+769
-656
lines changed

10 files changed

+769
-656
lines changed

org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/MessageJsonHandler.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
import java.util.Map;
1414
import java.util.function.Consumer;
1515

16-
import org.eclipse.lsp4j.jsonrpc.json.adapters.CollectionTypeAdapterFactory;
17-
import org.eclipse.lsp4j.jsonrpc.json.adapters.EitherTypeAdapterFactory;
18-
import org.eclipse.lsp4j.jsonrpc.json.adapters.EnumTypeAdapterFactory;
19-
import org.eclipse.lsp4j.jsonrpc.json.adapters.MessageTypeAdapterFactory;
16+
import org.eclipse.lsp4j.jsonrpc.json.adapters.CollectionTypeAdapter;
17+
import org.eclipse.lsp4j.jsonrpc.json.adapters.EitherTypeAdapter;
18+
import org.eclipse.lsp4j.jsonrpc.json.adapters.EnumTypeAdapter;
19+
import org.eclipse.lsp4j.jsonrpc.json.adapters.MessageTypeAdapter;
2020
import org.eclipse.lsp4j.jsonrpc.messages.CancelParams;
2121
import org.eclipse.lsp4j.jsonrpc.messages.Message;
2222

@@ -51,10 +51,10 @@ public MessageJsonHandler(Map<String, JsonRpcMethod> supportedMethods, Consumer<
5151

5252
public GsonBuilder getDefaultGsonBuilder() {
5353
return new GsonBuilder()
54-
.registerTypeAdapterFactory(new CollectionTypeAdapterFactory())
55-
.registerTypeAdapterFactory(new EitherTypeAdapterFactory())
56-
.registerTypeAdapterFactory(new EnumTypeAdapterFactory())
57-
.registerTypeAdapterFactory(new MessageTypeAdapterFactory(this));
54+
.registerTypeAdapterFactory(new CollectionTypeAdapter.Factory())
55+
.registerTypeAdapterFactory(new EitherTypeAdapter.Factory())
56+
.registerTypeAdapterFactory(new EnumTypeAdapter.Factory())
57+
.registerTypeAdapterFactory(new MessageTypeAdapter.Factory(this));
5858
}
5959

6060
public JsonRpcMethod getJsonRpcMethod(String name) {
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2016, 2017 TypeFox GmbH (http://www.typefox.io) and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*******************************************************************************/
8+
package org.eclipse.lsp4j.jsonrpc.json.adapters;
9+
10+
import java.io.IOException;
11+
import java.lang.reflect.Constructor;
12+
import java.lang.reflect.Type;
13+
import java.lang.reflect.TypeVariable;
14+
import java.util.ArrayList;
15+
import java.util.Collection;
16+
import java.util.LinkedHashSet;
17+
import java.util.LinkedList;
18+
import java.util.Queue;
19+
import java.util.Set;
20+
import java.util.SortedSet;
21+
import java.util.TreeSet;
22+
import java.util.function.Supplier;
23+
24+
import com.google.gson.Gson;
25+
import com.google.gson.TypeAdapter;
26+
import com.google.gson.TypeAdapterFactory;
27+
import com.google.gson.reflect.TypeToken;
28+
import com.google.gson.stream.JsonReader;
29+
import com.google.gson.stream.JsonToken;
30+
import com.google.gson.stream.JsonWriter;
31+
32+
/**
33+
* A specialized type adapter for collections that can handle single values.
34+
*/
35+
public class CollectionTypeAdapter<E> extends TypeAdapter<Collection<E>> {
36+
37+
public static class Factory implements TypeAdapterFactory {
38+
39+
@Override
40+
@SuppressWarnings({ "unchecked" })
41+
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
42+
if (!Collection.class.isAssignableFrom(typeToken.getRawType()))
43+
return null;
44+
45+
Type[] elementTypes = TypeUtils.getElementTypes(typeToken, Collection.class);
46+
if (elementTypes.length != 1)
47+
return null;
48+
TypeAdapter<?> elementTypeAdapter = gson.getAdapter(TypeToken.get(elementTypes[0]));
49+
Supplier<Collection<Object>> constructor = getConstructor((Class<Collection<Object>>) typeToken.getRawType());
50+
return (TypeAdapter<T>) create(gson, elementTypes[0], elementTypeAdapter, constructor);
51+
}
52+
53+
@SuppressWarnings({ "unchecked", "rawtypes" })
54+
protected TypeAdapter<?> create(Gson gson, Type elementType, TypeAdapter<?> elementTypeAdapter, Supplier<Collection<Object>> constructor) {
55+
return new CollectionTypeAdapter(gson, elementType, elementTypeAdapter, constructor);
56+
}
57+
58+
protected <E> Supplier<Collection<E>> getConstructor(Class<Collection<E>> rawType) {
59+
try {
60+
Constructor<Collection<E>> constructor = rawType.getDeclaredConstructor();
61+
if (!constructor.isAccessible())
62+
constructor.setAccessible(true);
63+
return () -> {
64+
try {
65+
return constructor.newInstance();
66+
} catch (Exception e) {
67+
throw new RuntimeException(e);
68+
}
69+
};
70+
} catch (NoSuchMethodException e) {
71+
if (SortedSet.class.isAssignableFrom(rawType)) {
72+
return () -> {
73+
return new TreeSet<E>();
74+
};
75+
} else if (Set.class.isAssignableFrom(rawType)) {
76+
return () -> {
77+
return new LinkedHashSet<E>();
78+
};
79+
} else if (Queue.class.isAssignableFrom(rawType)) {
80+
return () -> {
81+
return new LinkedList<E>();
82+
};
83+
} else {
84+
return () -> {
85+
return new ArrayList<E>();
86+
};
87+
}
88+
}
89+
}
90+
91+
}
92+
93+
private final Gson gson;
94+
private final Type elementType;
95+
private final TypeAdapter<E> elementTypeAdapter;
96+
private final Supplier<Collection<E>> constructor;
97+
98+
public CollectionTypeAdapter(Gson gson, Type elementType, TypeAdapter<E> elementTypeAdapter, Supplier<Collection<E>> constructor) {
99+
this.gson = gson;
100+
this.elementType = elementType;
101+
this.elementTypeAdapter = elementTypeAdapter;
102+
this.constructor = constructor;
103+
}
104+
105+
@Override
106+
public Collection<E> read(JsonReader in) throws IOException {
107+
JsonToken peek = in.peek();
108+
if (peek == JsonToken.NULL) {
109+
in.nextNull();
110+
return null;
111+
} else if (peek == JsonToken.BEGIN_ARRAY) {
112+
Collection<E> collection = constructor.get();
113+
in.beginArray();
114+
while (in.hasNext()) {
115+
E instance = elementTypeAdapter.read(in);
116+
collection.add(instance);
117+
}
118+
in.endArray();
119+
return collection;
120+
} else {
121+
Collection<E> collection = constructor.get();
122+
E instance = elementTypeAdapter.read(in);
123+
collection.add(instance);
124+
return collection;
125+
}
126+
}
127+
128+
@Override
129+
public void write(JsonWriter out, Collection<E> collection) throws IOException {
130+
if (collection == null) {
131+
out.nullValue();
132+
return;
133+
}
134+
out.beginArray();
135+
for (E element : collection) {
136+
if (element != null && elementType != element.getClass()
137+
&& (elementType instanceof TypeVariable<?> || elementType instanceof Class<?>)) {
138+
@SuppressWarnings("unchecked")
139+
TypeAdapter<E> runtimeTypeAdapter = (TypeAdapter<E>) gson.getAdapter(TypeToken.get(element.getClass()));
140+
runtimeTypeAdapter.write(out, element);
141+
} else {
142+
elementTypeAdapter.write(out, element);
143+
}
144+
}
145+
out.endArray();
146+
}
147+
}

org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/CollectionTypeAdapterFactory.java

Lines changed: 15 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -7,136 +7,30 @@
77
*******************************************************************************/
88
package org.eclipse.lsp4j.jsonrpc.json.adapters;
99

10-
import java.io.IOException;
11-
import java.lang.reflect.Constructor;
1210
import java.lang.reflect.Type;
13-
import java.lang.reflect.TypeVariable;
14-
import java.util.ArrayList;
1511
import java.util.Collection;
16-
import java.util.LinkedHashSet;
17-
import java.util.LinkedList;
18-
import java.util.Queue;
19-
import java.util.Set;
20-
import java.util.SortedSet;
21-
import java.util.TreeSet;
2212
import java.util.function.Supplier;
2313

2414
import com.google.gson.Gson;
2515
import com.google.gson.TypeAdapter;
26-
import com.google.gson.TypeAdapterFactory;
27-
import com.google.gson.reflect.TypeToken;
28-
import com.google.gson.stream.JsonReader;
29-
import com.google.gson.stream.JsonToken;
30-
import com.google.gson.stream.JsonWriter;
3116

3217
/**
33-
* A specialized type adapter factory for collections that can handle single values.
18+
* @deprecated Use {@link CollectionTypeAdapter.Factory} instead.
3419
*/
35-
public class CollectionTypeAdapterFactory implements TypeAdapterFactory {
36-
37-
@Override
38-
@SuppressWarnings({ "unchecked", "rawtypes" })
39-
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
40-
if (!Collection.class.isAssignableFrom(typeToken.getRawType()))
41-
return null;
42-
43-
Type[] elementTypes = TypeUtils.getElementTypes(typeToken, Collection.class);
44-
if (elementTypes.length != 1)
45-
return null;
46-
TypeAdapter<?> elementTypeAdapter = gson.getAdapter(TypeToken.get(elementTypes[0]));
47-
Supplier<Collection<Object>> constructor = getConstructor((Class<Collection<Object>>) typeToken.getRawType());
48-
return new Adapter(gson, elementTypes[0], elementTypeAdapter, constructor);
49-
}
50-
51-
protected static class Adapter<E> extends TypeAdapter<Collection<E>> {
52-
53-
private final Gson gson;
54-
private final Type elementType;
55-
private final TypeAdapter<E> elementTypeAdapter;
56-
private final Supplier<Collection<E>> constructor;
57-
58-
public Adapter(Gson gson, Type elementType, TypeAdapter<E> elementTypeAdapter, Supplier<Collection<E>> constructor) {
59-
this.gson = gson;
60-
this.elementType = elementType;
61-
this.elementTypeAdapter = elementTypeAdapter;
62-
this.constructor = constructor;
63-
}
64-
65-
@Override
66-
public Collection<E> read(JsonReader in) throws IOException {
67-
JsonToken peek = in.peek();
68-
if (peek == JsonToken.NULL) {
69-
in.nextNull();
70-
return null;
71-
} else if (peek == JsonToken.BEGIN_ARRAY) {
72-
Collection<E> collection = constructor.get();
73-
in.beginArray();
74-
while (in.hasNext()) {
75-
E instance = elementTypeAdapter.read(in);
76-
collection.add(instance);
77-
}
78-
in.endArray();
79-
return collection;
80-
} else {
81-
Collection<E> collection = constructor.get();
82-
E instance = elementTypeAdapter.read(in);
83-
collection.add(instance);
84-
return collection;
85-
}
86-
}
87-
88-
@Override
89-
public void write(JsonWriter out, Collection<E> collection) throws IOException {
90-
if (collection == null) {
91-
out.nullValue();
92-
return;
93-
}
94-
out.beginArray();
95-
for (E element : collection) {
96-
if (element != null && elementType != element.getClass()
97-
&& (elementType instanceof TypeVariable<?> || elementType instanceof Class<?>)) {
98-
@SuppressWarnings("unchecked")
99-
TypeAdapter<E> runtimeTypeAdapter = (TypeAdapter<E>) gson.getAdapter(TypeToken.get(element.getClass()));
100-
runtimeTypeAdapter.write(out, element);
101-
} else {
102-
elementTypeAdapter.write(out, element);
103-
}
104-
}
105-
out.endArray();
20+
@Deprecated
21+
public class CollectionTypeAdapterFactory extends CollectionTypeAdapter.Factory {
22+
23+
/**
24+
* @deprecated Use {@link CollectionTypeAdapter} instead.
25+
*/
26+
@Deprecated
27+
protected static class Adapter<E> extends CollectionTypeAdapter<E> {
28+
29+
public Adapter(Gson gson, Type elementType, TypeAdapter<E> elementTypeAdapter,
30+
Supplier<Collection<E>> constructor) {
31+
super(gson, elementType, elementTypeAdapter, constructor);
10632
}
33+
10734
}
108-
109-
private <E> Supplier<Collection<E>> getConstructor(Class<Collection<E>> rawType) {
110-
try {
111-
Constructor<Collection<E>> constructor = rawType.getDeclaredConstructor();
112-
if (!constructor.isAccessible())
113-
constructor.setAccessible(true);
114-
return () -> {
115-
try {
116-
return constructor.newInstance();
117-
} catch (Exception e) {
118-
throw new RuntimeException(e);
119-
}
120-
};
121-
} catch (NoSuchMethodException e) {
122-
if (SortedSet.class.isAssignableFrom(rawType)) {
123-
return () -> {
124-
return new TreeSet<E>();
125-
};
126-
} else if (Set.class.isAssignableFrom(rawType)) {
127-
return () -> {
128-
return new LinkedHashSet<E>();
129-
};
130-
} else if (Queue.class.isAssignableFrom(rawType)) {
131-
return () -> {
132-
return new LinkedList<E>();
133-
};
134-
} else {
135-
return () -> {
136-
return new ArrayList<E>();
137-
};
138-
}
139-
}
140-
}
141-
35+
14236
}

0 commit comments

Comments
 (0)