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 021package org.granite.generator.ant; 022 023import java.io.File; 024import java.io.FileNotFoundException; 025import java.io.PrintWriter; 026import java.io.StringWriter; 027import java.util.ArrayList; 028import java.util.HashMap; 029import java.util.List; 030import java.util.Map; 031 032import org.apache.tools.ant.AntClassLoader; 033import org.apache.tools.ant.BuildException; 034import org.apache.tools.ant.DirectoryScanner; 035import org.apache.tools.ant.Project; 036import org.apache.tools.ant.Task; 037import org.apache.tools.ant.types.FileSet; 038import org.apache.tools.ant.types.Path; 039import org.apache.tools.ant.types.Reference; 040import org.apache.tools.ant.types.ZipFileSet; 041import org.granite.generator.Generator; 042import org.granite.generator.Output; 043import org.granite.generator.TemplateUri; 044import org.granite.generator.Transformer; 045import org.granite.generator.as3.As3TypeFactory; 046import org.granite.generator.as3.DefaultEntityFactory; 047import org.granite.generator.as3.DefaultRemoteDestinationFactory; 048import org.granite.generator.as3.EntityFactory; 049import org.granite.generator.as3.JavaAs3GroovyConfiguration; 050import org.granite.generator.as3.JavaAs3Input; 051import org.granite.generator.as3.PackageTranslator; 052import org.granite.generator.as3.RemoteDestinationFactory; 053import org.granite.generator.as3.reflect.JavaType.Kind; 054import org.granite.generator.gsp.GroovyTemplateFactory; 055 056/** 057 * @author Franck WOLFF 058 */ 059public 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 = normalizeClassName(name.substring(0, name.length() - 6)); 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 = normalizeClassName(name.substring(0, name.length() - 6)); 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 private String normalizeClassName(String name) { 459 return name.replace('/', '.').replace('\\', '.'); 460 } 461 462 /////////////////////////////////////////////////////////////////////////// 463 // Configuration implementation methods. 464 465 @Override 466 public As3TypeFactory getAs3TypeFactory() { 467 return clientTypeFactoryImpl; 468 } 469 470 public As3TypeFactory getClientTypeFactory() { 471 return clientTypeFactoryImpl; 472 } 473 474 @Override 475 public EntityFactory getEntityFactory() { 476 return entityFactoryImpl; 477 } 478 479 @Override 480 public RemoteDestinationFactory getRemoteDestinationFactory() { 481 return remoteDestinationFactoryImpl; 482 } 483 484 @Override 485 public File getBaseOutputDir(JavaAs3Input input) { 486 if (baseOutputDirFile == null) 487 baseOutputDirFile = new File(baseoutputdir != null ? baseoutputdir : outputdir); 488 return baseOutputDirFile; 489 } 490 491 @Override 492 public File getOutputDir(JavaAs3Input input) { 493 if (outputDirFile == null) 494 outputDirFile = new File(outputdir); 495 return outputDirFile; 496 } 497 498 @Override 499 public TemplateUri[] getTemplateUris(Kind kind, Class<?> clazz) { 500 switch (kind) { 501 case ENTITY: 502 return entityTemplateUris; 503 case INTERFACE: 504 return interfaceTemplateUris; 505 case ENUM: 506 return enumTemplateUris; 507 case BEAN: 508 return beanTemplateUris; 509 case REMOTE_DESTINATION: 510 return remoteTemplateUris; 511 default: 512 throw new IllegalArgumentException("Unknown template kind: " + kind + " / " + clazz); 513 } 514 } 515 516 protected abstract String defaultTemplateUri(String type); 517 518 @Override 519 public List<PackageTranslator> getTranslators() { 520 return translators; 521 } 522 523 @Override 524 public String getUid() { 525 return uid; 526 } 527 528 @Override 529 public boolean isGenerated(Class<?> clazz) { 530 return filesetClasses.containsKey(clazz); 531 } 532 533 @Override 534 public ClassLoader getClassLoader() { 535 return Thread.currentThread().getContextClassLoader(); 536 } 537 538 @Override 539 public GroovyTemplateFactory getGroovyTemplateFactory() { 540 if (groovyTemplateFactory == null) 541 groovyTemplateFactory = new GroovyTemplateFactory(); 542 return groovyTemplateFactory; 543 } 544 545 @Override 546 public File getWorkingDirectory() { 547 return getProject().getBaseDir(); 548 } 549 550 /////////////////////////////////////////////////////////////////////////// 551 // Utilities. 552 553 @SuppressWarnings("unchecked") 554 private <T> T newInstance(ClassLoader loader, String className) { 555 try { 556 return (T)loader.loadClass(className).newInstance(); 557 } catch (Exception e) { 558 log(getStackTrace(e)); 559 throw new BuildException("Could not instantiate custom class: " + className, e); 560 } 561 } 562 563 private static String getStackTrace(Exception e) { 564 StringWriter sw = new StringWriter(); 565 PrintWriter pw = new PrintWriter(sw); 566 e.printStackTrace(pw); 567 return sw.toString(); 568 } 569 570 private TemplateUri[] createTemplateUris(String baseUri, String uri) { 571 TemplateUri[] templateUris = new TemplateUri[baseUri == null ? 1 : 2]; 572 int i = 0; 573 if (baseUri != null) 574 templateUris[i++] = new TemplateUri(baseUri, true); 575 templateUris[i] = new TemplateUri(uri, false); 576 return templateUris; 577 } 578}