// Path: core/src/main/java/io/github/theangrydev/fluentbdd/core/WithFluentBdd.java
// @SuppressWarnings("FunctionalInterfaceClash") // This is unfortunate, but seems like the lesser evil than having many method names
// public interface WithFluentBdd<TestResult> extends FluentBddCommands<TestResult> {
// 
//     /**
//      * This should be implemented by referring to a {@link FluentBdd} field that is annotated as a JUnit {@link org.junit.Rule}.
//      *
//      * @return The delegate {@link FluentBdd}.
//      */
//     FluentBdd<TestResult> fluentBdd();
// 
//     @Override
//     default TestResult theResult() {
//         return fluentBdd().theResult();
//     }
// 
//     @Override
//     default void given(Given given) {
//         fluentBdd().given(given);
//     }
// 
//     @Override
//     default <T extends When<TestResult>> void when(T when) {
//         fluentBdd().when(when);
//     }
// 
//     @Override
//     default <Then> Then then(ThenAssertion<Then, TestResult> thenAssertion) {
//         return fluentBdd().then(thenAssertion);
//     }
// 
//     @Override
//     default void then(ThenVerification<TestResult> thenVerification) {
//         fluentBdd().then(thenVerification);
//     }
// }
// 
// Path: assertj-extensions-generator/src/main/java/io/github/theangrydev/fluentbdd/assertjgenerator/SuppressWarningsAnnotation.java
// public static SuppressWarnings suppressWarnings(final String... value) {
//     return new SuppressWarningsAnnotation(value);
// }
// 
// Path: assertj-extensions-generator/src/main/java/io/github/theangrydev/fluentbdd/assertjgenerator/TypeNameDetermination.java
// public static TypeNameDetermination typeNameDetermination(List<TypeVariableName> typeVariableNames, PackageNameByClassName packageNameByClassName) {
//     Map<String, TypeVariableName> typeVariableNameByName = typeVariableNames.stream()
//             .collect(toMap(typeVariableName -> typeVariableName.name, identity()));
//     return new TypeNameDetermination(typeVariableNameByName, packageNameByClassName);
// }


import java.util.List;
import static io.github.theangrydev.fluentbdd.assertjgenerator.SuppressWarningsAnnotation.suppressWarnings;
import static io.github.theangrydev.fluentbdd.assertjgenerator.TypeNameDetermination.typeNameDetermination;
import static java.util.stream.Collectors.toList;
import static javax.lang.model.element.Modifier.*;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseException;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.TypeParameter;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.body.TypeDeclaration;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.squareup.javapoet.*;
import io.github.theangrydev.fluentbdd.core.WithFluentBdd;
import org.assertj.core.api.WithAssertions;
import java.io.InputStream;
        TypeNameDetermination typeNameDetermination = typeNameDetermination(rawTypeVariableNames, packageNames);

        List<TypeVariableName> boundTypeVariableNames = methodDeclaration.getTypeParameters().stream()
                .map(typeParameter -> typeVariableName(typeParameter, typeNameDetermination))
                .collect(toList());

        TypeName returnTypeName = typeNameDetermination.determineTypeName(methodDeclaration.getType());
        String methodName = methodDeclaration.getName().replace(ASSERT_THAT_METHOD_PREFIX, thenMethodPrefix);
        MethodSpec.Builder builder = MethodSpec.methodBuilder(methodName)
                .addModifiers(PUBLIC, DEFAULT)
                .addCode(thenMethodCodeEmitter.code(methodDeclaration, thenMethodPrefix))
                .addJavadoc(javadocEmitter.javadoc(thenMethodPrefix, methodDeclaration.getJavaDoc()))
                .returns(returnTypeName);

        boundTypeVariableNames.forEach(builder::addTypeVariable);

        if (methodDeclaration.getParameters().stream().anyMatch(this::isSuppressWarningsUnchecked)) {
            builder.addAnnotation(AnnotationSpec.get(SUPPRESS_WARNINGS_UNCHECKED));
        }

        for (Parameter parameter : methodDeclaration.getParameters()) {
            builder.addParameter(typeNameDetermination.determineTypeName(parameter.getType()), parameter.getName());
        }
        return builder.build();
    }

    private boolean isSuppressWarningsUnchecked(Parameter parameter) {
        return parameter.getAnnotations().stream().anyMatch(annotation -> annotation.toString().equals(SUPPRESS_WARNINGS_UNCHECKED_STRING));
    }

