Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions src/main/java/org/openrewrite/java/template/Semantics.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.openrewrite.java.template;

import org.openrewrite.ExecutionContext;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.template.function.*;
Expand Down Expand Up @@ -112,4 +113,94 @@ public static JavaTemplate.Builder statement(JavaVisitor<?> owner, String name,
public static JavaTemplate.Builder statement(JavaVisitor<?> owner, String name, Stat10<?, ?, ?, ?, ?, ?, ?, ?, ?, ?> p) {
return new PatternBuilder(name).build(owner);
}

// Again, but with ExecutionContext argument

public static JavaTemplate.Builder expression(ExecutionContext ctx, JavaVisitor<?> owner, String name, Expr0<?> f) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder expression(ExecutionContext ctx, JavaVisitor<?> owner, String name, Expr1<?, ?> f) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder expression(ExecutionContext ctx, JavaVisitor<?> owner, String name, Expr2<?, ?, ?> f) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder expression(ExecutionContext ctx, JavaVisitor<?> owner, String name, Expr3<?, ?, ?, ?> f) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder expression(ExecutionContext ctx, JavaVisitor<?> owner, String name, Expr4<?, ?, ?, ?, ?> f) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder expression(ExecutionContext ctx, JavaVisitor<?> owner, String name, Expr5<?, ?, ?, ?, ?, ?> f) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder expression(ExecutionContext ctx, JavaVisitor<?> owner, String name, Expr6<?, ?, ?, ?, ?, ?, ?> f) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder expression(ExecutionContext ctx, JavaVisitor<?> owner, String name, Expr7<?, ?, ?, ?, ?, ?, ?, ?> f) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder expression(ExecutionContext ctx, JavaVisitor<?> owner, String name, Expr8<?, ?, ?, ?, ?, ?, ?, ?, ?> f) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder expression(ExecutionContext ctx, JavaVisitor<?> owner, String name, Expr9<?, ?, ?, ?, ?, ?, ?, ?, ?, ?> f) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder expression(ExecutionContext ctx, JavaVisitor<?> owner, String name, Expr10<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?> f) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder statement(ExecutionContext ctx, JavaVisitor<?> owner, String name, Stat0 p) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder statement(ExecutionContext ctx, JavaVisitor<?> owner, String name, Stat1<?> p) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder statement(ExecutionContext ctx, JavaVisitor<?> owner, String name, Stat2<?, ?> p) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder statement(ExecutionContext ctx, JavaVisitor<?> owner, String name, Stat3<?, ?, ?> p) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder statement(ExecutionContext ctx, JavaVisitor<?> owner, String name, Stat4<?, ?, ?, ?> p) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder statement(ExecutionContext ctx, JavaVisitor<?> owner, String name, Stat5<?, ?, ?, ?, ?> p) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder statement(ExecutionContext ctx, JavaVisitor<?> owner, String name, Stat6<?, ?, ?, ?, ?, ?> p) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder statement(ExecutionContext ctx, JavaVisitor<?> owner, String name, Stat7<?, ?, ?, ?, ?, ?, ?> p) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder statement(ExecutionContext ctx, JavaVisitor<?> owner, String name, Stat8<?, ?, ?, ?, ?, ?, ?, ?> p) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder statement(ExecutionContext ctx, JavaVisitor<?> owner, String name, Stat9<?, ?, ?, ?, ?, ?, ?, ?, ?> p) {
return new PatternBuilder(name).build(ctx, owner);
}

public static JavaTemplate.Builder statement(ExecutionContext ctx, JavaVisitor<?> owner, String name, Stat10<?, ?, ?, ?, ?, ?, ?, ?, ?, ?> p) {
return new PatternBuilder(name).build(ctx, owner);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.TreeScanner;
import org.jspecify.annotations.Nullable;

import javax.tools.JavaFileObject;
import java.util.LinkedHashSet;
Expand All @@ -38,7 +39,7 @@ public class ClasspathJarNameDetector {
public static Set<String> classpathFor(JCTree input, List<Symbol> imports) {
Set<String> jarNames = new LinkedHashSet<String>() {
@Override
public boolean add(String s) {
public boolean add(@Nullable String s) {
return s != null && super.add(s);
}
};
Expand All @@ -64,7 +65,7 @@ public void scan(JCTree tree) {
}


private static String jarNameFor(Symbol anImport) {
private static @Nullable String jarNameFor(Symbol anImport) {
Symbol.ClassSymbol enclClass = anImport instanceof Symbol.ClassSymbol ? (Symbol.ClassSymbol) anImport : anImport.enclClass();
while (enclClass.enclClass() != null && enclClass.enclClass() != enclClass) {
enclClass = enclClass.enclClass();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package org.openrewrite.java.template.internal;

import lombok.Value;
import org.openrewrite.ExecutionContext;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.JavaVisitor;

Expand All @@ -38,4 +39,16 @@ public JavaTemplate.Builder build(JavaVisitor<?> owner) {
throw new RuntimeException(e);
}
}

public JavaTemplate.Builder build(ExecutionContext ctx, JavaVisitor<?> owner) {
try {
Class<?> templateClass = Class.forName(owner.getClass().getName() + "_" + name, true,
owner.getClass().getClassLoader());
Method getTemplate = templateClass.getDeclaredMethod("getTemplate", ExecutionContext.class);
return (JavaTemplate.Builder) getTemplate.invoke(null, ctx);
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,15 @@

public class TemplateCode {

public static <T extends JCTree> String process(T tree, @Nullable Type returnType, List<JCTree.JCVariableDecl> parameters, List<JCTree.JCTypeParameter> typeParameters, int pos, boolean asStatement, boolean fullyQualified) {
public static <T extends JCTree> String process(
T tree,
@Nullable Type returnType,
List<JCTree.JCVariableDecl> parameters,
List<JCTree.JCTypeParameter> typeParameters,
int pos,
boolean asStatement,
boolean fullyQualified,
boolean classpathFromResources) {
StringWriter writer = new StringWriter();
TemplateCodePrinter printer = new TemplateCodePrinter(writer, parameters, pos, fullyQualified);
try {
Expand Down Expand Up @@ -68,12 +76,13 @@ public static <T extends JCTree> String process(T tree, @Nullable Type returnTyp
jarNames.addAll(ClasspathJarNameDetector.classpathFor(parameter, ImportDetector.imports(parameter)));
}
if (!jarNames.isEmpty()) {
// It might be preferable to enumerate exactly the needed dependencies rather than the full classpath
// But this is expedient
// See https://github.com/openrewrite/rewrite-templating/issues/86
// String classpath = jarNames.stream().map(jarName -> '"' + jarName + '"').sorted().collect(joining(", "));
// builder.append("\n .javaParser(JavaParser.fromJavaVersion().classpath(").append(classpath).append("))");
builder.append("\n .javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath()))");
builder.append("\n .javaParser(JavaParser.fromJavaVersion()");
if (classpathFromResources) {
String joinedJarNames = jarNames.stream().collect(joining(", ", "\"", "\""));
builder.append(".classpathFromResources(ctx, ").append(joinedJarNames).append("))\n ");
} else {
builder.append(".classpath(JavaParser.runtimeClasspath()))\n ");
}
}
return builder.toString();
} catch (IOException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,19 @@ public String toJavaTemplateBuilder(int pos) {
tree = ((JCTree.JCReturn) tree).getExpression();
}

String javaParserClasspathFrom = processingEnv.getOptions().get("rewrite.javaParserClasspathFrom");
boolean classpathFromResources = "resources".equals(javaParserClasspathFrom);

List<JCTree.JCTypeParameter> typeParameters = classDecl.typarams == null ? emptyList() : classDecl.typarams;
return TemplateCode.process(tree, method.getReturnType().type, method.getParameters(), typeParameters, pos, method.restype.type instanceof Type.JCVoidType, true);
return TemplateCode.process(
tree,
method.getReturnType().type,
method.getParameters(),
typeParameters,
pos,
method.restype.type instanceof Type.JCVoidType,
true,
classpathFromResources);
}

public boolean validate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,21 +139,33 @@ public void visitApply(JCTree.JCMethodInvocation tree) {
}
}

String templateCode = TemplateCode.process(resolved.get(template.getBody()), null, parameters, emptyList(), 0, "statement".equals(name), false);
String javaParserClasspathFrom = processingEnv.getOptions().get("rewrite.javaParserClasspathFrom");
boolean classpathFromResources = "resources".equals(javaParserClasspathFrom);

String templateCode = TemplateCode.process(
resolved.get(template.getBody()),
null,
parameters,
emptyList(),
0,
"statement".equals(name),
false,
classpathFromResources);

Symbol.PackageSymbol pkg = classDecl.sym.packge();
JavaFileObject builderFile = processingEnv.getFiler().createSourceFile(templateFqn);
try (Writer out = new BufferedWriter(builderFile.openWriter())) {
if (!pkg.isUnnamed()) {
out.write("package " + pkg.fullname + ";\n");
out.write("\n");
out.write("package " + pkg.fullname + ";\n\n");
}
if (classpathFromResources) {
out.write("import org.openrewrite.ExecutionContext;\n");
}
if (templateCode.contains("JavaParser")) {
out.write("import org.openrewrite.java.JavaParser;\n");
}
out.write("import org.openrewrite.java.JavaTemplate;\n");
out.write("import org.openrewrite.java.JavaTemplate;\n\n");

out.write("\n");
out.write("/**\n * OpenRewrite `" + templateName.getValue() + "` template created for {@code " + templateFqn.split("_")[0] + "}.\n */\n");
String templateClassName = templateFqn.substring(templateFqn.lastIndexOf('.') + 1);
out.write("@SuppressWarnings(\"all\")\n");
Expand All @@ -166,7 +178,9 @@ public void visitApply(JCTree.JCMethodInvocation tree) {
out.write(" * Get the {@code JavaTemplate.Builder} to match or replace.\n");
out.write(" * @return the JavaTemplate builder.\n");
out.write(" */\n");
out.write(" public static JavaTemplate.Builder getTemplate() {\n");
out.write(" public static JavaTemplate.Builder getTemplate("+
(classpathFromResources ? "ExecutionContext ctx" : "")
+") {\n");
Comment thread
timtebeek marked this conversation as resolved.
Outdated
out.write(" return " + indentNewLine(templateCode, 12) + ";\n");
out.write(" }\n");
out.write("}\n");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
import jakarta.annotation.Generated;
import org.intellij.lang.annotations.Language;
import org.jspecify.annotations.NullMarked;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
Expand Down Expand Up @@ -161,22 +160,11 @@ void jakartaGeneratedAnnotationOverride() throws Exception {
}

private static Compilation compileResource(String resourceName) {
return compileResource(resourceName, new RefasterTemplateProcessor());
}

static Compilation compileResource(String resourceName, TypeAwareProcessor processor) {
// As per https://github.com/google/compile-testing/blob/v0.21.0/src/main/java/com/google/testing/compile/package-info.java#L53-L55
return compile(JavaFileObjects.forResource(resourceName), processor);
}

@SuppressWarnings("unused") // use when text blocks are available
static Compilation compileSource(String fqn, @Language("java") String source) {
return compile(JavaFileObjects.forSourceString(fqn, source), new RefasterTemplateProcessor());
}

@SuppressWarnings("unused") // use when text blocks are available
static Compilation compileSource(String fqn, @Language("java") String source, TypeAwareProcessor processor) {
return compile(JavaFileObjects.forSourceString(fqn, source), processor);
return compile(
JavaFileObjects.forResource(resourceName),
new RefasterTemplateProcessor(),
"-Arewrite.javaParserClasspathFrom=resources");
}

static Compilation compile(JavaFileObject javaFileObject, TypeAwareProcessor processor, Object... options) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ void parserClasspath() {
.hasSourceEquivalentTo(JavaFileObjects.forResource("template/LoggerRecipe$1_info.java"));
}

@Test
void parserClasspathFromResources() {
Compilation compilation = compileResource("template/LoggerRecipe.java",
"-Arewrite.javaParserClasspathFrom=resources");
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("template/LoggerRecipe$1_logger")
.hasSourceEquivalentTo(JavaFileObjects.forResource("template/LoggerRecipeFromResources$1_logger.java"));
assertThat(compilation)
.generatedSourceFile("template/LoggerRecipe$1_info")
.hasSourceEquivalentTo(JavaFileObjects.forResource("template/LoggerRecipeFromResources$1_info.java"));
}

@Test
void anonymousClass() {
Compilation compilation = compileResource("template/AnonymousClass.java");
Expand Down Expand Up @@ -107,8 +120,8 @@ void unnamedPackage() {
.hasSourceEquivalentTo(JavaFileObjects.forResource("template/UnnamedPackage$1_message.java"));
}

static Compilation compileResource(String resourceName) {
static Compilation compileResource(String resourceName, Object... options) {
// As per https://github.com/google/compile-testing/blob/v0.21.0/src/main/java/com/google/testing/compile/package-info.java#L53-L55
return compile(JavaFileObjects.forResource(resourceName), new TemplateProcessor());
return compile(JavaFileObjects.forResource(resourceName), new TemplateProcessor(), options);
}
}
2 changes: 0 additions & 2 deletions src/test/resources/refaster/EmptyAfterMethodRecipes.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ public String getDescription() {
public TreeVisitor<?, ExecutionContext> getVisitor() {
JavaVisitor<ExecutionContext> javaVisitor = new AbstractRefasterJavaVisitor() {
JavaTemplate before;

@Override
public J visitBinary(J.Binary elem, ExecutionContext ctx) {
JavaTemplate.Matcher matcher;
Expand Down Expand Up @@ -144,7 +143,6 @@ public String getDescription() {
public TreeVisitor<?, ExecutionContext> getVisitor() {
JavaVisitor<ExecutionContext> javaVisitor = new AbstractRefasterJavaVisitor() {
JavaTemplate before;

@Override
public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) {
JavaTemplate.Matcher matcher;
Expand Down
6 changes: 4 additions & 2 deletions src/test/resources/refaster/EscapesRecipes.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,15 @@ public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) {
if (before == null) {
before = JavaTemplate.builder("String.format(\"\\\"%s\\\"\", com.google.common.base.Strings.nullToEmpty(#{value:any(java.lang.String)}))")
.bindType("java.lang.String")
.javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath())).build();
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "guava"))
.build();
}
if ((matcher = before.matcher(getCursor())).find()) {
if (after == null) {
after = JavaTemplate.builder("com.google.common.base.Strings.lenientFormat(#{value:any(java.lang.String)})")
.bindType("java.lang.String")
.javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath())).build();
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "guava"))
.build();
}
return embed(
after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)),
Expand Down
1 change: 0 additions & 1 deletion src/test/resources/refaster/FindListAddRecipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ public String getDescription() {
public TreeVisitor<?, ExecutionContext> getVisitor() {
JavaVisitor<ExecutionContext> javaVisitor = new AbstractRefasterJavaVisitor() {
JavaTemplate before;

@Override
public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) {
JavaTemplate.Matcher matcher;
Expand Down
Loading