/* * JBoss, Home of Professional Open Source * Copyright 2008, Red Hat, Inc., and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * 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 org.jboss.weld.util; 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.HashMap; import java.util.Map; import java.util.Set; import java.util.function.Function; import org.jboss.weld.resolution.CovariantTypes; import org.jboss.weld.util.collections.Arrays2; import org.jboss.weld.util.collections.ImmutableSet; import org.jboss.weld.util.reflection.GenericArrayTypeImpl; import org.jboss.weld.util.reflection.ParameterizedTypeImpl; import org.jboss.weld.util.reflection.Reflections; /** * Utility class for Types * * @author Pete Muir */ public class Types { public static final Function<Type, Class<?>> TYPE_TO_CLASS_FUNCTION = Reflections::getRawType; private Types() { } /** * Gets the boxed type of a class * * @param type The type * @return The boxed type */ public static Type boxedType(Type type) { if (type instanceof Class<?>) { return boxedClass((Class<?>) type); } else { return type; } } public static Class<?> boxedClass(Class<?> type) { if (!type.isPrimitive()) { return type; } else if (type.equals(Boolean.TYPE)) { return Boolean.class; } else if (type.equals(Character.TYPE)) { return Character.class; } else if (type.equals(Byte.TYPE)) { return Byte.class; } else if (type.equals(Short.TYPE)) { return Short.class; } else if (type.equals(Integer.TYPE)) { return Integer.class; } else if (type.equals(Long.TYPE)) { return Long.class; } else if (type.equals(Float.TYPE)) { return Float.class; } else if (type.equals(Double.TYPE)) { return Double.class; } else if (type.equals(Void.TYPE)) { return Void.class; } else { // Vagaries of if/else statement, can't be reached ;-) return type; } } public static String getTypeId(Type type) { if (type instanceof Class<?>) { return Reflections.<Class<?>> cast(type).getName(); } if (type instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) type; StringBuilder builder = new StringBuilder(getTypeId(pt.getRawType())); builder.append("<"); for (int i = 0; i < pt.getActualTypeArguments().length; i++) { if (i > 0) { builder.append(","); } builder.append(getTypeId(pt.getActualTypeArguments()[i])); } builder.append(">"); return builder.toString(); } if (type instanceof GenericArrayType) { GenericArrayType arrayType = (GenericArrayType) type; StringBuilder builder = new StringBuilder(getTypeId(arrayType.getGenericComponentType())); builder.append("[]"); return builder.toString(); } throw new IllegalArgumentException("Cannot create type id for " + type.toString()); } /** * Returns a canonical type for a given class. * * If the class is a raw type of a parameterized class, the matching {@link ParameterizedType} (with unresolved type * variables) is resolved. * * If the class is an array then the component type of the array is canonicalized * * Otherwise, the class is returned. * * @return */ public static Type getCanonicalType(Class<?> clazz) { if (clazz.isArray()) { Class<?> componentType = clazz.getComponentType(); Type resolvedComponentType = getCanonicalType(componentType); if (componentType != resolvedComponentType) { // identity check intentional // a different identity means that we actually replaced the component Class with a ParameterizedType return new GenericArrayTypeImpl(resolvedComponentType); } } if (clazz.getTypeParameters().length > 0) { Type[] actualTypeParameters = clazz.getTypeParameters(); return new ParameterizedTypeImpl(clazz, actualTypeParameters, clazz.getDeclaringClass()); } return clazz; } /** * * @param type * @return */ public static Type getCanonicalType(Type type) { if (type instanceof Class<?>) { Class<?> clazz = (Class<?>) type; return getCanonicalType(clazz); } return type; } public static boolean containsTypeVariable(Type type) { type = Types.getCanonicalType(type); if (type instanceof TypeVariable<?>) { return true; } if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; for (Type t : parameterizedType.getActualTypeArguments()) { if (containsTypeVariable(t)) { return true; } } } if (type instanceof GenericArrayType) { GenericArrayType genericArrayType = (GenericArrayType) type; return containsTypeVariable(genericArrayType.getGenericComponentType()); } return false; } public static Set<Class<?>> getRawTypes(Set<Type> types) { return types.stream().map(Reflections::getRawType).collect(ImmutableSet.collector()); } public static Class<?>[] getRawTypes(Type[] types) { if (types.length == 0) { return Arrays2.EMPTY_CLASS_ARRAY; } Class<?>[] result = new Class<?>[types.length]; for (int i = 0; i < types.length; i++) { result[i] = TYPE_TO_CLASS_FUNCTION.apply(types[i]); } return result; } /** * Builds (class name -> class) map for given classes. */ @SuppressWarnings("all") public static <C extends Class<?>> Map<String, C> buildClassNameMap(Iterable<C> set) { Map<String, C> classNameMap = new HashMap<String, C>(); for (C javaClass : set) { classNameMap.put(javaClass.getName(), javaClass); } return classNameMap; } /** * Determines whether the given type is an actual type. A type is considered actual if it is a raw type, a parameterized type * or an array type. * * @param type the given type * @return true if and only if the given type is an actual type */ public static boolean isActualType(Type type) { return (type instanceof Class<?>) || (type instanceof ParameterizedType) || (type instanceof GenericArrayType); } /** * Determines whether the given type is an array type. * * @param type the given type * @return true if the given type is a subclass of java.lang.Class or implements GenericArrayType */ public static boolean isArray(Type type) { return (type instanceof GenericArrayType) || (type instanceof Class<?> && ((Class<?>) type).isArray()); } /** * Determines the component type for a given array type. * * @param type the given array type * @return the component type of a given array type */ public static Type getArrayComponentType(Type type) { if (type instanceof GenericArrayType) { return GenericArrayType.class.cast(type).getGenericComponentType(); } if (type instanceof Class<?>) { Class<?> clazz = (Class<?>) type; if (clazz.isArray()) { return clazz.getComponentType(); } } throw new IllegalArgumentException("Not an array type " + type); } /** * Determines whether the given array only contains unbounded type variables or Object.class. * * @param types the given array of types * @return true if and only if the given array only contains unbounded type variables or Object.class */ public static boolean isArrayOfUnboundedTypeVariablesOrObjects(Type[] types) { for (Type type : types) { if (Object.class.equals(type)) { continue; } if (type instanceof TypeVariable<?>) { Type[] bounds = ((TypeVariable<?>) type).getBounds(); if (bounds == null || bounds.length == 0 || (bounds.length == 1 && Object.class.equals(bounds[0]))) { continue; } } return false; } return true; } public static boolean isRawGenericType(Type type) { if (!(type instanceof Class<?>)) { return false; } Class<?> clazz = (Class<?>) type; if (clazz.isArray()) { Class<?> componentType = clazz.getComponentType(); return isRawGenericType(componentType); } return clazz.getTypeParameters().length > 0; } /** * * @param beanType * @return <code>true</code> if the given type is not a legal bean type, <code>false</code> otherwise */ public static boolean isIllegalBeanType(Type beanType) { boolean result = false; if (beanType instanceof TypeVariable<?>) { result = true; } else if (beanType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) beanType; for (Type typeArgument : parameterizedType.getActualTypeArguments()) { if (typeArgument instanceof TypeVariable<?>) { // Parameterized type with type variable is legal continue; } else if (typeArgument instanceof WildcardType || isIllegalBeanType(typeArgument)) { result = true; break; } } } else if (beanType instanceof GenericArrayType) { GenericArrayType arrayType = (GenericArrayType) beanType; result = isIllegalBeanType(arrayType.getGenericComponentType()); } return result; } /** * * @param type1 * @param type2 * @return <code>true</code> if the first type is more specific than the second type (is a subtype of), <code>false</code> otherwise */ public static boolean isMoreSpecific(Type type1, Type type2) { if(type1.equals(type2)) { return false; } return CovariantTypes.isAssignableFrom(type2, type1); } }