如何以程序方式验证Java代码?

huangapple go评论73阅读模式
英文:

How to validate Java code programatically?

问题

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();

    public final boolean compiles(Set<File> srcFiles, SourceVersion version) throws IOException {

        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);
        task.setProcessors(List.of(new SourceVersionProcessor(version))); // Set the SourceVersionProcessor
        boolean success = task.call();

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

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

    // SourceVersionProcessor to support a specific source version
    private static final class SourceVersionProcessor implements Processor {
        private final SourceVersion version;

        SourceVersionProcessor(SourceVersion version) {
            this.version = version;
        }

        @Override
        public Set<String> getSupportedOptions() {
            return Set.of();
        }

        @Override
        public Set<String> getSupportedAnnotationTypes() {
            return Set.of();
        }

        @Override
        public SourceVersion getSupportedSourceVersion() {
            return version;
        }

        @Override
        public void init(ProcessingEnvironment processingEnv) {
        }

        @Override
        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
            return false;
        }
    }
}
英文:

Given the source code and the Java version, I need to be able to validate whether or not the code will compile. If the code does not compile, I need to be able to return the errors within the source code.

The following solution works, but only for the Java version currently being used on your machine.

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&lt;File&gt; srcFiles) throws IOException {
//Convert files to JavaCompiler API compatible files
List&lt;JavaFileObject&gt; compilationUnits = new ArrayList&lt;&gt;();
for (File file : srcFiles) {
CompilableFile compilableFile = new CompilableFile(file.getName(), Files.readString(file.toPath()));
compilationUnits.add(compilableFile);
}
DiagnosticCollector&lt;JavaFileObject&gt; diagnosticCollector = new DiagnosticCollector&lt;&gt;();
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&lt;?&gt; 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(&quot;string:///&quot; + name.replace(&#39;.&#39;, &#39;/&#39;) + Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}
}

Is there anyway I could implement the following function?

public final boolean compiles(Set&lt;File&gt; srcFiles, SourceVersion version) {...}

答案1

得分: 1

以下是翻译好的内容:

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();
    
    // 假设 srcFiles 是可以读取的 Java 文件
    public final boolean compiles(Set<File> srcFiles, String javaVersion) throws IOException {

        // 将文件转换为 JavaCompiler API 兼容的文件
        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();

        // 显示每个警告、错误等的信息
        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));
    }

    /**
     * 此类的实例可以使用 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;
        }
    }
}
英文:

The range of version that this solution works on seem to be compiler specific, but for OpenJDK 11 and 15, I noticed this solution worked for [7, SYSTEM_COMPILER_VERSION]

The JavaCompiler API allows you to pass command line options as an Iterable when you call the method JavaCompiler.CompilationTask::getTask, so you can pass List.of(&quot;--release&quot;, &quot;&lt;version&gt;&quot;) where &lt;version&gt; is replaced by the version you are verifying

With this in mind, the solution becomes

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&lt;File&gt; srcFiles, String javaVersion) throws IOException {
//Convert files to JavaCompiler API compatible files
List&lt;JavaFileObject&gt; compilationUnits = new ArrayList&lt;&gt;();
for (File file : srcFiles) {
CompilableFile compilableFile = new CompilableFile(file.getName(), Files.readString(file.toPath()));
compilationUnits.add(compilableFile);
}
DiagnosticCollector&lt;JavaFileObject&gt; diagnosticCollector = new DiagnosticCollector&lt;&gt;();
JavaCompiler.CompilationTask task = COMPILER.getTask(null, null, diagnosticCollector, List.of(&quot;--release&quot;, 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&lt;?&gt; 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(&quot;string:///&quot; + name.replace(&#39;.&#39;, &#39;/&#39;) + Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}
}

huangapple
  • 本文由 发表于 2020年9月25日 23:34:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/64067188.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定