org.perfectable.introspection.ClassIntrospection Maven / Gradle / Ivy
package org.perfectable.introspection;
import org.perfectable.introspection.query.AnnotationQuery;
import org.perfectable.introspection.query.ConstructorQuery;
import org.perfectable.introspection.query.FieldQuery;
import org.perfectable.introspection.query.InheritanceQuery;
import org.perfectable.introspection.query.MethodQuery;
import org.perfectable.introspection.type.ClassView;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import static com.google.common.base.Preconditions.checkState;
/**
* Entry point for class introspections.
*
* Use {@link Introspections#introspect(Class)} to get instance of this class.
*
* @param introspected class
*/
public final class ClassIntrospection {
static ClassIntrospection of(Class type) {
return new ClassIntrospection<>(type);
}
private final Class type;
/**
* Query for fields of introspected class.
*
* @return field query on introspected class.
*/
public FieldQuery fields() {
return FieldQuery.of(this.type);
}
/**
* Query for constructors of introspected class.
*
* @return constructors query on introspected class.
*/
public ConstructorQuery constructors() {
return ConstructorQuery.of(this.type);
}
/**
* Query for methods of introspected class.
*
* @return methods query on introspected class.
*/
public MethodQuery methods() {
return MethodQuery.of(this.type);
}
/**
* Query for implemented/extended interfaces/classes of introspected class.
*
* @return inheritance query on introspected class.
*/
public InheritanceQuery inheritance() {
return InheritanceQuery.of(this.type);
}
/**
* Query for implemented interfaces of introspected class.
*
* This will list transitively implemented interfaces.
*
* @return query for interfaces of introspected class.
*/
public InheritanceQuery interfaces() {
return inheritance().onlyInterfaces();
}
/**
* Query for extended superclasses of introspected class.
*
* @return query for superclasses of introspected class.
*/
public InheritanceQuery superclasses() {
return inheritance().onlyClasses();
}
/**
* Query for runtime-visible annotations on introspected class.
*
* @return query for annotations of introspected class.
*/
public AnnotationQuery annotations() {
return AnnotationQuery.of(this.type);
}
/**
* Wrap introspected class in {@link ClassView}.
*
* @return ClassView of the introspected class
*/
public ClassView view() {
return ClassView.of(this.type);
}
/**
* Introspect a classloader of this type.
*
* This method deals with {@link Class#getClassLoader} returning potentially null, which indicates that
* this class was loaded by bootstrap classloader.
*
* @return introspection of this type classloader
*/
public ClassLoaderIntrospection classLoader() {
return ClassLoaderIntrospection.of(type.getClassLoader());
}
/**
* Tests if the class is instantiable.
*
*
This checks if there is way to instantiate this class using some of its constructor.
*
* @return if the class can be instantiated using constructor
*/
@SuppressWarnings("BooleanExpressionComplexity")
public boolean isInstantiable() {
return !type.isInterface()
&& !type.isArray()
&& (type.getModifiers() & Modifier.ABSTRACT) == 0
&& !type.isPrimitive();
}
/**
* Tries to create an instance of this class using parameterless constructor.
*
*
If the construction fails, unchecked exception is thrown.
*
*
This method should be only used when the class is assumed to have parameterless constructor. If this
* is not the case, and the constructor to use is unknown, use the {@link #constructors} to search for suitable
* one.
*
* @return new instance of a class
*/
@SuppressWarnings({"PMD.AvoidThrowingRawExceptionTypes", "ThrowSpecificExceptions"})
public X instantiate() {
checkState(isInstantiable(), "%s is not isInstantiable", type);
try {
return parameterlessConstructor().newInstance();
}
catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e); // SUPPRESS JavadocMethod
}
}
/**
* Gets parameterless constructor for introspected class.
*
*
This method also marks the constructor as {@link Constructor#setAccessible(boolean)} for immediate use.
*
*
This method should be only used when the class is assumed to have parameterless constructor. If this
* is not the case, and the constructor to use is unknown, use the {@link #constructors} to search for suitable
* one.
*
* @return parameterless constructor
*/
@SuppressWarnings({"PMD.AvoidThrowingRawExceptionTypes", "ThrowSpecificExceptions"})
public Constructor parameterlessConstructor() {
try {
Constructor constructor = type.getDeclaredConstructor();
constructor.setAccessible(true);
return constructor;
}
catch (NoSuchMethodException e) {
throw new RuntimeException(e); // SUPPRESS JavadocMethod
}
}
/**
* Allows cast-less conversion from raw class to either generic form, or parametrized class.
*
* This function exists, because creating ClassIntrospection from class literal, either by
* {@link ClassIntrospection#of}, or {@link Introspections#introspect(Class)} will produce
* introspection with raw type as argument. When provided literal is a generic class, produced type should
* parameterized with unbounded wildcards, but isn't. This method allows adjusting the type easily.
*
*
Example:
*
* ClassIntrospection<List> rawIntrospection = ClassIntrospection.of(List.class);
* ClassIntrospection<List<?>> genericIntrospection = rawView.adjustWildcards();
*
*
* This method is equivalent to just casting to parameterized class with wildcards,
* but without unchecked warning.
*
*
WARNING: This method can be used to cast to inheriting types, i.e.
* {@code ClassIntrospection>} in previous example. If you are concerned that this
* might be the case, avoid this method, its only for convenience.
*
* @param parameterized type to cast to
* @return casted class introspection
*/
@SuppressWarnings("unchecked")
public Class asGeneric() {
return (Class) type;
}
private ClassIntrospection(Class type) {
this.type = type;
}
}