ObjectSynthesizer.java
package com.github.dakusui.osynth;
import com.github.dakusui.osynth.core.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import static com.github.dakusui.osynth.core.AbstractObjectSynthesizer.Preprocessor.importDescriptorFromAnotherSynthesizedObject;
import static com.github.dakusui.osynth.core.utils.MethodUtils.*;
import static java.lang.String.format;
/**
* The main entry pont of the `osynth` object synthesizer library.
*/
public class ObjectSynthesizer extends AbstractObjectSynthesizer<ObjectSynthesizer> {
public ObjectSynthesizer() {
super(new SynthesizedObject.Descriptor.Builder()
.fallbackObject(DEFAULT_FALLBACK_OBJECT));
}
public ObjectSynthesizer(SynthesizedObject.Descriptor descriptor) {
super(new SynthesizedObject.Descriptor.Builder(descriptor));
}
public static MethodHandlerEntry.Builder methodCall(String methodName, Class<?>... parameterTypes) {
return methodCall(MethodSignature.create(methodName, parameterTypes));
}
public static MethodHandlerEntry.Builder methodCall(MethodSignature methodSignature) {
return new MethodHandlerEntry.Builder().matcher(matchingExactly(methodSignature));
}
public static MethodHandlerEntry.Builder methodCall(MethodMatcher matcher) {
return new MethodHandlerEntry.Builder().matcher(matcher);
}
public static MethodMatcher matchingExactly(MethodSignature signature) {
return MethodMatcher.overrideToString(mm -> ("matchingExactly:" + mm.toString()), nameMatchingExactly(signature.name()).and(parameterTypesMatchingExactly(signature.parameterTypes())));
}
/**
* Returns a "lenient" method matcher by signature.
* The returned matcher checks if
*
* 1. The name of a method to be tested if it is matching the name of the `targetMethodSignature` as a regular expression.
* 2. Every parameter types of the method to be tested is equal to or more special than the corresponding parameter type in the `signature`.
*
* If the signature doesn't have any parameter types, it matches a method without
* any parameters.
* In case you want to create a matcher that matches a method with a specific name but
* doesn't care any parameter types, use {@link ObjectSynthesizer#nameMatchingRegex(String)}
* or {@link ObjectSynthesizer#nameMatchingExactly(String)}.
*
* ,@param signature The method signature that matches a returned matcher.
*
* @return A method matcher by signature.
*/
public static MethodMatcher matchingLeniently(MethodSignature signature) {
return MethodMatcher.overrideToString(mm -> ("matchingLeniently:" + mm.toString()), nameMatchingRegex(signature.name()).and(parameterTypesMatchingLeniently(signature.parameterTypes())));
}
public static MethodMatcher nameMatchingExactly(String methodName) {
return MethodMatcher.create(
(mm) -> format("nameMatchingExactly[%s]", methodName),
method -> Objects.equals(methodName, method.getName()));
}
public static MethodMatcher nameMatchingRegex(String regexForMethodName) {
Pattern regex = Pattern.compile(regexForMethodName);
return MethodMatcher.create(
(mm) -> format("nameMatchingRegex[%s]", regexForMethodName),
method -> regex.matcher(method.getName()).matches());
}
public static MethodMatcher parameterTypesMatchingExactly(Class<?>[] parameterTypes) {
return MethodMatcher.create(
(mm) -> format("parameterTypesMatchingExactly%s", Arrays.toString(parameterTypes)),
method -> Arrays.equals(parameterTypes, method.getParameterTypes())
);
}
public static MethodMatcher parameterTypesMatchingLeniently(Class<?>[] parameterTypes) {
return MethodMatcher.create(
(mm) -> format("parameterTypesMatchingLeniently%s", Arrays.toString(parameterTypes)),
method -> {
AtomicInteger i = new AtomicInteger(0);
return parameterTypes.length == method.getParameterTypes().length &&
Arrays.stream(method.getParameterTypes())
.allMatch(type -> type.isAssignableFrom(parameterTypes[i.getAndIncrement()]));
}
);
}
public static <A extends Annotation> MethodMatcher annotatedWith(Class<A> annotationClass) {
return matching(
m -> "annotatedWith(@" + simpleClassNameOf(annotationClass) + ")",
method -> method.isAnnotationPresent(annotationClass));
}
public static <A extends Annotation> MethodMatcher annotatedWith(Class<A> annotationClass, Predicate<A> annotationPredicate) {
return annotatedWith(annotationClass).and(
matching(
m -> "satisfying:" + prettierToString(m),
m -> annotationPredicate.test(m.getAnnotation(annotationClass))));
}
public static MethodMatcher matching(Function<MethodMatcher, String> nameComposer, Predicate<Method> p) {
return MethodMatcher.create(nameComposer, p);
}
public static ObjectSynthesizer from(SynthesizedObject.Descriptor descriptor) {
ObjectSynthesizer ret;
return (ret = new ObjectSynthesizer()).preprocessWith(Preprocessor.sequence(
importDescriptorFromAnotherSynthesizedObject(descriptor),
ret.preprocessor()));
}
}