java - 如何以编程方式验证 Java 代码?

标签 java java-compiler-api javacompiler

鉴于源代码和 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) {...}

最佳答案

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

JavaCompiler API 允许您将命令行选项作为 Iterable 传递当您调用方法时 JavaCompiler.CompilationTask::getTask , 所以你可以通过 List.of("--release", "<version>")其中 <version>替换为您正在验证的版本

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

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;
        }
    }
}


关于java - 如何以编程方式验证 Java 代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64067188/

相关文章:

java - 跨项目指定 XSD schemaLocation 的正确或正确方法是什么?

java - AppEngine,如何使用静态html文件

java - 如何将 ActionListener 设置为 JFormattedTextField?

java - 如何检查生成的 Java 代码是否符合 Java 语言规范?

java - Java 8 中 try-with-resources block 中的锁风险

android - 解决错误 : cannot access Publisher in Android Studio?

java - 将 2 个 Controller 方法映射到具有不同查询参数的同一端点

javascript - 如何在客户端的网页中访问服务器端终端?

java - 用java编译一个Jar

android - 错误 : when I replace compile with implementation in gradle(dependency)