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.apache.tools.ant.types.ZipFileSet;
041 import org.granite.generator.Generator;
042 import org.granite.generator.Output;
043 import org.granite.generator.TemplateUri;
044 import org.granite.generator.Transformer;
045 import org.granite.generator.as3.As3TypeFactory;
046 import org.granite.generator.as3.DefaultEntityFactory;
047 import org.granite.generator.as3.DefaultRemoteDestinationFactory;
048 import org.granite.generator.as3.EntityFactory;
049 import org.granite.generator.as3.JavaAs3GroovyConfiguration;
050 import org.granite.generator.as3.JavaAs3Input;
051 import org.granite.generator.as3.PackageTranslator;
052 import org.granite.generator.as3.RemoteDestinationFactory;
053 import org.granite.generator.as3.reflect.JavaType.Kind;
054 import org.granite.generator.gsp.GroovyTemplateFactory;
055
056 /**
057 * @author Franck WOLFF
058 */
059 public abstract class AbstractAntJavaGenTask extends Task implements JavaAs3GroovyConfiguration {
060
061 ///////////////////////////////////////////////////////////////////////////
062 // Configurable fields (xml attributes).
063
064 private String outputdir = ".";
065 private String baseoutputdir = null;
066
067 private String uid = "uid";
068
069 private String entitytemplate = null;
070 private String entitybasetemplate = null;
071
072 private String beantemplate = null;
073 private String beanbasetemplate = null;
074
075 private String interfacetemplate = null;
076
077 private String enumtemplate = null;
078
079 private String remotetemplate = null;
080 private String remotebasetemplate = null;
081
082 private boolean tide = false;
083
084 private String clienttypefactory = null;
085 private String entityfactory = null;
086 private String remotedestinationfactory = null;
087 private String transformer = null;
088
089 private Path classpath = null;
090 private List<FileSet> fileSets = new ArrayList<FileSet>();
091 private List<ZipFileSet> zipFileSets = new ArrayList<ZipFileSet>();
092
093 private List<PackageTranslator> translators = new ArrayList<PackageTranslator>();
094
095 ///////////////////////////////////////////////////////////////////////////
096 // Configuration implementation fields.
097
098 private File outputDirFile = null;
099 private File baseOutputDirFile = null;
100
101 private As3TypeFactory clientTypeFactoryImpl = null;
102 private Transformer<?, ?, ?> transformerImpl = null;
103 private EntityFactory entityFactoryImpl = null;
104 private RemoteDestinationFactory remoteDestinationFactoryImpl = null;
105
106 private GroovyTemplateFactory groovyTemplateFactory = null;
107
108 private TemplateUri[] entityTemplateUris = null;
109 private TemplateUri[] interfaceTemplateUris = null;
110 private TemplateUri[] beanTemplateUris = null;
111 private TemplateUri[] enumTemplateUris = null;
112 private TemplateUri[] remoteTemplateUris = null;
113
114 private Map<Class<?>, File> filesetClasses = null;
115
116 ///////////////////////////////////////////////////////////////////////////
117 // Task attributes.
118
119 public void setOutputdir(String outputdir) {
120 this.outputdir = outputdir;
121 }
122
123 public void setBaseoutputdir(String baseoutputdir) {
124 this.baseoutputdir = baseoutputdir;
125 }
126
127 public void setAs3typefactory(String as3typefactory) {
128 this.clienttypefactory = as3typefactory;
129 }
130
131 public void setClienttypefactory(String clienttypefactory) {
132 this.clienttypefactory = clienttypefactory;
133 }
134
135 public void setEntityfactory(String entityfactory) {
136 this.entityfactory = entityfactory;
137 }
138
139 public void setRemotedestinationfactory(String remotedestinationfactory) {
140 this.remotedestinationfactory = remotedestinationfactory;
141 }
142
143 public void setUid(String uid) {
144 this.uid = uid;
145 }
146
147 public void setEntitytemplate(String entitytemplate) {
148
149 this.entitytemplate = entitytemplate;
150 }
151 public void setEntitybasetemplate(String entitybasetemplate) {
152 this.entitybasetemplate = entitybasetemplate;
153 }
154
155 public void setBeantemplate(String beantemplate) {
156 this.beantemplate = beantemplate;
157 }
158 public void setBeanbasetemplate(String beanbasetemplate) {
159 this.beanbasetemplate = beanbasetemplate;
160 }
161
162 public void setInterfacetemplate(String interfacetemplate) {
163 this.interfacetemplate = interfacetemplate;
164 }
165
166 public void setEnumtemplate(String enumtemplate) {
167 this.enumtemplate = enumtemplate;
168 }
169
170 public void setRemotetemplate(String remotetemplate) {
171 this.remotetemplate = remotetemplate;
172 }
173 public void setRemotebasetemplate(String remotebasetemplate) {
174 this.remotebasetemplate = remotebasetemplate;
175 }
176
177 public void setTide(boolean tide) {
178 this.tide = tide;
179 }
180
181 public void setTransformer(String transformer) {
182 this.transformer = transformer;
183 }
184
185 protected abstract As3TypeFactory initDefaultClientTypeFactory();
186
187 protected abstract Transformer<?, ?, ?> initDefaultTransformer();
188
189 ///////////////////////////////////////////////////////////////////////////
190 // Task inner elements.
191
192 public void addFileset(FileSet fileSet) {
193 fileSets.add(fileSet);
194 }
195
196 public void addZipfileset(ZipFileSet zipFileSet) {
197 zipFileSets.add(zipFileSet);
198 }
199
200 public void setClasspath(Path path) {
201 if (classpath == null)
202 classpath = path;
203 else
204 classpath.append(path);
205 }
206
207 public Path createClasspath() {
208 if (classpath == null)
209 classpath = new Path(getProject());
210 return classpath.createPath();
211 }
212
213 public void setClasspathRef(Reference r) {
214 createClasspath().setRefid(r);
215 }
216
217 public void addTranslator(PackageTranslator translator) {
218 translators.add(translator);
219 }
220
221 ///////////////////////////////////////////////////////////////////////////
222 // Task execution.
223
224 @Override
225 public void execute() throws BuildException {
226
227 log("Using output dir: " + outputdir, Project.MSG_INFO);
228 log("Using classpath: " + classpath, Project.MSG_INFO);
229
230 AntClassLoader loader = new AntClassLoader(AntJavaAs3Task.class.getClassLoader(), getProject(), classpath, true);
231 try {
232 loader.setThreadContextLoader();
233
234 filesetClasses = new HashMap<Class<?>, File>();
235
236 // Build a Set of all ".class" files in filesets (ignoring nested classes).
237 log("Loading all Java classes referenced by inner fileset(s) {", Project.MSG_INFO);
238 for (FileSet fileSet : fileSets) {
239 DirectoryScanner scanner = fileSet.getDirectoryScanner(getProject());
240 scanner.setCaseSensitive(true);
241 scanner.scan();
242
243 StringBuilder sb = new StringBuilder(" ");
244 String[] names = scanner.getIncludedFiles();
245 for (String name : names) {
246 if (name.endsWith(".class")) {
247 log(name, Project.MSG_VERBOSE);
248 try {
249 File jFile = new File(scanner.getBasedir(), name);
250 if (!jFile.exists())
251 throw new FileNotFoundException(jFile.toString());
252
253 String jClassName = name.substring(0, name.length() - 6).replace(File.separatorChar, '.');
254 Class<?> jClass = loader.loadClass(jClassName);
255
256 if (!jClass.isMemberClass() || jClass.isEnum()) {
257 sb.setLength(4);
258 sb.append(jClass.toString());
259 log(sb.toString(), Project.MSG_INFO);
260
261 filesetClasses.put(jClass, jFile);
262 }
263 } catch (Exception e) {
264 log(getStackTrace(e));
265 throw new BuildException("Could not load Java class file: " + name, e);
266 }
267 }
268 else
269 log("Skipping non class file: " + name, Project.MSG_WARN);
270 }
271 }
272 log("}", Project.MSG_INFO);
273
274 // Add all ".class" files from zipfilesets (ignoring nested classes).
275 log("Loading all Java classes referenced by inner zipfileset(s) {", Project.MSG_INFO);
276
277 for (ZipFileSet zipFileSet : zipFileSets) {
278 File zip = zipFileSet.getSrc(getProject());
279 if (zip == null)
280 throw new BuildException("zipfileset must use the src attribute");
281 if (!zip.getName().endsWith(".jar"))
282 throw new BuildException("Zipfileset src isn't a '.jar' file: " + zip);
283
284 log(" -- scanning: " + zip + " --", Project.MSG_INFO);
285
286 DirectoryScanner scanner = zipFileSet.getDirectoryScanner(getProject());
287 scanner.setCaseSensitive(true);
288 scanner.scan();
289
290 StringBuilder sb = new StringBuilder(" ");
291 String[] names = scanner.getIncludedFiles();
292 for (String name : names) {
293 if (name.endsWith(".class")) {
294 log(name, Project.MSG_VERBOSE);
295 try {
296 String jClassName = name.substring(0, name.length() - 6).replace(File.separatorChar, '.');
297 Class<?> jClass = loader.loadClass(jClassName);
298
299 if (!jClass.isMemberClass() || jClass.isEnum()) {
300 sb.setLength(4);
301 sb.append(jClass.toString());
302 log(sb.toString(), Project.MSG_INFO);
303
304 filesetClasses.put(jClass, zip);
305 }
306 } catch (Exception e) {
307 log(getStackTrace(e));
308 throw new BuildException("Could not load Java class file: " + name, e);
309 }
310 }
311 else
312 log("Skipping non class file: " + name, Project.MSG_WARN);
313 }
314 }
315
316 log("}", Project.MSG_INFO);
317
318 log("Setting up the generator...", Project.MSG_INFO);
319
320 // ClientTypeFactory.
321 if (clienttypefactory == null) {
322 clientTypeFactoryImpl = initDefaultClientTypeFactory();
323 }
324 else {
325 log("Instantiating custom ClientTypeFactory class: " + clienttypefactory, Project.MSG_INFO);
326 clientTypeFactoryImpl = newInstance(loader, clienttypefactory);
327 }
328
329 // EntityFactory
330 if (entityfactory == null)
331 entityFactoryImpl = new DefaultEntityFactory();
332 else {
333 log("Instantiating custom EntityFactory class: " + entityfactory, Project.MSG_INFO);
334 entityFactoryImpl = newInstance(loader, entityfactory);
335 }
336
337 // RemoteDestinationFactory
338 if (remotedestinationfactory == null)
339 remoteDestinationFactoryImpl = new DefaultRemoteDestinationFactory();
340 else {
341 log("Instantiating custom RemoteDestinationFactory class: " + remotedestinationfactory, Project.MSG_INFO);
342 remoteDestinationFactoryImpl = newInstance(loader, remotedestinationfactory);
343 }
344
345 // Listener.
346 AntListener listener = new AntListener(this);
347
348 // Transformer.
349 if (transformer == null)
350 transformerImpl = initDefaultTransformer();
351 else {
352 log("Instantiating custom Transformer class: " + transformer, Project.MSG_INFO);
353 transformerImpl = newInstance(loader, transformer);
354 }
355 transformerImpl.setListener(listener);
356
357 // Enum templates.
358 String baseTemplateUri = null;
359 String templateUri = defaultTemplateUri("ENUM");
360 if (enumtemplate != null) {
361 log("Using custom enum template: " + enumtemplate, Project.MSG_INFO);
362 templateUri = enumtemplate;
363 }
364 enumTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
365
366 // Interface templates.
367 templateUri = defaultTemplateUri("INTERFACE");
368 if (interfacetemplate != null) {
369 log("Using custom interface template: " + interfacetemplate, Project.MSG_INFO);
370 templateUri = interfacetemplate;
371 }
372 interfaceTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
373
374 // Entity templates.
375 baseTemplateUri = defaultTemplateUri("ENTITY_BASE");
376 templateUri = defaultTemplateUri("ENTITY");
377 if (entitytemplate != null) {
378 log("Using custom entity template: " + entitytemplate, Project.MSG_INFO);
379 templateUri = entitytemplate;
380 }
381 if (entitybasetemplate != null) {
382 log("Using custom entity base template: " + entitybasetemplate, Project.MSG_INFO);
383 baseTemplateUri = entitybasetemplate;
384 }
385 else if (tide) {
386 log("Using tide entity base template.", Project.MSG_INFO);
387 baseTemplateUri = defaultTemplateUri("TIDE_ENTITY_BASE");
388 }
389 entityTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
390
391 // Other bean templates.
392 baseTemplateUri = defaultTemplateUri("BEAN_BASE");
393 templateUri = defaultTemplateUri("BEAN");
394 if (beantemplate != null) {
395 log("Using custom bean template: " + beantemplate, Project.MSG_INFO);
396 templateUri = beantemplate;
397 }
398 if (beanbasetemplate != null) {
399 log("Using custom bean base template: " + beanbasetemplate, Project.MSG_INFO);
400 baseTemplateUri = beanbasetemplate;
401 }
402 else if (tide) {
403 log("Using tide bean base template.", Project.MSG_INFO);
404 baseTemplateUri = defaultTemplateUri("TIDE_BEAN_BASE");
405 }
406 beanTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
407
408 // Remote service templates.
409 baseTemplateUri = defaultTemplateUri("REMOTE_BASE");
410 templateUri = defaultTemplateUri("REMOTE");
411 if (remotetemplate != null) {
412 log("Using custom remote template: " + remotetemplate, Project.MSG_INFO);
413 templateUri = remotetemplate;
414 }
415 else if (tide) {
416 log("Using Tide remote destination template.", Project.MSG_INFO);
417 templateUri = defaultTemplateUri("TIDE_REMOTE");
418 }
419 if (remotebasetemplate != null) {
420 log("Using custom remote base template: " + remotebasetemplate, Project.MSG_INFO);
421 baseTemplateUri = remotebasetemplate;
422 }
423 else if (tide) {
424 log("Using Tide remote destination base template.", Project.MSG_INFO);
425 baseTemplateUri = defaultTemplateUri("TIDE_REMOTE_BASE");
426 }
427 remoteTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
428
429 // Create the generator.
430 Generator generator = new Generator(this);
431 generator.add(transformerImpl);
432
433 // Call the generator for each ".class".
434 log("Calling the generator for each Java class {", Project.MSG_INFO);
435 int count = 0;
436 for (Map.Entry<Class<?>, File> classFile : filesetClasses.entrySet()) {
437 if (classFile.getKey().isAnonymousClass())
438 continue;
439 try {
440 JavaAs3Input input = new JavaAs3Input(classFile.getKey(), classFile.getValue());
441 for (Output<?> output : generator.generate(input)) {
442 if (output.isOutdated())
443 count++;
444 }
445 } catch (Exception e) {
446 log(getStackTrace(e));
447 throw new BuildException("Could not generate AS3 beans for: " + classFile.getKey(), e);
448 }
449 }
450
451 log("}", Project.MSG_INFO);
452 log("Files affected: " + count + (count == 0 ? " (nothing to do)." : "."));
453 } finally {
454 loader.resetThreadContextLoader();
455 }
456 }
457
458 ///////////////////////////////////////////////////////////////////////////
459 // Configuration implementation methods.
460
461 @Override
462 public As3TypeFactory getAs3TypeFactory() {
463 return clientTypeFactoryImpl;
464 }
465
466 public As3TypeFactory getClientTypeFactory() {
467 return clientTypeFactoryImpl;
468 }
469
470 @Override
471 public EntityFactory getEntityFactory() {
472 return entityFactoryImpl;
473 }
474
475 @Override
476 public RemoteDestinationFactory getRemoteDestinationFactory() {
477 return remoteDestinationFactoryImpl;
478 }
479
480 @Override
481 public File getBaseOutputDir(JavaAs3Input input) {
482 if (baseOutputDirFile == null)
483 baseOutputDirFile = new File(baseoutputdir != null ? baseoutputdir : outputdir);
484 return baseOutputDirFile;
485 }
486
487 @Override
488 public File getOutputDir(JavaAs3Input input) {
489 if (outputDirFile == null)
490 outputDirFile = new File(outputdir);
491 return outputDirFile;
492 }
493
494 @Override
495 public TemplateUri[] getTemplateUris(Kind kind, Class<?> clazz) {
496 switch (kind) {
497 case ENTITY:
498 return entityTemplateUris;
499 case INTERFACE:
500 return interfaceTemplateUris;
501 case ENUM:
502 return enumTemplateUris;
503 case BEAN:
504 return beanTemplateUris;
505 case REMOTE_DESTINATION:
506 return remoteTemplateUris;
507 default:
508 throw new IllegalArgumentException("Unknown template kind: " + kind + " / " + clazz);
509 }
510 }
511
512 protected abstract String defaultTemplateUri(String type);
513
514 @Override
515 public List<PackageTranslator> getTranslators() {
516 return translators;
517 }
518
519 @Override
520 public String getUid() {
521 return uid;
522 }
523
524 @Override
525 public boolean isGenerated(Class<?> clazz) {
526 return filesetClasses.containsKey(clazz);
527 }
528
529 @Override
530 public ClassLoader getClassLoader() {
531 return Thread.currentThread().getContextClassLoader();
532 }
533
534 @Override
535 public GroovyTemplateFactory getGroovyTemplateFactory() {
536 if (groovyTemplateFactory == null)
537 groovyTemplateFactory = new GroovyTemplateFactory();
538 return groovyTemplateFactory;
539 }
540
541 @Override
542 public File getWorkingDirectory() {
543 return getProject().getBaseDir();
544 }
545
546 ///////////////////////////////////////////////////////////////////////////
547 // Utilities.
548
549 @SuppressWarnings("unchecked")
550 private <T> T newInstance(ClassLoader loader, String className) {
551 try {
552 return (T)loader.loadClass(className).newInstance();
553 } catch (Exception e) {
554 log(getStackTrace(e));
555 throw new BuildException("Could not instantiate custom class: " + className, e);
556 }
557 }
558
559 private static String getStackTrace(Exception e) {
560 StringWriter sw = new StringWriter();
561 PrintWriter pw = new PrintWriter(sw);
562 e.printStackTrace(pw);
563 return sw.toString();
564 }
565
566 private TemplateUri[] createTemplateUris(String baseUri, String uri) {
567 TemplateUri[] templateUris = new TemplateUri[baseUri == null ? 1 : 2];
568 int i = 0;
569 if (baseUri != null)
570 templateUris[i++] = new TemplateUri(baseUri, true);
571 templateUris[i] = new TemplateUri(uri, false);
572 return templateUris;
573 }
574 }