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