KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > groovy > control > CompilationUnit


1 /*
2  $Id: CompilationUnit.java,v 1.10 2004/12/15 00:19:52 zohar Exp $
3
4  Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5
6  Redistribution and use of this software and associated documentation
7  ("Software"), with or without modification, are permitted provided
8  that the following conditions are met:
9
10  1. Redistributions of source code must retain copyright
11     statements and notices. Redistributions must also contain a
12     copy of this document.
13
14  2. Redistributions in binary form must reproduce the
15     above copyright notice, this list of conditions and the
16     following disclaimer in the documentation and/or other
17     materials provided with the distribution.
18
19  3. The name "groovy" must not be used to endorse or promote
20     products derived from this Software without prior written
21     permission of The Codehaus. For written permission,
22     please contact info@codehaus.org.
23
24  4. Products derived from this Software may not be called "groovy"
25     nor may "groovy" appear in their names without prior written
26     permission of The Codehaus. "groovy" is a registered
27     trademark of The Codehaus.
28
29  5. Due credit should be given to The Codehaus -
30     http://groovy.codehaus.org/
31
32  THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33  ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
36  THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43  OF THE POSSIBILITY OF SUCH DAMAGE.
44
45  */

46
47 package org.codehaus.groovy.control;
48
49 import org.codehaus.groovy.GroovyBugError;
50 import org.codehaus.groovy.ast.ClassNode;
51 import org.codehaus.groovy.ast.CompileUnit;
52 import org.codehaus.groovy.ast.ModuleNode;
53 import org.codehaus.groovy.classgen.*;
54 import org.codehaus.groovy.control.io.InputStreamReaderSource;
55 import org.codehaus.groovy.control.io.ReaderSource;
56 import org.codehaus.groovy.control.messages.ExceptionMessage;
57 import org.codehaus.groovy.control.messages.Message;
58 import org.codehaus.groovy.tools.GroovyClass;
59 import org.objectweb.asm.ClassVisitor;
60 import org.objectweb.asm.ClassWriter;
61
62 import java.io.*;
63 import java.net.MalformedURLException JavaDoc;
64 import java.net.URL JavaDoc;
65 import java.security.CodeSource JavaDoc;
66 import java.util.*;
67
68
69 /**
70  * Collects all compilation data as it is generated by the compiler system.
71  * Allows additional source units to be added and compilation run again (to
72  * affect only the deltas).
73  *
74  * @author <a HREF="mailto:cpoirier@dreaming.org">Chris Poirier</a>
75  * @version $Id: CompilationUnit.java,v 1.10 2004/12/15 00:19:52 zohar Exp $
76  */

