Search code examples
javaopenrewrite

How can I reference the class the recipe is applied on in Openrewrite


When I try my test for the recipe I get an IllegalStateException :

java.lang.IllegalStateException: LST contains missing or invalid type information
Identifier->FieldAccess->MethodInvocation->NamedVariable->VariableDeclarations->Block->ClassDeclaration->CompilationUnit
/*~~(Identifier type is missing or malformed)~~>*/Test

As far as I understand this is the case because I did not add the Test class to the classpath like I did with IString.

So my question is whether there is a way to add Test to the classpath or some other way to achive that Test.class does not throw an exception.

TEST

public class StringToIStringTest implements RewriteTest {
    @Override
    public void defaults(RecipeSpec spec) {
        spec.recipe(new StringToIString("Test")).parser(JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(true).classpath("com.something.core.services"));
    }

    @Test
    void prerequisiteTest() {
        rewriteRun(
          //language=java
          java(
            """
              class Test {

              }
              """,
            """
              import com.something.core.services.IString;

              class Test {
                  private static final IString.Builder STRBLD = IString.builder(Test.class);
                  
              }
              """
          )
        );
    }
}

RECIPE

public class StringToIString extends Recipe {

    @NonNull
    String fullyQualifiedClassName;

    @JsonCreator
    public StringToIString(@NonNull @JsonProperty("fullyQualifiedClassName") String fullyQualifiedClassName) {
        this.fullyQualifiedClassName = fullyQualifiedClassName;
    }

    @Override
    public TreeVisitor<?, ExecutionContext> getVisitor() {
        return new StringVisitor();
    }

    public class StringVisitor extends JavaIsoVisitor<ExecutionContext> {
        private final JavaTemplate stringBuilderTemplate = JavaTemplate.builder("private static final IString.Builder STRBLD = IString.builder(#{}.class);").imports("com.something.core.services.IString").javaParser(JavaParser.fromJavaVersion().classpath(fullyQualifiedClassName).classpath("com.something.core.services")).build();

        @Override
        public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext executionContext) {
            //check for right class
            if (classDecl.getType() == null || !classDecl.getType().getFullyQualifiedName().equals(fullyQualifiedClassName)) {
                return classDecl;
            }
            //check if STRBLD is present and add STRBLD
            boolean stringBuilderExists = classDecl.getBody().getStatements().stream().filter(statement -> statement instanceof J.VariableDeclarations).map(J.VariableDeclarations.class::cast).anyMatch(variableDeclarations -> variableDeclarations.toString().equals("private static final IString.Builder STRBLD = IString.builder(" +fullyQualifiedClassName + ".class)"));
            if (!stringBuilderExists) {
                classDecl = classDecl.withBody(stringBuilderTemplate.apply(new Cursor(getCursor(), classDecl.getBody()), classDecl.getBody().getCoordinates().lastStatement(), fullyQualifiedClassName));
                maybeAddImport("com.something.core.services.IString");
            }
            return classDecl;
        }
    }
}

So far I tried to avoid referencing Test by using #{} and replacing it with the provided class name.


Solution

  • In your case calling .contextSensitive() on your JavaTemplate is likely to help; that sets up a wider context of your surrounding class, which you can see through .doBeforeParseTemplate(System.out::println). You can then likely simplify your JavaTemplate String argument again. Hope that helps!