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