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 023package org.granite.generator.ant; 024 025import java.io.File; 026import java.io.FileNotFoundException; 027import java.io.PrintWriter; 028import java.io.StringWriter; 029import java.util.ArrayList; 030import java.util.HashMap; 031import java.util.List; 032import java.util.Map; 033 034import org.apache.tools.ant.AntClassLoader; 035import org.apache.tools.ant.BuildException; 036import org.apache.tools.ant.DirectoryScanner; 037import org.apache.tools.ant.Project; 038import org.apache.tools.ant.Task; 039import org.apache.tools.ant.types.FileSet; 040import org.apache.tools.ant.types.Path; 041import org.apache.tools.ant.types.Reference; 042import org.apache.tools.ant.types.ZipFileSet; 043import org.granite.generator.Generator; 044import org.granite.generator.Output; 045import org.granite.generator.TemplateUri; 046import org.granite.generator.Transformer; 047import org.granite.generator.as3.As3TypeFactory; 048import org.granite.generator.as3.DefaultEntityFactory; 049import org.granite.generator.as3.DefaultRemoteDestinationFactory; 050import org.granite.generator.as3.EntityFactory; 051import org.granite.generator.as3.JavaAs3GroovyConfiguration; 052import org.granite.generator.as3.JavaAs3Input; 053import org.granite.generator.as3.PackageTranslator; 054import org.granite.generator.as3.RemoteDestinationFactory; 055import org.granite.generator.as3.reflect.JavaType.Kind; 056import org.granite.generator.gsp.GroovyTemplateFactory; 057 058/** 059 * @author Franck WOLFF 060 */ 061public 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}