【问题标题】:How to validate Java code programatically?如何以编程方式验证 Java 代码?
【发布时间】:2021-01-11 23:17:47
【问题描述】:

鉴于源代码和 Java 版本,我需要能够验证代码是否可以编译。如果代码无法编译,我需要能够在源代码中返回错误。

以下解决方案有效,但仅适用于您机器上当前使用的 Java 版本。

import javax.tools.*;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class Validator {
    private final JavaCompiler COMPILER = ToolProvider.getSystemJavaCompiler();
    
    //Assume srcFiles are java files that can be read
    public final boolean compiles(Set<File> srcFiles) throws IOException {

        //Convert files to JavaCompiler API compatible files
        List<JavaFileObject> compilationUnits = new ArrayList<>();
        for (File file : srcFiles) {
            CompilableFile compilableFile = new CompilableFile(file.getName(), Files.readString(file.toPath()));
            compilationUnits.add(compilableFile);
        }

        DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
        JavaCompiler.CompilationTask task = COMPILER.getTask(null, null, diagnosticCollector, null, null, compilationUnits);
        boolean success = task.call();

        //Displaying the info of each warning, error, etc
        diagnosticCollector.getDiagnostics().forEach(Validator::printDiagnostic);

        return success;
    }

    private static void printDiagnostic(Diagnostic<?> diagnostic) {
        System.out.println(diagnostic.getCode());
        System.out.println(diagnostic.getKind());
        System.out.println(diagnostic.getPosition());
        System.out.println(diagnostic.getStartPosition());
        System.out.println(diagnostic.getEndPosition());
        System.out.println(diagnostic.getSource());
        System.out.println(diagnostic.getMessage(null));
    }

    /**
     * Instances of this class can be compiled with the JavaCompiler API
     */
    private static final class CompilableFile extends SimpleJavaFileObject {
        final String code;

        CompilableFile(String name, String code) {
            super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
            this.code = code;
        }

        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return code;
        }
    }
}

有没有我可以实现以下功能?

public final boolean compiles(Set<File> srcFiles, SourceVersion version) {...}

【问题讨论】:

  • @Abra 我看不出这有什么关系。我只是想验证代码,我不一定需要编译它,理论上我可以使用 Java 9 实现一个 Java 10 编译器,然后用它来验证我的代码。即便如此,如果您有 tools.jar 文件,您也可以使用 Java 9 编译 Java 10 代码。见here。此外,如果我有一个 Java 15 编译器,那将无法验证它之前的所有 Java 代码,因为我可以给它一个在以前的版本中无法运行的有效 Java 15 程序。
  • @Progman 我的问题不是那种形式吗?那我该怎么改写呢?我没有看到任何其他方式来询问如何做某事
  • 从未尝试实现我自己的,但您是否尝试设置source 选项?我看到您的编译器选项设置为空。也许有办法以这种方式设置source 编译器选项?
  • @IQbrod 谢谢!即使传递 -source 选项不起作用,调查它让我尝试传递 --release 选项,它确实有效

标签: java java-compiler-api javacompiler


【解决方案1】:

此解决方案适用的版本范围似乎是特定于编译器的,但对于 OpenJDK 11 和 15,我注意到此解决方案适用于 [7, SYSTEM_COMPILER_VERSION]

JavaCompiler API 允许您在调用方法JavaCompiler.CompilationTask::getTask 时将命令行选项作为Iterable 传递,因此您可以传递List.of("--release", "&lt;version&gt;"),其中&lt;version&gt; 将替换为您正在验证的版本

考虑到这一点,解决方案就变成了

import javax.tools.*;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class Validator {
    private final JavaCompiler COMPILER = ToolProvider.getSystemJavaCompiler();
    
    //Assume srcFiles are java files that can be read
    public final boolean compiles(Set<File> srcFiles, String javaVersion) throws IOException {

        //Convert files to JavaCompiler API compatible files
        List<JavaFileObject> compilationUnits = new ArrayList<>();
        for (File file : srcFiles) {
            CompilableFile compilableFile = new CompilableFile(file.getName(), Files.readString(file.toPath()));
            compilationUnits.add(compilableFile);
        }

        DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
        JavaCompiler.CompilationTask task = COMPILER.getTask(null, null, diagnosticCollector, List.of("--release", javaVersion), null, compilationUnits);
        boolean success = task.call();

        //Displaying the info of each warning, error, etc
        diagnosticCollector.getDiagnostics().forEach(Validator::printDiagnostic);

        return success;
    }

    private static void printDiagnostic(Diagnostic<?> diagnostic) {
        System.out.println(diagnostic.getCode());
        System.out.println(diagnostic.getKind());
        System.out.println(diagnostic.getPosition());
        System.out.println(diagnostic.getStartPosition());
        System.out.println(diagnostic.getEndPosition());
        System.out.println(diagnostic.getSource());
        System.out.println(diagnostic.getMessage(null));
    }

    /**
     * Instances of this class can be compiled with the JavaCompiler API
     */
    private static final class CompilableFile extends SimpleJavaFileObject {
        final String code;

        CompilableFile(String name, String code) {
            super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
            this.code = code;
        }

        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return code;
        }
    }
}


【讨论】:

    猜你喜欢
    • 2019-05-09
    • 2023-01-20
    • 2013-07-03
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多