KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  $Id: SourceUnit.java,v 1.2 2004/07/10 03:31:41 bran 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 java.io.File JavaDoc;
50 import java.io.IOException JavaDoc;
51 import java.io.Reader JavaDoc;
52 import java.lang.reflect.Constructor JavaDoc;
53 import java.lang.reflect.InvocationTargetException JavaDoc;
54 import java.net.URL JavaDoc;
55 import java.util.List JavaDoc;
56
57 import org.codehaus.groovy.GroovyBugError;
58 import org.codehaus.groovy.ast.ClassNode;
59 import org.codehaus.groovy.ast.FieldNode;
60 import org.codehaus.groovy.ast.MethodNode;
61 import org.codehaus.groovy.ast.ModuleNode;
62 import org.codehaus.groovy.ast.stmt.BlockStatement;
63 import org.codehaus.groovy.ast.stmt.Statement;
64 import org.codehaus.groovy.control.io.FileReaderSource;
65 import org.codehaus.groovy.control.io.ReaderSource;
66 import org.codehaus.groovy.control.io.StringReaderSource;
67 import org.codehaus.groovy.control.io.URLReaderSource;
68 import org.codehaus.groovy.control.messages.LocatedMessage;
69 import org.codehaus.groovy.control.messages.Message;
70 import org.codehaus.groovy.control.messages.SimpleMessage;
71 import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
72 import org.codehaus.groovy.control.messages.WarningMessage;
73 import org.codehaus.groovy.syntax.CSTNode;
74 import org.codehaus.groovy.syntax.Reduction;
75 import org.codehaus.groovy.syntax.SyntaxException;
76 import org.codehaus.groovy.syntax.Token;
77 import org.codehaus.groovy.syntax.TokenStream;
78 import org.codehaus.groovy.syntax.Types;
79 import org.codehaus.groovy.syntax.lexer.GroovyLexer;
80 import org.codehaus.groovy.syntax.lexer.LexerTokenStream;
81 import org.codehaus.groovy.syntax.lexer.ReaderCharStream;
82 import org.codehaus.groovy.syntax.parser.ASTBuilder;
83 import org.codehaus.groovy.syntax.parser.Parser;
84 import org.codehaus.groovy.syntax.parser.UnexpectedTokenException;
85 import org.codehaus.groovy.tools.Utilities;
86
87
88
89 /**
90  * Provides an anchor for a single source unit (usually a script file)
91  * as it passes through the compiler system.
92  *
93  * @author <a HREF="mailto:cpoirier@dreaming.org">Chris Poirier</a>
94  * @author <a HREF="mailto:b55r@sina.com">Bing Ran</a>
95  *
96  * @version $Id: SourceUnit.java,v 1.2 2004/07/10 03:31:41 bran Exp $
97  */