77
78 public class CompilationUnit extends ProcessingUnit {
79
80     //---------------------------------------------------------------------------
81
// CONSTRUCTION AND SUCH
82

83     protected HashMap sources; // The SourceUnits from which this unit is built
84
protected ArrayList names; // Names for each SourceUnit in sources.
85

86     protected CompileUnit ast; // The overall AST for this CompilationUnit.
87
protected ArrayList classes; // The classes generated during classgen.
88

89     protected Verifier verifier; // For use by verify().
90

91     protected ClassCompletionVerifier completionVerifier; // for use by checkClassCompletion
92

93     protected boolean debug; // Controls behaviour of classgen() and other routines.
94
protected boolean configured; // Set true after the first configure() operation
95

96     protected ClassgenCallback classgenCallback; // A callback for use during classgen()
97
protected ProgressCallback progressCallback; // A callback for use during compile()
98

99
100     /**
101      * Initializes the CompilationUnit with defaults.
102      */

103     public CompilationUnit() {
104         this(null, null, null);
105     }
106
107
108     /**
109      * Initializes the CompilationUnit with defaults except for class loader.
110      */

111     public CompilationUnit(ClassLoader JavaDoc loader) {
112         this(null, null, loader);
113     }
114
115
116     /**
117      * Initializes the CompilationUnit with no security considerations.
118      */

119     public CompilationUnit(CompilerConfiguration configuration) {
120         this(configuration, null, null);
121     }
122
123
124     /**
125      * Initializes the CompilationUnit with a CodeSource for controlling
126      * security stuff and a class loader for loading classes.
127      */

128
129     public CompilationUnit(CompilerConfiguration configuration, CodeSource JavaDoc security, ClassLoader JavaDoc loader) {
130         super(configuration, loader);
131
132         this.names = new ArrayList();
133         this.sources = new HashMap();
134
135         this.ast = new CompileUnit(this.classLoader, security, this.configuration);
136         this.classes = new ArrayList();
137
138         this.verifier = new Verifier();
139         this.completionVerifier = new ClassCompletionVerifier();
140
141         this.classgenCallback = null;
142     }
143
144
145     /**
146      * Reconfigures the CompilationUnit.
147      */

148
149     public void configure(CompilerConfiguration configuration) {
150         super.configure(configuration);
151         this.debug = configuration.getDebug();
152
153
154         //
155
// Configure our class loader's classpath, if it is of
156
// a configurable type. We can't reconfigure it,
157
// unfortunately, due to limitations in URLClassLoader.
158

159         if (!this.configured && this.classLoader instanceof CompilerClassLoader) {
160             CompilerClassLoader loader = (CompilerClassLoader) this.classLoader;
161
162             Iterator iterator = configuration.getClasspath().iterator();
163             while (iterator.hasNext()) {
164                 try {
165                     this.configured = true;
166                     loader.addPath((String JavaDoc) iterator.next());
167                 } catch (MalformedURLException JavaDoc e) {
168                     throw new ConfigurationException(e);
169                 }
170             }
171         }
172     }
173
174
175     /**
176      * Returns the CompileUnit that roots our AST.
177      */

178
179     public CompileUnit getAST() {
180         return this.ast;
181     }
182
183
184     /**
185      * Get the GroovyClasses generated by compile().
186      */

187
188     public List getClasses() {
189         return classes;
190     }
191
192
193     /**
194      * Convenience routine to get the first ClassNode, for
195      * when you are sure there is only one.
196      */

197
198     public ClassNode getFirstClassNode() {
199         return (ClassNode) ((ModuleNode) this.ast.getModules().get(0)).getClasses().get(0);
200     }
201
202
203     /**
204      * Convenience routine to get the named ClassNode.
205      */

206
207     public ClassNode getClassNode(final String JavaDoc name) {
208         final ClassNode[] result = new ClassNode[]{null};
209         LoopBodyForPrimaryClassNodeOperations handler = new LoopBodyForPrimaryClassNodeOperations() {
210             public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) {
211                 if (classNode.getName().equals(name)) {
212                     result[0] = classNode;
213                 }
214             }
215         };
216
217         try {
218             applyToPrimaryClassNodes(handler);
219         } catch (CompilationFailedException e) {
220             if (debug) e.printStackTrace();
221         }
222         return result[0];
223     }
224
225
226
227
228
229
230     //---------------------------------------------------------------------------
231
// SOURCE CREATION
232

233     /**
234      * Adds a set of file paths to the unit.
235      */

236
237     public void addSources(String JavaDoc[] paths) {
238         for (int i = 0; i < paths.length; i++) {
239             File file = new File(paths[i]);
240             addSource(file);
241         }
242     }
243
244
245     /**
246      * Adds a set of source files to the unit.
247      */

248
249     public void addSources(File[] files) {
250         for (int i = 0; i < files.length; i++) {
251             addSource(files[i]);
252         }
253     }
254
255
256     /**
257      * Adds a source file to the unit.
258      */

259
260     public SourceUnit addSource(File file) {
261         return addSource(new SourceUnit(file, configuration, classLoader));
262     }
263
264
265     /**
266      * Adds a source file to the unit.
267      */

268
269     public SourceUnit addSource(URL JavaDoc url) {
270         return addSource(new SourceUnit(url, configuration, classLoader));
271     }
272
273
274     /**
275      * Adds a InputStream source to the unit.
276      */

