1+ package gr .gousiosg .javacg ;
2+
3+ import java .io .BufferedInputStream ;
4+ import java .io .File ;
5+ import java .io .FileInputStream ;
6+ import java .io .FileNotFoundException ;
7+ import java .io .FileOutputStream ;
8+ import java .io .IOException ;
9+ import java .io .StringWriter ;
10+ import java .net .URI ;
11+ import java .util .Arrays ;
12+ import java .util .Collection ;
13+ import java .util .LinkedList ;
14+ import java .util .jar .Attributes ;
15+ import java .util .jar .JarEntry ;
16+ import java .util .jar .JarOutputStream ;
17+ import java .util .jar .Manifest ;
18+
19+ import javax .tools .Diagnostic ;
20+ import javax .tools .DiagnosticCollector ;
21+ import javax .tools .JavaCompiler ;
22+ import javax .tools .JavaCompiler .CompilationTask ;
23+ import javax .tools .JavaFileObject ;
24+ import javax .tools .SimpleJavaFileObject ;
25+ import javax .tools .StandardJavaFileManager ;
26+ import javax .tools .StandardLocation ;
27+ import javax .tools .ToolProvider ;
28+
29+ public class JARBuilder {
30+ private static final String TEMP_DIR = System .getProperty ("java.io.tmpdir" );
31+
32+ private final JavaCompiler compiler = ToolProvider .getSystemJavaCompiler ();
33+ private final DiagnosticCollector <JavaFileObject > diagnostics = new DiagnosticCollector <JavaFileObject >();
34+ private final StandardJavaFileManager fileManager = compiler .getStandardFileManager (diagnostics , null , null );
35+ private final LinkedList <JavaFileObject > compilationUnits = new LinkedList <>();
36+ private final Collection <File > classFiles = new LinkedList <>();
37+
38+ public JARBuilder () throws IOException {
39+ fileManager .setLocation (StandardLocation .CLASS_OUTPUT , Arrays .asList (new File (TEMP_DIR )));
40+ }
41+
42+ public void add (String className , String classCode ) throws IOException {
43+ compilationUnits .add (createJavaFile (className , classCode ));
44+ classFiles .add (new File (TEMP_DIR , className + ".class" ));
45+ }
46+
47+ public File build () throws FileNotFoundException , IOException {
48+ CompilationTask task = compiler .getTask (null , fileManager , diagnostics , null , null , compilationUnits );
49+ boolean success = task .call ();
50+ if (!success ) {
51+ displayDiagnostic (diagnostics );
52+ throw new RuntimeException ("Cannot compile classes for the JAR" );
53+ }
54+
55+ File file = File .createTempFile ("test" , ".jar" );
56+ JarOutputStream jar = new JarOutputStream (new FileOutputStream (file ), createManifest ());
57+ for (File classFile : classFiles ) {
58+ add (classFile , jar );
59+ }
60+ jar .close ();
61+ return file ;
62+ }
63+
64+ private void displayDiagnostic (DiagnosticCollector <JavaFileObject > diagnostics ) {
65+ for (Diagnostic <?> diagnostic : diagnostics .getDiagnostics ()) {
66+ JavaSourceFromString sourceClass = (JavaSourceFromString ) diagnostic .getSource ();
67+ System .err .println ("-----" );
68+ System .err .println ("Source: " + sourceClass .getName ());
69+ System .err .println ("Message: " + diagnostic .getMessage (null ));
70+ System .err .println ("Position: " + diagnostic .getPosition ());
71+ System .err .println (diagnostic .getKind () + " " + diagnostic .getCode ());
72+ }
73+ }
74+
75+ private JavaFileObject createJavaFile (String className , String classCode ) throws IOException {
76+ StringWriter writer = new StringWriter ();
77+ writer .append (classCode );
78+ writer .close ();
79+ JavaFileObject file = new JavaSourceFromString (className , writer .toString ());
80+ return file ;
81+ }
82+
83+ private Manifest createManifest () {
84+ Manifest manifest = new Manifest ();
85+ manifest .getMainAttributes ().put (Attributes .Name .MANIFEST_VERSION , "1.0" );
86+ return manifest ;
87+ }
88+
89+ private void add (File classFile , JarOutputStream jar ) throws IOException {
90+ JarEntry entry = new JarEntry (classFile .getPath ().replace ("\\ " , "/" ));
91+ jar .putNextEntry (entry );
92+ try (BufferedInputStream in = new BufferedInputStream (new FileInputStream (classFile ))) {
93+ byte [] buffer = new byte [1024 ];
94+ while (true ) {
95+ int count = in .read (buffer );
96+ if (count == -1 )
97+ break ;
98+ jar .write (buffer , 0 , count );
99+ }
100+ jar .closeEntry ();
101+ }
102+ }
103+ }
104+
105+ class JavaSourceFromString extends SimpleJavaFileObject {
106+ final String code ;
107+
108+ JavaSourceFromString (String name , String code ) throws IOException {
109+ super (URI .create ("string:///" + name .replace ('.' , '/' ) + Kind .SOURCE .extension ), Kind .SOURCE );
110+ this .code = code ;
111+ }
112+
113+ @ Override
114+ public CharSequence getCharContent (boolean ignoreEncodingErrors ) {
115+ return code ;
116+ }
117+ }
0 commit comments