98
99 public class SourceUnit extends ProcessingUnit
100 {
101     
102   //---------------------------------------------------------------------------
103
// CONSTRUCTION AND SUCH
104

105     protected ReaderSource source; // Where we can get Readers for our source unit
106
protected String JavaDoc name; // A descriptive name of the source unit
107
protected Reduction cst; // A Concrete Syntax Tree of the source
108
protected ModuleNode ast; // The root of the Abstract Syntax Tree for the source
109

110
111     
112    /**
113     * Initializes the SourceUnit from existing machinery.
114     */

115     
116     public SourceUnit( String JavaDoc name, ReaderSource source, CompilerConfiguration flags, ClassLoader JavaDoc loader )
117     {
118         super( flags, loader );
119         
120         this.name = name;
121         this.source = source;
122     }
123     
124     
125
126    /**
127     * Initializes the SourceUnit from the specified file.
128     */

129     
130     public SourceUnit( File JavaDoc source, CompilerConfiguration configuration, ClassLoader JavaDoc loader )
131     {
132         this( source.getPath(), new FileReaderSource(source, configuration), configuration, loader );
133     }
134     
135
136    /**
137     * Initializes the SourceUnit from the specified URL.
138     */

139     
140     public SourceUnit( URL JavaDoc source, CompilerConfiguration configuration, ClassLoader JavaDoc loader )
141     {
142         this( source.getPath(), new URLReaderSource(source, configuration), configuration, loader );
143     }
144     
145     
146    
147    /**
148     * Initializes the SourceUnit for a string of source.
149     */

150     
151     public SourceUnit( String JavaDoc name, String JavaDoc source, CompilerConfiguration configuration, ClassLoader JavaDoc loader )
152     {
153         this( name, new StringReaderSource(source, configuration), configuration, loader );
154     }
155     
156     
157    /**
158     * Returns the name for the SourceUnit.
159     */

160     
161     public String JavaDoc getName()
162     {
163         return name;
164     }
165     
166     
167     
168    /**
169     * Returns the Concrete Syntax Tree produced during parse()ing.
170     */

171     
172     public Reduction getCST()
173     {
174         return this.cst;
175     }
176     
177     
178     
179    /**
180     * Returns the Abstract Syntax Tree produced during parse()ing
181     * and expanded during later phases.
182     */

183     
184     public ModuleNode getAST()
185     {
186         return this.ast;
187     }
188     
189     
190     
191    /**
192     * Convenience routine, primarily for use by the InteractiveShell,
193     * that returns true if parse() failed with an unexpected EOF.
194     */

195     
196     public boolean failedWithUnexpectedEOF()
197     {
198         boolean result = false;
199         
200         if( this.errors != null )
201         {
202             Message last = (Message)errors.get(errors.size() - 1);
203             if( last instanceof SyntaxErrorMessage )
204             {
205                 SyntaxException cause = ((SyntaxErrorMessage)last).getCause();
206                 if( cause instanceof UnexpectedTokenException )
207                 {
208                     Token unexpected = ((UnexpectedTokenException)cause).getUnexpectedToken();
209                     if( unexpected.isA(Types.EOF) )
210                     {
211                         result = true;
212                     }
213                 }
214             }
215         }
216         
217         return result;
218     }
219
220
221     
222   //---------------------------------------------------------------------------
223
// FACTORIES
224

225     
226    /**
227     * A convenience routine to create a standalone SourceUnit on a String
228     * with defaults for almost everything that is configurable.
229     */

230     
231     public static SourceUnit create( String JavaDoc name, String JavaDoc source )
232     {
233         CompilerConfiguration configuration = new CompilerConfiguration();
234         configuration.setTolerance( 1 );
235         
236         return new SourceUnit( name, source, configuration, null );
237     }
238     
239
240     
241    /**
242     * A convenience routine to create a standalone SourceUnit on a String
243     * with defaults for almost everything that is configurable.
244     */

245      
246     public static SourceUnit create( String JavaDoc name, String JavaDoc source, int tolerance )
247     {
248         CompilerConfiguration configuration = new CompilerConfiguration();
249         configuration.setTolerance( tolerance );
250         
251         return new SourceUnit( name, source, configuration, null );
252     }
253      
254
255      
256      
257     
258   //---------------------------------------------------------------------------
259
// PROCESSING
260

261     
262    /**
263     * Parses the source to a CST. You can retrieve it with getCST().
264     */

265     
266     public void parse() throws CompilationFailedException
267     {
268         if( this.phase > Phases.PARSING )
269         {
270             throw new GroovyBugError( "parsing is already complete" );
271         }
272         
273         if( this.phase == Phases.INITIALIZATION )
274         {
275             nextPhase();
276         }
277     
278         
279         //
280
// Create a reader on the source and run the parser.
281

282         Reader JavaDoc reader = null;
283         try
284         {
285             reader = source.getReader();
286
287             //
288
// Create a lexer and token stream
289

290             GroovyLexer lexer = new GroovyLexer( new ReaderCharStream(reader) );
291             TokenStream stream = new LexerTokenStream( lexer );
292             
293             //
294
// Do the parsing
295

296             Parser parser = new Parser( this, stream );
297             this.cst = parser.parse();
298             
299             completePhase();
300         }
301         catch( IOException JavaDoc e )
302         {
303             addFatalError( new SimpleMessage(e.getMessage()) );
304         }
305         finally
306         {
307             if( reader != null )
308             {
309                 try { reader.close(); } catch( IOException JavaDoc e ) {}
310             }
311         }
312     }
313     
314     
315     
316    /**
317     * Generates an AST from the CST. You can retrieve it with getAST().
318     */

319
320     public void convert() throws CompilationFailedException
321     {
322         if( this.phase == Phases.PARSING && this.phaseComplete )
323         {
324             gotoPhase( Phases.CONVERSION );
325         }
326         
327         if( this.phase != Phases.CONVERSION )
328         {
329             throw new GroovyBugError( "SourceUnit not ready for convert()" );
330         }
331
332         
333         //
334
// Build the AST
335

336         try
337         {
338             ASTBuilder builder = new ASTBuilder( this, this.classLoader );
339             this.ast = builder.build( this.cst );
340             this.ast.setDescription( this.name );
341         }
342         catch( SyntaxException e )
343         {
344             addError( new SyntaxErrorMessage(e) );
345         }
346         
347         completePhase();
348     }
349     
350     
351
352     
353   //---------------------------------------------------------------------------
354
// ERROR REPORTING
355

356     
357    /**
358     * Convenience wrapper for addWarning() that won't create an object
359     * unless it is relevant.
360     */

361      
362     public void addWarning( int importance, String JavaDoc text, CSTNode context )
363     {
364         if( WarningMessage.isRelevant(importance, this.warningLevel) )
365         {
366             addWarning( new WarningMessage(importance, text, context) );
367         }
368     }
369      
370     
371     
372     /**
373      * Convenience wrapper for addWarning() that won't create an object
374      * unless it is relevant.
375      */

376       
377     public void addWarning( int importance, String JavaDoc text, Object JavaDoc data, CSTNode context )
378     {
379         if( WarningMessage.isRelevant(importance, this.warningLevel) )
380         {
381             addWarning( new WarningMessage(importance, text, data, context) );
382         }
383     }
384
385      
386      
387    /**
388     * Convenience wrapper for addError().
389     */

390     
391     public void addError( SyntaxException error ) throws CompilationFailedException
392     {
393         addError( Message.create(error), error.isFatal() );
394     }
395     
396     
397     
398    /**
399     * Convenience wrapper for addError().
400     */

401     
402     public void addError( String JavaDoc text, CSTNode context ) throws CompilationFailedException
403     {
404         addError( new LocatedMessage(text, context) );
405     }
406
407     
408     
409     
410   //---------------------------------------------------------------------------
411
// SOURCE SAMPLING
412

413     
414    /**
415     * Returns a sampling of the source at the specified line and column,
416     * of null if it is unavailable.
417     */

418     
419     public String JavaDoc getSample( int line, int column, Janitor janitor )
420     {
421         String JavaDoc sample = null;
422         String JavaDoc text = source.getLine( line, janitor );
423
424         if( text != null )
425         {
426             if( column > 0 )
427             {
428                 String JavaDoc marker = Utilities.repeatString(" ", column-1) + "^";
429
430                 if( column > 40 )
431                 {
432                     int start = column - 30 - 1;
433                     int end = (column + 10 > text.length() ? text.length() : column + 10 - 1);
434                     sample = " " + text.substring( start, end ) + Utilities.eol() + " " + marker.substring( start, marker.length() );
435                 }
436                 else
437                 {
438                     sample = " " + text + Utilities.eol() + " " + marker;
439                 }
440             }
441             else
442             {
443                 sample = text;
444             }
445         }
446
447         return sample;
448     }
449     
450     /**
451      * to quickly create a ModuleNode from a piece of Groovy code
452      * @param code
453      * @return
454      * @throws CompilationFailedException
455      */

456     public static ModuleNode createModule(String JavaDoc code) throws CompilationFailedException {
457         SourceUnit su = create("NodeGen", code);
458         su.parse();
459         su.convert();
460         return su.getAST();
461     }
462     
463     public static ClassNode createClassNode(String JavaDoc code) throws CompilationFailedException {
464         ModuleNode module = createModule(code);
465         List JavaDoc classes = module.getClasses();
466         if (classes.size() > 1) {
467             throw new RuntimeException JavaDoc("The code defines more than one class");
468         }
469         return (ClassNode) classes.get(0);
470     }
471
472     /**
473      * Takes a field definition statement and wrap it in class definition. The FieldNode object
474      * representing the field is extracted and returned, Types need to be fully qualified.
475      * @param code a naked statement to define a field, such as: String prop = "hello"
476      * @return a FieldNode object.
477      * @throws CompilationFailedException
478      */

479     public static FieldNode createFieldNode(String JavaDoc code) throws CompilationFailedException {
480         ClassNode classNode = createClassNode(wrapCode(code));
481         List JavaDoc flds = classNode.getFields();
482         if (flds.size() > 1)
483             throw new RuntimeException JavaDoc("The code defines more than one field");
484         return (FieldNode) flds.get(0);
485     }
486     
487     public Statement createStatement(String JavaDoc code) throws CompilationFailedException {
488         ModuleNode module = createModule(code);
489         BlockStatement block = module.getStatementBlock();
490         if (block == null)
491             throw new RuntimeException JavaDoc("no proper statement block is created.");
492         List JavaDoc stats = block.getStatements();
493         if (stats == null || stats.size() != 1)
494             throw new RuntimeException JavaDoc("no proper statement node is created.");
495         return (Statement)stats.get(0);
496     }
497     
498     public MethodNode createMethodNode(String JavaDoc code) throws CompilationFailedException {
499         code = code.trim();
500         if (code.indexOf("def") != 0) {
501             code = "def " + code;
502         }
503         ModuleNode module = createModule(code);
504         List JavaDoc ms = module.getMethods();
505         if (ms == null || ms.size() != 1)
506             throw new RuntimeException JavaDoc("no proper method node is created.");
507         return (MethodNode)ms.get(0);
508     }
509     
510     private static String JavaDoc wrapCode(String JavaDoc code) {
511         String JavaDoc prefix = "class SynthedClass {\n";
512         String JavaDoc suffix = "\n }";
513         return prefix + code + suffix;
514             
515     }
516 }
517
518
519
520
521
Popular Tags