2 * Copyright (C) 2011 Google Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com
.google
.gson
.internal
;
19 import com
.google
.gson
.InstanceCreator
;
20 import com
.google
.gson
.JsonIOException
;
21 import com
.google
.gson
.reflect
.TypeToken
;
23 import java
.lang
.reflect
.Constructor
;
24 import java
.lang
.reflect
.InvocationTargetException
;
25 import java
.lang
.reflect
.ParameterizedType
;
26 import java
.lang
.reflect
.Type
;
27 import java
.util
.ArrayList
;
28 import java
.util
.Collection
;
29 import java
.util
.EnumSet
;
30 import java
.util
.LinkedHashMap
;
31 import java
.util
.LinkedHashSet
;
32 import java
.util
.LinkedList
;
34 import java
.util
.Queue
;
36 import java
.util
.SortedMap
;
37 import java
.util
.SortedSet
;
38 import java
.util
.TreeMap
;
39 import java
.util
.TreeSet
;
42 * Returns a function that can construct an instance of a requested type.
44 public final class ConstructorConstructor
{
45 private final Map
<Type
, InstanceCreator
<?
>> instanceCreators
;
47 public ConstructorConstructor(Map
<Type
, InstanceCreator
<?
>> instanceCreators
) {
48 this.instanceCreators
= instanceCreators
;
51 public <T
> ObjectConstructor
<T
> get(TypeToken
<T
> typeToken
) {
52 final Type type
= typeToken
.getType();
53 final Class
<?
super T
> rawType
= typeToken
.getRawType();
55 // first try an instance creator
57 @SuppressWarnings("unchecked") // types must agree
58 final InstanceCreator
<T
> typeCreator
= (InstanceCreator
<T
>) instanceCreators
.get(type
);
59 if (typeCreator
!= null) {
60 return new ObjectConstructor
<T
>() {
61 public T
construct() {
62 return typeCreator
.createInstance(type
);
67 // Next try raw type match for instance creators
68 @SuppressWarnings("unchecked") // types must agree
69 final InstanceCreator
<T
> rawTypeCreator
=
70 (InstanceCreator
<T
>) instanceCreators
.get(rawType
);
71 if (rawTypeCreator
!= null) {
72 return new ObjectConstructor
<T
>() {
73 public T
construct() {
74 return rawTypeCreator
.createInstance(type
);
79 ObjectConstructor
<T
> defaultConstructor
= newDefaultConstructor(rawType
);
80 if (defaultConstructor
!= null) {
81 return defaultConstructor
;
84 ObjectConstructor
<T
> defaultImplementation
= newDefaultImplementationConstructor(type
, rawType
);
85 if (defaultImplementation
!= null) {
86 return defaultImplementation
;
90 return newUnsafeAllocator(type
, rawType
);
93 private <T
> ObjectConstructor
<T
> newDefaultConstructor(Class
<?
super T
> rawType
) {
95 final Constructor
<?
super T
> constructor
= rawType
.getDeclaredConstructor();
96 if (!constructor
.isAccessible()) {
97 constructor
.setAccessible(true);
99 return new ObjectConstructor
<T
>() {
100 @SuppressWarnings("unchecked") // T is the same raw type as is requested
101 public T
construct() {
103 Object
[] args
= null;
104 return (T
) constructor
.newInstance(args
);
105 } catch (InstantiationException e
) {
106 // TODO: JsonParseException ?
107 throw new RuntimeException("Failed to invoke " + constructor
+ " with no args", e
);
108 } catch (InvocationTargetException e
) {
109 // TODO: don't wrap if cause is unchecked!
110 // TODO: JsonParseException ?
111 throw new RuntimeException("Failed to invoke " + constructor
+ " with no args",
112 e
.getTargetException());
113 } catch (IllegalAccessException e
) {
114 throw new AssertionError(e
);
118 } catch (NoSuchMethodException e
) {
124 * Constructors for common interface types like Map and List and their
127 @SuppressWarnings("unchecked") // use runtime checks to guarantee that 'T' is what it is
128 private <T
> ObjectConstructor
<T
> newDefaultImplementationConstructor(
129 final Type type
, Class
<?
super T
> rawType
) {
130 if (Collection
.class.isAssignableFrom(rawType
)) {
131 if (SortedSet
.class.isAssignableFrom(rawType
)) {
132 return new ObjectConstructor
<T
>() {
133 public T
construct() {
134 return (T
) new TreeSet
<Object
>();
137 } else if (EnumSet
.class.isAssignableFrom(rawType
)) {
138 return new ObjectConstructor
<T
>() {
139 @SuppressWarnings("rawtypes")
140 public T
construct() {
141 if (type
instanceof ParameterizedType
) {
142 Type elementType
= ((ParameterizedType
) type
).getActualTypeArguments()[0];
143 if (elementType
instanceof Class
) {
144 return (T
) EnumSet
.noneOf((Class
)elementType
);
146 throw new JsonIOException("Invalid EnumSet type: " + type
.toString());
149 throw new JsonIOException("Invalid EnumSet type: " + type
.toString());
153 } else if (Set
.class.isAssignableFrom(rawType
)) {
154 return new ObjectConstructor
<T
>() {
155 public T
construct() {
156 return (T
) new LinkedHashSet
<Object
>();
159 } else if (Queue
.class.isAssignableFrom(rawType
)) {
160 return new ObjectConstructor
<T
>() {
161 public T
construct() {
162 return (T
) new LinkedList
<Object
>();
166 return new ObjectConstructor
<T
>() {
167 public T
construct() {
168 return (T
) new ArrayList
<Object
>();
174 if (Map
.class.isAssignableFrom(rawType
)) {
175 if (SortedMap
.class.isAssignableFrom(rawType
)) {
176 return new ObjectConstructor
<T
>() {
177 public T
construct() {
178 return (T
) new TreeMap
<Object
, Object
>();
181 } else if (type
instanceof ParameterizedType
&& !(String
.class.isAssignableFrom(
182 TypeToken
.get(((ParameterizedType
) type
).getActualTypeArguments()[0]).getRawType()))) {
183 return new ObjectConstructor
<T
>() {
184 public T
construct() {
185 return (T
) new LinkedHashMap
<Object
, Object
>();
189 return new ObjectConstructor
<T
>() {
190 public T
construct() {
191 return (T
) new LinkedTreeMap
<String
, Object
>();
200 private <T
> ObjectConstructor
<T
> newUnsafeAllocator(
201 final Type type
, final Class
<?
super T
> rawType
) {
202 return new ObjectConstructor
<T
>() {
203 private final UnsafeAllocator unsafeAllocator
= UnsafeAllocator
.create();
204 @SuppressWarnings("unchecked")
205 public T
construct() {
207 Object newInstance
= unsafeAllocator
.newInstance(rawType
);
208 return (T
) newInstance
;
209 } catch (Exception e
) {
210 throw new RuntimeException(("Unable to invoke no-args constructor for " + type
+ ". "
211 + "Register an InstanceCreator with Gson for this type may fix this problem."), e
);
217 @Override public String
toString() {
218 return instanceCreators
.toString();