001 /*
002 GRANITE DATA SERVICES
003 Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004
005 This file is part of Granite Data Services.
006
007 Granite Data Services is free software; you can redistribute it and/or modify
008 it under the terms of the GNU Library General Public License as published by
009 the Free Software Foundation; either version 2 of the License, or (at your
010 option) any later version.
011
012 Granite Data Services is distributed in the hope that it will be useful, but
013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015 for more details.
016
017 You should have received a copy of the GNU Library General Public License
018 along with this library; if not, see <http://www.gnu.org/licenses/>.
019 */
020
021 package org.granite.generator.ant;
022
023 import java.io.File;
024 import java.io.FileNotFoundException;
025 import java.io.PrintWriter;
026 import java.io.StringWriter;
027 import java.util.ArrayList;
028 import java.util.HashMap;
029 import java.util.List;
030 import java.util.Map;
031
032 import org.apache.tools.ant.AntClassLoader;
033 import org.apache.tools.ant.BuildException;
034 import org.apache.tools.ant.DirectoryScanner;
035 import org.apache.tools.ant.Project;
036 import org.apache.tools.ant.Task;
037 import org.apache.tools.ant.types.FileSet;
038 import org.apache.tools.ant.types.Path;
039 import org.apache.tools.ant.types.Reference;
040 import org.granite.generator.Generator;
041 import org.granite.generator.Output;
042 import org.granite.generator.TemplateUri;
043 import org.granite.generator.Transformer;
044 import org.granite.generator.as3.As3TypeFactory;
045 import org.granite.generator.as3.DefaultAs3TypeFactory;
046 import org.granite.generator.as3.DefaultRemoteDestinationFactory;
047 import org.granite.generator.as3.EntityFactory;
048 import org.granite.generator.as3.DefaultEntityFactory;
049 import org.granite.generator.as3.JavaAs3GroovyConfiguration;
050 import org.granite.generator.as3.JavaAs3GroovyTransformer;
051 import org.granite.generator.as3.JavaAs3Input;
052 import org.granite.generator.as3.PackageTranslator;
053 import org.granite.generator.as3.RemoteDestinationFactory;
054 import org.granite.generator.as3.reflect.JavaType.Kind;
055 import org.granite.generator.gsp.GroovyTemplateFactory;
056 import org.granite.generator.template.StandardTemplateUris;
057
058 /**
059 * @author Franck WOLFF
060 */
061 public class AntJavaAs3Task extends Task implements JavaAs3GroovyConfiguration {
062
063 ///////////////////////////////////////////////////////////////////////////
064 // Configurable fields (xml attributes).
065
066 private String outputdir = ".";
067 private String baseoutputdir = null;
068
069 private String uid = "uid";
070
071 private String entitytemplate = null;
072 private String entitybasetemplate = null;
073
074 private String beantemplate = null;
075 private String beanbasetemplate = null;
076
077 private String interfacetemplate = null;
078
079 private String enumtemplate = null;
080
081 private String remotetemplate = null;
082 private String remotebasetemplate = null;
083
084 private boolean tide = false;
085
086 private String as3typefactory = null;
087 private String entityfactory = null;
088 private String remotedestinationfactory = null;
089 private String transformer = null;
090
091 private boolean externalizelong = false;
092 private boolean externalizebiginteger = false;
093 private boolean externalizebigdecimal = false;
094
095 private Path classpath = null;
096 private List<FileSet> fileSets = new ArrayList<FileSet>();
097
098 private List<PackageTranslator> translators = new ArrayList<PackageTranslator>();
099
100 ///////////////////////////////////////////////////////////////////////////
101 // Configuration implementation fields.
102
103 private File outputDirFile = null;
104 private File baseOutputDirFile = null;
105
106 private As3TypeFactory as3TypeFactoryImpl = null;
107 private Transformer<?, ?, ?> transformerImpl = null;
108 private EntityFactory entityFactoryImpl = null;
109 private RemoteDestinationFactory remoteDestinationFactoryImpl = null;
110
111 private GroovyTemplateFactory groovyTemplateFactory = null;
112
113 private TemplateUri[] entityTemplateUris = null;
114 private TemplateUri[] interfaceTemplateUris = null;
115 private TemplateUri[] beanTemplateUris = null;
116 private TemplateUri[] enumTemplateUris = null;
117 private TemplateUri[] remoteTemplateUris = null;
118
119 private Map<Class<?>, File> filesetClasses = null;
120
121 ///////////////////////////////////////////////////////////////////////////
122 // Task attributes.
123
124 public void setOutputdir(String outputdir) {
125 this.outputdir = outputdir;
126 }
127
128 public void setBaseoutputdir(String baseoutputdir) {
129 this.baseoutputdir = baseoutputdir;
130 }
131
132 public void setAs3typefactory(String as3typefactory) {
133 this.as3typefactory = as3typefactory;
134 }
135
136 public void setEntityfactory(String entityfactory) {
137 this.entityfactory = entityfactory;
138 }
139
140 public void setRemotedestinationfactory(String remotedestinationfactory) {
141 this.remotedestinationfactory = remotedestinationfactory;
142 }
143
144 public void setUid(String uid) {
145 this.uid = uid;
146 }
147
148 public void setEntitytemplate(String entitytemplate) {
149
150 this.entitytemplate = entitytemplate;
151 }
152 public void setEntitybasetemplate(String entitybasetemplate) {
153 this.entitybasetemplate = entitybasetemplate;
154 }
155
156 public void setBeantemplate(String beantemplate) {
157 this.beantemplate = beantemplate;
158 }
159 public void setBeanbasetemplate(String beanbasetemplate) {
160 this.beanbasetemplate = beanbasetemplate;
161 }
162
163 public void setInterfacetemplate(String interfacetemplate) {
164 this.interfacetemplate = interfacetemplate;
165 }
166
167 public void setEnumtemplate(String enumtemplate) {
168 this.enumtemplate = enumtemplate;
169 }
170
171 public void setRemotetemplate(String remotetemplate) {
172 this.remotetemplate = remotetemplate;
173 }
174 public void setRemotebasetemplate(String remotebasetemplate) {
175 this.remotebasetemplate = remotebasetemplate;
176 }
177
178 public void setTide(boolean tide) {
179 this.tide = tide;
180 }
181
182 public void setExternalizelong(boolean externalizelong) {
183 this.externalizelong = externalizelong;
184 }
185
186 public void setExternalizebiginteger(boolean externalizebiginteger) {
187 this.externalizebiginteger = externalizebiginteger;
188 }
189
190 public void setExternalizebigdecimal(boolean externalizebigdecimal) {
191 this.externalizebigdecimal = externalizebigdecimal;
192 }
193
194 public void setTransformer(String transformer) {
195 this.transformer = transformer;
196 }
197
198 ///////////////////////////////////////////////////////////////////////////
199 // Task inner elements.
200
201 public void addFileset(FileSet fileSet) {
202 fileSets.add(fileSet);
203 }
204
205 public void setClasspath(Path path) {
206 if (classpath == null)
207 classpath = path;
208 else
209 classpath.append(path);
210 }
211
212 public Path createClasspath() {
213 if (classpath == null)
214 classpath = new Path(getProject());
215 return classpath.createPath();
216 }
217
218 public void setClasspathRef(Reference r) {
219 createClasspath().setRefid(r);
220 }
221
222 public void addTranslator(PackageTranslator translator) {
223 translators.add(translator);
224 }
225
226 ///////////////////////////////////////////////////////////////////////////
227 // Task execution.
228
229 @Override
230 public void execute() throws BuildException {
231
232 log("Using output dir: " + outputdir, Project.MSG_INFO);
233 log("Using classpath: " + classpath, Project.MSG_INFO);
234
235 AntClassLoader loader = new AntClassLoader(AntJavaAs3Task.class.getClassLoader(), getProject(), classpath, true);
236 try {
237 loader.setThreadContextLoader();
238
239 // Build a Set of all ".class" files in filesets (ignoring nested classes).
240 log("Loading all Java classes referenced by inner fileset(s) {", Project.MSG_INFO);
241
242 filesetClasses = new HashMap<Class<?>, File>();
243 for (FileSet fileSet : fileSets) {
244 DirectoryScanner scanner = fileSet.getDirectoryScanner(getProject());
245 scanner.setCaseSensitive(true);
246 scanner.scan();
247
248 StringBuilder sb = new StringBuilder(" ");
249 String[] names = scanner.getIncludedFiles();
250 for (String name : names) {
251 if (name.endsWith(".class")) {
252 log(name, Project.MSG_VERBOSE);
253 try {
254 File jFile = new File(scanner.getBasedir(), name);
255 if (!jFile.exists())
256 throw new FileNotFoundException(jFile.toString());
257
258 String jClassName = name.substring(0, name.length() - 6).replace(File.separatorChar, '.');
259 Class<?> jClass = loader.loadClass(jClassName);
260
261 if (!jClass.isMemberClass() || jClass.isEnum()) {
262 sb.setLength(4);
263 sb.append(jClass.toString());
264 log(sb.toString(), Project.MSG_INFO);
265
266 filesetClasses.put(jClass, jFile);
267 }
268 } catch (Exception e) {
269 log(getStackTrace(e));
270 throw new BuildException("Could not load Java class file: " + name, e);
271 }
272 }
273 else
274 log("Skipping non class file: " + name, Project.MSG_WARN);
275 }
276 }
277 log("}", Project.MSG_INFO);
278
279 log("Setting up the generator...", Project.MSG_INFO);
280
281 // As3TypeFactory.
282 if (as3typefactory == null) {
283 as3TypeFactoryImpl = new DefaultAs3TypeFactory();
284 as3TypeFactoryImpl.configure(externalizelong, externalizebiginteger, externalizebigdecimal);
285 }
286 else {
287 log("Instantiating custom As3TypeFactory class: " + as3typefactory, Project.MSG_INFO);
288 as3TypeFactoryImpl = newInstance(loader, as3typefactory);
289 }
290
291 // EntityFactory
292 if (entityfactory == null)
293 entityFactoryImpl = new DefaultEntityFactory();
294 else {
295 log("Instantiating custom EntityFactory class: " + entityfactory, Project.MSG_INFO);
296 entityFactoryImpl = newInstance(loader, entityfactory);
297 }
298
299 // RemoteDestinationFactory
300 if (remotedestinationfactory == null)
301 remoteDestinationFactoryImpl = new DefaultRemoteDestinationFactory();
302 else {
303 log("Instantiating custom RemoteDestinationFactory class: " + remotedestinationfactory, Project.MSG_INFO);
304 remoteDestinationFactoryImpl = newInstance(loader, remotedestinationfactory);
305 }
306
307 // Listener.
308 AntListener listener = new AntListener(this);
309
310 // Transformer.
311 if (transformer == null)
312 transformerImpl = new JavaAs3GroovyTransformer();
313 else {
314 log("Instantiating custom Transformer class: " + transformer, Project.MSG_INFO);
315 transformerImpl = newInstance(loader, transformer);
316 }
317 transformerImpl.setListener(listener);
318
319 // Enum templates.
320 String baseTemplateUri = null;
321 String templateUri = StandardTemplateUris.ENUM;
322 if (enumtemplate != null) {
323 log("Using custom enum template: " + enumtemplate, Project.MSG_INFO);
324 templateUri = enumtemplate;
325 }
326 enumTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
327
328 // Interface templates.
329 templateUri = StandardTemplateUris.INTERFACE;
330 if (interfacetemplate != null) {
331 log("Using custom interface template: " + interfacetemplate, Project.MSG_INFO);
332 templateUri = interfacetemplate;
333 }
334 interfaceTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
335
336 // Entity templates.
337 baseTemplateUri = StandardTemplateUris.ENTITY_BASE;
338 templateUri = StandardTemplateUris.ENTITY;
339 if (entitytemplate != null) {
340 log("Using custom entity template: " + entitytemplate, Project.MSG_INFO);
341 templateUri = entitytemplate;
342 }
343 if (entitybasetemplate != null) {
344 log("Using custom entity base template: " + entitybasetemplate, Project.MSG_INFO);
345 baseTemplateUri = entitybasetemplate;
346 }
347 else if (tide) {
348 log("Using tide entity base template.", Project.MSG_INFO);
349 baseTemplateUri = StandardTemplateUris.TIDE_ENTITY_BASE;
350 }
351 entityTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
352
353 // Other bean templates.
354 baseTemplateUri = StandardTemplateUris.BEAN_BASE;
355 templateUri = StandardTemplateUris.BEAN;
356 if (beantemplate != null) {
357 log("Using custom bean template: " + beantemplate, Project.MSG_INFO);
358 templateUri = beantemplate;
359 }
360 if (beanbasetemplate != null) {
361 log("Using custom bean base template: " + beanbasetemplate, Project.MSG_INFO);
362 baseTemplateUri = beanbasetemplate;
363 }
364 else if (tide) {
365 log("Using tide bean base template.", Project.MSG_INFO);
366 baseTemplateUri = StandardTemplateUris.TIDE_BEAN_BASE;
367 }
368 beanTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
369
370 // Remote service templates.
371 baseTemplateUri = StandardTemplateUris.REMOTE_BASE;
372 templateUri = StandardTemplateUris.REMOTE;
373 if (remotetemplate != null) {
374 log("Using custom remote template: " + remotetemplate, Project.MSG_INFO);
375 templateUri = remotetemplate;
376 }
377 if (remotebasetemplate != null) {
378 log("Using custom remote base template: " + remotebasetemplate, Project.MSG_INFO);
379 baseTemplateUri = remotebasetemplate;
380 }
381 else if (tide) {
382 log("Using tide remote destination base template.", Project.MSG_INFO);
383 baseTemplateUri = StandardTemplateUris.TIDE_REMOTE_BASE;
384 }
385 remoteTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
386
387 // Create the generator.
388 Generator generator = new Generator(this);
389 generator.add(transformerImpl);
390
391 // Call the generator for each ".class".
392 log("Calling the generator for each Java class {", Project.MSG_INFO);
393 int count = 0;
394 for (Map.Entry<Class<?>, File> classFile : filesetClasses.entrySet()) {
395 if (classFile.getKey().isAnonymousClass())
396 continue;
397 try {
398 JavaAs3Input input = new JavaAs3Input(classFile.getKey(), classFile.getValue());
399 for (Output<?> output : generator.generate(input)) {
400 if (output.isOutdated())
401 count++;
402 }
403 } catch (Exception e) {
404 log(getStackTrace(e));
405 throw new BuildException("Could not generate AS3 beans for: " + classFile.getKey(), e);
406 }
407 }
408
409 log("}", Project.MSG_INFO);
410 log("Files affected: " + count + (count == 0 ? " (nothing to do)." : "."));
411 } finally {
412 loader.resetThreadContextLoader();
413 }
414 }
415
416 ///////////////////////////////////////////////////////////////////////////
417 // Configuration implementation methods.
418
419 public As3TypeFactory getAs3TypeFactory() {
420 return as3TypeFactoryImpl;
421 }
422
423 public EntityFactory getEntityFactory() {
424 return entityFactoryImpl;
425 }
426
427 public RemoteDestinationFactory getRemoteDestinationFactory() {
428 return remoteDestinationFactoryImpl;
429 }
430
431 public File getBaseOutputDir(JavaAs3Input input) {
432 if (baseOutputDirFile == null)
433 baseOutputDirFile = new File(baseoutputdir != null ? baseoutputdir : outputdir);
434 return baseOutputDirFile;
435 }
436
437 public File getOutputDir(JavaAs3Input input) {
438 if (outputDirFile == null)
439 outputDirFile = new File(outputdir);
440 return outputDirFile;
441 }
442
443 public TemplateUri[] getTemplateUris(Kind kind, Class<?> clazz) {
444 switch (kind) {
445 case ENTITY:
446 return entityTemplateUris;
447 case INTERFACE:
448 return interfaceTemplateUris;
449 case ENUM:
450 return enumTemplateUris;
451 case BEAN:
452 return beanTemplateUris;
453 case REMOTE_DESTINATION:
454 return remoteTemplateUris;
455 default:
456 throw new IllegalArgumentException("Unknown template kind: " + kind + " / " + clazz);
457 }
458 }
459
460 public List<PackageTranslator> getTranslators() {
461 return translators;
462 }
463
464 public String getUid() {
465 return uid;
466 }
467
468 public boolean isGenerated(Class<?> clazz) {
469 return filesetClasses.containsKey(clazz);
470 }
471
472 public ClassLoader getClassLoader() {
473 return Thread.currentThread().getContextClassLoader();
474 }
475
476 public GroovyTemplateFactory getGroovyTemplateFactory() {
477 if (groovyTemplateFactory == null)
478 groovyTemplateFactory = new GroovyTemplateFactory();
479 return groovyTemplateFactory;
480 }
481
482 ///////////////////////////////////////////////////////////////////////////
483 // Utilities.
484
485 @SuppressWarnings("unchecked")
486 private <T> T newInstance(ClassLoader loader, String className) {
487 try {
488 return (T)loader.loadClass(className).newInstance();
489 } catch (Exception e) {
490 log(getStackTrace(e));
491 throw new BuildException("Could not instantiate custom class: " + className, e);
492 }
493 }
494
495 private static String getStackTrace(Exception e) {
496 StringWriter sw = new StringWriter();
497 PrintWriter pw = new PrintWriter(sw);
498 e.printStackTrace(pw);
499 return sw.toString();
500 }
501
502 private TemplateUri[] createTemplateUris(String baseUri, String uri) {
503 TemplateUri[] templateUris = new TemplateUri[baseUri == null ? 1 : 2];
504 int i = 0;
505 if (baseUri != null)
506 templateUris[i++] = new TemplateUri(baseUri, true);
507 templateUris[i] = new TemplateUri(uri, false);
508 return templateUris;
509 }
510 }