1
16
17 package com.google.gson.internal;
18
19 import java.lang.reflect.Constructor;
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.ParameterizedType;
22 import java.lang.reflect.Type;
23 import java.util.ArrayDeque;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.EnumSet;
27 import java.util.LinkedHashMap;
28 import java.util.LinkedHashSet;
29 import java.util.Map;
30 import java.util.Queue;
31 import java.util.Set;
32 import java.util.SortedMap;
33 import java.util.SortedSet;
34 import java.util.TreeMap;
35 import java.util.TreeSet;
36 import java.util.concurrent.ConcurrentHashMap;
37 import java.util.concurrent.ConcurrentMap;
38 import java.util.concurrent.ConcurrentNavigableMap;
39 import java.util.concurrent.ConcurrentSkipListMap;
40
41 import com.google.gson.InstanceCreator;
42 import com.google.gson.JsonIOException;
43 import com.google.gson.internal.reflect.ReflectionAccessor;
44 import com.google.gson.reflect.TypeToken;
45
46
49 public final class ConstructorConstructor {
50 private final Map<Type, InstanceCreator<?>> instanceCreators;
51 private final ReflectionAccessor accessor = ReflectionAccessor.getInstance();
52
53 public ConstructorConstructor(Map<Type, InstanceCreator<?>> instanceCreators) {
54 this.instanceCreators = instanceCreators;
55 }
56
57 public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
58 final Type type = typeToken.getType();
59 final Class<? super T> rawType = typeToken.getRawType();
60
61
62
63 @SuppressWarnings("unchecked")
64 final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);
65 if (typeCreator != null) {
66 return new ObjectConstructor<T>() {
67 @Override public T construct() {
68 return typeCreator.createInstance(type);
69 }
70 };
71 }
72
73
74 @SuppressWarnings("unchecked")
75 final InstanceCreator<T> rawTypeCreator =
76 (InstanceCreator<T>) instanceCreators.get(rawType);
77 if (rawTypeCreator != null) {
78 return new ObjectConstructor<T>() {
79 @Override public T construct() {
80 return rawTypeCreator.createInstance(type);
81 }
82 };
83 }
84
85 ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);
86 if (defaultConstructor != null) {
87 return defaultConstructor;
88 }
89
90 ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);
91 if (defaultImplementation != null) {
92 return defaultImplementation;
93 }
94
95
96 return newUnsafeAllocator(type, rawType);
97 }
98
99 private <T> ObjectConstructor<T> newDefaultConstructor(Class<? super T> rawType) {
100 try {
101 final Constructor<? super T> constructor = rawType.getDeclaredConstructor();
102 if (!constructor.isAccessible()) {
103 accessor.makeAccessible(constructor);
104 }
105 return new ObjectConstructor<T>() {
106 @SuppressWarnings("unchecked")
107 @Override public T construct() {
108 try {
109 Object[] args = null;
110 return (T) constructor.newInstance(args);
111 } catch (InstantiationException e) {
112
113 throw new RuntimeException("Failed to invoke " + constructor + " with no args", e);
114 } catch (InvocationTargetException e) {
115
116
117 throw new RuntimeException("Failed to invoke " + constructor + " with no args",
118 e.getTargetException());
119 } catch (IllegalAccessException e) {
120 throw new AssertionError(e);
121 }
122 }
123 };
124 } catch (NoSuchMethodException e) {
125 return null;
126 }
127 }
128
129
133 @SuppressWarnings("unchecked")
134 private <T> ObjectConstructor<T> newDefaultImplementationConstructor(
135 final Type type, Class<? super T> rawType) {
136 if (Collection.class.isAssignableFrom(rawType)) {
137 if (SortedSet.class.isAssignableFrom(rawType)) {
138 return new ObjectConstructor<T>() {
139 @Override public T construct() {
140 return (T) new TreeSet<Object>();
141 }
142 };
143 } else if (EnumSet.class.isAssignableFrom(rawType)) {
144 return new ObjectConstructor<T>() {
145 @SuppressWarnings("rawtypes")
146 @Override public T construct() {
147 if (type instanceof ParameterizedType) {
148 Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
149 if (elementType instanceof Class) {
150 return (T) EnumSet.noneOf((Class)elementType);
151 } else {
152 throw new JsonIOException("Invalid EnumSet type: " + type.toString());
153 }
154 } else {
155 throw new JsonIOException("Invalid EnumSet type: " + type.toString());
156 }
157 }
158 };
159 } else if (Set.class.isAssignableFrom(rawType)) {
160 return new ObjectConstructor<T>() {
161 @Override public T construct() {
162 return (T) new LinkedHashSet<Object>();
163 }
164 };
165 } else if (Queue.class.isAssignableFrom(rawType)) {
166 return new ObjectConstructor<T>() {
167 @Override public T construct() {
168 return (T) new ArrayDeque<Object>();
169 }
170 };
171 } else {
172 return new ObjectConstructor<T>() {
173 @Override public T construct() {
174 return (T) new ArrayList<Object>();
175 }
176 };
177 }
178 }
179
180 if (Map.class.isAssignableFrom(rawType)) {
181 if (ConcurrentNavigableMap.class.isAssignableFrom(rawType)) {
182 return new ObjectConstructor<T>() {
183 @Override public T construct() {
184 return (T) new ConcurrentSkipListMap<Object, Object>();
185 }
186 };
187 } else if (ConcurrentMap.class.isAssignableFrom(rawType)) {
188 return new ObjectConstructor<T>() {
189 @Override public T construct() {
190 return (T) new ConcurrentHashMap<Object, Object>();
191 }
192 };
193 } else if (SortedMap.class.isAssignableFrom(rawType)) {
194 return new ObjectConstructor<T>() {
195 @Override public T construct() {
196 return (T) new TreeMap<Object, Object>();
197 }
198 };
199 } else if (type instanceof ParameterizedType && !(String.class.isAssignableFrom(
200 TypeToken.get(((ParameterizedType) type).getActualTypeArguments()[0]).getRawType()))) {
201 return new ObjectConstructor<T>() {
202 @Override public T construct() {
203 return (T) new LinkedHashMap<Object, Object>();
204 }
205 };
206 } else {
207 return new ObjectConstructor<T>() {
208 @Override public T construct() {
209 return (T) new LinkedTreeMap<String, Object>();
210 }
211 };
212 }
213 }
214
215 return null;
216 }
217
218 private <T> ObjectConstructor<T> newUnsafeAllocator(
219 final Type type, final Class<? super T> rawType) {
220 return new ObjectConstructor<T>() {
221 private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
222 @SuppressWarnings("unchecked")
223 @Override public T construct() {
224 try {
225 Object newInstance = unsafeAllocator.newInstance(rawType);
226 return (T) newInstance;
227 } catch (Exception e) {
228 throw new RuntimeException(("Unable to invoke no-args constructor for " + type + ". "
229 + "Registering an InstanceCreator with Gson for this type may fix this problem."), e);
230 }
231 }
232 };
233 }
234
235 @Override public String toString() {
236 return instanceCreators.toString();
237 }
238 }
239