View Javadoc

1   /*
2    $Id: CompilationUnit.java,v 1.23 2005/06/13 16:21:54 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  
57  import org.codehaus.groovy.GroovyBugError;
58  
59  import org.codehaus.groovy.ast.ASTNode;
60  
61  import org.codehaus.groovy.ast.ClassNode;
62  
63  import org.codehaus.groovy.ast.CompileUnit;
64  import org.codehaus.groovy.ast.ModuleNode;
65  import org.codehaus.groovy.classgen.*;
66  import org.codehaus.groovy.control.io.InputStreamReaderSource;
67  import org.codehaus.groovy.control.io.ReaderSource;
68  import org.codehaus.groovy.control.messages.ExceptionMessage;
69  import org.codehaus.groovy.control.messages.Message;
70  import org.codehaus.groovy.syntax.SyntaxException;
71  import org.codehaus.groovy.tools.GroovyClass;
72  import org.objectweb.asm.ClassVisitor;
73  import org.objectweb.asm.ClassWriter;
74  
75  
76  import groovy.lang.GroovyRuntimeException;
77  
78  
79  import java.io.*;
80  import java.net.MalformedURLException;
81  import java.net.URL;
82  import java.security.CodeSource;
83  import java.util.*;
84  
85  
86  
87  
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.23 2005/06/13 16:21:54 blackdrag Exp $
96   */
97  
98  
99  public class CompilationUnit extends ProcessingUnit {
100 
101 
102     //---------------------------------------------------------------------------
103     // CONSTRUCTION AND SUCH
104 
105 
106     protected HashMap sources;    // The SourceUnits from which this unit is built
107     protected ArrayList names;      // Names for each SourceUnit in sources.
108 
109 
110     protected CompileUnit ast;        // The overall AST for this CompilationUnit.
111     protected ArrayList classes;    // The classes generated during classgen.
112 
113 
114     protected Verifier verifier;   // For use by verify().
115 
116 
117     protected ClassCompletionVerifier completionVerifier; // for use by checkClassCompletion
118 
119 
120     protected boolean debug;      // Controls behaviour of classgen() and other routines.
121     protected boolean configured; // Set true after the first configure() operation
122 
123 
124     protected ClassgenCallback classgenCallback;  // A callback for use during classgen()
125     protected ProgressCallback progressCallback;  // A callback for use during compile()
126 
127 
128 
129     /***
130      * Initializes the CompilationUnit with defaults.
131      */
132     public CompilationUnit() {
133         this(null, null, null);
134     }
135 
136 
137 
138     /***
139      * Initializes the CompilationUnit with defaults except for class loader.
140      */
141     public CompilationUnit(ClassLoader loader) {
142         this(null, null, loader);
143     }
144 
145 
146 
147     /***
148      * Initializes the CompilationUnit with no security considerations.
149      */
150     public CompilationUnit(CompilerConfiguration configuration) {
151         this(configuration, null, null);
152     }
153 
154     /***
155      * Initializes the CompilationUnit with a CodeSource for controlling
156      * security stuff and a class loader for loading classes.
157      */
158     public CompilationUnit(CompilerConfiguration configuration, CodeSource security, ClassLoader loader) {
159         super(configuration, loader, null);        
160         
161         this.names = new ArrayList();
162         this.sources = new HashMap();
163 
164 
165         this.ast = new CompileUnit(this.classLoader, security, this.configuration);
166         this.classes = new ArrayList();
167 
168 
169         this.verifier = new Verifier();
170         this.completionVerifier = new ClassCompletionVerifier();
171 
172 
173         this.classgenCallback = null;
174     }
175 
176 
177     /***
178      * Reconfigures the CompilationUnit.
179      */
180     public void configure(CompilerConfiguration configuration) {
181         super.configure(configuration);
182         this.debug = configuration.getDebug();
183 
184 
185         //
186         // Configure our class loader's classpath, if it is of
187         // a configurable type.  We can't reconfigure it,
188         // unfortunately, due to limitations in URLClassLoader.
189         if (!this.configured && this.classLoader instanceof CompilerClassLoader) {
190             CompilerClassLoader loader = (CompilerClassLoader) this.classLoader;
191 
192 
193             Iterator iterator = configuration.getClasspath().iterator();
194             while (iterator.hasNext()) {
195                 try {
196                     this.configured = true;
197                     loader.addPath((String) iterator.next());
198                 } catch (MalformedURLException e) {
199                     throw new ConfigurationException(e);
200                 }
201             }
202         }
203     }
204 
205 
206     /***
207      * Returns the CompileUnit that roots our AST.
208      */
209     public CompileUnit getAST() {
210         return this.ast;
211     }
212 
213 
214     /***
215      * Get the GroovyClasses generated by compile().
216      */
217 
218 
219     public List getClasses() {
220         return classes;
221     }
222 
223 
224     /***
225      * Convenience routine to get the first ClassNode, for
226      * when you are sure there is only one.
227      */
228     public ClassNode getFirstClassNode() {
229         return (ClassNode) ((ModuleNode) this.ast.getModules().get(0)).getClasses().get(0);
230     }
231 
232 
233     /***
234      * Convenience routine to get the named ClassNode.
235      */
236     public ClassNode getClassNode(final String name) {
237         final ClassNode[] result = new ClassNode[]{null};
238         LoopBodyForPrimaryClassNodeOperations handler = new LoopBodyForPrimaryClassNodeOperations() {
239 
240 
241             public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) {
242 
243 
244                 if (classNode.getName().equals(name)) {
245 
246 
247                     result[0] = classNode;
248 
249 
250                 }
251 
252 
253             }
254 
255 
256         };
257 
258 
259         try {
260             applyToPrimaryClassNodes(handler);
261         } catch (CompilationFailedException e) {
262             if (debug) e.printStackTrace();
263         }
264         return result[0];
265     }
266 
267 
268 
269 
270 
271     //---------------------------------------------------------------------------
272     // SOURCE CREATION
273 
274 
275     /***
276      * Adds a set of file paths to the unit.
277      */
278     public void addSources(String[] paths) {
279         for (int i = 0; i < paths.length; i++) {
280             File file = new File(paths[i]);
281             addSource(file);
282         }
283     }
284 
285 
286     /***
287      * Adds a set of source files to the unit.
288      */
289     public void addSources(File[] files) {
290         for (int i = 0; i < files.length; i++) {
291             addSource(files[i]);
292         }
293     }
294 
295 
296     /***
297      * Adds a source file to the unit.
298      */
299     public SourceUnit addSource(File file) {
300         return addSource(new SourceUnit(file, configuration, classLoader, getErrorCollector()));
301     }
302 
303 
304     /***
305      * Adds a source file to the unit.
306      */
307     public SourceUnit addSource(URL url) {
308         return addSource(new SourceUnit(url, configuration, classLoader,getErrorCollector()));
309     }
310 
311 
312     /***
313      * Adds a InputStream source to the unit.
314      */
315     public SourceUnit addSource(String name, InputStream stream) {
316         ReaderSource source = new InputStreamReaderSource(stream, configuration);
317         return addSource(new SourceUnit(name, source, configuration, classLoader, getErrorCollector()));
318     }
319 
320 
321     /***
322      * Adds a SourceUnit to the unit.
323      */
324     public SourceUnit addSource(SourceUnit source) {
325         String name = source.getName();
326 
327 
328         source.setClassLoader(this.classLoader);
329 
330 
331         names.add(name);
332         sources.put(name, source);
333 
334 
335         return source;
336     }
337 
338 
339     /***
340      * Returns an iterator on the unit's SourceUnits.
341      */
342     public Iterator iterator() {
343         return new Iterator() {
344             Iterator nameIterator = names.iterator();
345 
346 
347             public boolean hasNext() {
348                 return nameIterator.hasNext();
349             }
350 
351 
352             public Object next() {
353                 String name = (String) nameIterator.next();
354                 return sources.get(name);
355             }
356 
357 
358             public void remove() {
359                 throw new UnsupportedOperationException();
360             }
361         };
362     }
363 
364 
365     /***
366      * Adds a ClassNode directly to the unit (ie. without source).
367      * Used primarily for testing support.
368      */
369     public void addClassNode(ClassNode node) {
370         ModuleNode module = new ModuleNode(this.ast);
371         this.ast.addModule(module);
372         module.addClass(node);
373     }
374 
375 
376 
377 
378 
379     //---------------------------------------------------------------------------
380     // EXTERNAL CALLBACKS
381 
382 
383     /***
384      * A callback interface you can use to "accompany" the classgen()
385      * code as it traverses the ClassNode tree.  You will be called-back
386      * for each primary and inner class.  Use setClassgenCallback() before
387      * running compile() to set your callback.
388      */
389     public static abstract class ClassgenCallback {
390         public abstract void call(ClassVisitor writer, ClassNode node) throws CompilationFailedException;
391     }
392 
393 
394     /***
395      * Sets a ClassgenCallback.  You can have only one, and setting
396      * it to null removes any existing setting.
397      */
398     public void setClassgenCallback(ClassgenCallback visitor) {
399         this.classgenCallback = visitor;
400     }
401 
402 
403     /***
404      * A callback interface you can use to get a callback after every
405      * unit of the compile process.  You will be called-back with a
406      * ProcessingUnit and a phase indicator.  Use setProgressCallback()
407      * before running compile() to set your callback.
408      */
409     public static abstract class ProgressCallback {
410 
411         public abstract void call(ProcessingUnit context, int phase) throws CompilationFailedException;
412     }
413 
414     /***
415      * Sets a ProgressCallback.  You can have only one, and setting
416      * it to null removes any existing setting.
417      */
418     public void setProgressCallback(ProgressCallback callback) {
419         this.progressCallback = callback;
420     }
421 
422 
423     //---------------------------------------------------------------------------
424     // ACTIONS
425 
426 
427     /***
428      * Synonym for compile(Phases.ALL).
429      */
430     public void compile() throws CompilationFailedException {
431         compile(Phases.ALL);
432     }
433 
434 
435     /***
436      * Compiles the compilation unit from sources.
437      */
438     public void compile(int throughPhase) throws CompilationFailedException {
439         //
440         // To support delta compilations, we always restart
441         // the compiler.  The individual passes are responsible
442         // for not reprocessing old code.
443         gotoPhase(Phases.INITIALIZATION);
444 
445 
446         do {
447             if (throughPhase < Phases.PARSING) {
448                 break;
449             }
450             gotoPhase(Phases.PARSING);
451             parse();
452 
453 
454             if (throughPhase < Phases.CONVERSION) {
455                 break;
456             }
457 
458 
459             gotoPhase(Phases.CONVERSION);
460             convert();
461 
462 
463             if (throughPhase < Phases.CLASS_GENERATION) {
464                 break;
465             }
466 
467 
468             gotoPhase(Phases.CLASS_GENERATION);
469             classgen();
470 
471 
472             if (throughPhase < Phases.OUTPUT) {
473                 break;
474             }
475 
476 
477             gotoPhase(Phases.OUTPUT);
478             output();
479 
480 
481             if (throughPhase < Phases.FINALIZATION) {
482                 break;
483             }
484 
485 
486             gotoPhase(Phases.FINALIZATION);
487         } while (false);
488     }
489 
490 
491 
492     /***
493      * Parses all sources.
494      */
495     public void parse() throws CompilationFailedException {
496         if (this.phase != Phases.PARSING) {
497             throw new GroovyBugError("CompilationUnit not read for parse()");
498         }
499 
500 
501         applyToSourceUnits(parse);
502         completePhase();
503         applyToSourceUnits(mark);
504     }
505 
506 
507     /***
508      * Runs parse() on a single SourceUnit.
509      */
510     private LoopBodyForSourceUnitOperations parse = new LoopBodyForSourceUnitOperations() {
511         public void call(SourceUnit source) throws CompilationFailedException {
512             source.parse();
513 
514 
515             if (CompilationUnit.this.progressCallback != null) {
516                 CompilationUnit.this.progressCallback.call(source, CompilationUnit.this.phase);
517             }
518         }
519     };
520 
521 
522     /***
523      * Builds ASTs for all parsed sources.
524      */
525     public void convert() throws CompilationFailedException {
526         if (this.phase != Phases.CONVERSION) {
527             throw new GroovyBugError("CompilationUnit not ready for convert()");
528         }
529 
530 
531         applyToSourceUnits(convert);
532 
533 
534         completePhase();
535         applyToSourceUnits(mark);
536     }
537 
538 
539     /***
540      * Runs convert() on a single SourceUnit.
541      */
542     private LoopBodyForSourceUnitOperations convert = new LoopBodyForSourceUnitOperations() {
543         public void call(SourceUnit source) throws CompilationFailedException {
544             source.convert();
545             CompilationUnit.this.ast.addModule(source.getAST());
546 
547 
548             if (CompilationUnit.this.progressCallback != null) {
549                 CompilationUnit.this.progressCallback.call(source, CompilationUnit.this.phase);
550             }
551         }
552     };
553 
554 
555     /***
556      * Expands and canonicalizes the ASTs generated during
557      * parsing and conversion, then generates classes.
558      */
559     public void classgen() throws CompilationFailedException {
560         if (this.phase != Phases.CLASS_GENERATION) {
561             throw new GroovyBugError("CompilationUnit not ready for classgen()");
562         }
563 
564         applyToPrimaryClassNodes(classgen);
565 
566         completePhase();
567         applyToSourceUnits(mark);
568 
569         //
570         // Callback progress, if necessary
571 
572 
573         if (this.progressCallback != null) {
574             this.progressCallback.call(this, CompilationUnit.this.phase);
575         }
576     }
577  
578     /***
579      * Runs classgen() on a single ClassNode.
580      */
581     private LoopBodyForPrimaryClassNodeOperations classgen = new LoopBodyForPrimaryClassNodeOperations() {
582         public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
583             //
584             // Run the Verifier on the outer class
585             //
586             try {
587                 verifier.visitClass(classNode);
588             } catch (GroovyRuntimeException rpe) {
589                 ASTNode node = rpe.getNode();
590                 getErrorCollector().addError(
591                         new SyntaxException(rpe.getMessage(),null,node.getLineNumber(),node.getColumnNumber()),
592                         source
593                 );
594             }          
595             
596             //
597             // do scoping 
598             //
599             if (source!=null && (!classNode.isSynthetic()) && (!"false".equals(System.getProperty("groovy.jsr.check")))) {
600                 JSRVariableScopeCodeVisitor scopeVisitor = new JSRVariableScopeCodeVisitor(null ,source);
601                 scopeVisitor.visitClass(classNode);
602                 source.getErrorCollector().failIfErrors();
603             }  
604 
605             //
606             // Prep the generator machinery
607             //
608             ClassVisitor visitor = createClassVisitor();
609 
610 
611             String sourceName = (source == null ? classNode.getModule().getDescription() : source.getName());
612             ClassGenerator generator = new AsmClassGenerator(context, visitor, classLoader, sourceName);
613 
614 
615             //
616             // Run the generation and create the class (if required)
617             //
618             generator.visitClass(classNode);
619             completionVerifier.visitClass(classNode);
620 
621 
622             if (!debug) {
623                 byte[] bytes = ((ClassWriter) visitor).toByteArray();
624                 /* this. */classes.add(new GroovyClass(classNode.getName(), bytes));
625             }
626 
627 
628             //
629             // Handle any callback that's been set
630 
631 
632             if (CompilationUnit.this.classgenCallback != null) {
633                 classgenCallback.call(visitor, classNode);
634             }
635 
636 
637             //
638             // Recurse for inner classes
639 
640             LinkedList innerClasses = generator.getInnerClasses();
641             while (!innerClasses.isEmpty()) {
642                 classgen.call(source, context, (ClassNode) innerClasses.removeFirst());
643             }
644         }
645     };
646 
647 
648     protected ClassVisitor createClassVisitor() {
649         /*** avoid runtime dependency on asm util
650          ClassVisitor visitor;
651          if( debug )
652          {
653          visitor = new DumpClassVisitor(output);
654          }
655          else
656          {
657          visitor = new ClassWriter(true);
658          }
659          return visitor;
660          */
661         return new ClassWriter(true);
662     }
663 
664 
665     /***
666      * Outputs the generated class files to permanent storage.
667      */
668     public void output() throws CompilationFailedException {
669         if (this.phase != Phases.OUTPUT && !(this.phase == Phases.CLASS_GENERATION && this.phaseComplete)) {
670             throw new GroovyBugError("CompilationUnit not ready for output()");
671         }
672 
673 
674         boolean failures = false;
675 
676 
677         Iterator iterator = this.classes.iterator();
678         while (iterator.hasNext()) {
679             //
680             // Get the class and calculate its filesystem name
681 
682 
683             GroovyClass gclass = (GroovyClass) iterator.next();
684             String name = gclass.getName().replace('.', File.separatorChar) + ".class";
685             File path = new File(configuration.getTargetDirectory(), name);
686 
687 
688             //
689             // Ensure the path is ready for the file
690 
691 
692             File directory = path.getParentFile();
693             if (directory != null && !directory.exists()) {
694                 directory.mkdirs();
695             }
696 
697 
698             //
699             // Create the file and write out the data
700 
701             byte[] bytes = gclass.getBytes();
702 
703 
704             FileOutputStream stream = null;
705             try {
706                 stream = new FileOutputStream(path);
707                 stream.write(bytes, 0, bytes.length);
708             } catch (IOException e) {
709                 getErrorCollector().addError(Message.create(e.getMessage(),this));
710                 failures = true;
711             } finally {
712                 if (stream != null) {
713                     try {
714                         stream.close();
715                     } catch (Exception e) {
716                     }
717                 }
718             }
719         }
720 
721 
722         getErrorCollector().failIfErrors();
723 
724 
725         completePhase();
726         applyToSourceUnits(mark);
727 
728 
729         //
730         // Callback progress, if necessary
731 
732 
733         if (CompilationUnit.this.progressCallback != null) {
734             CompilationUnit.this.progressCallback.call(this, this.phase);
735         }
736     }
737 
738     //---------------------------------------------------------------------------
739     // PHASE HANDLING
740 
741 
742     /***
743      * Updates the phase marker on all sources.
744      */
745     protected void mark() throws CompilationFailedException {
746         applyToSourceUnits(mark);
747     }
748 
749 
750     /***
751      * Marks a single SourceUnit with the current phase,
752      * if it isn't already there yet.
753      */
754     private LoopBodyForSourceUnitOperations mark = new LoopBodyForSourceUnitOperations() {
755         public void call(SourceUnit source) throws CompilationFailedException {
756             if (source.phase < phase) {
757                 source.gotoPhase(phase);
758             }
759 
760 
761             if (source.phase == phase && phaseComplete && !source.phaseComplete) {
762                 source.completePhase();
763             }
764         }
765     };
766 
767 
768 
769 
770 
771     //---------------------------------------------------------------------------
772     // LOOP SIMPLIFICATION FOR SourceUnit OPERATIONS
773 
774 
775     /***
776      * An callback interface for use in the applyToSourceUnits loop driver.
777      */
778     public abstract class LoopBodyForSourceUnitOperations {
779         public abstract void call(SourceUnit source) throws CompilationFailedException;
780     }
781 
782 
783 
784 
785     /***
786      * A loop driver for applying operations to all SourceUnits.
787      * Automatically skips units that have already been processed
788      * through the current phase.
789      */
790     public void applyToSourceUnits(LoopBodyForSourceUnitOperations body) throws CompilationFailedException {
791         boolean failures = false;
792 
793 
794         Iterator keys = names.iterator();
795         while (keys.hasNext()) {
796             String name = (String) keys.next();
797             SourceUnit source = (SourceUnit) sources.get(name);
798             if (source.phase <= phase) {
799                 try {
800                     body.call(source);
801                 } catch (CompilationFailedException e) {
802                     throw e;
803                 } catch (Exception e) {
804                     throw new GroovyBugError(e);
805                 }
806             }
807         }
808 
809 
810         getErrorCollector().failIfErrors();
811     }
812     
813 
814     //---------------------------------------------------------------------------
815     // LOOP SIMPLIFICATION FOR PRIMARY ClassNode OPERATIONS
816 
817 
818 
819     /***
820      * An callback interface for use in the applyToSourceUnits loop driver.
821      */
822     public abstract class LoopBodyForPrimaryClassNodeOperations {
823         public abstract void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException;
824     }
825 
826 
827 
828     /***
829      * A loop driver for applying operations to all primary ClassNodes in
830      * our AST.  Automatically skips units that have already been processed
831      * through the current phase.
832      */
833     public void applyToPrimaryClassNodes(LoopBodyForPrimaryClassNodeOperations body) throws CompilationFailedException {
834         boolean failures = false;
835 
836 
837         Iterator modules = this.ast.getModules().iterator();
838         while (modules.hasNext()) {
839             ModuleNode module = (ModuleNode) modules.next();
840 
841 
842             try {
843                 Iterator classNodes = module.getClasses().iterator();
844                 while (classNodes.hasNext()) {
845                     ClassNode classNode = (ClassNode) classNodes.next();
846                     SourceUnit context = module.getContext();
847                     if (context == null || context.phase <= phase) {
848                         body.call(module.getContext(), new GeneratorContext(this.ast), classNode);
849                     }
850                 }
851             } catch (CompilationFailedException e) {
852                 // fall thorugh, getErrorREporter().failIfErrors() will triger
853             } catch (Exception e) {
854                 failures = true;
855 //                String msg = e.getMessage();
856 //                if (e instanceof RuntimeParserException) {
857 //                    RuntimeParserException rpe = (RuntimeParserException) e;
858 //                    ASTNode node = rpe.getNode();
859 //                    msg += ". The probable error location: [" + node.getLineNumber() + ":" + node.getColumnNumber() + "]";
860 //                }
861                 
862                 // check the exception for a nested compilation exception
863                 ErrorCollector nestedCollector = null;
864                 for (Throwable next = e.getCause(); next!=e && next!=null; next=next.getCause()) {
865                     if (!(next instanceof MultipleCompilationErrorsException)) continue;
866                     MultipleCompilationErrorsException mcee = (MultipleCompilationErrorsException) next;
867                     nestedCollector = mcee.collector;
868                     break;
869                 }
870                 
871                 if (nestedCollector!=null) {
872                     getErrorCollector().addCollectorContents(nestedCollector);
873                 } else {
874                     getErrorCollector().addError(new ExceptionMessage(e,configuration.getDebug(),this));
875                 }
876             }
877         }
878 
879         getErrorCollector().failIfErrors();
880     }
881 
882 
883     //---------------------------------------------------------------------------
884     // OUTPUT
885 
886 
887     /***
888      * Writes error messages to the specified PrintWriter.
889      */
890     /*public void write(PrintWriter writer, Janitor janitor) {
891         super.write(writer, janitor);
892 
893         Iterator keys = names.iterator();
894         while (keys.hasNext()) {
895             String name = (String) keys.next();
896             SourceUnit source = (SourceUnit) sources.get(name);
897 
898             if (source.hasErrors()) {
899                 source.write(writer, janitor);
900             }
901         }
902     }*/
903 
904 
905 }