277
278     public SourceUnit addSource(String JavaDoc name, InputStream stream) {
279         ReaderSource source = new InputStreamReaderSource(stream, configuration);
280         return addSource(new SourceUnit(name, source, configuration, classLoader));
281     }
282
283
284     /**
285      * Adds a SourceUnit to the unit.
286      */

287
288     public SourceUnit addSource(SourceUnit source) {
289         String JavaDoc name = source.getName();
290
291         source.setClassLoader(this.classLoader);
292
293         names.add(name);
294         sources.put(name, source);
295
296         return source;
297     }
298
299
300     /**
301      * Returns an iterator on the unit's SourceUnits.
302      */

303
304     public Iterator iterator() {
305         return new Iterator() {
306             Iterator nameIterator = names.iterator();
307
308             public boolean hasNext() {
309                 return nameIterator.hasNext();
310             }
311
312             public Object JavaDoc next() {
313                 String JavaDoc name = (String JavaDoc) nameIterator.next();
314                 return sources.get(name);
315             }
316
317             public void remove() {
318                 throw new UnsupportedOperationException JavaDoc();
319             }
320         };
321     }
322
323
324     /**
325      * Adds a ClassNode directly to the unit (ie. without source).
326      * Used primarily for testing support.
327      */

328
329     public void addClassNode(ClassNode node) {
330         ModuleNode module = new ModuleNode(this.ast);
331         this.ast.addModule(module);
332         module.addClass(node);
333     }
334
335
336
337
338     //---------------------------------------------------------------------------
339
// EXTERNAL CALLBACKS
340

341     /**
342      * A callback interface you can use to "accompany" the classgen()
343      * code as it traverses the ClassNode tree. You will be called-back
344      * for each primary and inner class. Use setClassgenCallback() before
345      * running compile() to set your callback.
346      */

347
348     public static abstract class ClassgenCallback {
349         public abstract void call(ClassVisitor writer, ClassNode node) throws CompilationFailedException;
350     }
351
352
353     /**
354      * Sets a ClassgenCallback. You can have only one, and setting
355      * it to null removes any existing setting.
356      */

357
358     public void setClassgenCallback(ClassgenCallback visitor) {
359         this.classgenCallback = visitor;
360     }
361
362
363     /**
364      * A callback interface you can use to get a callback after every
365      * unit of the compile process. You will be called-back with a
366      * ProcessingUnit and a phase indicator. Use setProgressCallback()
367      * before running compile() to set your callback.
368      */

369
370     public static abstract class ProgressCallback {
371         public abstract void call(ProcessingUnit context, int phase) throws CompilationFailedException;
372     }
373
374
375     /**
376      * Sets a ProgressCallback. You can have only one, and setting
377      * it to null removes any existing setting.
378      */

379
380     public void setProgressCallback(ProgressCallback callback) {
381         this.progressCallback = callback;
382     }
383
384
385
386
387     //---------------------------------------------------------------------------
388
// ACTIONS
389

390
391     /**
392      * Synonym for compile(Phases.ALL).
393      */

394
395     public void compile() throws CompilationFailedException {
396         compile(Phases.ALL);
397     }
398
399
400     /**
401      * Compiles the compilation unit from sources.
402      */

403
404     public void compile(int throughPhase) throws CompilationFailedException {
405         //
406
// To support delta compilations, we always restart
407
// the compiler. The individual passes are responsible
408
// for not reprocessing old code.
409

410         gotoPhase(Phases.INITIALIZATION);
411
412         do {
413             if (throughPhase < Phases.PARSING) {
414                 break;
415             }
416
417             gotoPhase(Phases.PARSING);
418             parse();
419
420             if (throughPhase < Phases.CONVERSION) {
421                 break;
422             }
423
424             gotoPhase(Phases.CONVERSION);
425             convert();
426
427             if (throughPhase < Phases.CLASS_GENERATION) {
428                 break;
429             }
430
431             gotoPhase(Phases.CLASS_GENERATION);
432             classgen();
433
434             if (throughPhase < Phases.OUTPUT) {
435                 break;
436             }
437
438             gotoPhase(Phases.OUTPUT);
439             output();
440
441             if (throughPhase < Phases.FINALIZATION) {
442                 break;
443             }
444
445             gotoPhase(Phases.FINALIZATION);
446
447         } while (false);
448
449     }
450
451
452     /**
453      * Parses all sources.
454      */

