View Javadoc

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