// Path: core/src/main/java/io/github/theangrydev/fluentbdd/core/WithFluentBdd.java
// @SuppressWarnings("FunctionalInterfaceClash") // This is unfortunate, but seems like the lesser evil than having many method names
// public interface WithFluentBdd<TestResult> extends FluentBddCommands<TestResult> {
// 
//     /**
//      * This should be implemented by referring to a {@link FluentBdd} field that is annotated as a JUnit {@link org.junit.Rule}.
//      *
//      * @return The delegate {@link FluentBdd}.
//      */
//     FluentBdd<TestResult> fluentBdd();
// 
//     @Override
//     default TestResult theResult() {
//         return fluentBdd().theResult();
//     }
// 
//     @Override
//     default void given(Given given) {
//         fluentBdd().given(given);
//     }
// 
//     @Override
//     default <T extends When<TestResult>> void when(T when) {
//         fluentBdd().when(when);
//     }
// 
//     @Override
//     default <Then> Then then(ThenAssertion<Then, TestResult> thenAssertion) {
//         return fluentBdd().then(thenAssertion);
//     }
// 
//     @Override
//     default void then(ThenVerification<TestResult> thenVerification) {
//         fluentBdd().then(thenVerification);
//     }
// }
// 
// Path: assertj-extensions-generator/src/main/java/io/github/theangrydev/fluentbdd/assertjgenerator/SuppressWarningsAnnotation.java
// public static SuppressWarnings suppressWarnings(final String... value) {
//     return new SuppressWarningsAnnotation(value);
// }
// 
// Path: assertj-extensions-generator/src/main/java/io/github/theangrydev/fluentbdd/assertjgenerator/TypeNameDetermination.java
// public static TypeNameDetermination typeNameDetermination(List<TypeVariableName> typeVariableNames, PackageNameByClassName packageNameByClassName) {
//     Map<String, TypeVariableName> typeVariableNameByName = typeVariableNames.stream()
//             .collect(toMap(typeVariableName -> typeVariableName.name, identity()));
//     return new TypeNameDetermination(typeVariableNameByName, packageNameByClassName);
// }


// Path: assertj-extensions-generator/src/main/java/io/github/theangrydev/fluentbdd/assertjgenerator/JavaEmitter.java
import java.util.List;
import static io.github.theangrydev.fluentbdd.assertjgenerator.SuppressWarningsAnnotation.suppressWarnings;
import static io.github.theangrydev.fluentbdd.assertjgenerator.TypeNameDetermination.typeNameDetermination;
import static java.util.stream.Collectors.toList;
import static javax.lang.model.element.Modifier.*;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseException;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.TypeParameter;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.body.TypeDeclaration;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.squareup.javapoet.*;
import io.github.theangrydev.fluentbdd.core.WithFluentBdd;
import org.assertj.core.api.WithAssertions;
import java.io.InputStream;

        TypeNameDetermination typeNameDetermination = typeNameDetermination(rawTypeVariableNames, packageNames);

        List<TypeVariableName> boundTypeVariableNames = methodDeclaration.getTypeParameters().stream()
                .map(typeParameter -> typeVariableName(typeParameter, typeNameDetermination))
                .collect(toList());

        TypeName returnTypeName = typeNameDetermination.determineTypeName(methodDeclaration.getType());
        String methodName = methodDeclaration.getName().replace(ASSERT_THAT_METHOD_PREFIX, thenMethodPrefix);
        MethodSpec.Builder builder = MethodSpec.methodBuilder(methodName)
                .addModifiers(PUBLIC, DEFAULT)
                .addCode(thenMethodCodeEmitter.code(methodDeclaration, thenMethodPrefix))
                .addJavadoc(javadocEmitter.javadoc(thenMethodPrefix, methodDeclaration.getJavaDoc()))
                .returns(returnTypeName);

        boundTypeVariableNames.forEach(builder::addTypeVariable);

        if (methodDeclaration.getParameters().stream().anyMatch(this::isSuppressWarningsUnchecked)) {
            builder.addAnnotation(AnnotationSpec.get(SUPPRESS_WARNINGS_UNCHECKED));
        }

        for (Parameter parameter : methodDeclaration.getParameters()) {
            builder.addParameter(typeNameDetermination.determineTypeName(parameter.getType()), parameter.getName());
        }
        return builder.build();
    }

    private boolean isSuppressWarningsUnchecked(Parameter parameter) {
        return parameter.getAnnotations().stream().anyMatch(annotation -> annotation.toString().equals(SUPPRESS_WARNINGS_UNCHECKED_STRING));
    }

    private TypeVariableName typeVariableName(TypeParameter typeParameter, TypeNameDetermination typeNameDetermination) {