static <A extends Annotation> void removeAnnotation(CtMethod<?> method, Class<A> annClass) {
   CtAnnotation<?> toRemove = null;
   for (CtAnnotation<? extends Annotation> ctAnnotation : method.getAnnotations()) {
     if (annClass.isAssignableFrom(ctAnnotation.getActualAnnotation().getClass())) {
       toRemove = ctAnnotation;
       break;
     }
   }
   if (toRemove != null) method.removeAnnotation(toRemove);
 }
  protected Map<Statement, Double> buildCodeFragmentFor(CtType cl, Coverage coverage) {
    Factory factory = cl.getFactory();
    Map<Statement, Double> codeFragments = new LinkedHashMap<>();

    for (CtMethod<?> mth : (Set<CtMethod>) cl.getMethods()) {
      if (!mth.getModifiers().contains(ModifierKind.ABSTRACT)
          && !mth.getModifiers().contains(ModifierKind.PRIVATE)) {
        //                    && getCoverageForMethod(coverage, cl, mth) != 1.0) {

        CtExecutableReference executableRef = factory.Executable().createReference(mth);
        executableRef.setStatic(mth.getModifiers().contains(ModifierKind.STATIC));
        CtInvocation invocation =
            factory.Code().createInvocation(buildVarRef(cl.getReference(), factory), executableRef);
        invocation.setArguments(
            mth.getParameters()
                .stream()
                .map(param -> buildVarRef(param.getType(), factory))
                .collect(Collectors.toList()));
        invocation.setType(mth.getType());
        Statement stmt = new Statement(invocation);
        codeFragments.put(stmt, getCoverageForMethod(coverage, cl, mth));
      }
    }
    return codeFragments;
  }
  private static <T> void processOverridden(
      CtClass<?> mergeInto, CtClass<?> toMerge, final CtMethod<T> methodToMerge) {
    List<CtInvocation<T>> superInvocations =
        mergeInto.getElements(
            new Filter<CtInvocation<T>>() {
              @Override
              public boolean matches(CtInvocation<T> invocation) {
                if (!(invocation.getTarget() instanceof CtSuperAccess)) return false;
                CtExecutable<?> m = invocation.getExecutable().getDeclaration();
                return m != null && MethodNode.overrides((CtMethod<?>) m, methodToMerge);
              }
            });

    methodToMerge.setSimpleName(classPrefixedName(toMerge, methodToMerge));
    methodToMerge.setVisibility(PRIVATE);
    removeAnnotation(methodToMerge, Override.class);

    for (CtInvocation<T> superInvocation : superInvocations) {
      superInvocation.setTarget(null);
      superInvocation.setExecutable(methodToMerge.getReference());
    }
    add(mergeInto, methodToMerge, mergeInto::addMethod);
  }
  protected List<InputContext> getInputContexts(CtMethod method) {
    List<InputContext> inputContexts = new ArrayList<>();

    List<CtStatement> statements = getAssertStatement(method);
    for (CtStatement stmt : statements) {
      Set<CtVariableReference> varRefs = new HashSet<>();
      for (CtLocalVariable var : getLocalVarInScope(stmt)) {
        varRefs.add(method.getFactory().Code().createLocalVariableReference(var));
      }

      inputContexts.add(new InputContext(varRefs));
    }

    return inputContexts;
  }
  private static <T> void processMethod(
      CtClass<?> mergeInto, CtClass<?> toMerge, CtMethod<T> methodToMerge) {
    if (methodToMerge.hasModifier(STATIC)) {
      add(mergeInto, methodToMerge, mergeInto::addMethod);
      return;
    }
    if (methodToMerge.hasModifier(ABSTRACT)) return;

    Optional<CtMethod<?>> overridingMethod =
        mergeInto
            .getMethods()
            .stream()
            .filter(m -> MethodNode.overrides(m, methodToMerge))
            .findFirst();
    if (overridingMethod.isPresent()) {
      @SuppressWarnings("unchecked")
      CtMethod<T> overriding = (CtMethod<T>) overridingMethod.get();
      boolean shouldRemoveAnn = methodToMerge.getAnnotation(Override.class) == null;
      if (!overriding.hasModifier(ABSTRACT)) processOverridden(mergeInto, toMerge, methodToMerge);
      if (shouldRemoveAnn) removeAnnotation(overriding, Override.class);
    } else {
      add(mergeInto, methodToMerge, mergeInto::addMethod);
    }
  }
  protected double getCoverageForMethod(Coverage coverage, CtType cl, CtMethod mth) {
    if (coverage == null) {
      return 0d;
    }

    String key =
        mth.getDeclaringType().getQualifiedName()
            + "_"
            + mth.getType().getQualifiedName()
            + "_"
            + mth.getSimpleName()
            + "("
            + mth.getParameters()
                .stream()
                .map(param -> ((CtParameter) param).getType().getQualifiedName())
                .collect(Collectors.joining(","))
            + ")";

    if (coverage.getMethodCoverage(key) != null) {
      return coverage.getMethodCoverage(key).coverage();
    } else {
      key =
          cl.getQualifiedName()
              + "_"
              + mth.getType().getQualifiedName()
              + "_"
              + mth.getSimpleName()
              + "("
              + mth.getParameters()
                  .stream()
                  .map(param -> ((CtParameter) param).getType().getQualifiedName())
                  .collect(Collectors.joining(","))
              + ")";
      if (coverage.getMethodCoverage(key) != null) {
        return coverage.getMethodCoverage(key).coverage();
      } else {
        return 0d;
      }
    }
  }
 static String classPrefixedName(CtClass<?> ctClass, CtMethod<?> method) {
   return "_" + ctClass.getSimpleName() + "_" + method.getSimpleName();
 }