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