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