View Javadoc

1   /*
2    $Id: SourceUnit.java,v 1.14 2005/11/13 16:42:11 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  
57  import org.codehaus.groovy.GroovyBugError;
58  import org.codehaus.groovy.ast.ModuleNode;
59  import org.codehaus.groovy.control.io.FileReaderSource;
60  import org.codehaus.groovy.control.io.ReaderSource;
61  import org.codehaus.groovy.control.io.StringReaderSource;
62  import org.codehaus.groovy.control.io.URLReaderSource;
63  import org.codehaus.groovy.control.messages.ExceptionMessage;
64  import org.codehaus.groovy.control.messages.Message;
65  import org.codehaus.groovy.control.messages.SimpleMessage;
66  import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
67  import org.codehaus.groovy.syntax.*;
68  import org.codehaus.groovy.tools.Utilities;
69  
70  import antlr.MismatchedTokenException;
71  import antlr.NoViableAltException;
72  
73  import com.thoughtworks.xstream.XStream;
74  
75  
76  /***
77   * Provides an anchor for a single source unit (usually a script file)
78   * as it passes through the compiler system.
79   *
80   * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
81   * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
82   * @version $Id: SourceUnit.java,v 1.14 2005/11/13 16:42:11 blackdrag Exp $
83   */
84  
85  public class SourceUnit extends ProcessingUnit {
86  
87      /***
88       * The pluggable parser used to generate the AST - we allow pluggability currently as we need to have Classic and JSR support
89       */
90      private ParserPlugin parserPlugin;
91  
92      /***
93       * Where we can get Readers for our source unit
94       */
95      protected ReaderSource source;
96      /***
97       * A descriptive name of the source unit
98       */
99      protected String name;
100     /***
101      * A Concrete Syntax Tree of the source
102      */
103     protected Reduction cst;
104 
105     /***
106      * A facade over the CST
107      */
108     protected SourceSummary sourceSummary;
109     /***
110      * The root of the Abstract Syntax Tree for the source
111      */
112     protected ModuleNode ast;
113 
114 
115     /***
116      * Initializes the SourceUnit from existing machinery.
117      */
118     public SourceUnit(String name, ReaderSource source, CompilerConfiguration flags, GroovyClassLoader loader, ErrorCollector er) {
119         super(flags, loader, er);
120 
121         this.name = name;
122         this.source = source;
123     }
124 
125 
126     /***
127      * Initializes the SourceUnit from the specified file.
128      */
129     public SourceUnit(File source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
130         this(source.getPath(), new FileReaderSource(source, configuration), configuration, loader, er);
131     }
132 
133 
134     /***
135      * Initializes the SourceUnit from the specified URL.
136      */
137     public SourceUnit(URL source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
138         this(source.getPath(), new URLReaderSource(source, configuration), configuration, loader, er);
139     }
140 
141 
142     /***
143      * Initializes the SourceUnit for a string of source.
144      */
145     public SourceUnit(String name, String source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
146         this(name, new StringReaderSource(source, configuration), configuration, loader, er);
147     }
148 
149 
150     /***
151      * Returns the name for the SourceUnit.
152      */
153     public String getName() {
154         return name;
155     }
156 
157 
158     /***
159      * Returns the Concrete Syntax Tree produced during parse()ing.
160      */
161     public Reduction getCST() {
162         return this.cst;
163     }
164 
165     /***
166      * Returns the Source Summary
167      */
168     public SourceSummary getSourceSummary() {
169         return this.sourceSummary;
170     }
171     /***
172      * Returns the Abstract Syntax Tree produced during parse()ing
173      * and expanded during later phases.
174      */
175     public ModuleNode getAST() {
176         return this.ast;
177     }
178 
179 
180     /***
181      * Convenience routine, primarily for use by the InteractiveShell,
182      * that returns true if parse() failed with an unexpected EOF.
183      */
184     public boolean failedWithUnexpectedEOF() {
185         boolean result = false;
186         if (getErrorCollector().hasErrors()) {
187             // Classic support
188             Message last = (Message) getErrorCollector().getLastError();
189             if (last instanceof SyntaxErrorMessage) {
190                 SyntaxException cause = ((SyntaxErrorMessage) last).getCause();
191                 if (cause instanceof UnexpectedTokenException) {
192                     Token unexpected = ((UnexpectedTokenException) cause).getUnexpectedToken();
193                     if (unexpected.isA(Types.EOF)) {
194                         result = true;
195                     }
196                 }
197             }
198             // JSR support
199             if (last instanceof ExceptionMessage) {
200                 ExceptionMessage exceptionMessage = (ExceptionMessage) last;
201                 Exception cause = exceptionMessage.getCause();
202                 if (cause instanceof NoViableAltException) {
203                     NoViableAltException antlrException = (NoViableAltException) cause;
204                     result = isEofToken(antlrException.token);
205                 }
206                 if (cause instanceof MismatchedTokenException) {
207                     MismatchedTokenException antlrException = (MismatchedTokenException) cause;
208                     result = isEofToken(antlrException.token);
209                 }
210             }
211         }
212         return result;    
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         if ("xml".equals(System.getProperty("groovy.ast"))) {
326             saveAsXML(name,ast);
327         }
328     }
329 
330     private void saveAsXML(String name, ModuleNode ast) {
331         XStream xstream = new XStream();
332         try {
333             xstream.toXML(ast,new FileWriter(name + ".xml"));
334             System.out.println("Written AST to " + name + ".xml");
335         } catch (Exception e) {
336             System.out.println("Couldn't write to " + name + ".xml");
337             e.printStackTrace();
338         }
339     }
340     //---------------------------------------------------------------------------    // SOURCE SAMPLING
341 
342     /***
343      * Returns a sampling of the source at the specified line and column,
344      * of null if it is unavailable.
345      */
346     public String getSample(int line, int column, Janitor janitor) {
347         String sample = null;
348         String text = source.getLine(line, janitor);
349 
350         if (text != null) {
351             if (column > 0) {
352                 String marker = Utilities.repeatString(" ", column - 1) + "^";
353 
354                 if (column > 40) {
355                     int start = column - 30 - 1;
356                     int end = (column + 10 > text.length() ? text.length() : column + 10 - 1);
357                     sample = "   " + text.substring(start, end) + Utilities.eol() + "   " + marker.substring(start, marker.length());
358                 }
359                 else {
360                     sample = "   " + text + Utilities.eol() + "   " + marker;
361                 }
362             }
363             else {
364                 sample = text;
365             }
366         }
367 
368         return sample;
369     }
370     
371     public void addException(Exception e) throws CompilationFailedException {
372         getErrorCollector().addException(e,this);
373     }
374     
375     public void addError(SyntaxException se) throws CompilationFailedException {
376         getErrorCollector().addError(se,this);
377     }
378 }
379 
380 
381 
382