Skip to content

Commit

Permalink
Add new JvmParser as supertype to JavaParser
Browse files Browse the repository at this point in the history
To allow the `JavaTemplate` mechanism to be used for other languages like Kotlin, the provided parser builder must provide a way to add a classpath entry. This is because the internal `__M__` and `__P__` types are required for the parameter substitution and rather than including them as source code in the compilation unit, the parser will load them from the classpath.

To this end this PR introduces new `JvmParser` and `JvmParser.Builder` types and the `JavaParser.Builder#classpath()` methods are moved to `JvmParser.Builder`. In order to not increase the API surface area, no corresponding getter was added to `JvmParser.Builder`. Instead, there is a new `Internals` class providing a static `getClasspath(JvmParser.Builder)` method.

Issue: openrewrite/rewrite-kotlin#218
  • Loading branch information
knutwannheden committed Aug 14, 2023
1 parent 68a3cd0 commit 14f6fea
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 48 deletions.
25 changes: 25 additions & 0 deletions rewrite-java/src/main/java/org/openrewrite/java/Internals.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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 java.nio.file.Path;
import java.util.Collection;

public class Internals {
public static Collection<Path> getClasspath(JvmParser.Builder<?, ?> parserBuilder) {
return parserBuilder.classpath;
}
}
16 changes: 2 additions & 14 deletions rewrite-java/src/main/java/org/openrewrite/java/JavaParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import org.intellij.lang.annotations.Language;
import org.openrewrite.ExecutionContext;
import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.Parser;
import org.openrewrite.SourceFile;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.internal.JavaTypeCache;
Expand Down Expand Up @@ -52,7 +51,7 @@
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;