455
456     public void parse() throws CompilationFailedException {
457         if (this.phase != Phases.PARSING) {
458             throw new GroovyBugError("CompilationUnit not read for parse()");
459         }
460
461         applyToSourceUnits(parse);
462
463         completePhase();
464         applyToSourceUnits(mark);
465     }
466
467
468     /**
469      * Runs parse() on a single SourceUnit.
470      */

471
472     private LoopBodyForSourceUnitOperations parse = new LoopBodyForSourceUnitOperations() {
473         public void call(SourceUnit source) throws CompilationFailedException {
474             source.parse();
475
476             if (CompilationUnit.this.progressCallback != null) {
477                 CompilationUnit.this.progressCallback.call(source, CompilationUnit.this.phase);
478             }
479         }
480     };
481
482
483     /**
484      * Builds ASTs for all parsed sources.
485      */

486
487     public void convert() throws CompilationFailedException {
488         if (this.phase != Phases.CONVERSION) {
489             throw new GroovyBugError("CompilationUnit not ready for convert()");
490         }
491
492         applyToSourceUnits(convert);
493
494         completePhase();
495         applyToSourceUnits(mark);
496     }
497
498
499     /**
500      * Runs convert() on a single SourceUnit.
501      */

502
503     private LoopBodyForSourceUnitOperations convert = new LoopBodyForSourceUnitOperations() {
504         public void call(SourceUnit source) throws CompilationFailedException {
505             source.convert();
506             CompilationUnit.this.ast.addModule(source.getAST());
507
508             if (CompilationUnit.this.progressCallback != null) {
509                 CompilationUnit.this.progressCallback.call(source, CompilationUnit.this.phase);
510             }
511         }
512     };
513
514
515     /**
516      * Expands and canonicalizes the ASTs generated during
517      * parsing and conversion, then generates classes.
518      */

519
520     public void classgen() throws CompilationFailedException {
521         if (this.phase != Phases.CLASS_GENERATION) {
522             throw new GroovyBugError("CompilationUnit not ready for classgen()");
523         }
524
525         applyToPrimaryClassNodes(classgen);
526
527         completePhase();
528         applyToSourceUnits(mark);
529
530
531         //
532
// Callback progress, if necessary
533

534         if (this.progressCallback != null) {
535             this.progressCallback.call(this, CompilationUnit.this.phase);
536         }
537
538     }
539
540
541     /**
542      * Runs classgen() on a single ClassNode.
543      */

544
545     private LoopBodyForPrimaryClassNodeOperations classgen = new LoopBodyForPrimaryClassNodeOperations() {
546         public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
547             //
548
// Run the Verifier on the outer class
549

550             verifier.visitClass(classNode);
551
552
553             //
554
// Prep the generator machinery
555

556             ClassVisitor visitor = createClassVisitor();
557
558             String JavaDoc sourceName = (source == null ? classNode.getModule().getDescription() : source.getName());
559             ClassGenerator generator = new AsmClassGenerator2(context, visitor, classLoader, sourceName);
560
561             //
562
// Run the generation and create the class (if required)
563

564             generator.visitClass(classNode);
565             completionVerifier.visitClass(classNode);
566
567             if (!debug) {
568                 byte[] bytes = ((ClassWriter) visitor).toByteArray();
569                 /* this. */classes.add(new GroovyClass(classNode.getName(), bytes));
570             }
571
572
573
574
575             //
576
// Handle any callback that's been set
577

578             if (CompilationUnit.this.classgenCallback != null) {
579                 if (debug) {
580                     try {
581                         classgenCallback.call(visitor, classNode);
582                     } catch (Throwable JavaDoc t) {
583                         output.println("Classgen callback threw: " + t);
584                         t.printStackTrace(output);
585                     }
586                 } else {
587                     classgenCallback.call(visitor, classNode);
588                 }
589             }
590
591
592             //
593
// Recurse for inner classes
594

595             LinkedList innerClasses = generator.getInnerClasses();
596             while (!innerClasses.isEmpty()) {
597                 classgen.call(source, context, (ClassNode) innerClasses.removeFirst());
598             }
599
600         }
601
602     };
603
604     protected ClassVisitor createClassVisitor() {
605         /** avoid runtime dependency on asm util
606          ClassVisitor visitor;
607          if( debug )
608          {
609          visitor = new DumpClassVisitor(output);
610          }
611          else
612
613          {
614          visitor = new ClassWriter(true);
615          }
616          return visitor;
617          */

618         return new ClassWriter(true);
619     }
620
621
622     /**
623      * Outputs the generated class files to permanent storage.
624      */

