blob: 73b977ee8db8935e1d450b8c52e4459ab8f17805 [file] [log] [blame]
/*
* 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;
}
private TypeUtils() {
}
}