| /* |
| * Copyright 2010 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.web.bindery.autobean.vm.impl; |
| |
| import java.lang.reflect.Array; |
| import java.lang.reflect.GenericArrayType; |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.lang.reflect.TypeVariable; |
| import java.lang.reflect.WildcardType; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Shared code for answering question about Class objects. This is a |
| * server-compatible analog to ModelUtils. |
| */ |
| public class TypeUtils { |
| static final Map<Class<?>, Class<?>> AUTOBOX_MAP; |
| static final Map<Class<?>, Object> DEFAULT_PRIMITIVE_VALUES; |
| @SuppressWarnings("unchecked") |
| static final Set<Class<?>> VALUE_TYPES = Collections.unmodifiableSet(new HashSet<Class<?>>( |
| Arrays.asList(Boolean.class, Character.class, Class.class, Date.class, |
| Enum.class, Number.class, String.class, Void.class))); |
| |
| static { |
| Map<Class<?>, Object> temp = new HashMap<Class<?>, Object>(); |
| temp.put(boolean.class, false); |
| temp.put(byte.class, (byte) 0); |
| temp.put(char.class, (char) 0); |
| temp.put(double.class, (double) 0); |
| temp.put(float.class, (float) 0); |
| temp.put(int.class, 0); |
| temp.put(long.class, (long) 0); |
| temp.put(short.class, (short) 0); |
| temp.put(void.class, null); |
| |
| DEFAULT_PRIMITIVE_VALUES = Collections.unmodifiableMap(temp); |
| } |
| |
| static { |
| Map<Class<?>, Class<?>> autoBoxMap = new HashMap<Class<?>, Class<?>>(); |
| autoBoxMap.put(boolean.class, Boolean.class); |
| autoBoxMap.put(byte.class, Byte.class); |
| autoBoxMap.put(char.class, Character.class); |
| autoBoxMap.put(double.class, Double.class); |
| autoBoxMap.put(float.class, Float.class); |
| autoBoxMap.put(int.class, Integer.class); |
| autoBoxMap.put(long.class, Long.class); |
| autoBoxMap.put(short.class, Short.class); |
| autoBoxMap.put(void.class, Void.class); |
| AUTOBOX_MAP = Collections.unmodifiableMap(autoBoxMap); |
| } |
| |
| /** |
| * Similar to ModelUtils#ensureBaseType(JType) but for the reflection API. |
| */ |
| public static Class<?> ensureBaseType(Type type) { |
| if (type instanceof Class<?>) { |
| return (Class<?>) type; |
| } |
| if (type instanceof GenericArrayType) { |
| return Array.newInstance( |
| ensureBaseType(((GenericArrayType) type).getGenericComponentType()), |
| 0).getClass(); |
| } |
| if (type instanceof ParameterizedType) { |
| return ensureBaseType(((ParameterizedType) type).getRawType()); |
| } |
| if (type instanceof TypeVariable<?>) { |
| return ensureBaseType(((TypeVariable<?>) type).getBounds()[0]); |
| } |
| if (type instanceof WildcardType) { |
| WildcardType wild = (WildcardType) type; |
| return ensureBaseType(wild.getUpperBounds()[0]); |
| } |
| throw new RuntimeException("Cannot handle " + type.getClass().getName()); |
| } |
| |
| /** |
| * Given a primitive Class type, return a default value. |
| */ |
| public static Object getDefaultPrimitiveValue(Class<?> clazz) { |
| assert clazz.isPrimitive() : "Expecting primitive type"; |
| return DEFAULT_PRIMITIVE_VALUES.get(clazz); |
| } |
| |
| public static Type[] getParameterization(Class<?> intf, Type... types) { |
| for (Type type : types) { |
| if (type == null) { |
| continue; |
| } else if (type instanceof ParameterizedType) { |
| ParameterizedType param = (ParameterizedType) type; |
| Type[] actualTypeArguments = param.getActualTypeArguments(); |
| Class<?> base = ensureBaseType(param.getRawType()); |
| Type[] typeParameters = base.getTypeParameters(); |
| |
| Map<Type, Type> map = new HashMap<Type, Type>(); |
| for (int i = 0, j = typeParameters.length; i < j; i++) { |
| map.put(typeParameters[i], actualTypeArguments[i]); |
| } |
| Type[] lookFor = intf.equals(base) ? intf.getTypeParameters() |
| : getParameterization(intf, base.getGenericInterfaces()); |
| List<Type> toReturn = new ArrayList<Type>(); |
| for (int i = 0, j = lookFor.length; i < j; i++) { |
| Type found = map.get(lookFor[i]); |
| if (found != null) { |
| toReturn.add(found); |
| } |
| } |
| return toReturn.toArray(new Type[toReturn.size()]); |
| } else if (type instanceof Class<?>) { |
| Class<?> clazz = (Class<?>) type; |
| if (intf.equals(clazz)) { |
| return intf.getTypeParameters(); |
| } |
| Type[] found = getParameterization(intf, clazz.getGenericSuperclass()); |
| if (found != null) { |
| return found; |
| } |
| found = getParameterization(intf, clazz.getGenericInterfaces()); |
| if (found != null) { |
| return found; |
| } |
| } |
| } |
| return null; |
| } |
| |
| public static Type getSingleParameterization(Class<?> intf, Type... types) { |
| Type[] found = getParameterization(intf, types); |
| return found == null ? null : found[0]; |
| } |
| |
| public static boolean isValueType(Class<?> clazz) { |
| if (clazz.isPrimitive() || VALUE_TYPES.contains(clazz)) { |
| return true; |
| } |
| for (Class<?> c : VALUE_TYPES) { |
| if (c.isAssignableFrom(clazz)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public static <V> Class<V> maybeAutobox(Class<V> domainType) { |
| @SuppressWarnings("unchecked") |
| Class<V> autoBoxType = (Class<V>) AUTOBOX_MAP.get(domainType); |
| return autoBoxType == null ? domainType : autoBoxType; |
| } |
| |
| public static Type resolveGenerics(Class<?> containingType, Type type) { |
| if (type instanceof Class<?>) { |
| return type; |
| } else if (type instanceof ParameterizedType) { |
| final ParameterizedType param = (ParameterizedType) type; |
| Type[] actualTypeArguments = param.getActualTypeArguments(); |
| boolean hadTypeVariable = false; |
| final ArrayList<Type> resolvedTypeArguments = new ArrayList<Type>(actualTypeArguments.length); |
| for (Type actualTypeArgument : actualTypeArguments) { |
| Type resolvedTypeArgument = resolveGenerics(containingType, actualTypeArgument); |
| hadTypeVariable = hadTypeVariable || (resolvedTypeArgument != actualTypeArgument); |
| resolvedTypeArguments.add(resolvedTypeArgument); |
| } |
| if (!hadTypeVariable) { |
| return type; |
| } |
| return new ParameterizedType() { |
| @Override |
| public Type getRawType() { |
| return param.getRawType(); |
| } |
| |
| @Override |
| public Type getOwnerType() { |
| return param.getOwnerType(); |
| } |
| |
| @Override |
| public Type[] getActualTypeArguments() { |
| return resolvedTypeArguments.toArray(new Type[resolvedTypeArguments.size()]); |
| } |
| }; |
| } else if (type instanceof TypeVariable<?>) { |
| TypeVariable<?> variable = (TypeVariable<?>) type; |
| Object genericDeclaration = variable.getGenericDeclaration(); |
| if (genericDeclaration instanceof Class<?>) { |
| Class<?> declaration = (Class<?>) genericDeclaration; |
| // could probably optimize, but would involve duplicating a bunch of |
| // getParameterization's code |
| Type[] types = getParameterization(declaration, containingType); |
| TypeVariable<?>[] x = declaration.getTypeParameters(); |
| for (int i = 0; i < x.length; i++) { |
| if (variable.equals(x[i])) { |
| return resolveGenerics(containingType, types[i]); |
| } |
| } |
| throw new IllegalStateException( |
| "TypeVariable not found in its generic declaration type; must be a JVM error"); |
| } else { |
| // Use "erasure" for method-level type variables; should we throw |
| // instead? |
| return ensureBaseType(type); |
| } |
| } else if (type instanceof WildcardType) { |
| return resolveGenerics(containingType, ((WildcardType) type).getUpperBounds()[0]); |
| } else { |
| throw new RuntimeException("Cannot handle " + type.getClass().getName()); |
| } |
| } |
| |
| private TypeUtils() { |
| } |
| } |