View Javadoc

1   /*
2    $Id: SourceUnit.java,v 1.17 2006/05/30 18:24:03 blackdrag 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 groovy.lang.GroovyClassLoader;
50  
51  import java.io.File;
52  import java.io.FileWriter;
53  import java.io.IOException;
54  import java.io.Reader;
55  import java.net.URL;
56  import java.security.AccessController;
57  import java.security.PrivilegedAction;
58  
59  import org.codehaus.groovy.GroovyBugError;
60  import org.codehaus.groovy.ast.ModuleNode;
61  import org.codehaus.groovy.control.io.FileReaderSource;
62  import org.codehaus.groovy.control.io.ReaderSource;
63  import org.codehaus.groovy.control.io.StringReaderSource;
64  import org.codehaus.groovy.control.io.URLReaderSource;
65  import org.codehaus.groovy.control.messages.Message;
66  import org.codehaus.groovy.control.messages.SimpleMessage;
67  import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
68  import org.codehaus.groovy.syntax.*;
69  import org.codehaus.groovy.tools.Utilities;
70  
71  import antlr.CharScanner;
72  import antlr.MismatchedTokenException;
73  import antlr.NoViableAltException;
74  import antlr.NoViableAltForCharException;
75  
76  import com.thoughtworks.xstream.XStream;
77  
78  
79  /***
80   * Provides an anchor for a single source unit (usually a script file)
81   * as it passes through the compiler system.
82   *
83   * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
84   * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
85   * @version $Id: SourceUnit.java,v 1.17 2006/05/30 18:24:03 blackdrag Exp $
86   */
87  
88  public class SourceUnit extends ProcessingUnit {
89  
90      /***
91       * The pluggable parser used to generate the AST - we allow pluggability currently as we need to have Classic and JSR support
92       */
93      private ParserPlugin parserPlugin;
94  
95      /***
96       * Where we can get Readers for our source unit
97       */
98      protected ReaderSource source;
99      /***
100      * A descriptive name of the source unit
101      */
102     protected String name;
103     /***
104      * A Concrete Syntax Tree of the source
105      */
106     protected Reduction cst;
107 
108     /***
109      * A facade over the CST
110      */
111     protected SourceSummary sourceSummary;
112     /***
113      * The root of the Abstract Syntax Tree for the source
114      */
115     protected ModuleNode ast;
116 
117 
118     /***
119      * Initializes the SourceUnit from existing machinery.
120      */
121     public SourceUnit(String name, ReaderSource source, CompilerConfiguration flags, GroovyClassLoader loader, ErrorCollector er) {
122         super(flags, loader, er);
123 
124         this.name = name;
125         this.source = source;
126     }
127 
128 
129     /***
130      * Initializes the SourceUnit from the specified file.
131      */
132     public SourceUnit(File source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
133         this(source.getPath(), new FileReaderSource(source, configuration), configuration, loader, er);
134     }
135 
136 
137     /***
138      * Initializes the SourceUnit from the specified URL.
139      */
140     public SourceUnit(URL source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
141         this(source.getPath(), new URLReaderSource(source, configuration), configuration, loader, er);
142     }
143 
144 
145     /***
146      * Initializes the SourceUnit for a string of source.
147      */
148     public SourceUnit(String name, String source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
149         this(name, new StringReaderSource(source, configuration), configuration, loader, er);
150     }
151 
152 
153     /***
154      * Returns the name for the SourceUnit.
155      */
156     public String getName() {
157         return name;
158     }
159 
160 
161     /***
162      * Returns the Concrete Syntax Tree produced during parse()ing.
163      */
164     public Reduction getCST() {
165         return this.cst;
166     }
167 
168     /***
169      * Returns the Source Summary
170      */
171     public SourceSummary getSourceSummary() {
172         return this.sourceSummary;
173     }
174     /***
175      * Returns the Abstract Syntax Tree produced during parse()ing
176      * and expanded during later phases.
177      */
178     public ModuleNode getAST() {
179         return this.ast;
180     }
181 
182 
183     /***
184      * Convenience routine, primarily for use by the InteractiveShell,
185      * that returns true if parse() failed with an unexpected EOF.
186      */
187     public boolean failedWithUnexpectedEOF() {
188     	// Implementation note - there are several ways for the Groovy compiler
189     	// to report an unexpected EOF. Perhaps this implementation misses some.
190     	// If you find another way, please add it.
191         if (getErrorCollector().hasErrors()) {
192             Message last = (Message) getErrorCollector().getLastError();
193             Throwable cause = null;
194             if (last instanceof SyntaxErrorMessage) {
195                 cause = ((SyntaxErrorMessage) last).getCause().getCause();
196             }
197             if (cause != null) {
198             	if (cause instanceof NoViableAltException) {
199                     return isEofToken(((NoViableAltException) cause).token);
200             	} else if (cause instanceof NoViableAltForCharException) {
201             		char badChar = ((NoViableAltForCharException) cause).foundChar;
202             		return badChar == CharScanner.EOF_CHAR;
203                 } else if (cause instanceof MismatchedTokenException) {
204                     return isEofToken(((MismatchedTokenException) cause).token);
205                 }
206             }
207         }
208         return false;    
209     }
210 
211     protected boolean isEofToken(antlr.Token token) {
212         return token.getType() == antlr.Token.EOF_TYPE;
213     }
214 
215 
216 
217     //---------------------------------------------------------------------------
218     // FACTORIES
219 
220 
221     /***
222      * A convenience routine to create a standalone SourceUnit on a String
223      * with defaults for almost everything that is configurable.
224      */
225     public static SourceUnit create(String name, String source) {
226         CompilerConfiguration configuration = new CompilerConfiguration();
227         configuration.setTolerance(1);
228 
229         return new SourceUnit(name, source, configuration, null, new ErrorCollector(configuration));
230     }
231 
232 
233     /***
234      * A convenience routine to create a standalone SourceUnit on a String
235      * with defaults for almost everything that is configurable.
236      */
237     public static SourceUnit create(String name, String source, int tolerance) {
238         CompilerConfiguration configuration = new CompilerConfiguration();
239         configuration.setTolerance(tolerance);
240 
241         return new SourceUnit(name, source, configuration, null, new ErrorCollector(configuration));
242     }
243 
244 
245 
246 
247 
248     //---------------------------------------------------------------------------
249     // PROCESSING
250 
251 
252     /***
253      * Parses the source to a CST.  You can retrieve it with getCST().
254      */
255     public void parse() throws CompilationFailedException {
256         if (this.phase > Phases.PARSING) {
257             throw new GroovyBugError("parsing is already complete");
258         }
259 
260         if (this.phase == Phases.INITIALIZATION) {
261             nextPhase();
262         }
263 
264 
265         //
266         // Create a reader on the source and run the parser.
267 
268         Reader reader = null;
269         try {
270             reader = source.getReader();
271 
272             // lets recreate the parser each time as it tends to keep around state
273             parserPlugin = getConfiguration().getPluginFactory().createParserPlugin();
274 
275             cst = parserPlugin.parseCST(this, reader);
276             sourceSummary = parserPlugin.getSummary();
277 
278             reader.close();
279             
280         }
281         catch (IOException e) {
282             getErrorCollector().addFatalError(new SimpleMessage(e.getMessage(),this));
283         }
284         finally {
285             if (reader != null) {
286                 try {
287                     reader.close();
288                 }
289                 catch (IOException e) {
290                 }
291             }
292         }
293     }
294 
295 
296     /***
297      * Generates an AST from the CST.  You can retrieve it with getAST().
298      */
299     public void convert() throws CompilationFailedException {
300         if (this.phase == Phases.PARSING && this.phaseComplete) {
301             gotoPhase(Phases.CONVERSION);
302         }
303 
304         if (this.phase != Phases.CONVERSION) {
305             throw new GroovyBugError("SourceUnit not ready for convert()");
306         }
307 
308         
309         //
310         // Build the AST
311         
312         try {
313             this.ast = parserPlugin.buildAST(this, this.classLoader, this.cst);
314 
315             this.ast.setDescription(this.name);
316         }
317         catch (SyntaxException e) {
318             getErrorCollector().addError(new SyntaxErrorMessage(e,this));
319         }
320 
321         String property = (String) AccessController.doPrivileged(new PrivilegedAction() {
322         	public Object run() {
323         		return System.getProperty("groovy.ast");
324         	}
325         });
326         
327         if ("xml".equals(property)) {
328             saveAsXML(name,ast);
329         }
330     }
331 
332     private void saveAsXML(String name, ModuleNode ast) {
333         XStream xstream = new XStream();
334         try {
335             xstream.toXML(ast,new FileWriter(name + ".xml"));
336             System.out.println("Written AST to " + name + ".xml");
337         } catch (Exception e) {
338             System.out.println("Couldn't write to " + name + ".xml");
339             e.printStackTrace();
340         }
341     }
342     //---------------------------------------------------------------------------    // SOURCE SAMPLING
343 
344     /***
345      * Returns a sampling of the source at the specified line and column,
346      * of null if it is unavailable.
347      */
348     public String getSample(int line, int column, Janitor janitor) {
349         String sample = null;
350         String text = source.getLine(line, janitor);
351 
352         if (text != null) {
353             if (column > 0) {
354                 String marker = Utilities.repeatString(" ", column - 1) + "^";
355 
356                 if (column > 40) {
357                     int start = column - 30 - 1;
358                     int end = (column + 10 > text.length() ? text.length() : column + 10 - 1);
359                     sample = "   " + text.substring(start, end) + Utilities.eol() + "   " + marker.substring(start, marker.length());
360                 }
361                 else {
362                     sample = "   " + text + Utilities.eol() + "   " + marker;
363                 }
364             }
365             else {
366                 sample = text;
367             }
368         }
369 
370         return sample;
371     }
372     
373     public void addException(Exception e) throws CompilationFailedException {
374         getErrorCollector().addException(e,this);
375     }
376     
377     public void addError(SyntaxException se) throws CompilationFailedException {
378         getErrorCollector().addError(se,this);
379     }
380 }
381 
382 
383 
384