All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openrewrite.java.JavaTemplate Maven / Gradle / Ivy

There is a newer version: 8.40.2
Show newest version
/*
 * Copyright 2020 the original author or authors.
 * 

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* https://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.openrewrite.java; import lombok.Getter; import lombok.Value; import lombok.experimental.NonFinal; import org.jspecify.annotations.Nullable; import org.openrewrite.Cursor; import org.openrewrite.Incubating; import org.openrewrite.internal.StringUtils; import org.openrewrite.java.internal.template.JavaTemplateJavaExtension; import org.openrewrite.java.internal.template.JavaTemplateParser; import org.openrewrite.java.internal.template.Substitutions; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaCoordinates; import org.openrewrite.template.SourceTemplate; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; @SuppressWarnings("unused") public class JavaTemplate implements SourceTemplate { @Nullable private static Path TEMPLATE_CLASSPATH_DIR; protected static Path getTemplateClasspathDir() { if (TEMPLATE_CLASSPATH_DIR == null) { try { TEMPLATE_CLASSPATH_DIR = Files.createTempDirectory("java-template"); Path templateDir = Files.createDirectories(TEMPLATE_CLASSPATH_DIR.resolve("org/openrewrite/java/internal/template")); Path mClass = templateDir.resolve("__M__.class"); Path pClass = templateDir.resolve("__P__.class"); // Delete in reverse order to avoid issues with non-empty directories for (Path path : new Path[]{ TEMPLATE_CLASSPATH_DIR, TEMPLATE_CLASSPATH_DIR.resolve("org"), TEMPLATE_CLASSPATH_DIR.resolve("org/openrewrite"), TEMPLATE_CLASSPATH_DIR.resolve("org/openrewrite/java"), TEMPLATE_CLASSPATH_DIR.resolve("org/openrewrite/java/internal"), templateDir, mClass, pClass}) { path.toFile().deleteOnExit(); } try (InputStream in = JavaTemplateParser.class.getClassLoader().getResourceAsStream("org/openrewrite/java/internal/template/__M__.class")) { assert in != null; Files.copy(in, mClass); } try (InputStream in = JavaTemplateParser.class.getClassLoader().getResourceAsStream("org/openrewrite/java/internal/template/__P__.class")) { assert in != null; Files.copy(in, pClass); } } catch (IOException e) { throw new RuntimeException(e); } } return TEMPLATE_CLASSPATH_DIR; } @Getter private final String code; private final Consumer onAfterVariableSubstitution; private final JavaTemplateParser templateParser; private JavaTemplate(boolean contextSensitive, JavaParser.Builder parser, String code, Set imports, Consumer onAfterVariableSubstitution, Consumer onBeforeParseTemplate) { this(code, onAfterVariableSubstitution, new JavaTemplateParser(contextSensitive, augmentClasspath(parser), onAfterVariableSubstitution, onBeforeParseTemplate, imports)); } private static JavaParser.Builder augmentClasspath(JavaParser.Builder parserBuilder) { return parserBuilder.addClasspathEntry(getTemplateClasspathDir()); } protected JavaTemplate(String code, Consumer onAfterVariableSubstitution, JavaTemplateParser templateParser) { this.code = code; this.onAfterVariableSubstitution = onAfterVariableSubstitution; this.templateParser = templateParser; } @Override @SuppressWarnings("unchecked") public J2 apply(Cursor scope, JavaCoordinates coordinates, Object... parameters) { if (!(scope.getValue() instanceof J)) { throw new IllegalArgumentException("`scope` must point to a J instance."); } Substitutions substitutions = substitutions(parameters); String substitutedTemplate = substitutions.substitute(); onAfterVariableSubstitution.accept(substitutedTemplate); //noinspection ConstantConditions return (J2) new JavaTemplateJavaExtension(templateParser, substitutions, substitutedTemplate, coordinates) .getMixin() .visit(scope.getValue(), 0, scope.getParentOrThrow()); } protected Substitutions substitutions(Object[] parameters) { return new Substitutions(code, parameters); } @Incubating(since = "8.0.0") public static boolean matches(String template, Cursor cursor) { return JavaTemplate.builder(template).build().matches(cursor); } @Incubating(since = "7.38.0") public boolean matches(Cursor cursor) { return matcher(cursor).find(); } @Incubating(since = "7.38.0") public Matcher matcher(Cursor cursor) { return new Matcher(cursor); } @Incubating(since = "7.38.0") @Value public class Matcher { Cursor cursor; @NonFinal JavaTemplateSemanticallyEqual.TemplateMatchResult matchResult; Matcher(Cursor cursor) { this.cursor = cursor; } public boolean find() { matchResult = JavaTemplateSemanticallyEqual.matchesTemplate(JavaTemplate.this, cursor); return matchResult.isMatch(); } public J parameter(int i) { return matchResult.getMatchedParameters().get(i); } } public static J2 apply(String template, Cursor scope, JavaCoordinates coordinates, Object... parameters) { return builder(template).build().apply(scope, coordinates, parameters); } public static Builder builder(String code) { return new Builder(code); } @SuppressWarnings("unused") public static class Builder { private final String code; private final Set imports = new HashSet<>(); private boolean contextSensitive; private JavaParser.Builder parser = org.openrewrite.java.JavaParser.fromJavaVersion(); private Consumer onAfterVariableSubstitution = s -> { }; private Consumer onBeforeParseTemplate = s -> { }; protected Builder(String code) { this.code = code.trim(); } /** * A template snippet is context-sensitive when it refers to the class, variables, methods, or other symbols * visible from its insertion scope. When a template is completely self-contained, it is not context-sensitive. * Context-free template snippets can be cached, since it does not matter where the resulting LST elements will * be inserted. Since the LST elements in a context-sensitive snippet vary depending on where they are inserted * the resulting LST elements cannot be reused between different insertion points and are not cached. *

* An example of a context-free snippet might be something like this, to be used as a local variable declaration: * int i = 1; *

* An example of a context-sensitive snippet is: * int i = a; * This cannot be made sense of without the surrounding scope which includes the declaration of "a". */ public Builder contextSensitive() { this.contextSensitive = true; return this; } public Builder imports(String... fullyQualifiedTypeNames) { for (String typeName : fullyQualifiedTypeNames) { validateImport(typeName); this.imports.add("import " + typeName + ";\n"); } return this; } public Builder staticImports(String... fullyQualifiedMemberTypeNames) { for (String typeName : fullyQualifiedMemberTypeNames) { validateImport(typeName); this.imports.add("import static " + typeName + ";\n"); } return this; } private void validateImport(String typeName) { if (StringUtils.isBlank(typeName)) { throw new IllegalArgumentException("Imports must not be blank"); } else if (typeName.startsWith("import ") || typeName.startsWith("static ")) { throw new IllegalArgumentException("Imports are expressed as fully-qualified names and should not include an \"import \" or \"static \" prefix"); } else if (typeName.endsWith(";") || typeName.endsWith("\n")) { throw new IllegalArgumentException("Imports are expressed as fully-qualified names and should not include a suffixed terminator"); } } public Builder javaParser(JavaParser.Builder parser) { this.parser = parser; return this; } public Builder doAfterVariableSubstitution(Consumer afterVariableSubstitution) { this.onAfterVariableSubstitution = afterVariableSubstitution; return this; } public Builder doBeforeParseTemplate(Consumer beforeParseTemplate) { this.onBeforeParseTemplate = beforeParseTemplate; return this; } public JavaTemplate build() { return new JavaTemplate(contextSensitive, parser.clone(), code, imports, onAfterVariableSubstitution, onBeforeParseTemplate); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy