View Javadoc

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