Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright 2003-2007 the original author or authors.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 package groovy.ui;
 
 
 
 import java.io.Writer;
 import java.util.Map;
 import java.util.Set;
 
 import  org.apache.commons.cli.CommandLine;
 import  org.apache.commons.cli.CommandLineParser;
 import  org.apache.commons.cli.OptionBuilder;
 import  org.apache.commons.cli.PosixParser;
 import  org.apache.commons.cli.Options;
 import  org.apache.commons.cli.HelpFormatter;
 
 
 //
 // TODO: See if there is any reason why this class is implemented in Java instead of Groovy, and if there
 //       is none, then port it over ;-)
 //
 
 //
 // NOTE: After GShell becomes a little more mature, this shell could be easily implemented as a set of GShell
 //       commands, and would inherit a lot of functionality and could be extended easily to allow groovysh
 //       to become very, very powerful
 //
 
A simple interactive shell for evaluating groovy expressions on the command line (aka. groovysh).

Author(s):
James Strachan
Chris Poirier
Yuri Schimke
Brian McCallistair
Guillaume Laforge
Dierk Koenig, include the inspect command, June 2005
Jason Dillon
Version:
$Revision: 12135 $
 
 public class InteractiveShell
     implements Runnable
 {
     private static final String NEW_LINE = System.getProperty("line.separator");
     private static final MessageSource MESSAGES = new MessageSource(InteractiveShell.class);
 
     private final GroovyShell shell;
     private final InputStream in// FIXME: This doesn't really need to be a field, but hold on to it for now
     private final PrintStream out;
     private final PrintStream err;
     private final ConsoleReader reader;
 
     private Object lastResult;
     private Closure beforeExecution;
     private Closure afterExecution;

    
Entry point when called directly.
 
     public static void main(final String args[]) {
         try {
             processCommandLineArguments(args);
 
            final InteractiveShell groovy = new InteractiveShell();
            groovy.run();
        }
        catch (Exception e) {
            ..println("FATAL: " + e);
            e.printStackTrace();
            System.exit(1);
        }
        System.exit(0);
    }

    
Process cli args when the shell is invoked via main().

Noinspection:
AccessStaticViaInstance
    private static void processCommandLineArguments(final String[] argsthrows Exception {
        assert args != null;
        //
        // TODO: Let this take a single, optional argument which is a file or URL to run?
        //
        
        Options options = new Options();
        options.addOption(OptionBuilder.withLongOpt("help")
            .withDescription(.getMessage("cli.option.help.description"))
            .create('h'));
        options.addOption(OptionBuilder.withLongOpt("version")
            .withDescription(.getMessage("cli.option.version.description"))
            .create('V'));
        //
        // TODO: Add more options, maybe even add an option to prime the buffer from a URL or File?
        //
        
        //
        // FIXME: This does not currently barf on unsupported options short options, though it does for long ones.
        //        Same problem with commons-cli 1.0 and 1.1
        //
        
        CommandLineParser parser = new PosixParser();
        CommandLine line = parser.parse(optionsargstrue);
        String[] lineargs = line.getArgs();
        // Puke if there were arguments, we don't support any right now
        if (lineargs.length != 0) {
            ..println(.format("cli.info.unexpected_args"new Object[] { DefaultGroovyMethods.join(lineargs" ") }));
            System.exit(1);
        }
        PrintWriter writer = new PrintWriter(.);
        if (line.hasOption('h')) {
            HelpFormatter formatter = new HelpFormatter();
            formatter.printHelp(
                writer,
                80, // width
                "groovysh [options]",
                "",
                options,
                4, // left pad
                4, // desc pad
                "",
                false); // auto usage
            writer.flush();
            System.exit(0);
        }
        if (line.hasOption('V')) {
            writer.println(.format("cli.info.version"new Object[] { InvokerHelper.getVersion() }));
            writer.flush();
            System.exit(0);
        }
    }

    
Default constructor, initializes uses new binding and system streams.
    public InteractiveShell() throws IOException {
        this(...);
    }

    
Constructs a new InteractiveShell instance

Parameters:
in The input stream to use
out The output stream to use
err The error stream to use
    public InteractiveShell(final InputStream infinal PrintStream outfinal PrintStream errthrows IOException {
        this(nullnew Binding(), inouterr);
    }

    
Constructs a new InteractiveShell instance

Parameters:
binding The binding instance
in The input stream to use
out The output stream to use
err The error stream to use
    
    public InteractiveShell(final Binding bindingfinal InputStream infinal PrintStream outfinal PrintStream errthrows IOException {
    	this(nullbindinginouterr);
    }
    
    
Constructs a new InteractiveShell instance

Parameters:
parent The parent ClassLoader
binding The binding instance
in The input stream to use
out The output stream to use
err The error stream to use
    public InteractiveShell(final ClassLoader parentfinal Binding bindingfinal InputStream infinal PrintStream outfinal PrintStream errthrows IOException {
        assert binding != null;
        assert in != null;
        assert out != null;
        assert err != null;
        this. = in;
        this. = out;
        this. = err;
        // Initialize the JLine console input reader
        Writer writer = new OutputStreamWriter(out);
         = new ConsoleReader(inwriter);
        .setDefaultPrompt("groovy> ");
        // Add some completors to fancy things up
        if (parent != null) {
             = new GroovyShell(parentbinding);
        }
        else {
             = new GroovyShell(binding);
        }        
        // Add some default variables to the shell
        Map map = .getContext().getVariables();
        //
        // FIXME: Um, is this right?  Only set the "shell" var in the context if its set already?
        //
        
        if (map.get("shell") != null) {
            map.put("shell");
        }
    }    
    //---------------------------------------------------------------------------
    // COMMAND LINE PROCESSING LOOP
    //
    // TODO: Add a general error display handler, and probably add a "ERROR: " prefix to the result for clarity ?
    //       Maybe add one for WARNING's too?
    //
    
    
Reads commands and statements from input stream and processes them.
    public void run() {
        // Display the startup banner
        .println(.format("startup_banner.0"new Object[] { InvokerHelper.getVersion(), System.getProperty("java.version") }));
        .println(.getMessage("startup_banner.1"));
        while (true) {
            // Read a code block to evaluate; this will deal with basic error handling
            final String code = read();
            // If we got a null, then quit
            if (code == null) {
                break;
            }
            reset();
            // Evaluate the code block if it was parsed
            if (code.length() > 0) {
                try {
                    if ( != null) {
                        .call();
                    }
                     = .evaluate(code);
                    
                    if ( != null) {
                        .call();
                    }
                    // Shows the result of the evaluated code
                    .print("===> ");
                    .println();
                }
                catch (CompilationFailedException e) {
                    .println(e);
                }
                catch (Throwable e) {
                    // Unroll invoker exceptions
                    if (e instanceof InvokerInvocationException) {
                        e = e.getCause();
                    }
                    
                    filterAndPrintStackTrace(e);
                }
            }
        }
    }

    
A closure that is executed before the exection of a given script

Parameters:
beforeExecution The closure to execute
    public void setBeforeExecution(final Closure beforeExecution) {
        this. = beforeExecution;
    }

    
A closure that is executed after the execution of the last script. The result of the execution is passed as the first argument to the closure (the value of 'it')

Parameters:
afterExecution The closure to execute
    public void setAfterExecution(final Closure afterExecution) {
        this. = afterExecution;
    }

    
Filter stacktraces to show only relevant lines of the exception thrown.

Parameters:
cause the throwable whose stacktrace needs to be filtered
    private void filterAndPrintStackTrace(final Throwable cause) {
        assert cause != null;
        //
        // TODO: Use message...
        //
        
        .print("ERROR: ");
        .println(cause);
        cause.printStackTrace();
        //
        // FIXME: What is the point of this?  AFAICT, this just produces crappy/corrupt traces and is completely useless
        //
        
//        StackTraceElement[] stackTrace = e.getStackTrace();
//
//        for (int i = 0; i < stackTrace.length; i++) {
//            StackTraceElement element = stackTrace[i];
//            String fileName = element.getFileName();
//
//            if ((fileName==null || (!fileName.endsWith(".java")) && (!element.getClassName().startsWith("gjdk")))) {
//                err.print("\tat ");
//                err.println(element);
//            }
//        }
    }
    //---------------------------------------------------------------------------
    // COMMAND LINE PROCESSING MACHINERY

    
The statement text accepted to date
    private StringBuffer accepted = new StringBuffer();

    
A line of statement text not yet accepted
    private String pending;
    //
    // FIXME: Doesn't look like 'line' is really used/needed anywhere... could drop it, or perhaps
    //        could use it to update the prompt er something to show the buffer size?
    //

    
The current line number
    private int line;
    
    
Set to force clear of accepted
    private boolean stale = false;

    
A SourceUnit used to check the statement
    private SourceUnit parser;

    
Any actual syntax error caught during parsing
    private Exception error;

    
Resets the command-line processing machinery after use.
    protected void reset() {
         = true;
         = null;
         = 1;
         = null;
         = null;
    }
    //
    // FIXME: This Javadoc is not correct... read() will return the full code block read until "go"
    //
    
    
Reads a single statement from the command line. Also identifies and processes command shell commands. Returns the command text on success, or null when command processing is complete. NOTE: Changed, for now, to read until 'execute' is issued. At 'execute', the statement must be complete.
    protected String read() {
        reset();
        
        boolean complete = false;
        boolean done = false;
        
        while (/* !complete && */ !done) {
            // Read a line.  If IOException or null, or command "exit", terminate processing.
            try {
                 = .readLine();
            }
            catch (IOException e) {
                //
                // FIXME: Shouldn't really eat this exception, may be something we need to see... ?
                //
            }
            // If result is null then we are shutting down
            if ( == null) {
                return null;
            }
            // First up, try to process the line as a command and proceed accordingly
            // Trim what we have for use in command bits, so things like "help " actually show the help screen
            String command = .trim();
            if (.containsKey(command)) {
                int code = ((Integer).get(command)).intValue();
                switch (code) {
                    case :
                        return null;
                    
                    case :
                        displayHelp();
                        break;
                    case :
                        reset();
                        done = true;
                        break;
                    case :
                        displayStatement();
                        break;
                    case :
                        explainStatement();
                        break;
                    case :
                        displayBinding();
                        break;
                    case :
                        if (complete) {
                            done = true;
                        }
                        else {
                            .println(.getMessage("command.execute.not_complete"));
                        }
                        break;
                    case :
                        resetLoadedClasses();
                        break;
                    case :
                        inspect();
                        break;
                    default:
                        throw new Error("BUG: Unknown command for code: " + code);
                }
                // Finished processing command bits, continue reading, don't need to process code
                continue;
            }
            // Otherwise, it's part of a statement.  If it's just whitespace,
            // we'll just accept it and move on.  Otherwise, parsing is attempted
            // on the cumulated statement text, and errors are reported.  The
            // pending input is accepted or rejected based on that parsing.
            freshen();
            if (.trim().length() == 0) {
                accept();
                continue;
            }
            // Try to parse the current code buffer
            final String code = current();
            
            if (parse(code)) {
                // Code parsed fine
                accept();
                complete = true;
            }
            else if ( == null) {
                // Um... ???
                accept();
            }
            else {
                // Parse failed, spit out something to the user
                report();
            }
        }
        // Get and return the statement.
        return accepted(complete);
    }
    private void inspect() {
        if ( == null){
            .println(.getMessage("command.inspect.no_result"));
            return;
        }
        //
        // FIXME: Update this once we have joint compile happy in the core build?
        //
        // this should read: groovy.inspect.swingui.ObjectBrowser.inspect(lastResult)
        // but this doesnt compile since ObjectBrowser.groovy is compiled after this class.
        //
        //
        // FIXME: When launching this, if the user tries to "exit" and the window is still opened, the shell will
        //        hang... not really nice user experence IMO.  Should try to fix this if we can.
        //
        
        try {
            Class type = Class.forName("groovy.inspect.swingui.ObjectBrowser");
            Method method = type.getMethod("inspect"new Class[]{ Object.class });
            method.invoke(typenew Object[]{  });
        }
        catch (Exception e) {
            .println("Cannot invoke ObjectBrowser");
            e.printStackTrace();
        }
    }

    
Returns the accepted statement as a string. If not complete, returns empty string.
    private String accepted(final boolean complete) {
        if (complete) {
            return .toString();
        }
        return "";
    }

    
Returns the current statement, including pending text.
    private String current() {
        return .toString() +  + ;
    }

    
Accepts the pending text into the statement.
    private void accept() {
         += 1;
    }

    
Clears accepted if stale.
    private void freshen() {
        if () {
            .setLength(0);
             = false;
        }
    }
    //---------------------------------------------------------------------------
    // SUPPORT ROUTINES

    
Attempts to parse the specified code with the specified tolerance. Updates the parser and error members appropriately. Returns true if the text parsed, false otherwise. The attempts to identify and suppress errors resulting from the unfinished source text.
    private boolean parse(final String codefinal int tolerance) {
        assert code != null;
        boolean parsed = false;
         = null;
         = null;
        // Create the parser and attempt to parse the text as a top-level statement.
        try {
             = SourceUnit.create("groovysh-script"codetolerance);
            .parse();
            parsed = true;
        }
        // We report errors other than unexpected EOF to the user.
        catch (CompilationFailedException e) {
            if (.getErrorCollector().getErrorCount() > 1 || !.failedWithUnexpectedEOF()) {
                 = e;
            }
        }
        catch (Exception e) {
             = e;
        }
        return parsed;
    }
    private boolean parse(final String code) {
        return parse(code, 1);
    }
    
    
Reports the last parsing error to the user.
    private void report() {
        .println("Discarding invalid text:"); // TODO: i18n
        new ErrorReporter(false).write();
    }
    //-----------------------------------------------------------------------
    // COMMANDS
    //
    // TODO: Add a simple command to read in a File/URL into the buffer for execution, but need better command
    //       support first (aka GShell) so we can allow commands to take args, etc.
    //
    private static final int COMMAND_ID_EXIT = 0;
    private static final int COMMAND_ID_HELP = 1;
    private static final int COMMAND_ID_DISCARD = 2;
    private static final int COMMAND_ID_DISPLAY = 3;
    private static final int COMMAND_ID_EXPLAIN = 4;
    private static final int COMMAND_ID_EXECUTE = 5;
    private static final int COMMAND_ID_BINDING = 6;
    private static final int COMMAND_ID_DISCARD_LOADED_CLASSES = 7;
    private static final int COMMAND_ID_INSPECT = 8;
    private static final int LAST_COMMAND_ID = 8;
    private static final String[] COMMANDS = {
        "exit",
        "help",
        "discard",
        "display",
        "explain",
        "execute",
        "binding",
        "discardclasses",
        "inspect"
    };
    private static final Map COMMAND_MAPPINGS = new HashMap();
    static {
        for (int i = 0; i <= i++) {
            .put([i], Integer.valueOf(i));
        }
        // A few synonyms
        .put("quit", Integer.valueOf());
        .put("go", Integer.valueOf());
    }
    private static final Map COMMAND_HELP = new HashMap();
    static {
        .put([],    "exit/quit         - " + .getMessage("command.exit.descripion"));
        .put([],    "help              - " + .getMessage("command.help.descripion"));
        .put([], "discard           - " + .getMessage("command.discard.descripion"));
        .put([], "display           - " + .getMessage("command.display.descripion"));
        //
        // FIXME: If this is disabled, then er comment it out, so it doesn't confuse the user
        //
        
        .put([], "explain           - " + .getMessage("command.explain.descripion"));
        .put([], "execute/go        - " + .getMessage("command.execute.descripion"));
        .put([], "binding           - " + .getMessage("command.binding.descripion"));
                                                       "discardclasses    - " + .getMessage("command.discardclasses.descripion"));
        .put([], "inspect           - " + .getMessage("command.inspect.descripion"));
    }

    
Displays help text about available commands.
    private void displayHelp() {
        .println(.getMessage("command.help.available_commands"));
        for (int i = 0; i <= i++) {
            .print("    ");
            .println(.get([i]));
        }
    }

    
Displays the accepted statement.
    private void displayStatement() {
        final String[] lines = .toString().split();
        if (lines.length == 1 && lines[0].trim().equals("")) {
            .println(.getMessage("command.display.buffer_empty"));
        }
        else {
            // Eh, try to pick a decent pad size... but don't try to hard
            int padsize = 2;
            if (lines.length >= 10) padsize++;
            if (lines.length >= 100) padsize++;
            if (lines.length >= 1000) padsize++;
            // Dump the current buffer with a line number prefix
            for (int i = 0; i < lines.lengthi++) {
                // Normalize the field size of the line number
                String lineno = DefaultGroovyMethods.padLeft(String.valueOf(i + 1), Integer.valueOf(padsize), " ");
                
                .print(lineno);
                .print("> ");
                .println(lines[i]);
            }
        }
    }

    
Displays the current binding used when instanciating the shell.
    private void displayBinding() {
        Binding context = .getContext();
        Map variables = context.getVariables();
        Set set = variables.keySet();
        if (set.isEmpty()) {
            .println(.getMessage("command.binding.binding_empty"));
        }
        else {
            .println(.getMessage("command.binding.available_variables"));
            Iterator iter = set.iterator();
            while (iter.hasNext()) {
                Object key = iter.next();
                .print("    ");
                .print(key);
                .print(" = ");
                .println(variables.get(key));
            }
        }
    }

    
Attempts to parse the accepted statement and display the parse tree for it.
    private void explainStatement() {
        if (parse(accepted(true), 10) ||  == null) {
            .println(.getMessage("command.explain.tree_header"));
            //out.println(tree);
        }
        else {
            .println(.getMessage("command.explain.unparsable"));
        }
    }
    private void resetLoadedClasses() {
        .resetLoadedClasses();
        
        .println(.getMessage("command.discardclasses.classdefs_discarded"));
    }
    //
    // Custom JLine Completors to fancy up the user experence more.
    //
    private class CommandNameCompletor
        extends SimpleCompletor
    {
        public CommandNameCompletor() {
            super(new String[0]);
            // Add each command name/alias as a candidate
            Iterator iter = .keySet().iterator();
            while (iter.hasNext()) {
                addCandidateString((String)iter.next());
            }
        }
    }
    //
    // TODO: Add local variable completion?
    //
    //
    // TODO: Add shell method complention?
    //
    /*
    private void findShellMethods(String complete) {
        List methods = shell.getMetaClass().getMetaMethods();
        for (Iterator i = methods.iterator(); i.hasNext();) {
            MetaMethod method = (MetaMethod) i.next();
            if (method.getName().startsWith(complete)) {
                if (method.getParameterTypes().length > 0) {
                    completions.add(method.getName() + "(");
                }
                else {
                    completions.add(method.getName() + "()");
                }
            }
        }
    }
    private void findLocalVariables(String complete) {
        Set names = shell.getContext().getVariables().keySet();
        for (Iterator i = names.iterator(); i.hasNext();) {
            String name = (String) i.next();
            if (name.startsWith(complete)) {
                completions.add(name);
            }
        }
    }
    */
New to GrepCode? Check out our FAQ X