View Javadoc

1   /*
2    $Id: CompilationUnit.java,v 1.34 2005/11/19 21:22:12 blackdrag Exp $
3   
4   
5    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
6   
7   
8    Redistribution and use of this software and associated documentation
9    ("Software"), with or without modification, are permitted provided
10   that the following conditions are met:
11  
12   1. Redistributions of source code must retain copyright
13      statements and notices.  Redistributions must also contain a
14      copy of this document.
15  
16  
17   2. Redistributions in binary form must reproduce the
18      above copyright notice, this list of conditions and the
19      following disclaimer in the documentation and/or other
20      materials provided with the distribution.
21  
22  
23   3. The name "groovy" must not be used to endorse or promote
24      products derived from this Software without prior written
25      permission of The Codehaus.  For written permission,
26      please contact info@codehaus.org.
27  
28  
29   4. Products derived from this Software may not be called "groovy"
30      nor may "groovy" appear in their names without prior written
31      permission of The Codehaus. "groovy" is a registered
32      trademark of The Codehaus.
33  
34  
35   5. Due credit should be given to The Codehaus -
36      http://groovy.codehaus.org/
37  
38  
39   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
40   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
41   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
42   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
43   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
44   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
45   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
46   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50   OF THE POSSIBILITY OF SUCH DAMAGE.
51   */
52  
53  
54  package org.codehaus.groovy.control;
55  
56  import java.io.File;
57  import java.io.FileOutputStream;
58  import java.io.IOException;
59  import java.io.InputStream;
60  import java.net.URL;
61  import java.security.CodeSource;
62  import java.util.*;
63  
64  import org.codehaus.groovy.GroovyBugError;
65  import org.codehaus.groovy.ast.ASTNode;
66  import org.codehaus.groovy.ast.ClassNode;
67  import org.codehaus.groovy.ast.CompileUnit;
68  import org.codehaus.groovy.ast.ModuleNode;
69  import org.codehaus.groovy.classgen.AsmClassGenerator;
70  import org.codehaus.groovy.classgen.ClassCompletionVerifier;
71  import org.codehaus.groovy.classgen.ClassGenerator;
72  import org.codehaus.groovy.classgen.GeneratorContext;
73  import org.codehaus.groovy.classgen.JSRVariableScopeCodeVisitor;
74  import org.codehaus.groovy.classgen.Verifier;
75  import org.codehaus.groovy.control.io.InputStreamReaderSource;
76  import org.codehaus.groovy.control.io.ReaderSource;
77  import org.codehaus.groovy.control.messages.ExceptionMessage;
78  import org.codehaus.groovy.control.messages.Message;
79  import org.codehaus.groovy.syntax.SyntaxException;
80  import org.codehaus.groovy.syntax.ClassSource;
81  import org.codehaus.groovy.syntax.SourceSummary;
82  import org.codehaus.groovy.tools.GroovyClass;
83  import org.objectweb.asm.ClassVisitor;
84  import org.objectweb.asm.ClassWriter;
85  
86  import groovy.lang.GroovyClassLoader;
87  import groovy.lang.GroovyRuntimeException;
88  
89  /***
90   * Collects all compilation data as it is generated by the compiler system.
91   * Allows additional source units to be added and compilation run again (to
92   * affect only the deltas).
93   *
94   * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
95   * @version $Id: CompilationUnit.java,v 1.34 2005/11/19 21:22:12 blackdrag Exp $
96   */
97  
98  public class CompilationUnit extends ProcessingUnit {
99  
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 }