625
626     public void output() throws CompilationFailedException {
627         if (this.phase != Phases.OUTPUT && !(this.phase == Phases.CLASS_GENERATION && this.phaseComplete)) {
628             throw new GroovyBugError("CompilationUnit not ready for output()");
629         }
630
631         boolean failures = false;
632
633         Iterator iterator = this.classes.iterator();
634         while (iterator.hasNext()) {
635             //
636
// Get the class and calculate its filesystem name
637

638             GroovyClass gclass = (GroovyClass) iterator.next();
639             String JavaDoc name = gclass.getName().replace('.', File.separatorChar) + ".class";
640             File path = new File(configuration.getTargetDirectory(), name);
641
642             //
643
// Ensure the path is ready for the file
644

645             File directory = path.getParentFile();
646             if (directory != null && !directory.exists()) {
647                 directory.mkdirs();
648             }
649
650             //
651
// Create the file and write out the data
652

653             byte[] bytes = gclass.getBytes();
654
655             FileOutputStream stream = null;
656             try {
657                 stream = new FileOutputStream(path);
658                 stream.write(bytes, 0, bytes.length);
659             } catch (IOException e) {
660                 addError(Message.create(e.getMessage()));
661                 failures = true;
662             } finally {
663                 if (stream != null) {
664                     try {
665                         stream.close();
666                     } catch (Exception JavaDoc e) {
667                     }
668                 }
669             }
670         }
671
672         if (failures) {
673             fail();
674         }
675
676         completePhase();
677         applyToSourceUnits(mark);
678
679
680         //
681
// Callback progress, if necessary
682

683         if (CompilationUnit.this.progressCallback != null) {
684             CompilationUnit.this.progressCallback.call(this, this.phase);
685         }
686     }
687
688
689     /**
690      * Returns true if there are any errors pending.
691      */

692
693     public boolean hasErrors() {
694         boolean hasErrors = false;
695
696         Iterator keys = names.iterator();
697         while (keys.hasNext()) {
698             String JavaDoc name = (String JavaDoc) keys.next();
699             SourceUnit source = (SourceUnit) sources.get(name);
700
701             if (source.hasErrors()) {
702                 hasErrors = true;
703                 break;
704             }
705         }
706
707         return hasErrors || super.hasErrors();
708     }
709
710
711
712
713     //---------------------------------------------------------------------------
714
// PHASE HANDLING
715

716
717     /**
718      * Updates the phase marker on all sources.
719      */

720
721     protected void mark() throws CompilationFailedException {
722         applyToSourceUnits(mark);
723     }
724
725
726     /**
727      * Marks a single SourceUnit with the current phase,
728      * if it isn't already there yet.
729      */