public interface JavaParser extends Parser {
public interface JavaParser extends JvmParser {

/**
* Set to <code>true</code> on an {@link ExecutionContext} supplied to parsing to skip generation of
Expand Down Expand Up @@ -298,8 +297,7 @@ default boolean accept(Path path) {
void setClasspath(Collection<Path> classpath);

@SuppressWarnings("unchecked")
abstract class Builder<P extends JavaParser, B extends Builder<P, B>> extends Parser.Builder {
protected Collection<Path> classpath = Collections.emptyList();
abstract class Builder<P extends JavaParser, B extends Builder<P, B>> extends JvmParser.Builder<P, B> {
protected Collection<byte[]> classBytesClasspath = Collections.emptyList();
protected JavaTypeCache javaTypeCache = new JavaTypeCache();

Expand Down Expand Up @@ -342,16 +340,6 @@ public B dependsOn(@Language("java") String... inputsAsStrings) {
return (B) this;
}

public B classpath(Collection<Path> classpath) {
this.classpath = classpath;
return (B) this;
}

public B classpath(String... classpath) {
this.classpath = dependenciesFromClasspath(classpath);
return (B) this;
}

@SuppressWarnings("UnusedReturnValue")
public B classpathFromResources(ExecutionContext ctx, String... classpath) {
this.classpath = dependenciesFromResources(ctx, classpath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ public class JavaTemplate implements SourceTemplate<J, JavaCoordinates> {
private final Consumer<String> onAfterVariableSubstitution;
private final JavaTemplateParser templateParser;

private JavaTemplate(boolean contextSensitive, JavaParser.Builder<?, ?> javaParser, String code, Set<String> imports,
private JavaTemplate(boolean contextSensitive, JvmParser.Builder<?, ?> parser, String code, Set<String> imports,
Consumer<String> onAfterVariableSubstitution, Consumer<String> onBeforeParseTemplate) {
this.code = code;
this.onAfterVariableSubstitution = onAfterVariableSubstitution;
this.parameterCount = StringUtils.countOccurrences(code, "#{");
this.templateParser = new JavaTemplateParser(contextSensitive, javaParser, onAfterVariableSubstitution, onBeforeParseTemplate, imports);
this.templateParser = new JavaTemplateParser(contextSensitive, parser, onAfterVariableSubstitution, onBeforeParseTemplate, imports);
}

public String getCode() {
Expand Down Expand Up @@ -126,7 +126,7 @@ public static class Builder {

private boolean contextSensitive;

private JavaParser.Builder<?, ?> javaParser = JavaParser.fromJavaVersion();
private JvmParser.Builder<?, ?> parser = JavaParser.fromJavaVersion();

private Consumer<String> onAfterVariableSubstitution = s -> {
};
Expand Down Expand Up @@ -168,8 +168,8 @@ private void validateImport(String typeName) {
}
}

public Builder javaParser(JavaParser.Builder<?, ?> javaParser) {
this.javaParser = javaParser;
public Builder javaParser(JvmParser.Builder<?, ?> parser) {
this.parser = parser;
return this;
}

Expand All @@ -184,7 +184,7 @@ public Builder doBeforeParseTemplate(Consumer<String> beforeParseTemplate) {
}

public JavaTemplate build() {
return new JavaTemplate(contextSensitive, javaParser, code, imports,
return new JavaTemplate(contextSensitive, parser, code, imports,
onAfterVariableSubstitution, onBeforeParseTemplate);
}
}
Expand Down
50 changes: 50 additions & 0 deletions rewrite-java/src/main/java/org/openrewrite/java/JvmParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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 org.openrewrite.Parser;
import org.openrewrite.SourceFile;

import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;

public interface JvmParser extends Parser {
abstract class Builder<P extends JavaParser, B extends JavaParser.Builder<P, B>> extends Parser.Builder {
protected Collection<Path> classpath = Collections.emptyList();

Builder(Class<? extends SourceFile> sourceFileType) {
super(sourceFileType);
}

@SuppressWarnings("unchecked")
public B classpath(Collection<Path> classpath) {
this.classpath = classpath;
return (B) this;
}

@SuppressWarnings("unchecked")
public B classpath(String... classpath) {
this.classpath = JavaParser.dependenciesFromClasspath(classpath);
return (B) this;
}

@Override
public JvmParser.Builder<?, ?> clone() {
return (JvmParser.Builder<?, ?>) super.clone();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,17 @@ private J replaceFieldAccess(Expression fieldAccess, JavaType.Variable fieldType

JavaTemplate.Builder templateBuilder;
if (fieldAccess instanceof J.Identifier) {
maybeAddImport(owningType, newConstantName, false);
templateBuilder = JavaTemplate.builder(newConstantName)
.staticImports(owningType + '.' + newConstantName);
templateBuilder = JavaTemplate.builder(newConstantName);
if (owningType.indexOf('.') != -1) {
templateBuilder.staticImports(owningType + '.' + newConstantName);
maybeAddImport(owningType, newConstantName, false);
}
} else {
maybeAddImport(owningType, false);
templateBuilder = JavaTemplate.builder(owningType.substring(owningType.lastIndexOf('.') + 1) + '.' + newConstantName)
.imports(owningType);
templateBuilder = JavaTemplate.builder(owningType.substring(owningType.lastIndexOf('.') + 1) + '.' + newConstantName);
if (owningType.indexOf('.') != -1) {
templateBuilder.imports(owningType);
maybeAddImport(owningType, false);
}
}

return templateBuilder.contextSensitive().build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,6 @@
public class BlockStatementTemplateGenerator {
private static final String TEMPLATE_COMMENT = "__TEMPLATE__";
private static final String STOP_COMMENT = "__TEMPLATE_STOP__";
static final String EXPR_STATEMENT_PARAM = "class __P__ {" +
" static native <T> T p();" +
" static native <T> T[] arrp();" +
" static native boolean booleanp();" +
" static native byte bytep();" +
" static native char charp();" +
" static native double doublep();" +
" static native int intp();" +
" static native long longp();" +
" static native short shortp();" +
" static native float floatp();" +
"}";
private static final String METHOD_INVOCATION_STUBS = "class __M__ {" +
" static native Object any(Object o);" +
" static native Object any(java.util.function.Predicate<Boolean> o);" +
" static native <T> Object anyT();" +
"}";

private final Set<String> imports;
private final boolean contextSensitive;
Expand Down Expand Up @@ -252,7 +235,7 @@ private void contextFreeTemplate(Cursor cursor, J j, StringBuilder before, Strin
after.append("\n}}");
}

before.insert(0, EXPR_STATEMENT_PARAM + METHOD_INVOCATION_STUBS);
before.insert(0, "import org.openrewrite.java.internal.template.__M__;\nimport org.openrewrite.java.internal.template.__P__;\n");
for (String anImport : imports) {
before.insert(0, anImport);
}
Expand All @@ -262,7 +245,7 @@ private void contextFreeTemplate(Cursor cursor, J j, StringBuilder before, Strin
private void contextTemplate(Cursor cursor, J prior, StringBuilder before, StringBuilder after, J insertionPoint, JavaCoordinates.Mode mode) {
J j = cursor.getValue();
if (j instanceof JavaSourceFile) {
before.insert(0, EXPR_STATEMENT_PARAM + METHOD_INVOCATION_STUBS);
before.insert(0, "import org.openrewrite.java.internal.template.__M__;\nimport org.openrewrite.java.internal.template.__P__;\n");

JavaSourceFile cu = (JavaSourceFile) j;
for (J.Import anImport : cu.getImports()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,20 @@
import org.openrewrite.Cursor;
import org.openrewrite.ExecutionContext;
import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.Parser;
import org.openrewrite.internal.ListUtils;
import org.openrewrite.internal.PropertyPlaceholderHelper;
import org.openrewrite.internal.lang.NonNull;
import org.openrewrite.java.Internals;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JvmParser;
import org.openrewrite.java.RandomizeIdVisitor;
import org.openrewrite.java.tree.*;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;
Expand All @@ -49,15 +57,32 @@ public class JavaTemplateParser {
@Language("java")
private static final String SUBSTITUTED_ANNOTATION = "@java.lang.annotation.Documented public @interface SubAnnotation { int value(); }";

private final JavaParser.Builder<?, ?> parser;
private static final Path TEMPLATE_CLASSPATH_DIR;

static {
try {
TEMPLATE_CLASSPATH_DIR = Files.createTempDirectory("java-template");
Path templateDir = Files.createDirectories(TEMPLATE_CLASSPATH_DIR.resolve("org/openrewrite/java/internal/template"));
try (InputStream in = JavaTemplateParser.class.getClassLoader().getResourceAsStream("org/openrewrite/java/internal/template/__M__.class")) {
Files.copy(in, templateDir.resolve("__M__.class"));
}
try (InputStream in = JavaTemplateParser.class.getClassLoader().getResourceAsStream("org/openrewrite/java/internal/template/__P__.class")) {
Files.copy(in, templateDir.resolve("__P__.class"));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private final JvmParser.Builder<?, ?> parser;
private final Consumer<String> onAfterVariableSubstitution;
private final Consumer<String> onBeforeParseTemplate;
private final Set<String> imports;
private final boolean contextSensitive;
private final BlockStatementTemplateGenerator statementTemplateGenerator;
private final AnnotationTemplateGenerator annotationTemplateGenerator;

public JavaTemplateParser(boolean contextSensitive, JavaParser.Builder<?, ?> parser, Consumer<String> onAfterVariableSubstitution,
public JavaTemplateParser(boolean contextSensitive, JvmParser.Builder<?, ?> parser, Consumer<String> onAfterVariableSubstitution,
Consumer<String> onBeforeParseTemplate, Set<String> imports) {
this.parser = parser;
this.onAfterVariableSubstitution = onAfterVariableSubstitution;
Expand Down Expand Up @@ -235,7 +260,7 @@ private JavaSourceFile compileTemplate(@Language("java") String stub) {
ExecutionContext ctx = new InMemoryExecutionContext();
ctx.putMessage(JavaParser.SKIP_SOURCE_SET_TYPE_GENERATION, true);
ctx.putMessage(ExecutionContext.REQUIRE_PRINT_EQUALS_INPUT, false);
JavaParser jp = parser.clone().build();
Parser jp = newParser();
return (stub.contains("@SubAnnotation") ?
jp.reset().parse(ctx, stub, SUBSTITUTED_ANNOTATION) :
jp.reset().parse(ctx, stub))
Expand All @@ -245,6 +270,20 @@ private JavaSourceFile compileTemplate(@Language("java") String stub) {
.orElseThrow(() -> new IllegalArgumentException("Could not parse as Java"));
}

@NonNull
private Parser newParser() {
JvmParser.Builder<?, ?> parserBuilder = parser.clone();
Collection<Path> classpath = Internals.getClasspath(parserBuilder);
if (classpath.isEmpty()) {
classpath = Collections.singletonList(TEMPLATE_CLASSPATH_DIR);
} else {
classpath = new ArrayList<>(classpath);
classpath.add(TEMPLATE_CLASSPATH_DIR);
}
parserBuilder.classpath(classpath);
return parserBuilder.build();
}

/**
* Return the result of parsing the stub.
* Cache the LST elements parsed from stub only if the stub is context free.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.internal.template;

public class __M__ {
public static native Object any(Object o);

public static native Object any(java.util.function.Predicate<Boolean> o);

public static native <T> Object anyT();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.internal.template;

public class __P__ {
public static native <T> T p();

public static native <T> T[] arrp();

public static native boolean booleanp();

public static native byte bytep();

public static native char charp();

public static native double doublep();

public static native int intp();

public static native long longp();

public static native short shortp();

public static native float floatp();
}

0 comments on commit 14f6fea

Please sign in to comment.