| 1 | /* |
| 2 | * Copyright (C) 2008 Google Inc. |
| 3 | * |
| 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 |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 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. |
| 15 | */ |
| 16 | |
| 17 | package com.google.gson.reflect; |
| 18 | |
| 19 | import com.google.gson.internal.$Gson$Types; |
| 20 | import com.google.gson.internal.$Gson$Preconditions; |
| 21 | import java.lang.reflect.GenericArrayType; |
| 22 | import java.lang.reflect.ParameterizedType; |
| 23 | import java.lang.reflect.Type; |
| 24 | import java.lang.reflect.TypeVariable; |
| 25 | import java.util.HashMap; |
| 26 | import java.util.Map; |
| 27 | |
| 28 | /** |
| 29 | * Represents a generic type {@code T}. Java doesn't yet provide a way to |
| 30 | * represent generic types, so this class does. Forces clients to create a |
| 31 | * subclass of this class which enables retrieval the type information even at |
| 32 | * runtime. |
| 33 | * |
| 34 | * <p>For example, to create a type literal for {@code List<String>}, you can |
| 35 | * create an empty anonymous inner class: |
| 36 | * |
| 37 | * <p> |
| 38 | * {@code TypeToken<List<String>> list = new TypeToken<List<String>>() {};} |
| 39 | * |
| 40 | * <p>This syntax cannot be used to create type literals that have wildcard |
| 41 | * parameters, such as {@code Class<?>} or {@code List<? extends CharSequence>}. |
| 42 | * |
| 43 | * @author Bob Lee |
| 44 | * @author Sven Mawson |
| 45 | * @author Jesse Wilson |
| 46 | */ |
| 47 | public class TypeToken<T> { |
| 48 | final Class<? super T> rawType; |
| 49 | final Type type; |
| 50 | final int hashCode; |
| 51 | |
| 52 | /** |
| 53 | * Constructs a new type literal. Derives represented class from type |
| 54 | * parameter. |
| 55 | * |
| 56 | * <p>Clients create an empty anonymous subclass. Doing so embeds the type |
| 57 | * parameter in the anonymous class's type hierarchy so we can reconstitute it |
| 58 | * at runtime despite erasure. |
| 59 | */ |
| 60 | @SuppressWarnings("unchecked") |
| 61 | protected TypeToken() { |
| 62 | this.type = getSuperclassTypeParameter(getClass()); |
| 63 | this.rawType = (Class<? super T>) $Gson$Types.getRawType(type); |
| 64 | this.hashCode = type.hashCode(); |
| 65 | } |
| 66 | |
| 67 | /** |
| 68 | * Unsafe. Constructs a type literal manually. |
| 69 | */ |
| 70 | @SuppressWarnings("unchecked") |
| 71 | TypeToken(Type type) { |
| 72 | this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type)); |
| 73 | this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type); |
| 74 | this.hashCode = this.type.hashCode(); |
| 75 | } |
| 76 | |
| 77 | /** |
| 78 | * Returns the type from super class's type parameter in {@link $Gson$Types#canonicalize |
| 79 | * canonical form}. |
| 80 | */ |
| 81 | static Type getSuperclassTypeParameter(Class<?> subclass) { |
| 82 | Type superclass = subclass.getGenericSuperclass(); |
| 83 | if (superclass instanceof Class) { |
| 84 | throw new RuntimeException("Missing type parameter."); |
| 85 | } |
| 86 | ParameterizedType parameterized = (ParameterizedType) superclass; |
| 87 | return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]); |
| 88 | } |
| 89 | |
| 90 | /** |
| 91 | * Returns the raw (non-generic) type for this type. |
| 92 | */ |
| 93 | public final Class<? super T> getRawType() { |
| 94 | return rawType; |
| 95 | } |
| 96 | |
| 97 | /** |
| 98 | * Gets underlying {@code Type} instance. |
| 99 | */ |
| 100 | public final Type getType() { |
| 101 | return type; |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * Check if this type is assignable from the given class object. |
| 106 | * |
| 107 | * @deprecated this implementation may be inconsistent with javac for types |
| 108 | * with wildcards. |
| 109 | */ |
| 110 | @Deprecated |
| 111 | public boolean isAssignableFrom(Class<?> cls) { |
| 112 | return isAssignableFrom((Type) cls); |
| 113 | } |
| 114 | |
| 115 | /** |
| 116 | * Check if this type is assignable from the given Type. |
| 117 | * |
| 118 | * @deprecated this implementation may be inconsistent with javac for types |
| 119 | * with wildcards. |
| 120 | */ |
| 121 | @Deprecated |
| 122 | public boolean isAssignableFrom(Type from) { |
| 123 | if (from == null) { |
| 124 | return false; |
| 125 | } |
| 126 | |
| 127 | if (type.equals(from)) { |
| 128 | return true; |
| 129 | } |
| 130 | |
| 131 | if (type instanceof Class<?>) { |
| 132 | return rawType.isAssignableFrom($Gson$Types.getRawType(from)); |
| 133 | } else if (type instanceof ParameterizedType) { |
| 134 | return isAssignableFrom(from, (ParameterizedType) type, |
| 135 | new HashMap<String, Type>()); |
| 136 | } else if (type instanceof GenericArrayType) { |
| 137 | return rawType.isAssignableFrom($Gson$Types.getRawType(from)) |
| 138 | && isAssignableFrom(from, (GenericArrayType) type); |
| 139 | } else { |
| 140 | throw buildUnexpectedTypeError( |
| 141 | type, Class.class, ParameterizedType.class, GenericArrayType.class); |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | /** |
| 146 | * Check if this type is assignable from the given type token. |
| 147 | * |
| 148 | * @deprecated this implementation may be inconsistent with javac for types |
| 149 | * with wildcards. |
| 150 | */ |
| 151 | @Deprecated |
| 152 | public boolean isAssignableFrom(TypeToken<?> token) { |
| 153 | return isAssignableFrom(token.getType()); |
| 154 | } |
| 155 | |
| 156 | /** |
| 157 | * Private helper function that performs some assignability checks for |
| 158 | * the provided GenericArrayType. |
| 159 | */ |
| 160 | private static boolean isAssignableFrom(Type from, GenericArrayType to) { |
| 161 | Type toGenericComponentType = to.getGenericComponentType(); |
| 162 | if (toGenericComponentType instanceof ParameterizedType) { |
| 163 | Type t = from; |
| 164 | if (from instanceof GenericArrayType) { |
| 165 | t = ((GenericArrayType) from).getGenericComponentType(); |
| 166 | } else if (from instanceof Class<?>) { |
| 167 | Class<?> classType = (Class<?>) from; |
| 168 | while (classType.isArray()) { |
| 169 | classType = classType.getComponentType(); |
| 170 | } |
| 171 | t = classType; |
| 172 | } |
| 173 | return isAssignableFrom(t, (ParameterizedType) toGenericComponentType, |
| 174 | new HashMap<String, Type>()); |
| 175 | } |
| 176 | // No generic defined on "to"; therefore, return true and let other |
| 177 | // checks determine assignability |
| 178 | return true; |
| 179 | } |
| 180 | |
| 181 | /** |
| 182 | * Private recursive helper function to actually do the type-safe checking |
| 183 | * of assignability. |
| 184 | */ |
| 185 | private static boolean isAssignableFrom(Type from, ParameterizedType to, |
| 186 | Map<String, Type> typeVarMap) { |
| 187 | |
| 188 | if (from == null) { |
| 189 | return false; |
| 190 | } |
| 191 | |
| 192 | if (to.equals(from)) { |
| 193 | return true; |
| 194 | } |
| 195 | |
| 196 | // First figure out the class and any type information. |
| 197 | Class<?> clazz = $Gson$Types.getRawType(from); |
| 198 | ParameterizedType ptype = null; |
| 199 | if (from instanceof ParameterizedType) { |
| 200 | ptype = (ParameterizedType) from; |
| 201 | } |
| 202 | |
| 203 | // Load up parameterized variable info if it was parameterized. |
| 204 | if (ptype != null) { |
| 205 | Type[] tArgs = ptype.getActualTypeArguments(); |
| 206 | TypeVariable<?>[] tParams = clazz.getTypeParameters(); |
| 207 | for (int i = 0; i < tArgs.length; i++) { |
| 208 | Type arg = tArgs[i]; |
| 209 | TypeVariable<?> var = tParams[i]; |
| 210 | while (arg instanceof TypeVariable<?>) { |
| 211 | TypeVariable<?> v = (TypeVariable<?>) arg; |
| 212 | arg = typeVarMap.get(v.getName()); |
| 213 | } |
| 214 | typeVarMap.put(var.getName(), arg); |
| 215 | } |
| 216 | |
| 217 | // check if they are equivalent under our current mapping. |
| 218 | if (typeEquals(ptype, to, typeVarMap)) { |
| 219 | return true; |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | for (Type itype : clazz.getGenericInterfaces()) { |
| 224 | if (isAssignableFrom(itype, to, new HashMap<String, Type>(typeVarMap))) { |
| 225 | return true; |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | // Interfaces didn't work, try the superclass. |
| 230 | Type sType = clazz.getGenericSuperclass(); |
| 231 | return isAssignableFrom(sType, to, new HashMap<String, Type>(typeVarMap)); |
| 232 | } |
| 233 | |
| 234 | /** |
| 235 | * Checks if two parameterized types are exactly equal, under the variable |
| 236 | * replacement described in the typeVarMap. |
| 237 | */ |
| 238 | private static boolean typeEquals(ParameterizedType from, |
| 239 | ParameterizedType to, Map<String, Type> typeVarMap) { |
| 240 | if (from.getRawType().equals(to.getRawType())) { |
| 241 | Type[] fromArgs = from.getActualTypeArguments(); |
| 242 | Type[] toArgs = to.getActualTypeArguments(); |
| 243 | for (int i = 0; i < fromArgs.length; i++) { |
| 244 | if (!matches(fromArgs[i], toArgs[i], typeVarMap)) { |
| 245 | return false; |
| 246 | } |
| 247 | } |
| 248 | return true; |
| 249 | } |
| 250 | return false; |
| 251 | } |
| 252 | |
| 253 | private static AssertionError buildUnexpectedTypeError( |
| 254 | Type token, Class<?>... expected) { |
| 255 | |
| 256 | // Build exception message |
| 257 | StringBuilder exceptionMessage = |
| 258 | new StringBuilder("Unexpected type. Expected one of: "); |
| 259 | for (Class<?> clazz : expected) { |
| 260 | exceptionMessage.append(clazz.getName()).append(", "); |
| 261 | } |
| 262 | exceptionMessage.append("but got: ").append(token.getClass().getName()) |
| 263 | .append(", for type token: ").append(token.toString()).append('.'); |
| 264 | |
| 265 | return new AssertionError(exceptionMessage.toString()); |
| 266 | } |
| 267 | |
| 268 | /** |
| 269 | * Checks if two types are the same or are equivalent under a variable mapping |
| 270 | * given in the type map that was provided. |
| 271 | */ |
| 272 | private static boolean matches(Type from, Type to, Map<String, Type> typeMap) { |
| 273 | return to.equals(from) |
| 274 | || (from instanceof TypeVariable |
| 275 | && to.equals(typeMap.get(((TypeVariable<?>) from).getName()))); |
| 276 | |
| 277 | } |
| 278 | |
| 279 | @Override public final int hashCode() { |
| 280 | return this.hashCode; |
| 281 | } |
| 282 | |
| 283 | @Override public final boolean equals(Object o) { |
| 284 | return o instanceof TypeToken<?> |
| 285 | && $Gson$Types.equals(type, ((TypeToken<?>) o).type); |
| 286 | } |
| 287 | |
| 288 | @Override public final String toString() { |
| 289 | return $Gson$Types.typeToString(type); |
| 290 | } |
| 291 | |
| 292 | /** |
| 293 | * Gets type literal for the given {@code Type} instance. |
| 294 | */ |
| 295 | public static TypeToken<?> get(Type type) { |
| 296 | return new TypeToken<Object>(type); |
| 297 | } |
| 298 | |
| 299 | /** |
| 300 | * Gets type literal for the given {@code Class} instance. |
| 301 | */ |
| 302 | public static <T> TypeToken<T> get(Class<T> type) { |
| 303 | return new TypeToken<T>(type); |
| 304 | } |
| 305 | } |