/*==========================================================================*\
| $Id: Type.java,v 1.5 2011/06/09 15:31:24 stedwar2 Exp $
|*-------------------------------------------------------------------------*|
| Copyright (C) 2011 Virginia Tech
|
| This file is part of the Student-Library.
|
| The Student-Library is free software; you can redistribute it and/or
| modify it under the terms of the GNU Lesser General Public License as
| published by the Free Software Foundation; either version 3 of the
| License, or (at your option) any later version.
|
| The Student-Library 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. See the
| GNU Lesser General Public License for more details.
|
| You should have received a copy of the GNU Lesser General Public License
| along with the Student-Library; if not, see <http://www.gnu.org/licenses/>.
\*==========================================================================*/
package student.testingsupport.reflection;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import student.testingsupport.reflection.internal.Types;
//-------------------------------------------------------------------------
/**
* TODO: document.
*
* TODO: add annotation support.
*
* @param <ClassType> If present, this is a constraint on the type that
* this object represents.
*
* @author Stephen Edwards
* @author Last changed by $Author: stedwar2 $
* @version $Revision: 1.5 $, $Date: 2011/06/09 15:31:24 $
*/
public class Type<ClassType>
extends NameFilter<Type<ClassType>, Class<ClassType>>
{
//~ Fields ................................................................
private static final List<Class<?>> PRIMITIVE_TYPES = Arrays.asList(
new Class<?>[] {
boolean.class,
byte.class,
short.class,
char.class,
int.class,
long.class,
float.class,
double.class
});
private static final List<Class<?>> WRAPPER_TYPES = Arrays.asList(
new Class<?>[] {
Boolean.class,
Byte.class,
Short.class,
Character.class,
Integer.class,
Long.class,
Float.class,
Double.class
});
//~ Constructor ...........................................................
// ----------------------------------------------------------
/**
* Create a new Type object that represents a type filter.
* @param previous The previous filter in the chain of filters.
* @param descriptionOfThisStage A description of this stage in the
* filter chain.
*/
protected Type(Type<ClassType> previous, String descriptionOfThisStage)
{
super(previous, descriptionOfThisStage);
}
//~ Public Methods ........................................................
// ----------------------------------------------------------
/**
* A starting point for type filters where the name is unspecified.
*/
public static final Type<?> type =
new Type<Object>(null, null).typeResolver();
// ----------------------------------------------------------
/**
* Create a new Type object.
* @param name The name of the type (class).
* @return a new Type filter object representing the named field.
*/
public static Type<?> type(String name)
{
return type.withName(name);
}
// ----------------------------------------------------------
/**
* Create a new Type object from a {@link Class} object.
* @param aClass The class to represent.
* @param <T> This type is deduced from aClass.
* @return a new Type object representing the given class.
*/
public static <T> Type<T> type(Class<T> aClass)
{
if (aClass == null)
{
@SuppressWarnings("unchecked")
Type<T> result = (Type<T>)type;
return result;
}
@SuppressWarnings("unchecked")
Type<T> result = ((Type<T>)type).createFreshFilter(aClass);
return result;
}
// ----------------------------------------------------------
/**
* Restrict type searches that look for all classes in a package, or
* all classes visible to a class loader, to the specified list of
* search locations (the default is the search path in the system
* property
* <code>student.testingsupport.reflection.internal.Types.searchPath</code>,
* or the set of directories visible from the class loader where
* this class was loaded).
* This restriction affects all type searches when a fully qualified
* class name is not provided.
* @param searchPath The search path to use. A classpath-like string
* denoting the classpath locations where searching should be performed.
*/
public static void restrictSearchesTo(String searchPath)
{
type.flush();
Types.restrictSearchesTo(searchPath);
}
// ----------------------------------------------------------
/**
* Force type searches to search all possible locations (i.e., remove
* the default initial restrictions, which limit searches either to just
* directories (not jar files) or to the locations specified in the
* system property
* <code>student.testingsupport.reflection.internal.Types.searchPath</code>).
* Note that in some instances this may cause some type filters to
* exhaust available PermGen space, requiring programs to be run
* with an additional JVM command line argument to increase the max
* PermGen size.
*/
public static void searchEverywhere()
{
Types.restrictSearchesTo(null);
}
// ----------------------------------------------------------
/**
* Restrict this filter to only admit declarations with the specified
* name. If the name is a simple name (that is,
* @param name The name required by the resulting filter.
* @return A new filter with the given restriction.
*/
public Type<ClassType> withName(String name)
{
if (name == null)
{
return this;
}
int pos = name.lastIndexOf('.');
if (pos > 0)
{
final String simpleName = name.substring(pos + 1);
return (new Type<ClassType>(this, "with name " + simpleName)
{
@Override
protected String getTargetSimpleName()
{
return simpleName;
}
}).inPackage(name.substring(0, pos));
}
else
{
final String simpleName = name;
return (new Type<ClassType>(this, "with name " + simpleName)
{
@Override
protected String getTargetSimpleName()
{
return simpleName;
}
}).typeResolver();
}
}
// ----------------------------------------------------------
/**
* Get a compile-time-constrained version of this Type that is limited
* to only types that are compatible with the specified class.
* @param superClass The constraint to add to this type.
* @param <T> The generic parameter T is deduced from the superClass.
* @return A constrained version of this type that represents a
* subclass of the given superClass.
*/
@SuppressWarnings("unchecked")
public <T> Type<T> as(final Class<T> superClass)
{
if (superClass == null)
{
Type<T> result = (Type<T>)type;
return result;
}
return new Type<T>(
(Type<T>)this, "as " + superClass.getCanonicalName())
{
private Type<T> clazz = type(superClass);
@Override
protected boolean thisFilterAccepts(Class<T> object)
{
return clazz.isAssignableFrom(object);
}
};
}
// ----------------------------------------------------------
/**
* Get a compile-time-constrained version of this Type that is limited
* to only types that are compatible with the second specified type.
* @param superClass The constraint to add to this type.
* @param <T> The generic parameter T is deduced from the superClass.
* @return A constrained version of this type that represents a
* subclass of the given superClass.
*/
@SuppressWarnings("unchecked")
public <T> Type<T> as(final Type<T> superClass)
{
if (superClass == null)
{
Type<T> result = (Type<T>)type;
return result;
}
return new Type<T>(
(Type<T>)this, "as " + superClass.getNameOrUnknown())
{
@Override
protected boolean thisFilterAccepts(Class<T> object)
{
return superClass.isAssignableFrom(object);
}
};
}
// ----------------------------------------------------------
/**
* Specify the class loader that should be used to look up (load) the
* class this type represents.
* @param loader The class loader to use to resolve this type.
* @return The current Type object, for method chaining.
*/
public Type<ClassType> fromClassLoader(final ClassLoader loader)
{
if (loader == null)
{
return this;
}
return (new Type<ClassType>(this, "loaded from " + loader)
{
@Override
protected ClassLoader getTargetLoader()
{
return loader;
}
}).typeResolver();
}
// ----------------------------------------------------------
/**
* Restrict this filter to only match types (classes) that extend a
* given super class. Unlike {@link #isAssignableFrom(Type)}, this
* method does <b>not</b> consider a class to "extend" itself, so
* this class and the given superClass must be distinct in order for
* the result to be true.
* @param superClass The class to test against.
* @param <T> This parameter is deduced from the superClass.
* @return The restricted filter.
*/
@SuppressWarnings("unchecked")
public <T> Type<T> extendingClass(final Type<T> superClass)
{
if (superClass == null)
{
Type<T> result = (Type<T>)type;
return result;
}
return new Type<T>(
(Type<T>)this, "extending class " + superClass.getNameOrUnknown())
{
@Override
protected boolean thisFilterAccepts(Class<T> object)
{
return classExtendsType(object, superClass);
}
};
}
// ----------------------------------------------------------
/**
* Restrict this filter to only match types (classes) that extend a
* given super class. Unlike {@link #isAssignableFrom(Type)}, this
* method does <b>not</b> consider a class to "extend" itself, so
* this class and the given superClass must be distinct in order for
* the result to be true.
* @param superClass The class to test against.
* @param <T> This parameter is deduced from the superClass.
* @return The restricted filter.
*/
public <T> Type<T> extendingClass(Class<T> superClass)
{
if (superClass == null)
{
@SuppressWarnings("unchecked")
Type<T> result = (Type<T>)type;
return result;
}
return extendingClass(type(superClass));
}
// ----------------------------------------------------------
/**
* Determine whether this type (class) extends a given super class.
* Unlike {@link #isAssignableFrom(Type)}, this method does <b>not</b>
* consider a class to "extend" itself, so this class and the given
* superClass must be distinct in order for the result to be true.
* @param superClass The class to test against.
* @return True if this type represents a class that "extends" the given
* superClass (the two must be distinct, and both must be classes, not
* enums or interfaces).
*/
public boolean extendsClass(final Type<?> superClass)
{
if (superClass == null)
{
return false;
}
return quantify.evaluate(new Predicate<Class<ClassType>>()
{
public boolean isSatisfiedBy(Class<ClassType> object)
{
return classExtendsType(object, superClass);
}
});
}
// ----------------------------------------------------------
/**
* Determine whether this type (class) extends a given super class.
* Unlike {@link #isAssignableFrom(Type)}, this method does <b>not</b>
* consider a class to "extend" itself, so this class and the given
* superClass must be distinct in order for the result to be true.
* @param superClass The class to test against.
* @return True if this type represents a class that "extends" the given
* superClass (the two must be distinct, and both must be classes, not
* enums or interfaces).
*/
public boolean extendsClass(Class<?> superClass)
{
if (superClass == null)
{
return false;
}
return extendsClass(type(superClass));
}
// ----------------------------------------------------------
/**
* Restrict this type (class or interface) to only match types that
* implement a given interface. Unlike {@link #isAssignableFrom(Type)},
* this method does <b>not</b> consider an interface to"implement" itself,
* so this class and the given interface must be distinct in order for
* the result to be true.
* @param anInterface The interface to test against.
* @param <T> This parameter is deduced from anInterface.
* @return The restricted type filter.
*/
@SuppressWarnings("unchecked")
public <T> Type<T> implementingInterface(final Type<T> anInterface)
{
if (anInterface == null)
{
Type<T> result = (Type<T>)type;
return result;
}
return new Type<T>(
(Type<T>)this,
"implementing interface " + anInterface.getNameOrUnknown())
{
@Override
protected boolean thisFilterAccepts(Class<T> object)
{
return classImplementsInterface(object, anInterface);
}
};
}
// ----------------------------------------------------------
/**
* Restrict this type (class or interface) to only match types that
* implement a given interface. Unlike {@link #isAssignableFrom(Type)},
* this method does <b>not</b> consider an interface to"implement" itself,
* so this class and the given interface must be distinct in order for
* the result to be true.
* @param anInterface The interface to test against.
* @param <T> This parameter is deduced from anInterface.
* @return The restricted type filter.
*/
public <T> Type<T> implementingInterface(final Class<T> anInterface)
{
if (anInterface == null)
{
@SuppressWarnings("unchecked")
Type<T> result = (Type<T>)type;
return result;
}
return implementingInterface(type(anInterface));
}
// ----------------------------------------------------------
/**
* Determine whether this type (class or interface) implements a given
* interface. Unlike {@link #isAssignableFrom(Type)}, this method does
* <b>not</b> consider an interface to"implement" itself, so this class
* and the given interface must be distinct in order for the result to
* be true.
* @param anInterface The interface to test against.
* @return True if this type represents a class or interface that
* "implements" anInterface (the two must be distinct, and anInterface
* must be an interface, not a class).
*/
public boolean implementsInterface(final Type<?> anInterface)
{
if (anInterface == null)
{
return false;
}
return quantify.evaluate(new Predicate<Class<ClassType>>()
{
public boolean isSatisfiedBy(Class<ClassType> object)
{
return classImplementsInterface(object, anInterface);
}
});
}
// ----------------------------------------------------------
/**
* Determine whether this type (class or interface) implements a given
* interface. Unlike {@link #isAssignableFrom(Type)}, this method does
* <b>not</b> consider an interface to"implement" itself, so this class
* and the given interface must be distinct in order for the result to
* be true.
* @param anInterface The interface to test against.
* @return True if this type represents a class or interface that
* "implements" anInterface (the two must be distinct, and anInterface
* must be an interface, not a class).
*/
public boolean implementsInterface(Class<?> anInterface)
{
if (anInterface == null)
{
return false;
}
return implementsInterface(type(anInterface));
}
// ----------------------------------------------------------
/**
* Determine whether a given object is an instance of this type. Mirrors
* {@link Class#isInstance(Object)}.
* @param object The object to test.
* @return True if the given object is an instance of this type.
*/
public boolean isInstance(final Object object)
{
if (object == null)
{
return false;
}
return quantify.evaluate(new Predicate<Class<ClassType>>()
{
public boolean isSatisfiedBy(Class<ClassType> clazz)
{
return clazz.isInstance(object);
}
});
}
// ----------------------------------------------------------
/**
* Restrict this filter to only admit declarations of interfaces.
* @return The restricted filter.
*/
public Type<ClassType> declaredAsInterface()
{
return withModifiers(Modifier.INTERFACE, "declared as interface");
}
// ----------------------------------------------------------
/**
* Determine whether this type is an interface.
* @return True if this is an interface.
*/
public boolean isInterface()
{
return hasModifiers(Modifier.INTERFACE);
}
// ----------------------------------------------------------
/**
* Restrict this filter to only admit declarations of interfaces.
* @return The restricted filter.
*/
public Type<ClassType> declaredAsEnum()
{
return new Type<ClassType>(this, "delcared as enum")
{
@Override
protected boolean thisFilterAccepts(Class<ClassType> object)
{
return object.isEnum();
}
};
}
// ----------------------------------------------------------
/**
* Determine whether this type is an enum.
* @return True if this is an enum.
*/
public boolean isEnum()
{
return quantify.evaluate(new Predicate<Class<ClassType>>()
{
public boolean isSatisfiedBy(Class<ClassType> object)
{
return object.isEnum();
}
});
}
// ----------------------------------------------------------
/**
* Restrict this filter to only admit declarations of abstract classes.
* @return The restricted filter.
*/
public Type<ClassType> declaredAbstract()
{
return withModifiers(Modifier.ABSTRACT, "declared abstract");
}
// ----------------------------------------------------------
/**
* Determine whether this type is an abstract class.
* @return True if this is an abstract class.
*/
public boolean isAbstract()
{
return hasModifiers(Modifier.ABSTRACT);
}
// ----------------------------------------------------------
/**
* Restrict this filter to only admit declarations of abstract classes.
* @return The restricted filter.
*/
@SuppressWarnings("unchecked")
public Type<ClassType> declaredAsAnnotation()
{
return (Type<ClassType>)implementingInterface(
java.lang.annotation.Annotation.class);
}
// ----------------------------------------------------------
/**
* Determine whether this type is an abstract class.
* @return True if this is an abstract class.
*/
public boolean isAnnotation()
{
return implementsInterface(java.lang.annotation.Annotation.class);
}
// ----------------------------------------------------------
/**
* Restrict this filter to only admit declarations of classes, not
* interfaces, enums, or primitive types.
* @return The restricted filter.
*/
public Type<ClassType> declaredAsClass()
{
return new Type<ClassType>(this, "declared as a class")
{
@Override
protected boolean thisFilterAccepts(Class<ClassType> object)
{
return isClass(object);
}
};
}
// ----------------------------------------------------------
/**
* Determine whether this type is a regular class, that is, if it is not
* an interface or an enum. Strictly speaking, enums are actually
* classes, but this method only returns true for "regular" (or abstract)
* classes. Use {@link #isEnum()} to find out if it is an enum instead.
* @return True if this is a class.
*/
public boolean isClass()
{
return quantify.evaluate(new Predicate<Class<ClassType>>()
{
public boolean isSatisfiedBy(Class<ClassType> object)
{
return isClass(object);
}
});
}
// ----------------------------------------------------------
/**
* Determine whether this type represents one of the eight primitive
* Java types.
* @return True if this is a primitive type.
*/
public boolean isPrimitive()
{
return quantify.evaluate(new Predicate<Class<ClassType>>()
{
public boolean isSatisfiedBy(Class<ClassType> object)
{
return isPrimitive(object);
}
});
}
// ----------------------------------------------------------
/**
* Restrict this filter to only types declared in the specified package.
* @param packageName The package where this type must be found. The
* empty string ("") represents the default package,
* while null represents any possible package.
* @return The restricted filter.
*/
public Type<ClassType> inPackage(final String packageName)
{
String constraint = "in any package";
if (packageName != null)
{
constraint = packageName.isEmpty()
? "in default package"
: ("in package " + packageName);
}
return (new Type<ClassType>(this, constraint)
{
@Override
protected boolean thisFilterAccepts(Class<ClassType> object)
{
return packageName == null
|| Types.isInPackage(object, packageName);
}
@Override
protected String getTargetPackage()
{
return packageName;
}
}).typeResolver();
}
// ----------------------------------------------------------
/**
* All this filter to match a corresponding type in any package.
* @return The (less) restricted filtered.
*/
public Type<ClassType> inAnyPackage()
{
return inPackage(null);
}
// ----------------------------------------------------------
/**
* Determine whether the match(es) of this filter are located in a
* specified package. This predicate is quantifiable.
* @param packageName The package to test against.
* @return True if the types matching this filter are found in the
* specified package, according to the current quantifier constraints.
*/
public boolean isInPackage(final String packageName)
{
return quantify.evaluate(new Predicate<Class<ClassType>>()
{
public boolean isSatisfiedBy(Class<ClassType> object)
{
return Types.isInPackage(object, packageName);
}
});
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: document.
* @return TODO: document.
*/
public boolean isInSamePackageAs(Type<?> type)
{
return isInPackage(type.getPackageName());
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: document.
* @return TODO: document.
*/
public boolean isInSamePackageAs(Class<?> type)
{
return isInSamePackageAs(Type.type(type));
}
// ----------------------------------------------------------
/**
* TODO: document.
* @return TODO: document.
*/
public String getPackageName()
{
Package pkg = raw().getPackage();
return (pkg == null) ? null : pkg.getName();
}
// ----------------------------------------------------------
/**
* TODO: document
* @param source TODO: document
* @return TODO: document
*/
public boolean isAssignableFrom(Type<?> source)
{
return isAssignableFrom(source.raw());
}
// ----------------------------------------------------------
/**
* TODO: document
* @param source TODO: document
* @return TODO: document
*/
public boolean isAssignableFrom(final Class<?> source)
{
if (exists())
{
return quantify.evaluate(new Predicate<Class<ClassType>>()
{
public boolean isSatisfiedBy(Class<ClassType> object)
{
return isAssignableFrom(object, source);
}
});
}
else
{
return false;
}
}
// ----------------------------------------------------------
/**
* TODO: document
* @param value TODO: document
* @return TODO: document
*/
public boolean isAssignableFrom(final Object value)
{
if (exists())
{
return quantify.evaluate(new Predicate<Class<ClassType>>()
{
public boolean isSatisfiedBy(Class<ClassType> object)
{
if (value == null)
{
return !isPrimitive(object);
}
else
{
return isAssignableFrom(object, value.getClass());
}
}
});
}
else
{
return false;
}
}
// ----------------------------------------------------------
/**
* TODO: document
* @param value The value to cast
* @return The cast value
*/
public ClassType cast(Object value)
{
Class<?> target = raw();
if (value == null)
{
if (isPrimitive(target))
{
throw new NullPointerException(
"Cannot cast null to type " + raw());
}
return null;
}
if (!isAssignableFrom(target, value.getClass()))
{
throw new ClassCastException(
value.getClass() + " cannot be cast to " + target);
}
@SuppressWarnings("unchecked")
ClassType result = (ClassType)value;
return result;
}
//~ Protected Methods .....................................................
// ----------------------------------------------------------
@Override
protected Type<ClassType> createFreshFilter(
Type<ClassType> previous, String descriptionOfThisStage)
{
return new Type<ClassType>(previous, descriptionOfThisStage);
}
// ----------------------------------------------------------
protected Type<ClassType> createFreshFilter(
final Class<ClassType> rawClass)
{
return new Type<ClassType>(null, describe(rawClass))
{
private List<Class<ClassType>> result =
new ArrayList<Class<ClassType>>();
{
result.add(rawClass);
}
// ----------------------------------------------------------
@Override
protected List<Class<ClassType>> candidatesFromThisFilter()
{
return result;
}
};
}
// ----------------------------------------------------------
@Override
protected String filteredObjectDescription()
{
return "type";
}
// ----------------------------------------------------------
@Override
protected String nameOf(Class<ClassType> object)
{
return object.getCanonicalName();
}
// ----------------------------------------------------------
@Override
protected int modifiersFor(Class<ClassType> object)
{
return object.getModifiers();
}
// ----------------------------------------------------------
/**
* Look up the class loader that should be used to resolve this type.
* A value of null implies using the default class loader.
* @return The class loader to use, or null if the default should be used.
*/
protected ClassLoader getTargetLoader()
{
if (previousFilter() != null
&& previousFilter() instanceof Type)
{
return ((Type<ClassType>)previousFilter()).getTargetLoader();
}
else
{
return null;
}
}
// ----------------------------------------------------------
/**
* Look up the package name that should be used to resolve this type.
* A value of "" implies using the default package. A value of null
* implies searching in <b>every</b> package. The default here is
* to see if anything earlier in the chain has a constraint, and if
* not, default to "".
* @return The package to search in.
*/
protected String getTargetPackage()
{
if (previousFilter() != null
&& previousFilter() instanceof Type)
{
return ((Type<ClassType>)previousFilter()).getTargetPackage();
}
else
{
return "";
}
}
// ----------------------------------------------------------
/**
* Look up the simple (package-less) class name that should be used to
* resolve this type.
* @return The simple (package-less) name to search for.
*/
protected String getTargetSimpleName()
{
if (previousFilter() != null
&& previousFilter() instanceof Type)
{
return ((Type<ClassType>)previousFilter()).getTargetSimpleName();
}
else
{
return null;
}
}
// ----------------------------------------------------------
private boolean isPrimitive(Class<?> clazz)
{
return PRIMITIVE_TYPES.contains(clazz);
}
// ----------------------------------------------------------
/**
* Determine whether the specified type is a class, or is instead an
* interface or enum.
* @param clazz The type to inspect.
* @return True if clazz is not an interface or enum, i.e., it is a
* regular class.
*/
protected boolean isClass(Class<?> clazz)
{
return !clazz.isInterface() && ! clazz.isEnum() && !isPrimitive(clazz);
}
// ----------------------------------------------------------
private boolean classExtendsType(Class<?> clazz, Type<?> type)
{
return !clazz.isInterface()
&& !isPrimitive(clazz)
&& !type.equals(clazz)
&& !type.isInterface()
&& !type.isPrimitive()
&& type.isAssignableFrom(clazz);
}
// ----------------------------------------------------------
private boolean classImplementsInterface(Class<?> clazz, Type<?> type)
{
return !isPrimitive(clazz)
&& !type.equals(clazz)
&& type.isInterface()
&& type.isAssignableFrom(clazz);
}
// ----------------------------------------------------------
/**
* TODO: document
* @param source TODO: document
* @return TODO: document
*/
private boolean isAssignableFrom(Class<?> destination, Class<?> source)
{
int primitiveDestIdx = PRIMITIVE_TYPES.indexOf(destination);
int primitiveSrcIdx = PRIMITIVE_TYPES.indexOf(source);
int wrapperSrcIdx = WRAPPER_TYPES.indexOf(source);
if (primitiveDestIdx >= 0)
{
// First, try to unbox
if (wrapperSrcIdx >= 0)
{
source = PRIMITIVE_TYPES.get(wrapperSrcIdx);
primitiveSrcIdx = wrapperSrcIdx;
}
if (primitiveSrcIdx < 0)
{
// No conversion possible
return false;
}
// No widening conversions *to* these types are performed
// automatically in Java
if (boolean.class.equals(destination)
|| char.class.equals(destination))
{
return primitiveDestIdx == primitiveSrcIdx;
}
else
{
// Now check for a widening conversion
return primitiveDestIdx >= primitiveSrcIdx;
}
}
else if (primitiveSrcIdx >= 0)
{
// If auto-boxing is possible, then try it, since the destination
// is not a primitive type
source = WRAPPER_TYPES.get(primitiveSrcIdx);
wrapperSrcIdx = primitiveSrcIdx;
}
return destination.isAssignableFrom(source);
}
// ----------------------------------------------------------
/**
* This creates a "filter" that simply performs type resolution. It
* deeply looks through the chain for package constraints, name
* constraints, and class loader constraints, and then uses them to
* perform the search.
* @return A new filter that performs type resolution when necessary.
*/
protected Type<ClassType> typeResolver()
{
return new Type<ClassType>(this, null)
{
@Override
public String getName()
{
String pkg = getTargetPackage();
String name = getTargetSimpleName();
if (name == null)
{
name = "<any-name>";
}
if (pkg == null)
{
pkg = "<any-package>";
}
if (!pkg.isEmpty())
{
name = pkg + "." + name;
}
return name;
}
@Override
protected boolean guaranteesMultipleMatches()
{
return getTargetPackage() == null
&& getTargetSimpleName() == null;
}
@Override
protected List<Class<ClassType>> candidatesFromThisFilter()
{
String pkg = getTargetPackage();
String name = getTargetSimpleName();
ClassLoader loader = getTargetLoader();
List<Class<?>> raw = null;
if (name != null)
{
if (pkg == null)
{
// Searching for all classes with this name!
raw = Types.allClassesWithSimpleName(name, loader);
}
else
{
if (!pkg.isEmpty())
{
name = pkg + "." + name;
}
Class<?> c = Types.classForName(name, loader);
if (c != null)
{
raw = new ArrayList<Class<?>>();
raw.add(c);
}
}
}
else if (pkg != null)
{
raw = Types.classesInPackage(pkg, loader);
}
else
{
raw = Types.allClasses(loader);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
List<Class<ClassType>> found = (List)raw;
return found;
}
};
}
}