001 /* 002 $Id: CompilationUnit.java,v 1.34 2005/11/19 21:22:12 blackdrag Exp $ 003 004 005 Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved. 006 007 008 Redistribution and use of this software and associated documentation 009 ("Software"), with or without modification, are permitted provided 010 that the following conditions are met: 011 012 1. Redistributions of source code must retain copyright 013 statements and notices. Redistributions must also contain a 014 copy of this document. 015 016 017 2. Redistributions in binary form must reproduce the 018 above copyright notice, this list of conditions and the 019 following disclaimer in the documentation and/or other 020 materials provided with the distribution. 021 022 023 3. The name "groovy" must not be used to endorse or promote 024 products derived from this Software without prior written 025 permission of The Codehaus. For written permission, 026 please contact info@codehaus.org. 027 028 029 4. Products derived from this Software may not be called "groovy" 030 nor may "groovy" appear in their names without prior written 031 permission of The Codehaus. "groovy" is a registered 032 trademark of The Codehaus. 033 034 035 5. Due credit should be given to The Codehaus - 036 http://groovy.codehaus.org/ 037 038 039 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS 040 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT 041 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 042 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 043 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 044 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 045 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 046 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 047 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 048 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 049 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 050 OF THE POSSIBILITY OF SUCH DAMAGE. 051 */ 052 053 054 package org.codehaus.groovy.control; 055 056 import java.io.File; 057 import java.io.FileOutputStream; 058 import java.io.IOException; 059 import java.io.InputStream; 060 import java.net.URL; 061 import java.security.CodeSource; 062 import java.util.*; 063 064 import org.codehaus.groovy.GroovyBugError; 065 import org.codehaus.groovy.ast.ASTNode; 066 import org.codehaus.groovy.ast.ClassNode; 067 import org.codehaus.groovy.ast.CompileUnit; 068 import org.codehaus.groovy.ast.ModuleNode; 069 import org.codehaus.groovy.classgen.AsmClassGenerator; 070 import org.codehaus.groovy.classgen.ClassCompletionVerifier; 071 import org.codehaus.groovy.classgen.ClassGenerator; 072 import org.codehaus.groovy.classgen.GeneratorContext; 073 import org.codehaus.groovy.classgen.JSRVariableScopeCodeVisitor; 074 import org.codehaus.groovy.classgen.Verifier; 075 import org.codehaus.groovy.control.io.InputStreamReaderSource; 076 import org.codehaus.groovy.control.io.ReaderSource; 077 import org.codehaus.groovy.control.messages.ExceptionMessage; 078 import org.codehaus.groovy.control.messages.Message; 079 import org.codehaus.groovy.syntax.SyntaxException; 080 import org.codehaus.groovy.syntax.ClassSource; 081 import org.codehaus.groovy.syntax.SourceSummary; 082 import org.codehaus.groovy.tools.GroovyClass; 083 import org.objectweb.asm.ClassVisitor; 084 import org.objectweb.asm.ClassWriter; 085 086 import groovy.lang.GroovyClassLoader; 087 import groovy.lang.GroovyRuntimeException; 088 089 /** 090 * Collects all compilation data as it is generated by the compiler system. 091 * Allows additional source units to be added and compilation run again (to 092 * affect only the deltas). 093 * 094 * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a> 095 * @version $Id: CompilationUnit.java,v 1.34 2005/11/19 21:22:12 blackdrag Exp $ 096 */ 097 098 public class CompilationUnit extends ProcessingUnit { 099 100 101 //--------------------------------------------------------------------------- 102 // CONSTRUCTION AND SUCH 103 104 105 protected HashMap sources; // The SourceUnits from which this unit is built 106 protected Map summariesBySourceName; // Summary of each SourceUnit 107 protected Map summariesByPublicClassName; // Summary of each SourceUnit 108 protected Map classSourcesByPublicClassName; // Summary of each Class 109 protected ArrayList names; // Names for each SourceUnit in sources. 110 protected LinkedList queuedSources; 111 112 113 protected CompileUnit ast; // The overall AST for this CompilationUnit. 114 protected ArrayList generatedClasses; // The classes generated during classgen. 115 116 117 protected Verifier verifier; // For use by verify(). 118 119 120 protected ClassCompletionVerifier completionVerifier; // for use by checkClassCompletion 121 122 123 protected boolean debug; // Controls behaviour of classgen() and other routines. 124 protected boolean configured; // Set true after the first configure() operation 125 126 127 protected ClassgenCallback classgenCallback; // A callback for use during classgen() 128 protected ProgressCallback progressCallback; // A callback for use during compile() 129 private ResolveVisitor resolveVisitor; 130 131 132 133 /** 134 * Initializes the CompilationUnit with defaults. 135 */ 136 public CompilationUnit() { 137 this(null, null, null); 138 } 139 140 141 142 /** 143 * Initializes the CompilationUnit with defaults except for class loader. 144 */ 145 public CompilationUnit(GroovyClassLoader loader) { 146 this(null, null, loader); 147 } 148 149 150 151 /** 152 * Initializes the CompilationUnit with no security considerations. 153 */ 154 public CompilationUnit(CompilerConfiguration configuration) { 155 this(configuration, null, null); 156 } 157 158 /** 159 * Initializes the CompilationUnit with a CodeSource for controlling 160 * security stuff and a class loader for loading classes. 161 */ 162 public CompilationUnit(CompilerConfiguration configuration, CodeSource security, GroovyClassLoader loader) { 163 super(configuration, loader, null); 164 this.names = new ArrayList(); 165 this.queuedSources = new LinkedList(); 166 this.sources = new HashMap(); 167 this.summariesBySourceName = new HashMap(); 168 this.summariesByPublicClassName = new HashMap(); 169 this.classSourcesByPublicClassName = new HashMap(); 170 171 this.ast = new CompileUnit(this.classLoader, security, this.configuration); 172 this.generatedClasses = new ArrayList(); 173 174 175 this.verifier = new Verifier(); 176 this.completionVerifier = new ClassCompletionVerifier(); 177 this.resolveVisitor = new ResolveVisitor(this); 178 179 180 this.classgenCallback = null; 181 } 182 183 /** 184 * Configures its debugging mode and classloader classpath from a given compiler configuration. 185 * This cannot be done more than once due to limitations in {@link java.net.URLClassLoader URLClassLoader}. 186 */ 187 public void configure(CompilerConfiguration configuration) { 188 super.configure(configuration); 189 this.debug = configuration.getDebug(); 190 191 if (!this.configured && this.classLoader instanceof GroovyClassLoader) { 192 appendCompilerConfigurationClasspathToClassLoader(configuration, (GroovyClassLoader) this.classLoader); 193 } 194 195 this.configured = true; 196 } 197 198 private void appendCompilerConfigurationClasspathToClassLoader(CompilerConfiguration configuration, GroovyClassLoader classLoader) { 199 for (Iterator iterator = configuration.getClasspath().iterator(); iterator.hasNext(); ) { 200 classLoader.addClasspath((String) iterator.next()); 201 } 202 } 203 204 /** 205 * Returns the CompileUnit that roots our AST. 206 */ 207 public CompileUnit getAST() { 208 return this.ast; 209 } 210 211 /** 212 * Get the source summaries 213 */ 214 public Map getSummariesBySourceName() { 215 return summariesBySourceName; 216 } 217 public Map getSummariesByPublicClassName() { 218 return summariesByPublicClassName; 219 } 220 public Map getClassSourcesByPublicClassName() { 221 return classSourcesByPublicClassName; 222 } 223 224 public boolean isPublicClass(String className) { 225 return summariesByPublicClassName.containsKey(className); 226 } 227 228 229 /** 230 * Get the GroovyClasses generated by compile(). 231 */ 232 public List getClasses() { 233 return generatedClasses; 234 } 235 236 237 /** 238 * Convenience routine to get the first ClassNode, for 239 * when you are sure there is only one. 240 */ 241 public ClassNode getFirstClassNode() { 242 return (ClassNode) ((ModuleNode) this.ast.getModules().get(0)).getClasses().get(0); 243 } 244 245 246 /** 247 * Convenience routine to get the named ClassNode. 248 */ 249 public ClassNode getClassNode(final String name) { 250 final ClassNode[] result = new ClassNode[]{null}; 251 LoopBodyForPrimaryClassNodeOperations handler = new LoopBodyForPrimaryClassNodeOperations() { 252 253 254 public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) { 255 256 257 if (classNode.getName().equals(name)) { 258 259 260 result[0] = classNode; 261 262 263 } 264 265 266 } 267 268 269 }; 270 271 272 try { 273 applyToPrimaryClassNodes(handler,false); 274 } catch (CompilationFailedException e) { 275 if (debug) e.printStackTrace(); 276 } 277 return result[0]; 278 } 279 280 281 282 283 284 //--------------------------------------------------------------------------- 285 // SOURCE CREATION 286 287 288 /** 289 * Adds a set of file paths to the unit. 290 */ 291 public void addSources(String[] paths) { 292 for (int i = 0; i < paths.length; i++) { 293 File file = new File(paths[i]); 294 addSource(file); 295 } 296 } 297 298 299 /** 300 * Adds a set of source files to the unit. 301 */ 302 public void addSources(File[] files) { 303 for (int i = 0; i < files.length; i++) { 304 addSource(files[i]); 305 } 306 } 307 308 309 /** 310 * Adds a source file to the unit. 311 */ 312 public SourceUnit addSource(File file) { 313 return addSource(new SourceUnit(file, configuration, classLoader, getErrorCollector())); 314 } 315 316 317 /** 318 * Adds a source file to the unit. 319 */ 320 public SourceUnit addSource(URL url) { 321 return addSource(new SourceUnit(url, configuration, classLoader,getErrorCollector())); 322 } 323 324 325 /** 326 * Adds a InputStream source to the unit. 327 */ 328 public SourceUnit addSource(String name, InputStream stream) { 329 ReaderSource source = new InputStreamReaderSource(stream, configuration); 330 return addSource(new SourceUnit(name, source, configuration, classLoader, getErrorCollector())); 331 } 332 333 334 /** 335 * Adds a SourceUnit to the unit. 336 */ 337 public SourceUnit addSource(SourceUnit source) { 338 String name = source.getName(); 339 source.setClassLoader(this.classLoader); 340 for (Iterator iter = queuedSources.iterator(); iter.hasNext();) { 341 SourceUnit su = (SourceUnit) iter.next(); 342 if (name.equals(su.getName())) return su; 343 } 344 queuedSources.add(source); 345 return source; 346 } 347 348 349 /** 350 * Returns an iterator on the unit's SourceUnits. 351 */ 352 public Iterator iterator() { 353 return new Iterator() { 354 Iterator nameIterator = names.iterator(); 355 356 357 public boolean hasNext() { 358 return nameIterator.hasNext(); 359 } 360 361 362 public Object next() { 363 String name = (String) nameIterator.next(); 364 return sources.get(name); 365 } 366 367 368 public void remove() { 369 throw new UnsupportedOperationException(); 370 } 371 }; 372 } 373 374 375 /** 376 * Adds a ClassNode directly to the unit (ie. without source). 377 * Used primarily for testing support. 378 */ 379 public void addClassNode(ClassNode node) { 380 ModuleNode module = new ModuleNode(this.ast); 381 this.ast.addModule(module); 382 module.addClass(node); 383 } 384 385 386 //--------------------------------------------------------------------------- 387 // EXTERNAL CALLBACKS 388 389 390 /** 391 * A callback interface you can use to "accompany" the classgen() 392 * code as it traverses the ClassNode tree. You will be called-back 393 * for each primary and inner class. Use setClassgenCallback() before 394 * running compile() to set your callback. 395 */ 396 public static abstract class ClassgenCallback { 397 public abstract void call(ClassVisitor writer, ClassNode node) throws CompilationFailedException; 398 } 399 400 401 /** 402 * Sets a ClassgenCallback. You can have only one, and setting 403 * it to null removes any existing setting. 404 */ 405 public void setClassgenCallback(ClassgenCallback visitor) { 406 this.classgenCallback = visitor; 407 } 408 409 410 /** 411 * A callback interface you can use to get a callback after every 412 * unit of the compile process. You will be called-back with a 413 * ProcessingUnit and a phase indicator. Use setProgressCallback() 414 * before running compile() to set your callback. 415 */ 416 public static abstract class ProgressCallback { 417 418 public abstract void call(ProcessingUnit context, int phase) throws CompilationFailedException; 419 } 420 421 /** 422 * Sets a ProgressCallback. You can have only one, and setting 423 * it to null removes any existing setting. 424 */ 425 public void setProgressCallback(ProgressCallback callback) { 426 this.progressCallback = callback; 427 } 428 429 430 //--------------------------------------------------------------------------- 431 // ACTIONS 432 433 434 /** 435 * Synonym for compile(Phases.ALL). 436 */ 437 public void compile() throws CompilationFailedException { 438 compile(Phases.ALL); 439 } 440 441 442 /** 443 * Compiles the compilation unit from sources. 444 */ 445 public void compile(int throughPhase) throws CompilationFailedException { 446 // 447 // To support delta compilations, we always restart 448 // the compiler. The individual passes are responsible 449 // for not reprocessing old code. 450 gotoPhase(Phases.INITIALIZATION); 451 452 453 do { 454 if (dequeued()) continue; 455 if (throughPhase < Phases.PARSING) break; 456 457 gotoPhase(Phases.PARSING); 458 parse(); 459 460 if (dequeued()) continue; 461 if (throughPhase < Phases.CONVERSION) break; 462 463 gotoPhase(Phases.CONVERSION); 464 convert(); 465 466 if (dequeued()) continue; 467 if (throughPhase < Phases.CLASS_GENERATION) break; 468 469 gotoPhase(Phases.SEMANTIC_ANALYSIS); 470 semanticAnalysis(); 471 472 if (dequeued()) continue; 473 if (throughPhase < Phases.CLASS_GENERATION) break; 474 475 gotoPhase(Phases.CLASS_GENERATION); 476 Iterator modules = this.ast.getModules().iterator(); 477 while (modules.hasNext()) { 478 ModuleNode module = (ModuleNode) modules.next(); 479 module.sortClasses(); 480 } 481 classgen(); 482 483 if (dequeued()) continue; 484 if (throughPhase < Phases.OUTPUT) break; 485 486 gotoPhase(Phases.OUTPUT); 487 output(); 488 489 if (dequeued()) continue; 490 if (throughPhase < Phases.FINALIZATION) break; 491 492 gotoPhase(Phases.FINALIZATION); 493 break; 494 } while (true); 495 } 496 497 /** 498 * Dequeues any source units add through addSource and resets the compiler phase 499 * to initialization. 500 * 501 * Note: this does not mean a file is recompiled. If a SoucreUnit has already passed 502 * a phase it is skipped until a higher phase is reached. 503 * @return TODO 504 * 505 * @throws CompilationFailedException 506 */ 507 protected boolean dequeued() throws CompilationFailedException { 508 boolean dequeue = !queuedSources.isEmpty(); 509 while (!queuedSources.isEmpty()) { 510 SourceUnit su = (SourceUnit) queuedSources.removeFirst(); 511 String name = su.getName(); 512 names.add(name); 513 sources.put(name,su); 514 } 515 if (dequeue) { 516 gotoPhase(Phases.INITIALIZATION); 517 } 518 return dequeue; 519 } 520 521 522 /** 523 * Parses all sources. 524 */ 525 public void parse() throws CompilationFailedException { 526 if (this.phase != Phases.PARSING) { 527 throw new GroovyBugError("CompilationUnit not read for parse()"); 528 } 529 530 applyToSourceUnits(parse); 531 applyToSourceUnits(summarize); 532 533 completePhase(); 534 applyToSourceUnits(mark); 535 } 536 537 538 /** 539 * Runs parse() on a single SourceUnit. 540 */ 541 private LoopBodyForSourceUnitOperations parse = new LoopBodyForSourceUnitOperations() { 542 public void call(SourceUnit source) throws CompilationFailedException { 543 source.parse(); 544 545 546 if (CompilationUnit.this.progressCallback != null) { 547 CompilationUnit.this.progressCallback.call(source, CompilationUnit.this.phase); 548 } 549 } 550 }; 551 552 /** 553 * Adds summary of each class to maps 554 */ 555 private LoopBodyForSourceUnitOperations summarize = new LoopBodyForSourceUnitOperations() { 556 public void call(SourceUnit source) throws CompilationFailedException { 557 SourceSummary sourceSummary = source.getSourceSummary(); 558 if (sourceSummary != null) { 559 summariesBySourceName.put(source.getName(),sourceSummary); 560 List publicClassSources = sourceSummary.getPublicClassSources(); 561 if (publicClassSources == null || publicClassSources.size() == 0) { 562 //todo - is this the best way to handle scripts? 563 summariesByPublicClassName.put("*NoName*",sourceSummary); 564 // nothing to put into classSourcesByClassName as no ClassSource 565 } else { 566 Iterator itr = publicClassSources.iterator(); 567 while (itr.hasNext()) { 568 ClassSource classSource = (ClassSource)itr.next(); 569 summariesByPublicClassName.put(classSource.getName(),sourceSummary); 570 classSourcesByPublicClassName.put(classSource.getName(),classSource); 571 } 572 } 573 } 574 } 575 }; 576 577 /** 578 * Resolves all types 579 */ 580 private LoopBodyForSourceUnitOperations resolve = new LoopBodyForSourceUnitOperations() { 581 public void call(SourceUnit source) throws CompilationFailedException { 582 List classes = source.ast.getClasses(); 583 for (Iterator it = classes.iterator(); it.hasNext();) { 584 ClassNode node = (ClassNode) it.next(); 585 resolveVisitor.startResolving(node,source); 586 } 587 588 } 589 }; 590 591 592 /** 593 * Builds ASTs for all parsed sources. 594 */ 595 public void convert() throws CompilationFailedException { 596 if (this.phase != Phases.CONVERSION) { 597 throw new GroovyBugError("CompilationUnit not ready for convert()"); 598 } 599 600 applyToSourceUnits(convert); 601 602 completePhase(); 603 applyToSourceUnits(mark); 604 } 605 606 /** 607 * Runs convert() on a single SourceUnit. 608 */ 609 private LoopBodyForSourceUnitOperations convert = new LoopBodyForSourceUnitOperations() { 610 public void call(SourceUnit source) throws CompilationFailedException { 611 source.convert(); 612 CompilationUnit.this.ast.addModule(source.getAST()); 613 614 615 if (CompilationUnit.this.progressCallback != null) { 616 CompilationUnit.this.progressCallback.call(source, CompilationUnit.this.phase); 617 } 618 } 619 }; 620 621 public void semanticAnalysis() throws CompilationFailedException { 622 if (this.phase != Phases.SEMANTIC_ANALYSIS) { 623 throw new GroovyBugError("CompilationUnit not ready for semanticAnalysis()"); 624 } 625 626 applyToSourceUnits(resolve); 627 628 completePhase(); 629 applyToSourceUnits(mark); 630 } 631 632 /** 633 * Expands and canonicalizes the ASTs generated during 634 * parsing and conversion, then generates classes. 635 */ 636 public void classgen() throws CompilationFailedException { 637 if (this.phase != Phases.CLASS_GENERATION) { 638 throw new GroovyBugError("CompilationUnit not ready for classgen()"); 639 } 640 641 642 applyToPrimaryClassNodes(classgen,true); 643 644 completePhase(); 645 applyToSourceUnits(mark); 646 647 // 648 // Callback progress, if necessary 649 650 651 if (this.progressCallback != null) { 652 this.progressCallback.call(this, CompilationUnit.this.phase); 653 } 654 } 655 656 /** 657 * Runs classgen() on a single ClassNode. 658 */ 659 private LoopBodyForPrimaryClassNodeOperations classgen = new LoopBodyForPrimaryClassNodeOperations() { 660 public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { 661 662 // 663 // Run the Verifier on the outer class 664 // 665 try { 666 verifier.visitClass(classNode); 667 } catch (GroovyRuntimeException rpe) { 668 ASTNode node = rpe.getNode(); 669 getErrorCollector().addError( 670 new SyntaxException(rpe.getMessage(),null,node.getLineNumber(),node.getColumnNumber()), 671 source 672 ); 673 } 674 675 // 676 // do scope checking 677 // 678 if (source!=null && (!classNode.isSynthetic()) && (!"false".equals(System.getProperty("groovy.jsr.check")))) { 679 JSRVariableScopeCodeVisitor scopeVisitor = new JSRVariableScopeCodeVisitor(null ,source); 680 scopeVisitor.visitClass(classNode); 681 source.getErrorCollector().failIfErrors(); 682 } 683 684 // 685 // Prep the generator machinery 686 // 687 ClassVisitor visitor = createClassVisitor(); 688 689 690 String sourceName = (source == null ? classNode.getModule().getDescription() : source.getName()); 691 // only show the file name and its extension like javac does in its stacktraces rather than the full path 692 // also takes care of both \ and / depending on the host compiling environment 693 if (sourceName != null) 694 sourceName = sourceName.substring(Math.max(sourceName.lastIndexOf('\\'), sourceName.lastIndexOf('/')) + 1); 695 ClassGenerator generator = new AsmClassGenerator(context, visitor, classLoader, sourceName); 696 697 698 // 699 // Run the generation and create the class (if required) 700 // 701 generator.visitClass(classNode); 702 completionVerifier.visitClass(classNode); 703 704 705 byte[] bytes = ((ClassWriter) visitor).toByteArray(); 706 generatedClasses.add(new GroovyClass(classNode.getName(), bytes)); 707 708 /* 709 byte[] bytes = ((ClassWriter) visitor).toByteArray(); 710 FileOutputStream fos; 711 try { 712 fos = new FileOutputStream(classNode.getType().getName()); 713 fos.write(bytes); 714 fos.close(); 715 } catch (IOException e) { 716 e.printStackTrace(); 717 }*/ 718 719 720 // 721 // Handle any callback that's been set 722 723 724 if (CompilationUnit.this.classgenCallback != null) { 725 classgenCallback.call(visitor, classNode); 726 } 727 728 729 // 730 // Recurse for inner classes 731 732 LinkedList innerClasses = generator.getInnerClasses(); 733 while (!innerClasses.isEmpty()) { 734 classgen.call(source, context, (ClassNode) innerClasses.removeFirst()); 735 } 736 } 737 }; 738 739 740 protected ClassVisitor createClassVisitor() { 741 /** avoid runtime dependency on asm util 742 ClassVisitor visitor; 743 if( debug ) 744 { 745 visitor = new DumpClassVisitor(output); 746 } 747 else 748 { 749 visitor = new ClassWriter(true); 750 } 751 return visitor; 752 */ 753 return new ClassWriter(true); 754 } 755 756 757 /** 758 * Outputs the generated class files to permanent storage. 759 */ 760 public void output() throws CompilationFailedException { 761 if (this.phase != Phases.OUTPUT && !(this.phase == Phases.CLASS_GENERATION && this.phaseComplete)) { 762 throw new GroovyBugError("CompilationUnit not ready for output()"); 763 } 764 765 766 boolean failures = false; 767 768 769 Iterator iterator = this.generatedClasses.iterator(); 770 while (iterator.hasNext()) { 771 // 772 // Get the class and calculate its filesystem name 773 // 774 GroovyClass gclass = (GroovyClass) iterator.next(); 775 String name = gclass.getName().replace('.', File.separatorChar) + ".class"; 776 File path = new File(configuration.getTargetDirectory(), name); 777 778 779 // 780 // Ensure the path is ready for the file 781 // 782 File directory = path.getParentFile(); 783 if (directory != null && !directory.exists()) { 784 directory.mkdirs(); 785 } 786 787 788 // 789 // Create the file and write out the data 790 // 791 byte[] bytes = gclass.getBytes(); 792 793 FileOutputStream stream = null; 794 try { 795 stream = new FileOutputStream(path); 796 stream.write(bytes, 0, bytes.length); 797 } catch (IOException e) { 798 getErrorCollector().addError(Message.create(e.getMessage(),this)); 799 failures = true; 800 } finally { 801 if (stream != null) { 802 try { 803 stream.close(); 804 } catch (Exception e) { 805 } 806 } 807 } 808 } 809 810 811 getErrorCollector().failIfErrors(); 812 813 814 completePhase(); 815 applyToSourceUnits(mark); 816 817 818 // 819 // Callback progress, if necessary 820 // 821 if (CompilationUnit.this.progressCallback != null) { 822 CompilationUnit.this.progressCallback.call(this, this.phase); 823 } 824 } 825 826 //--------------------------------------------------------------------------- 827 // PHASE HANDLING 828 829 830 /** 831 * Updates the phase marker on all sources. 832 */ 833 protected void mark() throws CompilationFailedException { 834 applyToSourceUnits(mark); 835 } 836 837 838 /** 839 * Marks a single SourceUnit with the current phase, 840 * if it isn't already there yet. 841 */ 842 private LoopBodyForSourceUnitOperations mark = new LoopBodyForSourceUnitOperations() { 843 public void call(SourceUnit source) throws CompilationFailedException { 844 if (source.phase < phase) { 845 source.gotoPhase(phase); 846 } 847 848 849 if (source.phase == phase && phaseComplete && !source.phaseComplete) { 850 source.completePhase(); 851 } 852 } 853 }; 854 855 856 857 858 859 //--------------------------------------------------------------------------- 860 // LOOP SIMPLIFICATION FOR SourceUnit OPERATIONS 861 862 863 /** 864 * An callback interface for use in the applyToSourceUnits loop driver. 865 */ 866 public abstract class LoopBodyForSourceUnitOperations { 867 public abstract void call(SourceUnit source) throws CompilationFailedException; 868 } 869 870 871 /** 872 * A loop driver for applying operations to all SourceUnits. 873 * Automatically skips units that have already been processed 874 * through the current phase. 875 */ 876 public void applyToSourceUnits(LoopBodyForSourceUnitOperations body) throws CompilationFailedException { 877 boolean failures = false; 878 879 Iterator keys = names.iterator(); 880 while (keys.hasNext()) { 881 String name = (String) keys.next(); 882 SourceUnit source = (SourceUnit) sources.get(name); 883 if ( (source.phase < phase) || (source.phase == phase && !source.phaseComplete)) { 884 try { 885 body.call(source); 886 } catch (CompilationFailedException e) { 887 throw e; 888 } catch (Exception e) { 889 throw new GroovyBugError(e); 890 } 891 } 892 } 893 894 895 getErrorCollector().failIfErrors(); 896 } 897 898 899 //--------------------------------------------------------------------------- 900 // LOOP SIMPLIFICATION FOR PRIMARY ClassNode OPERATIONS 901 902 903 904 /** 905 * An callback interface for use in the applyToSourceUnits loop driver. 906 */ 907 public abstract class LoopBodyForPrimaryClassNodeOperations { 908 public abstract void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException; 909 } 910 911 912 private List getPrimaryClassNodes(boolean sort) { 913 ArrayList unsorted = new ArrayList(); 914 Iterator modules = this.ast.getModules().iterator(); 915 while (modules.hasNext()) { 916 ModuleNode module = (ModuleNode) modules.next(); 917 918 Iterator classNodes = module.getClasses().iterator(); 919 while (classNodes.hasNext()) { 920 ClassNode classNode = (ClassNode) classNodes.next(); 921 unsorted.add(classNode); 922 } 923 } 924 925 if(sort==false) return unsorted; 926 927 int[] index = new int[unsorted.size()]; 928 { 929 int i = 0; 930 for (Iterator iter = unsorted.iterator(); iter.hasNext(); i++) { 931 ClassNode element = (ClassNode) iter.next(); 932 int count = 0; 933 while (element!=null){ 934 count++; 935 element = element.getSuperClass(); 936 } 937 index[i] = count; 938 } 939 } 940 941 ArrayList sorted = new ArrayList(unsorted.size()); 942 int start = 0; 943 for (int i=0; i<index.length; i++) { 944 int min = -1; 945 for (int j=0; j<index.length; j++) { 946 if (index[j]==-1) continue; 947 if (min==-1) { 948 min = j; 949 } else if (index[j]<index[min]) { 950 min = j; 951 } 952 } 953 sorted.add(unsorted.get(min)); 954 index[min] = -1; 955 } 956 957 return sorted; 958 } 959 960 /** 961 * A loop driver for applying operations to all primary ClassNodes in 962 * our AST. Automatically skips units that have already been processed 963 * through the current phase. 964 */ 965 public void applyToPrimaryClassNodes(LoopBodyForPrimaryClassNodeOperations body,boolean sort) throws CompilationFailedException { 966 boolean failures = false; 967 968 Iterator classNodes = getPrimaryClassNodes(sort).iterator(); 969 while (classNodes.hasNext()) { 970 try { 971 ClassNode classNode = (ClassNode) classNodes.next(); 972 SourceUnit context = classNode.getModule().getContext(); 973 if (context == null || context.phase <= phase) { 974 body.call(context, new GeneratorContext(this.ast), classNode); 975 } 976 } catch (CompilationFailedException e) { 977 // fall thorugh, getErrorREporter().failIfErrors() will triger 978 } catch (NullPointerException npe){ 979 throw npe; 980 } catch (Exception e) { 981 failures = true; 982 983 // check the exception for a nested compilation exception 984 ErrorCollector nestedCollector = null; 985 for (Throwable next = e.getCause(); next!=e && next!=null; next=next.getCause()) { 986 if (!(next instanceof MultipleCompilationErrorsException)) continue; 987 MultipleCompilationErrorsException mcee = (MultipleCompilationErrorsException) next; 988 nestedCollector = mcee.collector; 989 break; 990 } 991 992 if (nestedCollector!=null) { 993 getErrorCollector().addCollectorContents(nestedCollector); 994 } else { 995 getErrorCollector().addError(new ExceptionMessage(e,configuration.getDebug(),this)); 996 } 997 } 998 } 999 1000 getErrorCollector().failIfErrors(); 1001 } 1002 1003 }