001/* 002 * SPDX-FileCopyrightText: none 003 * SPDX-License-Identifier: CC0-1.0 004 */ 005 006package gov.nist.secauto.metaschema.databind.codegen; 007 008import gov.nist.secauto.metaschema.core.util.CollectionUtil; 009 010import org.apache.logging.log4j.LogManager; 011import org.apache.logging.log4j.Logger; 012 013import java.io.IOException; 014import java.io.Writer; 015import java.nio.file.Path; 016import java.util.LinkedHashSet; 017import java.util.LinkedList; 018import java.util.List; 019import java.util.Set; 020import java.util.stream.Collectors; 021 022import javax.tools.DiagnosticCollector; 023import javax.tools.JavaCompiler; 024import javax.tools.JavaFileObject; 025import javax.tools.StandardJavaFileManager; 026import javax.tools.ToolProvider; 027 028import edu.umd.cs.findbugs.annotations.NonNull; 029import edu.umd.cs.findbugs.annotations.Nullable; 030 031public class JavaCompilerSupport { 032 private static final Logger LOGGER = LogManager.getLogger(JavaCompilerSupport.class); 033 034 @NonNull 035 private final Path classDir; 036 @NonNull 037 private final Set<String> classPath = new LinkedHashSet<>(); 038 @NonNull 039 private final Set<String> modulePath = new LinkedHashSet<>(); 040 @NonNull 041 private final Set<String> rootModuleNames = new LinkedHashSet<>(); 042 043 public JavaCompilerSupport(@NonNull Path classDir) { 044 this.classDir = classDir; 045 } 046 047 public Set<String> getClassPath() { 048 return classPath; 049 } 050 051 public Set<String> getModulePath() { 052 return modulePath; 053 } 054 055 public Set<String> getRootModuleNames() { 056 return rootModuleNames; 057 } 058 059 public void addToClassPath(@NonNull String entry) { 060 classPath.add(entry); 061 } 062 063 public void addToModulePath(@NonNull String entry) { 064 modulePath.add(entry); 065 } 066 067 public void addRootModule(@NonNull String entry) { 068 rootModuleNames.add(entry); 069 } 070 071 @NonNull 072 protected List<String> generateCompilerOptions() { 073 List<String> options = new LinkedList<>(); 074 // options.add("-verbose"); 075 // options.add("-g"); 076 options.add("-d"); 077 options.add(classDir.toString()); 078 079 if (!classPath.isEmpty()) { 080 options.add("-classpath"); 081 options.add(classPath.stream() 082 .collect(Collectors.joining(":"))); 083 } 084 085 if (!modulePath.isEmpty()) { 086 options.add("-p"); 087 options.add(modulePath.stream() 088 .collect(Collectors.joining(":"))); 089 } 090 091 return options; 092 } 093 094 /** 095 * Generate and compile Java classes. 096 * 097 * @param classFiles 098 * the files to compile 099 * @param compileOut 100 * a Writer for additional output from the compiler; use System.err if 101 * null 102 * @return information about the generated classes 103 * @throws IOException 104 * if an error occurred while compiling the classes 105 * @throws IllegalArgumentException 106 * if any of the options are invalid, or if any of the given 107 * compilation units are of other kind than 108 * {@link javax.tools.JavaFileObject.Kind#SOURCE} 109 */ 110 111 public CompilationResult compile(@NonNull List<Path> classFiles, @Nullable Writer compileOut) throws IOException { 112 DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>(); 113 114 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 115 116 List<JavaFileObject> compilationUnits; 117 try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) { 118 119 compilationUnits = classFiles.stream() 120 .map(fileManager::getJavaFileObjects) 121 .map(CollectionUtil::toList) 122 .flatMap(List::stream) 123 .collect(Collectors.toUnmodifiableList()); 124 125 List<String> options = generateCompilerOptions(); 126 127 if (LOGGER.isDebugEnabled()) { 128 LOGGER.atDebug().log("Using options: {}", options); 129 } 130 131 JavaCompiler.CompilationTask task = compiler.getTask( 132 compileOut, 133 fileManager, 134 diagnostics, 135 options, 136 null, 137 compilationUnits); 138 139 task.addModules(rootModuleNames); 140 141 return new CompilationResult(task.call(), diagnostics); 142 } 143 } 144 145 public static final class CompilationResult { 146 private final boolean successful; 147 @NonNull 148 private final DiagnosticCollector<JavaFileObject> diagnostics; 149 150 private CompilationResult(boolean successful, @NonNull DiagnosticCollector<JavaFileObject> diagnostics) { 151 this.successful = successful; 152 this.diagnostics = diagnostics; 153 } 154 155 public boolean isSuccessful() { 156 return successful; 157 } 158 159 public DiagnosticCollector<?> getDiagnostics() { 160 return diagnostics; 161 } 162 } 163}