/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.config.reflect; 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.HashMap; import java.util.LinkedHashSet; import java.util.Set; import com.caucho.config.inject.InjectManager; import com.caucho.inject.Module; /** * type matching the web bean */ @Module abstract public class BaseType { private static final BaseType []NULL_PARAM = new BaseType[0]; private LinkedHashSet<Type> _typeSet; private LinkedHashSet<BaseType> _typeClosureSet; public static BaseType createForTarget(Type type, HashMap<String,BaseType> paramMap, String paramDeclName) { return create(type, paramMap, paramDeclName, ClassFill.TARGET); } public static BaseType createForSource(Type type, HashMap<String,BaseType> paramMap, String paramDeclName) { // return create(type, paramMap, false); return create(type, paramMap, paramDeclName, ClassFill.SOURCE); } public static BaseType create(Type type, HashMap<String,BaseType> paramMap, String paramDeclName, ClassFill classFill) { return create(type, paramMap, paramDeclName, null, classFill); } public static BaseType create(Type type, HashMap<String,BaseType> paramMap, String paramDeclName, Type parentType, ClassFill classFill) { if (type instanceof Class<?>) { Class<?> cl = (Class<?>) type; TypeVariable<?> []typeParam = cl.getTypeParameters(); if (typeParam == null || typeParam.length == 0) return ClassType.create(cl); if (classFill == ClassFill.PLAIN) return ClassType.create(cl); else if (classFill == ClassFill.SOURCE) return createGenericClass(cl); // ioc/0p80 vs ioc/1238 /* if (true) return ClassType.create(cl); */ BaseType []args = new BaseType[typeParam.length]; HashMap<String,BaseType> newParamMap = new HashMap<String,BaseType>(); for (int i = 0; i < args.length; i++) { BaseType value = null; if (paramMap != null) value = paramMap.get(typeParam[i].getName()); // ioc/0246 if (value != null) args[i] = value; else args[i] = TargetObjectType.OBJECT_TYPE; if (args[i] == null) { throw new NullPointerException("unsupported BaseType: " + type); } newParamMap.put(typeParam[i].getName(), args[i]); } return new GenericParamType(cl, args, newParamMap); } else if (type instanceof ParameterizedType) { ParameterizedType pType = (ParameterizedType) type; Class<?> rawType = (Class<?>) pType.getRawType(); Type []typeArgs = pType.getActualTypeArguments(); BaseType []args = new BaseType[typeArgs.length]; for (int i = 0; i < args.length; i++) { args[i] = create(typeArgs[i], paramMap, paramDeclName, type, ClassFill.TARGET); if (args[i] == null) { throw new NullPointerException("unsupported BaseType: " + type); } } HashMap<String,BaseType> newParamMap = new HashMap<String,BaseType>(); TypeVariable<?> []typeVars = rawType.getTypeParameters(); for (int i = 0; i < typeVars.length; i++) { newParamMap.put(typeVars[i].getName(), args[i]); } ParamType paramType = new ParamType(rawType, args, newParamMap); return paramType; } else if (type instanceof GenericArrayType) { GenericArrayType aType = (GenericArrayType) type; BaseType baseType = create(aType.getGenericComponentType(), paramMap, paramDeclName, classFill); Class<?> rawType = Array.newInstance(baseType.getRawClass(), 0).getClass(); return new ArrayType(baseType, rawType); } else if (type instanceof TypeVariable<?>) { TypeVariable<?> aType = (TypeVariable<?>) type; return createVar(aType, paramMap, paramDeclName, parentType, classFill); } else if (type instanceof WildcardType) { WildcardType aType = (WildcardType) type; BaseType []lowerBounds = toBaseType(aType.getLowerBounds(), paramMap, paramDeclName); BaseType []upperBounds = toBaseType(aType.getUpperBounds(), paramMap, paramDeclName); return new WildcardTypeImpl(lowerBounds, upperBounds); } else { throw new IllegalStateException("unsupported BaseType: " + type + " " + (type != null ? type.getClass() : null)); } } private static BaseType createVar(Type type, HashMap<String,BaseType> paramMap, String paramDeclName, Type parentType, ClassFill classFill) { TypeVariable aType = (TypeVariable) type; BaseType actualType = null; String aTypeName = aType.getName(); if (paramMap != null) { actualType = (BaseType) paramMap.get(aTypeName); if (actualType != null) return actualType; if (paramDeclName != null) { aTypeName = paramDeclName + "_" + aType.getName(); actualType = (BaseType) paramMap.get(aTypeName); } if (actualType != null) return actualType; } String varName; if (paramMap != null) varName = createVarName(paramMap, paramDeclName); else varName = aType.getName(); BaseType []baseBounds; if (aType.getBounds() != null) { Type []bounds = aType.getBounds(); baseBounds = new BaseType[bounds.length]; for (int i = 0; i < bounds.length; i++) { // ejb/1243 - Enum if (bounds[i] != parentType) baseBounds[i] = create(bounds[i], paramMap, paramDeclName, type, ClassFill.TARGET); else baseBounds[i] = ObjectType.OBJECT_TYPE; } } else baseBounds = new BaseType[0]; VarType<?> varType = new VarType(varName, baseBounds); if (paramMap != null) paramMap.put(aTypeName, varType); return varType; } private static String createVarName(HashMap<String,BaseType> paramMap, String paramDeclName) { for (int i = 0; true; i++) { String name = "T_" + i; if (paramDeclName != null) name = paramDeclName + "_" + name; if (! paramMap.containsKey(name)) return name; } } /** * Create a class-based type, where any parameters are filled with the * variables, not Object. */ public static BaseType createClass(Class<?> type) { // ioc/1238 // ioc/07f2 return ClassType.create(type); } /** * Create a class-based type, where any parameters are filled with the * variables, not Object. */ public static BaseType createGenericClass(Class<?> type) { TypeVariable<?> []typeParam = type.getTypeParameters(); if (typeParam == null || typeParam.length == 0) return ClassType.create(type); BaseType []args = new BaseType[typeParam.length]; HashMap<String,BaseType> newParamMap = new HashMap<String,BaseType>(); String paramDeclName = null; for (int i = 0; i < args.length; i++) { args[i] = create(typeParam[i], newParamMap, paramDeclName, ClassFill.TARGET); if (args[i] == null) { throw new NullPointerException("unsupported BaseType: " + type); } newParamMap.put(typeParam[i].getName(), args[i]); } // ioc/07f2 return new GenericParamType(type, args, newParamMap); } private static BaseType []toBaseType(Type []types, HashMap<String,BaseType> paramMap, String paramDeclName) { if (types == null) return NULL_PARAM; BaseType []baseTypes = new BaseType[types.length]; for (int i = 0; i < types.length; i++) { baseTypes[i] = create(types[i], paramMap, paramDeclName, ClassFill.TARGET); } return baseTypes; } abstract public Class<?> getRawClass(); public HashMap<String,BaseType> getParamMap() { return null; } public BaseType []getParameters() { return NULL_PARAM; } public boolean isWildcard() { return false; } /** * Returns true for a generic type like MyBean<X> or MyBean<?> */ public boolean isGeneric() { return false; } /** * Returns true for a generic variable type like MyBean<X>, but not MyBean<?> */ public boolean isGenericVariable() { return isVariable(); } /** * Returns true for a variable type like X */ public boolean isVariable() { return false; } /** * Returns true for a raw type like MyBean where the class definition * is MyBean<X>. */ public boolean isGenericRaw() { return false; } public boolean isPrimitive() { return false; } public boolean isObject() { return false; } protected BaseType []getWildcardBounds() { return NULL_PARAM; } public boolean isAssignableFrom(BaseType type) { throw new UnsupportedOperationException(getClass().getName()); } /** * Assignable as a parameter. */ public boolean isParamAssignableFrom(BaseType type) { return equals(type); } public Type toType() { throw new UnsupportedOperationException(getClass().getName()); } /** * Fills in a parameter with a given name. */ public BaseType fill(BaseType ... baseType) { throw new UnsupportedOperationException(getClass().getName()); } /** * Returns the type closure of the base type. */ public final Set<Type> getTypeClosure(InjectManager manager) { if (_typeSet == null) { LinkedHashSet<Type> typeSet = new LinkedHashSet<Type>(); fillTypeClosure(manager, typeSet); _typeSet = typeSet; } return _typeSet; } /** * Returns the type closure of the base type. */ public final Set<BaseType> getBaseTypeClosure(InjectManager manager) { if (_typeClosureSet == null) { LinkedHashSet<BaseType> baseTypeSet = new LinkedHashSet<BaseType>(); for (Type type : getTypeClosure(manager)) { baseTypeSet.add(manager.createSourceBaseType(type)); } _typeClosureSet = baseTypeSet; } return _typeClosureSet; } protected void fillTypeClosure(InjectManager manager, Set<Type> typeSet) { typeSet.add(toType()); } public void fillSyntheticTypes(Set<VarType<?>> varTypeList) { } public String getSimpleName() { return getRawClass().getSimpleName(); } @Override public String toString() { return getRawClass().getName(); } public enum ClassFill { PLAIN, SOURCE, TARGET; } }