/*
* 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;
}
}