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

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

How to validate Java code programatically?

问题

  1. import javax.tools.*;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.net.URI;
  5. import java.nio.file.Files;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. import java.util.Set;
  9. public class Validator {
  10. private final JavaCompiler COMPILER = ToolProvider.getSystemJavaCompiler();
  11. public final boolean compiles(Set<File> srcFiles, SourceVersion version) throws IOException {
  12. List<JavaFileObject> compilationUnits = new ArrayList<>();
  13. for (File file : srcFiles) {
  14. CompilableFile compilableFile = new CompilableFile(file.getName(), Files.readString(file.toPath()));
  15. compilationUnits.add(compilableFile);
  16. }
  17. DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
  18. JavaCompiler.CompilationTask task = COMPILER.getTask(null, null, diagnosticCollector, null, null, compilationUnits);
  19. task.setProcessors(List.of(new SourceVersionProcessor(version))); // Set the SourceVersionProcessor
  20. boolean success = task.call();
  21. diagnosticCollector.getDiagnostics().forEach(Validator::printDiagnostic);
  22. return success;
  23. }
  24. private static void printDiagnostic(Diagnostic<?> diagnostic) {
  25. System.out.println(diagnostic.getCode());
  26. System.out.println(diagnostic.getKind());
  27. System.out.println(diagnostic.getPosition());
  28. System.out.println(diagnostic.getStartPosition());
  29. System.out.println(diagnostic.getEndPosition());
  30. System.out.println(diagnostic.getSource());
  31. System.out.println(diagnostic.getMessage(null));
  32. }
  33. private static final class CompilableFile extends SimpleJavaFileObject {
  34. final String code;
  35. CompilableFile(String name, String code) {
  36. super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
  37. this.code = code;
  38. }
  39. @Override
  40. public CharSequence getCharContent(boolean ignoreEncodingErrors) {
  41. return code;
  42. }
  43. }
  44. // SourceVersionProcessor to support a specific source version
  45. private static final class SourceVersionProcessor implements Processor {
  46. private final SourceVersion version;
  47. SourceVersionProcessor(SourceVersion version) {
  48. this.version = version;
  49. }
  50. @Override
  51. public Set<String> getSupportedOptions() {
  52. return Set.of();
  53. }
  54. @Override
  55. public Set<String> getSupportedAnnotationTypes() {
  56. return Set.of();
  57. }
  58. @Override
  59. public SourceVersion getSupportedSourceVersion() {
  60. return version;
  61. }
  62. @Override
  63. public void init(ProcessingEnvironment processingEnv) {
  64. }
  65. @Override
  66. public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
  67. return false;
  68. }
  69. }
  70. }
