001    /*
002     $Id: CompilationUnit.java,v 1.23 2005/06/13 16:21:54 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    
057    import org.codehaus.groovy.GroovyBugError;
058    
059    import org.codehaus.groovy.ast.ASTNode;
060    
061    import org.codehaus.groovy.ast.ClassNode;
062    
063    import org.codehaus.groovy.ast.CompileUnit;
064    import org.codehaus.groovy.ast.ModuleNode;
065    import org.codehaus.groovy.classgen.*;
066    import org.codehaus.groovy.control.io.InputStreamReaderSource;
067    import org.codehaus.groovy.control.io.ReaderSource;
068    import org.codehaus.groovy.control.messages.ExceptionMessage;
069    import org.codehaus.groovy.control.messages.Message;
070    import org.codehaus.groovy.syntax.SyntaxException;
071    import org.codehaus.groovy.tools.GroovyClass;
072    import org.objectweb.asm.ClassVisitor;
073    import org.objectweb.asm.ClassWriter;
074    
075    
076    import groovy.lang.GroovyRuntimeException;
077    
078    
079    import java.io.*;
080    import java.net.MalformedURLException;
081    import java.net.URL;
082    import java.security.CodeSource;
083    import java.util.*;
084    
085    
086    
087    
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.23 2005/06/13 16:21:54 blackdrag Exp $
096     */
097    
098    
099    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    }