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