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.
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!