001    /*
002      GRANITE DATA SERVICES
003      Copyright (C) 2007-2010 ADEQUATE SYSTEMS SARL
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                    beanTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
365                
366                    // Remote service templates.
367                baseTemplateUri = StandardTemplateUris.REMOTE_BASE;
368                templateUri = StandardTemplateUris.REMOTE;
369                if (remotetemplate != null) {
370                    log("Using custom remote template: " + remotetemplate, Project.MSG_INFO);
371                    templateUri = remotetemplate;
372                }
373                if (remotebasetemplate != null) {
374                    log("Using custom remote base template: " + remotebasetemplate, Project.MSG_INFO);
375                    baseTemplateUri = remotebasetemplate;
376                }
377                else if (tide) {
378                    log("Using tide remote destination base template.", Project.MSG_INFO);
379                    baseTemplateUri = StandardTemplateUris.TIDE_REMOTE_BASE;
380                }
381                    remoteTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
382                    
383                    // Create the generator.
384                Generator generator = new Generator(this);
385                generator.add(transformerImpl);
386                
387                // Call the generator for each ".class".
388                log("Calling the generator for each Java class {", Project.MSG_INFO);
389                int count = 0;
390                for (Map.Entry<Class<?>, File> classFile : filesetClasses.entrySet()) {
391                    if (classFile.getKey().isAnonymousClass())
392                            continue;
393                    try {
394                            JavaAs3Input input = new JavaAs3Input(classFile.getKey(), classFile.getValue());
395                        for (Output<?> output : generator.generate(input)) {
396                            if (output.isOutdated())
397                                    count++;
398                        }
399                    } catch (Exception e) {
400                        log(getStackTrace(e));
401                        throw new BuildException("Could not generate AS3 beans for: " + classFile.getKey(), e);
402                    }
403                }
404    
405                log("}", Project.MSG_INFO);
406                log("Files affected: " + count +  (count == 0 ? " (nothing to do)." : "."));
407            } finally {
408                loader.resetThreadContextLoader();
409            }
410        }
411    
412        ///////////////////////////////////////////////////////////////////////////
413        // Configuration implementation methods.
414        
415        public As3TypeFactory getAs3TypeFactory() {
416                    return as3TypeFactoryImpl;
417            }
418        
419        public EntityFactory getEntityFactory() {
420            return entityFactoryImpl;
421        }
422        
423        public RemoteDestinationFactory getRemoteDestinationFactory() {
424            return remoteDestinationFactoryImpl;
425        }
426    
427            public File getBaseOutputDir(JavaAs3Input input) {
428                    if (baseOutputDirFile == null)
429                            baseOutputDirFile = new File(baseoutputdir != null ? baseoutputdir : outputdir);
430                    return baseOutputDirFile;
431            }
432    
433            public File getOutputDir(JavaAs3Input input) {
434                    if (outputDirFile == null)
435                            outputDirFile = new File(outputdir);
436                    return outputDirFile;
437            }
438    
439            public TemplateUri[] getTemplateUris(Kind kind, Class<?> clazz) {
440                    switch (kind) {
441                    case ENTITY:
442                    return entityTemplateUris;
443                    case INTERFACE:
444                            return interfaceTemplateUris;
445                    case ENUM:
446                            return enumTemplateUris;
447                    case BEAN:
448                            return beanTemplateUris;
449                    case REMOTE_DESTINATION:
450                            return remoteTemplateUris;
451                    default:
452                            throw new IllegalArgumentException("Unknown template kind: " + kind + " / " + clazz);
453                    }
454            }
455    
456            public List<PackageTranslator> getTranslators() {
457                    return translators;
458            }
459    
460            public String getUid() {
461                    return uid;
462            }
463    
464            public boolean isGenerated(Class<?> clazz) {
465                    return filesetClasses.containsKey(clazz);
466            }
467    
468            public ClassLoader getClassLoader() {
469                    return Thread.currentThread().getContextClassLoader();
470            }
471    
472            public GroovyTemplateFactory getGroovyTemplateFactory() {
473                    if (groovyTemplateFactory == null)
474                            groovyTemplateFactory = new GroovyTemplateFactory();
475                    return groovyTemplateFactory;
476            }
477    
478        ///////////////////////////////////////////////////////////////////////////
479        // Utilities.
480    
481            @SuppressWarnings("unchecked")
482            private <T> T newInstance(ClassLoader loader, String className) {
483                    try {
484                return (T)loader.loadClass(className).newInstance();
485            } catch (Exception e) {
486                log(getStackTrace(e));
487                throw new BuildException("Could not instantiate custom class: " + className, e);
488            }
489        }
490    
491        private static String getStackTrace(Exception e) {
492            StringWriter sw = new StringWriter();
493            PrintWriter pw = new PrintWriter(sw);
494            e.printStackTrace(pw);
495            return sw.toString();
496        }
497            
498            private TemplateUri[] createTemplateUris(String baseUri, String uri) {
499                    TemplateUri[] templateUris = new TemplateUri[baseUri == null ? 1 : 2];
500                    int i = 0;
501                    if (baseUri != null)
502                            templateUris[i++] = new TemplateUri(baseUri, true);
503                    templateUris[i] = new TemplateUri(uri, false);
504                    return templateUris;
505            }
506    }