Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.jqwik.engine.support.JqwikReflectionSupport Maven / Gradle / Ivy
package net.jqwik.engine.support;
import java.io.*;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.nio.file.*;
import java.util.*;
import java.util.function.*;
import java.util.logging.*;
import java.util.stream.*;
import org.junit.platform.commons.support.*;
import net.jqwik.api.*;
import net.jqwik.api.providers.*;
import net.jqwik.api.support.*;
import static java.util.stream.Collectors.*;
import static net.jqwik.engine.support.OverriddenMethodAnnotationSupport.*;
public class JqwikReflectionSupport {
private static final Logger LOG = Logger.getLogger(JqwikReflectionSupport.class.getName());
@SuppressWarnings("deprecation") // Deprecated as of Java 9
private static T makeAccessible(T object) {
if (!object.isAccessible()) {
object.setAccessible(true);
}
return object;
}
/**
* Create instance of a class that can potentially be a non static inner class
*
* @param The type of the instance to create
* @param clazz The class to instantiate
* @return the newly created instance
*/
public static T newInstanceWithDefaultConstructor(Class clazz) {
if (isInnerClass(clazz)) {
Object parentInstance = newInstanceWithDefaultConstructor(clazz.getDeclaringClass());
return ReflectionSupport.newInstance(clazz, parentInstance);
} else {
return ReflectionSupport.newInstance(clazz);
}
}
/**
* Create instance of a class that can potentially be a non static inner class
*
* @param clazz The class to instantiate
* @return all newly created instances with from most outer to most inner
*/
public static List newInstancesWithDefaultConstructor(Class> clazz) {
if (isInnerClass(clazz)) {
List instances = newInstancesWithDefaultConstructor(clazz.getDeclaringClass());
Object inner = ReflectionSupport.newInstance(clazz, instances.get(instances.size() - 1));
instances.add(inner);
return instances;
} else {
List instances = new ArrayList<>();
instances.add(ReflectionSupport.newInstance(clazz));
return instances;
}
}
/**
* Create instance of a class that can potentially be a non static inner class
* and its outer instance might be {@code context}
*
* @param The type of the instance to create
* @param clazz The class to instantiate
* @param context The potential context instance
* @return the newly created instance
*/
public static T newInstanceInTestContext(Class clazz, Object context) {
if (!isInnerClass(clazz)) {
return ReflectionSupport.newInstance(clazz);
}
Class> outerClass = clazz.getDeclaringClass();
Object parentInstance = outerClass.isAssignableFrom(context.getClass()) ?
context : newInstanceWithDefaultConstructor(outerClass);
try {
Constructor constructor = clazz.getDeclaredConstructor(outerClass);
return newInstance(constructor, parentInstance);
} catch (NoSuchMethodException e) {
return JqwikExceptionSupport.throwAsUncheckedException(e);
}
}
public static T newInstance(Constructor constructor, Object... args) {
try {
return makeAccessible(constructor).newInstance(args);
} catch (Throwable t) {
return JqwikExceptionSupport.throwAsUncheckedException(t);
}
}
/**
* Find all {@linkplain Method methods} as in ReflectionSupport.findMethods(..) but also use outer classes to look for
* methods.
*
*
* Duplicate methods (through) inheritance are de-duplicated. The first occurrence of a method is kept.
*
*
* @param clazz The class in which you start the search
* @param predicate The condition to check for all candidate methods
* @param traversalMode Traverse hierarchy up or down. Determines the order in resulting list.
* @return List of found methods
*/
public static List findMethodsPotentiallyOuter(
Class> clazz,
Predicate predicate,
HierarchyTraversalMode traversalMode
) {
List> searchClasses = getDeclaringClasses(clazz, traversalMode);
Set foundMethods = new LinkedHashSet<>();
for (Class> searchClass : searchClasses) {
foundMethods.addAll(ReflectionSupport.findMethods(searchClass, predicate, traversalMode));
}
return new ArrayList<>(foundMethods);
}
/**
* Find all {@linkplain Field field} but also use outer classes to look for
* methods.
*
* @param clazz The class in which you start the search
* @param predicate The condition to check for all candidate methods
* @param traversalMode Traverse hierarchy up or down. Determines the order in resulting list.
* @return List of found fields
*/
public static List findFieldsPotentiallyOuter(
Class> clazz,
Predicate predicate,
HierarchyTraversalMode traversalMode
) {
List> searchClasses = getDeclaringClasses(clazz, traversalMode);
List foundFields = new ArrayList<>();
for (Class> searchClass : searchClasses) {
foundFields.addAll(ReflectionSupport.findFields(searchClass, predicate, traversalMode));
}
return foundFields;
}
/**
* Read a field's value as in ReflectionSupport.getField(..) but potentially use outer instances if the field belongs to an inner class.
*
* @param field The field to read
* @param targetInstances The container instances to read the field from, from outermost to innermost
* @return The value of the field
*/
public static Object readFieldOnContainer(Field field, List targetInstances) {
makeAccessible(field);
return readField(field, new ArrayDeque<>(targetInstances));
}
private static Object readField(Field field, Deque instances) {
Object target = instances.removeLast();
List declaredFields = Arrays.stream(target.getClass().getDeclaredFields()).collect(toList());
if (declaredFields.contains(field)) {
try {
return field.get(target);
} catch (Exception exception) {
return JqwikExceptionSupport.throwAsUncheckedException(exception);
}
} else {
if (instances.isEmpty()) {
String message = String.format("Cannot access value of field %s", field);
throw new JqwikException(message);
}
return readField(field, instances);
}
}
/**
* Set a field's value as in ReflectionSupport.setField(..) but potentially use outer instances if the field belongs to an inner class.
*
* @param field The field to set
* @param value The value to set in the field
* @param targetInstances The container instances to set the field on, from outermost to innermost
*/
public static void setFieldOnContainer(Field field, Object value, List targetInstances) {
makeAccessible(field);
setField(field, value, new ArrayDeque(targetInstances));
}
private static void setField(Field field, Object value, Deque instances) {
Object target = instances.removeLast();
List declaredFields = Arrays.stream(target.getClass().getDeclaredFields()).collect(toList());
if (declaredFields.contains(field)) {
try {
if (isStatic(field)) {
field.set(null, value);
} else {
field.set(target, value);
}
} catch (Exception exception) {
//noinspection ResultOfMethodCallIgnored
JqwikExceptionSupport.throwAsUncheckedException(exception);
}
} else {
if (instances.isEmpty()) {
String message = String.format("Cannot set value of field %s", field);
throw new JqwikException(message);
}
setField(field, value, instances);
}
}
private static List> getDeclaringClasses(Class> clazz, HierarchyTraversalMode traversalMode) {
List> declaringClasses = new ArrayList<>();
Class> nextClass = clazz;
while (nextClass != null) {
if (traversalMode == HierarchyTraversalMode.BOTTOM_UP) {
declaringClasses.add(nextClass);
} else {
declaringClasses.add(0, nextClass);
}
nextClass = nextClass.getDeclaringClass();
}
return declaringClasses;
}
/**
* Invoke the supplied {@linkplain Method method} as in ReflectionSupport.invokeMethod(..) but potentially use outer
* instances if the method belongs to the outer instances of an object.
*
* @param method The method to invoke
* @param instances The container instances to invoke the method on, from outermost to innermost
* @param args The arguments of the method invocation
* @return Result of method invocation if there is one, otherwise null
*/
public static Object invokeMethodOnContainer(Method method, List instances, Object... args) {
return invokeMethod(method, new ArrayDeque<>(instances), args);
}
private static Object invokeMethod(Method method, Deque instances, Object... args) {
Object target = instances.removeLast();
if (method.getDeclaringClass().isAssignableFrom(target.getClass())) {
return ReflectionSupport.invokeMethod(method, target, args);
} else {
if (instances.isEmpty()) {
String message = String.format("Method [%s] cannot be invoked on target [%s].", method, target);
throw new IllegalArgumentException(message);
}
return invokeMethod(method, instances, args);
}
}
public static Set getAllClasspathRootDirectories() {
// TODO: This is quite a hack, since sometimes the classpath is quite different.
// Especially under Java >=9's module system this will probably no longer work.
String classpath = System.getProperty("java.class.path");
return Arrays.stream(classpath.split(File.pathSeparator))
.map(Paths::get).filter(Files::isDirectory)
.collect(CollectorsSupport.toLinkedHashSet());
}
public static List getMethodParameters(Executable method, Class> containerClass) {
List list = new ArrayList<>();
Parameter[] parameters = method.getParameters();
GenericsClassContext containerClassContext = GenericsSupport.contextFor(containerClass);
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
TypeResolution resolution = containerClassContext.resolveParameter(parameter);
MethodParameter methodParameter = new MethodParameter(parameter, resolution, i);
list.add(methodParameter);
}
return list;
}
public static MethodParameter getMethodParameter(Parameter parameter, int index, Class> containerClass) {
GenericsClassContext containerClassContext = GenericsSupport.contextFor(containerClass);
TypeResolution resolution = containerClassContext.resolveParameter(parameter);
return new MethodParameter(parameter, resolution, index);
}
public static Optional findGeneratorMethod(
String generatorToFind,
Class> containerClass,
Class extends Annotation> requiredGeneratorAnnotation,
Function generatorNameSupplier,
TypeUsage expectedReturnType
) {
List creators = findMethodsPotentiallyOuter(
containerClass,
isGeneratorMethod(expectedReturnType, requiredGeneratorAnnotation),
HierarchyTraversalMode.BOTTOM_UP
);
return creators.stream().filter(generatorMethod -> {
String generatorName = generatorNameSupplier.apply(generatorMethod);
if (generatorName.isEmpty()) {
generatorName = JqwikKotlinSupport.javaOrKotlinName(generatorMethod);
}
return generatorName.equals(generatorToFind);
}).findFirst();
}
public static Constructor findConstructor(Class type, Class>... parameterTypes) {
try {
Constructor ctor = type.getDeclaredConstructor(parameterTypes);
// Constructor ctor = type.getConstructor(parameterTypes);
ctor.setAccessible(true);
return ctor;
} catch (Throwable t) {
return JqwikExceptionSupport.throwAsUncheckedException(t);
}
}
private static Predicate isGeneratorMethod(TypeUsage expectedReturnType, Class extends Annotation> requiredAnnotation) {
return method -> {
if (!findDeclaredOrInheritedAnnotation(method, requiredAnnotation).isPresent()) {
return false;
}
TypeUsage generatorReturnType = TypeUsage.forType(method.getAnnotatedReturnType().getType());
return generatorReturnType.canBeAssignedTo(expectedReturnType);
};
}
public static boolean isInnerClass(Class> clazz) {
return clazz.isMemberClass() && !ModifierSupport.isStatic(clazz);
}
public static boolean isFunctionalType(Class> candidateType) {
if (!candidateType.isInterface()) {
return false;
}
return countInterfaceMethods(candidateType) == 1;
}
private static long countInterfaceMethods(Class> candidateType) {
Method[] methods = candidateType.getMethods();
return findInterfaceMethods(methods).size();
}
private static List findInterfaceMethods(Method[] methods) {
return Arrays
.stream(methods)
.filter(m -> !m.isDefault() && !ModifierSupport.isStatic(m))
.collect(Collectors.toList());
}
public static Optional getFunctionMethod(Class> candidateType) {
Method[] methods = candidateType.getMethods();
List candidates = findInterfaceMethods(methods);
if (candidates.size() != 1) {
return Optional.empty();
}
return Optional.of(candidates.get(0));
}
public static boolean isEqualsMethod(Method method) {
try {
return method.equals(Object.class.getDeclaredMethod("equals", Object.class));
} catch (NoSuchMethodException shouldNeverHappen) {
return false;
}
}
public static boolean isToStringMethod(Method method) {
try {
return method.equals(Object.class.getDeclaredMethod("toString"));
} catch (NoSuchMethodException shouldNeverHappen) {
return false;
}
}
public static boolean isHashCodeMethod(Method method) {
try {
return method.equals(Object.class.getDeclaredMethod("hashCode"));
} catch (NoSuchMethodException shouldNeverHappen) {
return false;
}
}
public static boolean hasDefaultConstructor(Class> aClass) {
return hasConstructor(aClass);
}
public static boolean hasConstructor(Class> aClass, Class>... parameterTypes) {
try {
aClass.getDeclaredConstructor(parameterTypes);
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
public static boolean isJava9orAbove() {
try {
//noinspection JavaReflectionMemberAccess
Runtime.class.getMethod("version");
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
public static boolean isStatic(Class> clazz) {
return Modifier.isStatic(clazz.getModifiers());
}
public static boolean isPrivate(Class> clazz) {
return Modifier.isPrivate(clazz.getModifiers());
}
public static boolean isNotStatic(Class> clazz) {
return !isStatic(clazz);
}
public static boolean isStatic(Member member) {
return Modifier.isStatic(member.getModifiers());
}
public static boolean isNotStatic(Member member) {
return !isStatic(member);
}
public static boolean returnsVoid(Method method) {
return method.getReturnType().equals(Void.TYPE);
}
public static boolean implementsMethod(
Class> aClass,
String methodName,
Class>[] parameterTypes,
Class> ignoreImplementationClass
) {
Optional optionalMethod = ReflectionSupport.findMethod(aClass, methodName, parameterTypes);
return optionalMethod.map(method -> !method.getDeclaringClass().equals(ignoreImplementationClass)).orElse(false);
}
public static Class> extractRawType(Type parameterizedType) {
if (parameterizedType instanceof Class) {
return (Class>) parameterizedType;
}
if (parameterizedType instanceof ParameterizedType) {
return (Class>) ((ParameterizedType) parameterizedType).getRawType();
}
if (parameterizedType instanceof GenericArrayType) {
return Object[].class;
}
// Now we have a type variable (java.lang.reflect.TypeVariable)
return Object.class;
}
}