ObjectSynthesizer.java

  1. package com.github.dakusui.osynth;

  2. import com.github.dakusui.osynth.core.*;

  3. import java.lang.annotation.Annotation;
  4. import java.lang.reflect.Method;
  5. import java.util.Arrays;
  6. import java.util.Objects;
  7. import java.util.concurrent.atomic.AtomicInteger;
  8. import java.util.function.Function;
  9. import java.util.function.Predicate;
  10. import java.util.regex.Pattern;

  11. import static com.github.dakusui.osynth.core.AbstractObjectSynthesizer.Preprocessor.importDescriptorFromAnotherSynthesizedObject;
  12. import static com.github.dakusui.osynth.core.utils.MethodUtils.*;
  13. import static java.lang.String.format;

  14. /**
  15.  * The main entry pont of the `osynth` object synthesizer library.
  16.  */
  17. public class ObjectSynthesizer extends AbstractObjectSynthesizer<ObjectSynthesizer> {

  18.   public ObjectSynthesizer() {
  19.     super(new SynthesizedObject.Descriptor.Builder()
  20.         .fallbackObject(DEFAULT_FALLBACK_OBJECT));
  21.   }

  22.   public ObjectSynthesizer(SynthesizedObject.Descriptor descriptor) {
  23.     super(new SynthesizedObject.Descriptor.Builder(descriptor));
  24.   }

  25.   public static MethodHandlerEntry.Builder methodCall(String methodName, Class<?>... parameterTypes) {
  26.     return methodCall(MethodSignature.create(methodName, parameterTypes));
  27.   }

  28.   public static MethodHandlerEntry.Builder methodCall(MethodSignature methodSignature) {
  29.     return new MethodHandlerEntry.Builder().matcher(matchingExactly(methodSignature));
  30.   }

  31.   public static MethodHandlerEntry.Builder methodCall(MethodMatcher matcher) {
  32.     return new MethodHandlerEntry.Builder().matcher(matcher);
  33.   }

  34.   public static MethodMatcher matchingExactly(MethodSignature signature) {
  35.     return MethodMatcher.overrideToString(mm -> ("matchingExactly:" + mm.toString()), nameMatchingExactly(signature.name()).and(parameterTypesMatchingExactly(signature.parameterTypes())));
  36.   }

  37.   /**
  38.    * Returns a "lenient" method matcher by signature.
  39.    * The returned matcher checks if
  40.    *
  41.    * 1. The name of a method to be tested if it is matching the name of the `targetMethodSignature` as a regular expression.
  42.    * 2. Every parameter types of the method to be tested is equal to or more special than the corresponding parameter type in the `signature`.
  43.    *
  44.    * If the signature doesn't have any parameter types, it matches a method without
  45.    * any parameters.
  46.    * In case you want to create a matcher that matches a method with a specific name but
  47.    * doesn't care any parameter types, use {@link ObjectSynthesizer#nameMatchingRegex(String)}
  48.    * or {@link ObjectSynthesizer#nameMatchingExactly(String)}.
  49.    *
  50.    * ,@param signature The method signature that matches a returned matcher.
  51.    *
  52.    * @return A method matcher by signature.
  53.    */
  54.   public static MethodMatcher matchingLeniently(MethodSignature signature) {
  55.     return MethodMatcher.overrideToString(mm -> ("matchingLeniently:" + mm.toString()), nameMatchingRegex(signature.name()).and(parameterTypesMatchingLeniently(signature.parameterTypes())));
  56.   }

  57.   public static MethodMatcher nameMatchingExactly(String methodName) {
  58.     return MethodMatcher.create(
  59.         (mm) -> format("nameMatchingExactly[%s]", methodName),
  60.         method -> Objects.equals(methodName, method.getName()));
  61.   }

  62.   public static MethodMatcher nameMatchingRegex(String regexForMethodName) {
  63.     Pattern regex = Pattern.compile(regexForMethodName);
  64.     return MethodMatcher.create(
  65.         (mm) -> format("nameMatchingRegex[%s]", regexForMethodName),
  66.         method -> regex.matcher(method.getName()).matches());
  67.   }

  68.   public static MethodMatcher parameterTypesMatchingExactly(Class<?>[] parameterTypes) {
  69.     return MethodMatcher.create(
  70.         (mm) -> format("parameterTypesMatchingExactly%s", Arrays.toString(parameterTypes)),
  71.         method -> Arrays.equals(parameterTypes, method.getParameterTypes())
  72.     );
  73.   }

  74.   public static MethodMatcher parameterTypesMatchingLeniently(Class<?>[] parameterTypes) {
  75.     return MethodMatcher.create(
  76.         (mm) -> format("parameterTypesMatchingLeniently%s", Arrays.toString(parameterTypes)),
  77.         method -> {
  78.           AtomicInteger i = new AtomicInteger(0);
  79.           return parameterTypes.length == method.getParameterTypes().length &&
  80.               Arrays.stream(method.getParameterTypes())
  81.                   .allMatch(type -> type.isAssignableFrom(parameterTypes[i.getAndIncrement()]));
  82.         }
  83.     );
  84.   }

  85.   public static <A extends Annotation> MethodMatcher annotatedWith(Class<A> annotationClass) {
  86.     return matching(
  87.         m -> "annotatedWith(@" + simpleClassNameOf(annotationClass) + ")",
  88.         method -> method.isAnnotationPresent(annotationClass));
  89.   }

  90.   public static <A extends Annotation> MethodMatcher annotatedWith(Class<A> annotationClass, Predicate<A> annotationPredicate) {
  91.     return annotatedWith(annotationClass).and(
  92.         matching(
  93.             m -> "satisfying:" + prettierToString(m),
  94.             m -> annotationPredicate.test(m.getAnnotation(annotationClass))));
  95.   }

  96.   public static MethodMatcher matching(Function<MethodMatcher, String> nameComposer, Predicate<Method> p) {
  97.     return MethodMatcher.create(nameComposer, p);
  98.   }

  99.   public static ObjectSynthesizer from(SynthesizedObject.Descriptor descriptor) {
  100.     ObjectSynthesizer ret;
  101.     return (ret = new ObjectSynthesizer()).preprocessWith(Preprocessor.sequence(
  102.         importDescriptorFromAnotherSynthesizedObject(descriptor),
  103.         ret.preprocessor()));
  104.   }
  105. }