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