1 16 package com.google.gwt.dev.shell; 17 18 import com.google.gwt.core.ext.GeneratorContext; 19 import com.google.gwt.core.ext.PropertyOracle; 20 import com.google.gwt.core.ext.TreeLogger; 21 import com.google.gwt.core.ext.UnableToCompleteException; 22 import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider; 23 import com.google.gwt.core.ext.typeinfo.JClassType; 24 import com.google.gwt.core.ext.typeinfo.NotFoundException; 25 import com.google.gwt.core.ext.typeinfo.TypeOracle; 26 import com.google.gwt.dev.jdt.CacheManager; 27 import com.google.gwt.dev.jdt.StaticCompilationUnitProvider; 28 import com.google.gwt.dev.jdt.TypeOracleBuilder; 29 import com.google.gwt.dev.jdt.URLCompilationUnitProvider; 30 import com.google.gwt.dev.util.Util; 31 32 import java.io.ByteArrayOutputStream ; 33 import java.io.CharArrayWriter ; 34 import java.io.File ; 35 import java.io.IOException ; 36 import java.io.OutputStream ; 37 import java.io.PrintWriter ; 38 import java.net.MalformedURLException ; 39 import java.net.URL ; 40 import java.util.ArrayList ; 41 import java.util.HashSet ; 42 import java.util.IdentityHashMap ; 43 import java.util.Iterator ; 44 import java.util.List ; 45 import java.util.Map ; 46 import java.util.Set ; 47 48 56 public class StandardGeneratorContext implements GeneratorContext { 57 58 64 private static class GeneratedCompilationUnitProvider extends 65 StaticCompilationUnitProvider { 66 67 public CharArrayWriter caw; 68 69 public PrintWriter pw; 70 71 public char[] source; 72 73 public GeneratedCompilationUnitProvider(String packageName, 74 String simpleTypeName) { 75 super(packageName, simpleTypeName, null); 76 caw = new CharArrayWriter (); 77 pw = new PrintWriter (caw, true); 78 } 79 80 83 public void commit() { 84 source = caw.toCharArray(); 85 pw.close(); 86 pw = null; 87 caw.close(); 88 caw = null; 89 } 90 91 public char[] getSource() { 92 if (source == null) { 93 throw new IllegalStateException ("source not committed"); 94 } 95 return source; 96 } 97 } 98 99 104 private static final class GeneratedCUP extends URLCompilationUnitProvider { 105 private GeneratedCUP(URL url, String name) { 106 super(url, name); 107 } 108 109 public long getLastModified() throws UnableToCompleteException { 110 return 0L; 113 } 114 115 public boolean isTransient() { 116 return true; 117 } 118 } 119 120 123 private static class PendingResource { 124 125 private final File pendingFile; 126 private final ByteArrayOutputStream baos = new ByteArrayOutputStream (); 127 128 public PendingResource(File pendingFile) { 129 this.pendingFile = pendingFile; 130 } 131 132 public void commit(TreeLogger logger) throws UnableToCompleteException { 133 logger = logger.branch(TreeLogger.TRACE, "Writing generated resource '" 134 + pendingFile.getAbsolutePath() + "'", null); 135 136 if (pendingFile.exists()) { 137 logger.log(TreeLogger.ERROR, 138 "The destination file already exists; aborting the commit", null); 139 throw new UnableToCompleteException(); 140 } 141 142 Util.writeBytesToFile(logger, pendingFile, baos.toByteArray()); 143 } 144 145 public File getFile() { 146 return pendingFile; 147 } 148 149 public OutputStream getOutputStream() { 150 return baos; 151 } 152 153 public boolean isSamePath(TreeLogger logger, File other) { 154 File failedFile = null; 155 try { 156 161 failedFile = pendingFile; 162 File thisFile = pendingFile.getCanonicalFile(); 163 failedFile = pendingFile; 164 File thatFile = other.getCanonicalFile(); 165 166 if (thisFile.equals(thatFile)) { 167 return true; 168 } else { 169 return false; 170 } 171 } catch (IOException e) { 172 logger.log(TreeLogger.ERROR, 173 "Unable to determine canonical path of pending resource '" 174 + failedFile.toString() + "'", e); 175 } 176 return false; 177 } 178 } 179 180 private final CacheManager cacheManager; 181 182 private final Set committedGeneratedCups = new HashSet (); 183 184 private final File genDir; 185 186 private final Set generatedTypeNames = new HashSet (); 187 188 private final File outDir; 189 190 private final Map pendingResourcesByOutputStream = new IdentityHashMap (); 191 192 private final PropertyOracle propOracle; 193 194 private final TypeOracle typeOracle; 195 196 private final Map uncommittedGeneratedCupsByPrintWriter = new IdentityHashMap (); 197 198 202 public StandardGeneratorContext(TypeOracle typeOracle, 203 PropertyOracle propOracle, File genDir, File outDir, 204 CacheManager cacheManager) { 205 this.propOracle = propOracle; 206 this.typeOracle = typeOracle; 207 this.genDir = genDir; 208 this.outDir = outDir; 209 this.cacheManager = cacheManager; 210 } 211 212 215 public final void commit(TreeLogger logger, PrintWriter pw) { 216 GeneratedCompilationUnitProvider gcup = (GeneratedCompilationUnitProvider) uncommittedGeneratedCupsByPrintWriter.get(pw); 217 if (gcup != null) { 218 gcup.commit(); 219 uncommittedGeneratedCupsByPrintWriter.remove(pw); 220 committedGeneratedCups.add(gcup); 221 } else { 222 logger.log(TreeLogger.WARN, 223 "Generator attempted to commit an unknown PrintWriter", null); 224 } 225 } 226 227 public void commitResource(TreeLogger logger, OutputStream os) 228 throws UnableToCompleteException { 229 230 PendingResource pendingResource = (PendingResource) pendingResourcesByOutputStream.get(os); 232 if (pendingResource != null) { 233 pendingResource.commit(logger); 235 236 pendingResourcesByOutputStream.remove(os); 241 } else { 242 logger.log(TreeLogger.WARN, 243 "Generator attempted to commit an unknown OutputStream", null); 244 throw new UnableToCompleteException(); 245 } 246 } 247 248 255 public final JClassType[] finish(TreeLogger logger) 256 throws UnableToCompleteException { 257 258 abortUncommittedResources(logger); 259 260 List genTypeNames = new ArrayList (); 262 263 try { 264 TreeLogger branch; 265 if (!committedGeneratedCups.isEmpty()) { 266 String msg = "Assimilating generated source"; 269 branch = logger.branch(TreeLogger.DEBUG, msg, null); 270 271 TreeLogger subBranch = null; 272 if (branch.isLoggable(TreeLogger.DEBUG)) { 273 subBranch = branch.branch(TreeLogger.DEBUG, 274 "Generated source files...", null); 275 } 276 277 assert (cacheManager.getTypeOracle() == typeOracle); 278 TypeOracleBuilder builder = new TypeOracleBuilder(cacheManager); 279 for (Iterator iter = committedGeneratedCups.iterator(); iter.hasNext();) { 280 GeneratedCompilationUnitProvider gcup = (GeneratedCompilationUnitProvider) iter.next(); 281 String typeName = gcup.getTypeName(); 282 String genTypeName = gcup.getPackageName() + "." + typeName; 283 genTypeNames.add(genTypeName); 284 CompilationUnitProvider cup = writeSource(logger, gcup, typeName); 285 builder.addCompilationUnit(cup); 286 cacheManager.addGeneratedCup(cup); 287 288 if (subBranch != null) { 289 subBranch.log(TreeLogger.DEBUG, cup.getLocation(), null); 290 } 291 } 292 293 builder.build(branch); 294 } 295 296 JClassType[] genTypes = new JClassType[genTypeNames.size()]; 298 int next = 0; 299 for (Iterator iter = genTypeNames.iterator(); iter.hasNext();) { 300 String genTypeName = (String ) iter.next(); 301 try { 302 genTypes[next++] = typeOracle.getType(genTypeName); 303 } catch (NotFoundException e) { 304 String msg = "Unable to find recently-generated type '" + genTypeName; 305 logger.log(TreeLogger.ERROR, msg, null); 306 throw new UnableToCompleteException(); 307 } 308 } 309 return genTypes; 310 } finally { 311 312 if (!uncommittedGeneratedCupsByPrintWriter.isEmpty()) { 314 String msg = "For the following type(s), generated source was never committed (did you forget to call commit()?)"; 315 logger = logger.branch(TreeLogger.WARN, msg, null); 316 317 for (Iterator iter = uncommittedGeneratedCupsByPrintWriter.values().iterator(); iter.hasNext();) { 318 StaticCompilationUnitProvider cup = (StaticCompilationUnitProvider) iter.next(); 319 String typeName = cup.getPackageName() + "." + cup.getTypeName(); 320 logger.log(TreeLogger.WARN, typeName, null); 321 } 322 } 323 324 uncommittedGeneratedCupsByPrintWriter.clear(); 325 committedGeneratedCups.clear(); 326 generatedTypeNames.clear(); 327 } 328 } 329 330 public File getOutputDir() { 331 return outDir; 332 } 333 334 public final PropertyOracle getPropertyOracle() { 335 return propOracle; 336 } 337 338 public final TypeOracle getTypeOracle() { 339 return typeOracle; 340 } 341 342 public final PrintWriter tryCreate(TreeLogger logger, String packageName, 343 String simpleTypeName) { 344 String typeName = packageName + "." + simpleTypeName; 345 346 JClassType existingType = typeOracle.findType(packageName, simpleTypeName); 348 if (existingType != null) { 349 logger.log(TreeLogger.DEBUG, "Type '" + typeName 350 + "' already exists and will not be re-created ", null); 351 return null; 352 } 353 354 if (generatedTypeNames.contains(typeName)) { 356 final String msg = "A request to create type '" 357 + typeName 358 + "' was received while the type itself was being created; this might be a generator or configuration bug"; 359 logger.log(TreeLogger.WARN, msg, null); 360 return null; 361 } 362 363 GeneratedCompilationUnitProvider gcup = new GeneratedCompilationUnitProvider( 366 packageName, simpleTypeName); 367 uncommittedGeneratedCupsByPrintWriter.put(gcup.pw, gcup); 368 generatedTypeNames.add(typeName); 369 370 return gcup.pw; 371 } 372 373 public OutputStream tryCreateResource(TreeLogger logger, String name) 374 throws UnableToCompleteException { 375 376 logger = logger.branch(TreeLogger.DEBUG, 377 "Preparing pending output resource '" + name + "'", null); 378 379 if (name == null || name.trim().equals("")) { 381 logger.log(TreeLogger.ERROR, 382 "The resource name must be a non-empty string", null); 383 throw new UnableToCompleteException(); 384 } 385 386 File f = new File (name); 388 if (f.isAbsolute()) { 389 logger.log( 390 TreeLogger.ERROR, 391 "Resource paths are intended to be relative to the compiled output directory and cannot be absolute", 392 null); 393 throw new UnableToCompleteException(); 394 } 395 396 if (name.indexOf('\\') >= 0) { 398 logger.log( 399 TreeLogger.ERROR, 400 "Resource paths must contain forward slashes (not backslashes) to denote subdirectories", 401 null); 402 throw new UnableToCompleteException(); 403 } 404 405 File pendingFile = new File (outDir, name); 407 408 for (Iterator iter = pendingResourcesByOutputStream.values().iterator(); iter.hasNext();) { 410 PendingResource pendingResource = (PendingResource) iter.next(); 411 if (pendingResource.isSamePath(logger, pendingFile)) { 412 logger.log(TreeLogger.WARN, "The file is already a pending resource", 414 null); 415 return null; 416 } 417 } 418 419 if (pendingFile.exists()) { 421 logger.log(TreeLogger.TRACE, "File already exists", null); 422 return null; 423 } 424 425 PendingResource pendingResource = new PendingResource(pendingFile); 427 OutputStream os = pendingResource.getOutputStream(); 428 pendingResourcesByOutputStream.put(os, pendingResource); 429 430 return os; 431 } 432 433 private void abortUncommittedResources(TreeLogger logger) { 434 if (pendingResourcesByOutputStream.isEmpty()) { 435 return; 437 } 438 439 logger = logger.branch( 441 TreeLogger.WARN, 442 "The following resources will not be created because they were never committed (did you forget to call commit()?)", 443 null); 444 445 try { 446 for (Iterator iter = pendingResourcesByOutputStream.values().iterator(); iter.hasNext();) { 447 PendingResource pendingResource = (PendingResource) iter.next(); 448 logger.log(TreeLogger.WARN, 449 pendingResource.getFile().getAbsolutePath(), null); 450 } 451 } finally { 452 pendingResourcesByOutputStream.clear(); 453 } 454 } 455 456 464 private CompilationUnitProvider writeSource(TreeLogger logger, 465 CompilationUnitProvider cup, String simpleTypeName) 466 throws UnableToCompleteException { 467 468 if (genDir == null) { 469 return cup; 471 } 472 473 if (Util.isCompilationUnitOnDisk(cup.getLocation())) { 474 return cup; 476 } 477 478 String typeName = cup.getPackageName() + "." + simpleTypeName; 480 String relativePath = typeName.replace('.', '/') + ".java"; 481 File srcFile = new File (genDir, relativePath); 482 Util.writeCharsAsFile(logger, srcFile, cup.getSource()); 483 484 Throwable caught = null; 486 try { 487 URL fileURL = srcFile.toURL(); 488 URLCompilationUnitProvider fileBaseCup = new GeneratedCUP(fileURL, 489 cup.getPackageName()); 490 return fileBaseCup; 491 } catch (MalformedURLException e) { 492 caught = e; 493 } 494 logger.log(TreeLogger.ERROR, 495 "Internal error: cannot build URL from synthesized file name '" 496 + srcFile.getAbsolutePath() + "'", caught); 497 throw new UnableToCompleteException(); 498 } 499 } | Popular Tags |