英文:

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.

  1. import javax.tools.*;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.net.URI;
  5. import java.nio.file.Files;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. import java.util.Set;
  9. public class Validator {
  10. private final JavaCompiler COMPILER = ToolProvider.getSystemJavaCompiler();
  11. //Assume srcFiles are java files that can be read
  12. public final boolean compiles(Set&lt;File&gt; srcFiles) throws IOException {
  13. //Convert files to JavaCompiler API compatible files
  14. List&lt;JavaFileObject&gt; compilationUnits = new ArrayList&lt;&gt;();
  15. for (File file : srcFiles) {
  16. CompilableFile compilableFile = new CompilableFile(file.getName(), Files.readString(file.toPath()));
  17. compilationUnits.add(compilableFile);
  18. }
  19. DiagnosticCollector&lt;JavaFileObject&gt; diagnosticCollector = new DiagnosticCollector&lt;&gt;();
  20. JavaCompiler.CompilationTask task = COMPILER.getTask(null, null, diagnosticCollector, null, null, compilationUnits);
  21. boolean success = task.call();
  22. //Displaying the info of each warning, error, etc
  23. diagnosticCollector.getDiagnostics().forEach(Validator::printDiagnostic);
  24. return success;
  25. }
  26. private static void printDiagnostic(Diagnostic&lt;?&gt; diagnostic) {
  27. System.out.println(diagnostic.getCode());
  28. System.out.println(diagnostic.getKind());
  29. System.out.println(diagnostic.getPosition());
  30. System.out.println(diagnostic.getStartPosition());
  31. System.out.println(diagnostic.getEndPosition());
  32. System.out.println(diagnostic.getSource());
  33. System.out.println(diagnostic.getMessage(null));
  34. }
  35. /**
  36. * Instances of this class can be compiled with the JavaCompiler API
  37. */
  38. private static final class CompilableFile extends SimpleJavaFileObject {
  39. final String code;
  40. CompilableFile(String name, String code) {
  41. super(URI.create(&quot;string:///&quot; + name.replace(&#39;.&#39;, &#39;/&#39;) + Kind.SOURCE.extension), Kind.SOURCE);
  42. this.code = code;
  43. }
  44. @Override
  45. public CharSequence getCharContent(boolean ignoreEncodingErrors) {
  46. return code;
  47. }
  48. }
  49. }

Is there anyway I could implement the following function?

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

答案1

得分: 1

以下是翻译好的内容:

  1. import javax.tools.*;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.net.URI;
  5. import java.nio.file.Files;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. import java.util.Set;
  9. public class Validator {
  10. private final JavaCompiler COMPILER = ToolProvider.getSystemJavaCompiler();
  11. // 假设 srcFiles 是可以读取的 Java 文件
  12. public final boolean compiles(Set<File> srcFiles, String javaVersion) throws IOException {
  13. // 将文件转换为 JavaCompiler API 兼容的文件
  14. List<JavaFileObject> compilationUnits = new ArrayList<>();
  15. for (File file : srcFiles) {
  16. CompilableFile compilableFile = new CompilableFile(file.getName(), Files.readString(file.toPath()));
  17. compilationUnits.add(compilableFile);
  18. }
  19. DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
  20. JavaCompiler.CompilationTask task = COMPILER.getTask(null, null, diagnosticCollector, List.of("--release", javaVersion), null, compilationUnits);
  21. boolean success = task.call();
  22. // 显示每个警告、错误等的信息
  23. diagnosticCollector.getDiagnostics().forEach(Validator::printDiagnostic);
  24. return success;
  25. }
  26. private static void printDiagnostic(Diagnostic<?> diagnostic) {
  27. System.out.println(diagnostic.getCode());
  28. System.out.println(diagnostic.getKind());
  29. System.out.println(diagnostic.getPosition());
  30. System.out.println(diagnostic.getStartPosition());
  31. System.out.println(diagnostic.getEndPosition());
  32. System.out.println(diagnostic.getSource());
  33. System.out.println(diagnostic.getMessage(null));
  34. }
  35. /**
  36. * 此类的实例可以使用 JavaCompiler API 进行编译
  37. */
  38. private static final class CompilableFile extends SimpleJavaFileObject {
  39. final String code;
  40. CompilableFile(String name, String code) {
  41. super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
  42. this.code = code;
  43. }
  44. @Override
  45. public CharSequence getCharContent(boolean ignoreEncodingErrors) {
  46. return code;
  47. }
  48. }
  49. }
英文:

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

  1. import javax.tools.*;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.net.URI;
  5. import java.nio.file.Files;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. import java.util.Set;
  9. public class Validator {
  10. private final JavaCompiler COMPILER = ToolProvider.getSystemJavaCompiler();
  11. //Assume srcFiles are java files that can be read
  12. public final boolean compiles(Set&lt;File&gt; srcFiles, String javaVersion) throws IOException {
  13. //Convert files to JavaCompiler API compatible files
  14. List&lt;JavaFileObject&gt; compilationUnits = new ArrayList&lt;&gt;();
  15. for (File file : srcFiles) {
  16. CompilableFile compilableFile = new CompilableFile(file.getName(), Files.readString(file.toPath()));
  17. compilationUnits.add(compilableFile);
  18. }
  19. DiagnosticCollector&lt;JavaFileObject&gt; diagnosticCollector = new DiagnosticCollector&lt;&gt;();
  20. JavaCompiler.CompilationTask task = COMPILER.getTask(null, null, diagnosticCollector, List.of(&quot;--release&quot;, javaVersion), null, compilationUnits);
  21. boolean success = task.call();
  22. //Displaying the info of each warning, error, etc
  23. diagnosticCollector.getDiagnostics().forEach(Validator::printDiagnostic);
  24. return success;
  25. }
  26. private static void printDiagnostic(Diagnostic&lt;?&gt; diagnostic) {
  27. System.out.println(diagnostic.getCode());
  28. System.out.println(diagnostic.getKind());
  29. System.out.println(diagnostic.getPosition());
  30. System.out.println(diagnostic.getStartPosition());
  31. System.out.println(diagnostic.getEndPosition());
  32. System.out.println(diagnostic.getSource());
  33. System.out.println(diagnostic.getMessage(null));
  34. }
  35. /**
  36. * Instances of this class can be compiled with the JavaCompiler API
  37. */
  38. private static final class CompilableFile extends SimpleJavaFileObject {
  39. final String code;
  40. CompilableFile(String name, String code) {
  41. super(URI.create(&quot;string:///&quot; + name.replace(&#39;.&#39;, &#39;/&#39;) + Kind.SOURCE.extension), Kind.SOURCE);
  42. this.code = code;
  43. }
  44. @Override
  45. public CharSequence getCharContent(boolean ignoreEncodingErrors) {
  46. return code;
  47. }
  48. }
  49. }

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:

确定