|
Groovy example source code file (InteractiveShell.java)
The Groovy InteractiveShell.java source code/* * 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 groovy.lang.Binding; import groovy.lang.Closure; import groovy.lang.GroovyShell; import groovy.lang.GroovySystem; import org.codehaus.groovy.tools.shell.util.MessageSource; import org.codehaus.groovy.control.CompilationFailedException; import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.runtime.InvokerInvocationException; import org.codehaus.groovy.runtime.DefaultGroovyMethods; import org.codehaus.groovy.tools.ErrorReporter; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.io.Writer; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Iterator; 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; import jline.ConsoleReader; import jline.SimpleCompletor; /** * A simple interactive shell for evaluating groovy expressions on the command line (aka. groovysh). * * @author <a href="mailto:james@coredevelopers.net">James Strachan * @author <a href="mailto:cpoirier@dreaming.org" >Chris Poirier * @author Yuri Schimke * @author Brian McCallistair * @author Guillaume Laforge * @author Dierk Koenig, include the inspect command, June 2005 * @author <a href="mailto:jason@planet57.com">Jason Dillon * * @version $Revision: 21223 $ */ @Deprecated 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) { System.err.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[] args) throws 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(MESSAGES.getMessage("cli.option.help.description")) .create('h')); options.addOption(OptionBuilder.withLongOpt("version") .withDescription(MESSAGES.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(options, args, true); String[] lineargs = line.getArgs(); // Puke if there were arguments, we don't support any right now if (lineargs.length != 0) { System.err.println(MESSAGES.format("cli.info.unexpected_args", new Object[] { DefaultGroovyMethods.join(lineargs, " ") })); System.exit(1); } PrintWriter writer = new PrintWriter(System.out); 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(MESSAGES.format("cli.info.version", new Object[] { GroovySystem.getVersion() })); writer.flush(); System.exit(0); } } /** * Default constructor, initializes uses new binding and system streams. */ public InteractiveShell() throws IOException { this(System.in, System.out, System.err); } /** * Constructs a new InteractiveShell instance * * @param in The input stream to use * @param out The output stream to use * @param err The error stream to use */ public InteractiveShell(final InputStream in, final PrintStream out, final PrintStream err) throws IOException { this(null, new Binding(), in, out, err); } /** * Constructs a new InteractiveShell instance * * @param binding The binding instance * @param in The input stream to use * @param out The output stream to use * @param err The error stream to use */ public InteractiveShell(final Binding binding, final InputStream in, final PrintStream out, final PrintStream err) throws IOException { this(null, binding, in, out, err); } /** * Constructs a new InteractiveShell instance * * @param parent The parent ClassLoader * @param binding The binding instance * @param in The input stream to use * @param out The output stream to use * @param err The error stream to use */ public InteractiveShell(final ClassLoader parent, final Binding binding, final InputStream in, final PrintStream out, final PrintStream err) throws IOException { assert binding != null; assert in != null; assert out != null; assert err != null; this.in = in; this.out = out; this.err = err; // Initialize the JLine console input reader Writer writer = new OutputStreamWriter(out); reader = new ConsoleReader(in, writer); reader.setDefaultPrompt("groovy> "); // Add some completors to fancy things up reader.addCompletor(new CommandNameCompletor()); if (parent != null) { shell = new GroovyShell(parent, binding); } else { shell = new GroovyShell(binding); } // Add some default variables to the shell Map map = shell.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", 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 out.println(MESSAGES.format("startup_banner.0", new Object[] { GroovySystem.getVersion(), System.getProperty("java.version") })); out.println(MESSAGES.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 (beforeExecution != null) { beforeExecution.call(); } lastResult = shell.evaluate(code); if (afterExecution != null) { afterExecution.call(); } // Shows the result of the evaluated code out.print("===> "); out.println(lastResult); } catch (CompilationFailedException e) { err.println(e); } catch (Throwable e) { // Unroll invoker exceptions if (e instanceof InvokerInvocationException) { e = e.getCause(); } filterAndPrintStackTrace(e); } } } } /** * A closure that is executed before the execution of a given script * * @param beforeExecution The closure to execute */ public void setBeforeExecution(final Closure beforeExecution) { this.beforeExecution = 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') * * @param afterExecution The closure to execute */ public void setAfterExecution(final Closure afterExecution) { this.afterExecution = afterExecution; } /** * Filter stacktraces to show only relevant lines of the exception thrown. * * @param cause the throwable whose stacktrace needs to be filtered */ private void filterAndPrintStackTrace(final Throwable cause) { assert cause != null; // // TODO: Use message... // err.print("ERROR: "); err.println(cause); cause.printStackTrace(err); // // 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() { stale = true; pending = null; line = 1; parser = null; error = 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 { pending = reader.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 (pending == 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 = pending.trim(); if (COMMAND_MAPPINGS.containsKey(command)) { int code = ((Integer)COMMAND_MAPPINGS.get(command)).intValue(); switch (code) { case COMMAND_ID_EXIT: return null; case COMMAND_ID_HELP: displayHelp(); break; case COMMAND_ID_DISCARD: reset(); done = true; break; case COMMAND_ID_DISPLAY: displayStatement(); break; case COMMAND_ID_EXPLAIN: explainStatement(); break; case COMMAND_ID_BINDING: displayBinding(); break; case COMMAND_ID_EXECUTE: if (complete) { done = true; } else { err.println(MESSAGES.getMessage("command.execute.not_complete")); } break; case COMMAND_ID_DISCARD_LOADED_CLASSES: resetLoadedClasses(); break; case COMMAND_ID_INSPECT: 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 accumulated statement text, and errors are reported. The // pending input is accepted or rejected based on that parsing. freshen(); if (pending.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 (error == 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 (lastResult == null){ err.println(MESSAGES.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 doesn't 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 experience 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(type, new Object[]{ lastResult }); } catch (Exception e) { err.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 accepted.toString(); } return ""; } /** * Returns the current statement, including pending text. */ private String current() { return accepted.toString() + pending + NEW_LINE; } /** * Accepts the pending text into the statement. */ private void accept() { accepted.append(pending).append(NEW_LINE); line += 1; } /** * Clears accepted if stale. */ private void freshen() { if (stale) { accepted.setLength(0); stale = false; } } //--------------------------------------------------------------------------- // SUPPORT ROUTINES /** * Attempts to parse the specified code with the specified tolerance. * Updates the <code>parser and Other Groovy examples (source code examples)Here is a short list of links related to this Groovy InteractiveShell.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.