730
731     private LoopBodyForSourceUnitOperations mark = new LoopBodyForSourceUnitOperations() {
732         public void call(SourceUnit source) throws CompilationFailedException {
733             if (source.phase < phase) {
734                 source.gotoPhase(phase);
735             }
736
737             if (source.phase == phase && phaseComplete && !source.phaseComplete) {
738                 source.completePhase();
739             }
740         }
741     };
742
743
744
745
746     //---------------------------------------------------------------------------
747
// LOOP SIMPLIFICATION FOR SourceUnit OPERATIONS
748

749     /**
750      * An callback interface for use in the applyToSourceUnits loop driver.
751      */

752
753     public abstract class LoopBodyForSourceUnitOperations {
754         public abstract void call(SourceUnit source) throws CompilationFailedException;
755     }
756
757
758     /**
759      * A loop driver for applying operations to all SourceUnits.
760      * Automatically skips units that have already been processed
761      * through the current phase.
762      */

763
764     public void applyToSourceUnits(LoopBodyForSourceUnitOperations body) throws CompilationFailedException {
765         boolean failures = false;
766
767         Iterator keys = names.iterator();
768         while (keys.hasNext()) {
769             String JavaDoc name = (String JavaDoc) keys.next();
770             SourceUnit source = (SourceUnit) sources.get(name);
771             if (source.phase <= phase) {
772                 try {
773                     body.call(source);
774                 } catch (CompilationFailedException e) {
775                     throw e;
776                 } catch (Exception JavaDoc e) {
777                     throw new GroovyBugError(e);
778                 }
779             }
780         }
781
782         if (failures) {
783             fail();
784         }
785     }
786
787
788
789
790     //---------------------------------------------------------------------------
791
// LOOP SIMPLIFICATION FOR PRIMARY ClassNode OPERATIONS
792

793
794     /**
795      * An callback interface for use in the applyToSourceUnits loop driver.
796      */

797
798     public abstract class LoopBodyForPrimaryClassNodeOperations {
799         public abstract void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException;
800     }
801
802
803     /**
804      * A loop driver for applying operations to all primary ClassNodes in
805      * our AST. Automatically skips units that have already been processed
806      * through the current phase.
807      */

808
809     public void applyToPrimaryClassNodes(LoopBodyForPrimaryClassNodeOperations body) throws CompilationFailedException {
810         boolean failures = false;
811
812         Iterator modules = this.ast.getModules().iterator();
813         while (modules.hasNext()) {
814             ModuleNode module = (ModuleNode) modules.next();
815
816             try {
817                 Iterator classNodes = module.getClasses().iterator();
818                 while (classNodes.hasNext()) {
819                     ClassNode classNode = (ClassNode) classNodes.next();
820                     SourceUnit context = module.getContext();
821                     if (context == null || context.phase <= phase) {
822                         body.call(module.getContext(), new GeneratorContext(this.ast), classNode);
823                     }
824                 }
825             } catch (CompilationFailedException e) {
826                 failures = true;
827                 addError(new ExceptionMessage(e));
828             } catch (Exception JavaDoc e) {
829                 failures = true;
830 // String msg = e.getMessage();
831
// if (e instanceof RuntimeParserException) {
832
// RuntimeParserException rpe = (RuntimeParserException) e;
833
// ASTNode node = rpe.getNode();
834
// msg += ". The probable error location: [" + node.getLineNumber() + ":" + node.getColumnNumber() + "]";
835
// }
836
addError(new ExceptionMessage(e));
837             }
838         }
839
840         if (failures) {
841             fail();
842         }
843     }
844
845
846
847
848     //---------------------------------------------------------------------------
849
// OUTPUT
850

851
852     /**
853      * Writes error messages to the specified PrintWriter.
854      */

855
856     public void write(PrintWriter writer, Janitor janitor) {
857         super.write(writer, janitor);
858
859         Iterator keys = names.iterator();
860         while (keys.hasNext()) {
861             String JavaDoc name = (String JavaDoc) keys.next();
862             SourceUnit source = (SourceUnit) sources.get(name);
863
864             if (source.hasErrors()) {
865                 source.write(writer, janitor);
866             }
867         }
868
869     }
870
871
872 }
873
874
875
876
877
Popular Tags