Skip to content

Commit 2344131

Browse files
committed
Initial commit of the Compiler class
1 parent 62db52c commit 2344131

File tree

1 file changed

+272
-0
lines changed

1 file changed

+272
-0
lines changed

src/javaxt/orm/Compiler.java

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
package javaxt.orm;
2+
3+
import java.io.*;
4+
import java.net.*;
5+
import java.util.*;
6+
import javax.tools.*;
7+
import java.nio.charset.Charset;
8+
9+
import static javaxt.utils.Console.console;
10+
11+
//******************************************************************************
12+
//** Compiler
13+
//******************************************************************************
14+
/**
15+
* Used to compile ORM models into Java classes. The classes are ephemeral
16+
* meaning they are not saved anywhere on disk (no class files or jar files).
17+
* Instead the classes are stored in memory and are tied to the lifecycle of
18+
* the JVM.
19+
*
20+
******************************************************************************/
21+
22+
public class Compiler {
23+
24+
private Class[] classes;
25+
private HashMap<String, SimpleJavaFileObject> outputFiles;
26+
private URLClassLoader urlClassLoader;
27+
private DiagnosticListener<JavaFileObject> listener = null;
28+
private Locale locale = null;
29+
private Charset charset = Charset.defaultCharset();
30+
31+
32+
//**************************************************************************
33+
//** Constructor
34+
//**************************************************************************
35+
public Compiler(Model[] models) throws Exception {
36+
37+
if (models==null || models.length==0){
38+
classes = new Class[0];
39+
}
40+
else{
41+
42+
outputFiles = new HashMap<>();
43+
urlClassLoader = getClassLoader();
44+
45+
46+
47+
//Convert models into an ArrayList
48+
ArrayList<Model> arr = new ArrayList<>();
49+
for (Model model : models){
50+
arr.add(model);
51+
}
52+
53+
54+
//Compile classes
55+
ArrayList<Class> classes = new ArrayList<>();
56+
int numErrors = 0;
57+
while (classes.size()<models.length){
58+
try{
59+
Model model = arr.get(0);
60+
classes.add(compile(model));
61+
arr.remove(0);
62+
}
63+
catch(Exception e){
64+
numErrors++;
65+
66+
//The following logic assumes that the compile error is due
67+
//to an ordering issue where one class depends on another
68+
//but the dependency hasn't been compiled yet. So we'll
69+
//shuffle the list of models and try again.
70+
71+
if (arr.size()==1) throw e;
72+
String firstClass = getClassName(arr.get(0));
73+
while (true){
74+
Collections.shuffle(arr);
75+
String className = getClassName(arr.get(0));
76+
if (!className.equals(firstClass)) break;
77+
}
78+
}
79+
80+
81+
//Safety switch
82+
if (numErrors>(models.length*models.length)){
83+
throw new Exception("Failed to compile " + getClassName(arr.get(0)));
84+
}
85+
}
86+
87+
88+
//Convert the class list into an array. Sorts the classes to match
89+
//the order of the input models.
90+
this.classes = new Class[classes.size()];
91+
for (int i=0; i<models.length; i++){
92+
String className = getClassName(models[i]);
93+
for (Class c : classes){
94+
String name = c.getPackageName() + "." + c.getSimpleName();
95+
if (name.equals(className)){
96+
this.classes[i] = c;
97+
break;
98+
}
99+
}
100+
}
101+
}
102+
}
103+
104+
105+
//**************************************************************************
106+
//** getClasses
107+
//**************************************************************************
108+
/** Returns classes generated by the compiler for each model
109+
*/
110+
public Class[] getClasses(){
111+
return classes;
112+
}
113+
114+
115+
116+
//**************************************************************************
117+
//** compile
118+
//**************************************************************************
119+
/** Used to compile a model and return a class
120+
*/
121+
private Class compile(Model model) throws Exception {
122+
123+
JavaCompiler c = ToolProvider.getSystemJavaCompiler();
124+
String className = getClassName(model);
125+
126+
127+
//Create input file object
128+
SimpleJavaFileObject src = new SimpleJavaFileObject(
129+
URI.create("string:///" + model.getName() + ".java"),
130+
JavaFileObject.Kind.SOURCE) {
131+
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
132+
return model.getJavaCode();
133+
}
134+
};
135+
136+
137+
138+
//Create output file object
139+
SimpleJavaFileObject cls = new SimpleJavaFileObject(
140+
URI.create("runtime:///" + model.getName() + ".class"),
141+
JavaFileObject.Kind.CLASS) {
142+
private ByteArrayOutputStream out = new ByteArrayOutputStream();
143+
public OutputStream openOutputStream() throws IOException {
144+
//console.log("openOutputStream");
145+
return out;
146+
}
147+
public InputStream openInputStream() throws IOException {
148+
//console.log("openInputStream");
149+
return new ByteArrayInputStream(out.toByteArray());
150+
}
151+
};
152+
outputFiles.put(className, cls);
153+
154+
155+
156+
//Create in-memory file manager
157+
StandardJavaFileManager fm = c.getStandardFileManager(listener, locale, charset);
158+
JavaFileManager fileManager = new ForwardingJavaFileManager(fm) {
159+
public JavaFileObject getJavaFileForOutput(
160+
JavaFileManager.Location location, String className,
161+
JavaFileObject.Kind kind, FileObject sibling) throws IOException {
162+
return outputFiles.get(className);
163+
}
164+
165+
166+
public Iterable list(JavaFileManager.Location location,
167+
String packageName, Set kinds, boolean recurse) throws IOException {
168+
169+
if (location==StandardLocation.CLASS_PATH){
170+
ArrayList<SimpleJavaFileObject> arr = new ArrayList<>();
171+
Iterator<String> it = outputFiles.keySet().iterator();
172+
while (it.hasNext()){
173+
String name = it.next();
174+
if (name.startsWith(packageName + ".")){
175+
if (!name.equals(className)){ //don't include the current file since it hasn't been compiled yet
176+
arr.add(outputFiles.get(name));
177+
}
178+
}
179+
}
180+
if (!arr.isEmpty()) return arr;
181+
}
182+
183+
return super.list(location, packageName, kinds, recurse);
184+
}
185+
186+
187+
188+
public String inferBinaryName(JavaFileManager.Location location, JavaFileObject file) {
189+
190+
if (location==StandardLocation.CLASS_PATH){
191+
Iterator<String> it = outputFiles.keySet().iterator();
192+
while (it.hasNext()){
193+
String name = it.next();
194+
SimpleJavaFileObject f = outputFiles.get(name);
195+
if (file==f) return name;
196+
}
197+
}
198+
199+
return super.inferBinaryName(location, file);
200+
}
201+
};
202+
203+
204+
205+
206+
//Compile class
207+
JavaCompiler.CompilationTask task = c.getTask(
208+
null,
209+
fileManager,
210+
listener,
211+
Collections.emptySet(),
212+
Collections.emptySet(),
213+
Collections.singleton(src)
214+
);
215+
216+
if (task.call()) {
217+
return urlClassLoader.loadClass(className);
218+
}
219+
else{
220+
throw new Exception("Failed to compile " + className);
221+
}
222+
}
223+
224+
225+
//**************************************************************************
226+
//** getClassLoader
227+
//**************************************************************************
228+
/** Returns a custom class loader used to find classes created by this class
229+
*/
230+
private URLClassLoader getClassLoader(){
231+
return new URLClassLoader(new URL[0]){
232+
protected Class<?> findClass(final String name) throws ClassNotFoundException {
233+
234+
235+
SimpleJavaFileObject f = outputFiles.get(name);
236+
if (f!=null){
237+
try (InputStream is = f.openInputStream()) {
238+
239+
240+
ByteArrayOutputStream out = new ByteArrayOutputStream();
241+
242+
int x;
243+
byte[] buffer = new byte[256];
244+
while ((x = is.read(buffer, 0, buffer.length)) != -1) {
245+
out.write(buffer, 0, x);
246+
}
247+
248+
out.flush();
249+
byte[] classBytes = out.toByteArray();
250+
return defineClass(name, classBytes, 0, classBytes.length);
251+
252+
}
253+
catch(Exception e){
254+
e.printStackTrace();
255+
}
256+
}
257+
258+
return super.findClass(name);
259+
}
260+
};
261+
}
262+
263+
264+
//**************************************************************************
265+
//** getClassName
266+
//**************************************************************************
267+
private String getClassName(Model model){
268+
return model.getPackageName() + "." + model.getName();
269+
}
270+
271+
272+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy