All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jqwik.engine.properties.arbitraries.randomized.AbstractFunctionGenerator Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
package net.jqwik.engine.properties.arbitraries.randomized;

import java.lang.invoke.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.function.*;

import net.jqwik.api.*;
import net.jqwik.api.Tuple.*;
import net.jqwik.engine.support.*;

abstract class AbstractFunctionGenerator implements RandomGenerator {
	final Class functionalType;
	final RandomGenerator resultGenerator;
	final List>, Function, R>>> conditions;

	AbstractFunctionGenerator(
		Class functionalType,
		RandomGenerator resultGenerator,
		List>, Function, R>>> conditions
	) {
		this.functionalType = functionalType;
		this.resultGenerator = resultGenerator;
		this.conditions = conditions;
	}

	@SuppressWarnings("unchecked")
	F createFunctionProxy(InvocationHandler handler) {
		return (F) Proxy.newProxyInstance(functionalType.getClassLoader(), new Class[]{functionalType}, handler);
	}


	public Shrinkable createConstantFunction(Shrinkable shrinkableConstant) {
		return shrinkableConstant.map(this::constantFunction);
	}

	private F constantFunction(R constant) {
		InvocationHandler handler = (proxy, method, args) -> {
			if (JqwikReflectionSupport.isEqualsMethod(method)) {
				return handleEqualsMethod(proxy, args);
			}
			if (JqwikReflectionSupport.isToStringMethod(method)) {
				return handleToStringOfConstantMethod(constant);
			}
			if (JqwikReflectionSupport.isHashCodeMethod(method)) {
				return constant.hashCode() + constant.hashCode();
			}
			if (method.isDefault()) {
				return handleDefaultMethod(proxy, method, args);
			}

			return conditionalResult(args).orElse(new Object[]{constant})[0];
		};
		return createFunctionProxy(handler);
	}

	protected Object handleEqualsMethod(final Object proxy, Object[] args) {
		return proxy == args[0];
	}

	private Object handleToStringOfConstantMethod(final R constant) {
		return String.format(
			"Constant Function<%s>(%s)",
			functionalType.getSimpleName(),
			JqwikStringSupport.displayString(constant)
		);
	}

	// Returns result wrapped in array to allow null as result
	protected Optional conditionalResult(Object[] args) {
		Optional conditionalResult = Optional.empty();
		for (Tuple2>, Function, R>> condition : conditions) {
			List params = Arrays.asList(args);
			if (condition.get1().test(params)) {
				Object[] result = new Object[]{condition.get2().apply(params)};
				conditionalResult = Optional.of(result);
				break;
			}
		}
		return conditionalResult;
	}

	protected Object handleDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable {
		MethodHandle handle = handleForDefaultMethod(method);
		return handle.bindTo(proxy).invokeWithArguments(args);
	}

	protected MethodHandle handleForDefaultMethod(Method method) throws Throwable {
		return new DefaultMethodHandleFactory().create(method);
	}

}