0001: /*****************************************************************************
0002: * *
0003: * This file is part of the BeanShell Java Scripting distribution. *
0004: * Documentation and updates may be found at http://www.beanshell.org/ *
0005: * *
0006: * Sun Public License Notice: *
0007: * *
0008: * The contents of this file are subject to the Sun Public License Version *
0009: * 1.0 (the "License"); you may not use this file except in compliance with *
0010: * the License. A copy of the License is available at http://www.sun.com *
0011: * *
0012: * The Original Code is BeanShell. The Initial Developer of the Original *
0013: * Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright *
0014: * (C) 2000. All Rights Reserved. *
0015: * *
0016: * GNU Public License Notice: *
0017: * *
0018: * Alternatively, the contents of this file may be used under the terms of *
0019: * the GNU Lesser General Public License (the "LGPL"), in which case the *
0020: * provisions of LGPL are applicable instead of those above. If you wish to *
0021: * allow use of your version of this file only under the terms of the LGPL *
0022: * and not to allow others to use your version of this file under the SPL, *
0023: * indicate your decision by deleting the provisions above and replace *
0024: * them with the notice and other provisions required by the LGPL. If you *
0025: * do not delete the provisions above, a recipient may use your version of *
0026: * this file under either the SPL or the LGPL. *
0027: * *
0028: * Patrick Niemeyer (pat@pat.net) *
0029: * Author of Learning Java, O'Reilly & Associates *
0030: * http://www.pat.net/~pat/ *
0031: * *
0032: *****************************************************************************/package bsh;
0033:
0034: import java.util.Vector;
0035: import java.io.*;
0036: import java.lang.reflect.Method;
0037: import java.lang.reflect.InvocationTargetException;
0038:
0039: /**
0040: The BeanShell script interpreter.
0041:
0042: An instance of Interpreter can be used to source scripts and evaluate
0043: statements or expressions.
0044: <p>
0045: Here are some examples:
0046:
0047: <p><blockquote><pre>
0048: Interpeter bsh = new Interpreter();
0049:
0050: // Evaluate statements and expressions
0051: bsh.eval("foo=Math.sin(0.5)");
0052: bsh.eval("bar=foo*5; bar=Math.cos(bar);");
0053: bsh.eval("for(i=0; i<10; i++) { print(\"hello\"); }");
0054: // same as above using java syntax and apis only
0055: bsh.eval("for(int i=0; i<10; i++) { System.out.println(\"hello\"); }");
0056:
0057: // Source from files or streams
0058: bsh.source("myscript.bsh"); // or bsh.eval("source(\"myscript.bsh\")");
0059:
0060: // Use set() and get() to pass objects in and out of variables
0061: bsh.set( "date", new Date() );
0062: Date date = (Date)bsh.get( "date" );
0063: // This would also work:
0064: Date date = (Date)bsh.eval( "date" );
0065:
0066: bsh.eval("year = date.getYear()");
0067: Integer year = (Integer)bsh.get("year"); // primitives use wrappers
0068:
0069: // With Java1.3+ scripts can implement arbitrary interfaces...
0070: // Script an awt event handler (or source it from a file, more likely)
0071: bsh.eval( "actionPerformed( e ) { print( e ); }");
0072: // Get a reference to the script object (implementing the interface)
0073: ActionListener scriptedHandler =
0074: (ActionListener)bsh.eval("return (ActionListener)this");
0075: // Use the scripted event handler normally...
0076: new JButton.addActionListener( script );
0077: </pre></blockquote>
0078: <p>
0079:
0080: In the above examples we showed a single interpreter instance, however
0081: you may wish to use many instances, depending on the application and how
0082: you structure your scripts. Interpreter instances are very light weight
0083: to create, however if you are going to execute the same script repeatedly
0084: and require maximum performance you should consider scripting the code as
0085: a method and invoking the scripted method each time on the same interpreter
0086: instance (using eval()).
0087: <p>
0088:
0089: See the BeanShell User's Manual for more information.
0090: */
0091: public class Interpreter implements Runnable, ConsoleInterface,
0092: Serializable {
0093: /* --- Begin static members --- */
0094:
0095: public static final String VERSION = "2.0b5";
0096: /*
0097: Debug utils are static so that they are reachable by code that doesn't
0098: necessarily have an interpreter reference (e.g. tracing in utils).
0099: In the future we may want to allow debug/trace to be turned on on
0100: a per interpreter basis, in which case we'll need to use the parent
0101: reference in some way to determine the scope of the command that
0102: turns it on or off.
0103: */
0104: public static boolean DEBUG, TRACE, LOCALSCOPING;
0105:
0106: // This should be per instance
0107: transient static PrintStream debug;
0108: static String systemLineSeparator = "\n"; // default
0109:
0110: static {
0111: staticInit();
0112: }
0113:
0114: /** Shared system object visible under bsh.system */
0115: static This sharedObject;
0116:
0117: /**
0118: Strict Java mode
0119: @see #setStrictJava( boolean )
0120: */
0121: private boolean strictJava = false;
0122:
0123: /* --- End static members --- */
0124:
0125: /* --- Instance data --- */
0126:
0127: transient Parser parser;
0128: NameSpace globalNameSpace;
0129: transient Reader in;
0130: transient PrintStream out;
0131: transient PrintStream err;
0132: ConsoleInterface console;
0133:
0134: /** If this interpeter is a child of another, the parent */
0135: Interpreter parent;
0136:
0137: /** The name of the file or other source that this interpreter is reading */
0138: String sourceFileInfo;
0139:
0140: /** by default in interactive mode System.exit() on EOF */
0141: private boolean exitOnEOF = true;
0142:
0143: protected boolean evalOnly, // Interpreter has no input stream, use eval() only
0144: interactive; // Interpreter has a user, print prompts, etc.
0145:
0146: /** Control the verbose printing of results for the show() command. */
0147: private boolean showResults;
0148:
0149: /* --- End instance data --- */
0150:
0151: /**
0152: The main constructor.
0153: All constructors should now pass through here.
0154:
0155: @param namespace If namespace is non-null then this interpreter's
0156: root namespace will be set to the one provided. If it is null a new
0157: one will be created for it.
0158: @param parent The parent interpreter if this interpreter is a child
0159: of another. May be null. Children share a BshClassManager with
0160: their parent instance.
0161: @param sourceFileInfo An informative string holding the filename
0162: or other description of the source from which this interpreter is
0163: reading... used for debugging. May be null.
0164: */
0165: public Interpreter(Reader in, PrintStream out, PrintStream err,
0166: boolean interactive, NameSpace namespace,
0167: Interpreter parent, String sourceFileInfo) {
0168: //System.out.println("New Interpreter: "+this +", sourcefile = "+sourceFileInfo );
0169: parser = new Parser(in);
0170: long t1 = System.currentTimeMillis();
0171: this .in = in;
0172: this .out = out;
0173: this .err = err;
0174: this .interactive = interactive;
0175: debug = err;
0176: this .parent = parent;
0177: if (parent != null)
0178: setStrictJava(parent.getStrictJava());
0179: this .sourceFileInfo = sourceFileInfo;
0180:
0181: BshClassManager bcm = BshClassManager.createClassManager(this );
0182: if (namespace == null)
0183: this .globalNameSpace = new NameSpace(bcm, "global");
0184: else
0185: this .globalNameSpace = namespace;
0186:
0187: // now done in NameSpace automatically when root
0188: // The classes which are imported by default
0189: //globalNameSpace.loadDefaultImports();
0190:
0191: /*
0192: Create the root "bsh" system object if it doesn't exist.
0193: */
0194: if (!(getu("bsh") instanceof bsh.This))
0195: initRootSystemObject();
0196:
0197: if (interactive)
0198: loadRCFiles();
0199:
0200: long t2 = System.currentTimeMillis();
0201: if (Interpreter.DEBUG)
0202: Interpreter.debug("Time to initialize interpreter: "
0203: + (t2 - t1));
0204: }
0205:
0206: public Interpreter(Reader in, PrintStream out, PrintStream err,
0207: boolean interactive, NameSpace namespace) {
0208: this (in, out, err, interactive, namespace, null, null);
0209: }
0210:
0211: public Interpreter(Reader in, PrintStream out, PrintStream err,
0212: boolean interactive) {
0213: this (in, out, err, interactive, null);
0214: }
0215:
0216: /**
0217: Construct a new interactive interpreter attached to the specified
0218: console using the specified parent namespace.
0219: */
0220: public Interpreter(ConsoleInterface console,
0221: NameSpace globalNameSpace) {
0222:
0223: this (console.getIn(), console.getOut(), console.getErr(), true,
0224: globalNameSpace);
0225:
0226: setConsole(console);
0227: }
0228:
0229: /**
0230: Construct a new interactive interpreter attached to the specified
0231: console.
0232: */
0233: public Interpreter(ConsoleInterface console) {
0234: this (console, null);
0235: }
0236:
0237: /**
0238: Create an interpreter for evaluation only.
0239: */
0240: public Interpreter() {
0241: this (new StringReader(""), System.out, System.err, false, null);
0242: evalOnly = true;
0243: setu("bsh.evalOnly", Primitive.TRUE);
0244: }
0245:
0246: // End constructors
0247:
0248: /**
0249: Attach a console
0250: Note: this method is incomplete.
0251: */
0252: public void setConsole(ConsoleInterface console) {
0253: this .console = console;
0254: setu("bsh.console", console);
0255: // redundant with constructor
0256: setOut(console.getOut());
0257: setErr(console.getErr());
0258: // need to set the input stream - reinit the parser?
0259: }
0260:
0261: private void initRootSystemObject() {
0262: BshClassManager bcm = getClassManager();
0263: // bsh
0264: setu("bsh", new NameSpace(bcm, "Bsh Object").getThis(this ));
0265:
0266: // init the static shared sharedObject if it's not there yet
0267: if (sharedObject == null)
0268: sharedObject = new NameSpace(bcm,
0269: "Bsh Shared System Object").getThis(this );
0270: // bsh.system
0271: setu("bsh.system", sharedObject);
0272: setu("bsh.shared", sharedObject); // alias
0273:
0274: // bsh.help
0275: This helpText = new NameSpace(bcm, "Bsh Command Help Text")
0276: .getThis(this );
0277: setu("bsh.help", helpText);
0278:
0279: // bsh.cwd
0280: try {
0281: setu("bsh.cwd", System.getProperty("user.dir"));
0282: } catch (SecurityException e) {
0283: // applets can't see sys props
0284: setu("bsh.cwd", ".");
0285: }
0286:
0287: // bsh.interactive
0288: setu("bsh.interactive", interactive ? Primitive.TRUE
0289: : Primitive.FALSE);
0290: // bsh.evalOnly
0291: setu("bsh.evalOnly", evalOnly ? Primitive.TRUE
0292: : Primitive.FALSE);
0293: }
0294:
0295: /**
0296: Set the global namespace for this interpreter.
0297: <p>
0298:
0299: Note: This is here for completeness. If you're using this a lot
0300: it may be an indication that you are doing more work than you have
0301: to. For example, caching the interpreter instance rather than the
0302: namespace should not add a significant overhead. No state other
0303: than the debug status is stored in the interpreter.
0304: <p>
0305:
0306: All features of the namespace can also be accessed using the
0307: interpreter via eval() and the script variable 'this.namespace'
0308: (or global.namespace as necessary).
0309: */
0310: public void setNameSpace(NameSpace globalNameSpace) {
0311: this .globalNameSpace = globalNameSpace;
0312: }
0313:
0314: /**
0315: Get the global namespace of this interpreter.
0316: <p>
0317:
0318: Note: This is here for completeness. If you're using this a lot
0319: it may be an indication that you are doing more work than you have
0320: to. For example, caching the interpreter instance rather than the
0321: namespace should not add a significant overhead. No state other than
0322: the debug status is stored in the interpreter.
0323: <p>
0324:
0325: All features of the namespace can also be accessed using the
0326: interpreter via eval() and the script variable 'this.namespace'
0327: (or global.namespace as necessary).
0328: */
0329: public NameSpace getNameSpace() {
0330: return globalNameSpace;
0331: }
0332:
0333: /**
0334: Run the text only interpreter on the command line or specify a file.
0335: */
0336: public static void main(String[] args) {
0337: if (args.length > 0) {
0338: String filename = args[0];
0339:
0340: String[] bshArgs;
0341: if (args.length > 1) {
0342: bshArgs = new String[args.length - 1];
0343: System.arraycopy(args, 1, bshArgs, 0, args.length - 1);
0344: } else
0345: bshArgs = new String[0];
0346:
0347: Interpreter interpreter = new Interpreter();
0348: //System.out.println("run i = "+interpreter);
0349: interpreter.setu("bsh.args", bshArgs);
0350: try {
0351: Object result = interpreter.source(filename,
0352: interpreter.globalNameSpace);
0353: if (result instanceof Class)
0354: try {
0355: invokeMain((Class) result, bshArgs);
0356: } catch (Exception e) {
0357: Object o = e;
0358: if (e instanceof InvocationTargetException)
0359: o = ((InvocationTargetException) e)
0360: .getTargetException();
0361: System.err.println("Class: " + result
0362: + " main method threw exception:" + o);
0363: }
0364: } catch (FileNotFoundException e) {
0365: System.out.println("File not found: " + e);
0366: } catch (TargetError e) {
0367: System.out.println("Script threw exception: " + e);
0368: if (e.inNativeCode())
0369: e.printStackTrace(DEBUG, System.err);
0370: } catch (EvalError e) {
0371: System.out.println("Evaluation Error: " + e);
0372: } catch (IOException e) {
0373: System.out.println("I/O Error: " + e);
0374: }
0375: } else {
0376: // Workaround for JDK bug 4071281, where system.in.available()
0377: // returns too large a value. This bug has been fixed in JDK 1.2.
0378: InputStream src;
0379: if (System.getProperty("os.name").startsWith("Windows")
0380: && System.getProperty("java.version").startsWith(
0381: "1.1.")) {
0382: src = new FilterInputStream(System.in) {
0383: public int available() throws IOException {
0384: return 0;
0385: }
0386: };
0387: } else
0388: src = System.in;
0389:
0390: Reader in = new CommandLineReader(
0391: new InputStreamReader(src));
0392: Interpreter interpreter = new Interpreter(in, System.out,
0393: System.err, true);
0394: interpreter.run();
0395: }
0396: }
0397:
0398: public static void invokeMain(Class clas, String[] args)
0399: throws Exception {
0400: Method main = Reflect.resolveJavaMethod(
0401: null/*BshClassManager*/, clas, "main",
0402: new Class[] { String[].class }, true/*onlyStatic*/);
0403: if (main != null)
0404: main.invoke(null, new Object[] { args });
0405: }
0406:
0407: /**
0408: Run interactively. (printing prompts, etc.)
0409: */
0410: public void run() {
0411: if (evalOnly)
0412: throw new RuntimeException("bsh Interpreter: No stream");
0413:
0414: /*
0415: We'll print our banner using eval(String) in order to
0416: exercise the parser and get the basic expression classes loaded...
0417: This ameliorates the delay after typing the first statement.
0418: */
0419: if (interactive)
0420: try {
0421: eval("printBanner();");
0422: } catch (EvalError e) {
0423: println("BeanShell " + VERSION
0424: + " - by Pat Niemeyer (pat@pat.net)");
0425: }
0426:
0427: // init the callstack.
0428: CallStack callstack = new CallStack(globalNameSpace);
0429:
0430: boolean eof = false;
0431: while (!eof) {
0432: try {
0433: // try to sync up the console
0434: System.out.flush();
0435: System.err.flush();
0436: Thread.yield(); // this helps a little
0437:
0438: if (interactive)
0439: print(getBshPrompt());
0440:
0441: eof = Line();
0442:
0443: if (get_jjtree().nodeArity() > 0) // number of child nodes
0444: {
0445: SimpleNode node = (SimpleNode) (get_jjtree()
0446: .rootNode());
0447:
0448: if (DEBUG)
0449: node.dump(">");
0450:
0451: Object ret = node.eval(callstack, this );
0452:
0453: // sanity check during development
0454: if (callstack.depth() > 1)
0455: throw new InterpreterError(
0456: "Callstack growing: " + callstack);
0457:
0458: if (ret instanceof ReturnControl)
0459: ret = ((ReturnControl) ret).value;
0460:
0461: if (ret != Primitive.VOID) {
0462: setu("$_", ret);
0463: if (showResults)
0464: println("<" + ret + ">");
0465: }
0466: }
0467: } catch (ParseException e) {
0468: error("Parser Error: " + e.getMessage(DEBUG));
0469: if (DEBUG)
0470: e.printStackTrace();
0471: if (!interactive)
0472: eof = true;
0473:
0474: parser.reInitInput(in);
0475: } catch (InterpreterError e) {
0476: error("Internal Error: " + e.getMessage());
0477: e.printStackTrace();
0478: if (!interactive)
0479: eof = true;
0480: } catch (TargetError e) {
0481: error("// Uncaught Exception: " + e);
0482: if (e.inNativeCode())
0483: e.printStackTrace(DEBUG, err);
0484: if (!interactive)
0485: eof = true;
0486: setu("$_e", e.getTarget());
0487: } catch (EvalError e) {
0488: if (interactive)
0489: error("EvalError: " + e.toString());
0490: else
0491: error("EvalError: " + e.getMessage());
0492:
0493: if (DEBUG)
0494: e.printStackTrace();
0495:
0496: if (!interactive)
0497: eof = true;
0498: } catch (Exception e) {
0499: error("Unknown error: " + e);
0500: if (DEBUG)
0501: e.printStackTrace();
0502: if (!interactive)
0503: eof = true;
0504: } catch (TokenMgrError e) {
0505: error("Error parsing input: " + e);
0506:
0507: /*
0508: We get stuck in infinite loops here when unicode escapes
0509: fail. Must re-init the char stream reader
0510: (ASCII_UCodeESC_CharStream.java)
0511: */
0512: parser.reInitTokenInput(in);
0513:
0514: if (!interactive)
0515: eof = true;
0516: } finally {
0517: get_jjtree().reset();
0518: // reinit the callstack
0519: if (callstack.depth() > 1) {
0520: callstack.clear();
0521: callstack.push(globalNameSpace);
0522: }
0523: }
0524: }
0525:
0526: if (interactive && exitOnEOF)
0527: System.exit(0);
0528: }
0529:
0530: // begin source and eval
0531:
0532: /**
0533: Read text from fileName and eval it.
0534: */
0535: public Object source(String filename, NameSpace nameSpace)
0536: throws FileNotFoundException, IOException, EvalError {
0537: File file = pathToFile(filename);
0538: if (Interpreter.DEBUG)
0539: debug("Sourcing file: " + file);
0540: Reader sourceIn = new BufferedReader(new FileReader(file));
0541: try {
0542: return eval(sourceIn, nameSpace, filename);
0543: } finally {
0544: sourceIn.close();
0545: }
0546: }
0547:
0548: /**
0549: Read text from fileName and eval it.
0550: Convenience method. Use the global namespace.
0551: */
0552: public Object source(String filename) throws FileNotFoundException,
0553: IOException, EvalError {
0554: return source(filename, globalNameSpace);
0555: }
0556:
0557: /**
0558: Spawn a non-interactive local interpreter to evaluate text in the
0559: specified namespace.
0560:
0561: Return value is the evaluated object (or corresponding primitive
0562: wrapper).
0563:
0564: @param sourceFileInfo is for information purposes only. It is used to
0565: display error messages (and in the future may be made available to
0566: the script).
0567: @throws EvalError on script problems
0568: @throws TargetError on unhandled exceptions from the script
0569: */
0570: /*
0571: Note: we need a form of eval that passes the callstack through...
0572: */
0573: /*
0574: Can't this be combined with run() ?
0575: run seems to have stuff in it for interactive vs. non-interactive...
0576: compare them side by side and see what they do differently, aside from the
0577: exception handling.
0578: */
0579:
0580: public Object eval(Reader in, NameSpace nameSpace,
0581: String sourceFileInfo
0582: /*, CallStack callstack */) throws EvalError {
0583: Object retVal = null;
0584: if (Interpreter.DEBUG)
0585: debug("eval: nameSpace = " + nameSpace);
0586:
0587: /*
0588: Create non-interactive local interpreter for this namespace
0589: with source from the input stream and out/err same as
0590: this interpreter.
0591: */
0592: Interpreter localInterpreter = new Interpreter(in, out, err,
0593: false, nameSpace, this , sourceFileInfo);
0594:
0595: CallStack callstack = new CallStack(nameSpace);
0596:
0597: boolean eof = false;
0598: while (!eof) {
0599: SimpleNode node = null;
0600: try {
0601: eof = localInterpreter.Line();
0602: if (localInterpreter.get_jjtree().nodeArity() > 0) {
0603: node = (SimpleNode) localInterpreter.get_jjtree()
0604: .rootNode();
0605: // quick filter for when we're running as a compiler only
0606: if (getSaveClasses()
0607: && !(node instanceof BSHClassDeclaration)
0608: && !(node instanceof BSHImportDeclaration)
0609: && !(node instanceof BSHPackageDeclaration))
0610: continue;
0611:
0612: // nodes remember from where they were sourced
0613: node.setSourceFile(sourceFileInfo);
0614:
0615: if (TRACE)
0616: println("// " + node.getText());
0617:
0618: retVal = node.eval(callstack, localInterpreter);
0619:
0620: // sanity check during development
0621: if (callstack.depth() > 1)
0622: throw new InterpreterError(
0623: "Callstack growing: " + callstack);
0624:
0625: if (retVal instanceof ReturnControl) {
0626: retVal = ((ReturnControl) retVal).value;
0627: break; // non-interactive, return control now
0628: }
0629:
0630: if (localInterpreter.showResults
0631: && retVal != Primitive.VOID)
0632: println("<" + retVal + ">");
0633: }
0634: } catch (ParseException e) {
0635: /*
0636: throw new EvalError(
0637: "Sourced file: "+sourceFileInfo+" parser Error: "
0638: + e.getMessage( DEBUG ), node, callstack );
0639: */
0640: if (DEBUG)
0641: // show extra "expecting..." info
0642: error(e.getMessage(DEBUG));
0643:
0644: // add the source file info and throw again
0645: e.setErrorSourceFile(sourceFileInfo);
0646: throw e;
0647:
0648: } catch (InterpreterError e) {
0649: e.printStackTrace();
0650: throw new EvalError("Sourced file: " + sourceFileInfo
0651: + " internal Error: " + e.getMessage(), node,
0652: callstack);
0653: } catch (TargetError e) {
0654: // failsafe, set the Line as the origin of the error.
0655: if (e.getNode() == null)
0656: e.setNode(node);
0657: e.reThrow("Sourced file: " + sourceFileInfo);
0658: } catch (EvalError e) {
0659: if (DEBUG)
0660: e.printStackTrace();
0661: // failsafe, set the Line as the origin of the error.
0662: if (e.getNode() == null)
0663: e.setNode(node);
0664: e.reThrow("Sourced file: " + sourceFileInfo);
0665: } catch (Exception e) {
0666: if (DEBUG)
0667: e.printStackTrace();
0668: throw new EvalError("Sourced file: " + sourceFileInfo
0669: + " unknown error: " + e.getMessage(), node,
0670: callstack);
0671: } catch (TokenMgrError e) {
0672: throw new EvalError("Sourced file: " + sourceFileInfo
0673: + " Token Parsing Error: " + e.getMessage(),
0674: node, callstack);
0675: } finally {
0676: localInterpreter.get_jjtree().reset();
0677:
0678: // reinit the callstack
0679: if (callstack.depth() > 1) {
0680: callstack.clear();
0681: callstack.push(nameSpace);
0682: }
0683: }
0684: }
0685: return Primitive.unwrap(retVal);
0686: }
0687:
0688: /**
0689: Evaluate the inputstream in this interpreter's global namespace.
0690: */
0691: public Object eval(Reader in) throws EvalError {
0692: return eval(in, globalNameSpace, "eval stream");
0693: }
0694:
0695: /**
0696: Evaluate the string in this interpreter's global namespace.
0697: */
0698: public Object eval(String statements) throws EvalError {
0699: if (Interpreter.DEBUG)
0700: debug("eval(String): " + statements);
0701: return eval(statements, globalNameSpace);
0702: }
0703:
0704: /**
0705: Evaluate the string in the specified namespace.
0706: */
0707: public Object eval(String statements, NameSpace nameSpace)
0708: throws EvalError {
0709:
0710: String s = (statements.endsWith(";") ? statements : statements
0711: + ";");
0712: return eval(new StringReader(s), nameSpace,
0713: "inline evaluation of: ``" + showEvalString(s) + "''");
0714: }
0715:
0716: private String showEvalString(String s) {
0717: s = s.replace('\n', ' ');
0718: s = s.replace('\r', ' ');
0719: if (s.length() > 80)
0720: s = s.substring(0, 80) + " . . . ";
0721: return s;
0722: }
0723:
0724: // end source and eval
0725:
0726: /**
0727: Print an error message in a standard format on the output stream
0728: associated with this interpreter. On the GUI console this will appear
0729: in red, etc.
0730: */
0731: public final void error(Object o) {
0732: if (console != null)
0733: console.error("// Error: " + o + "\n");
0734: else {
0735: err.println("// Error: " + o);
0736: err.flush();
0737: }
0738: }
0739:
0740: // ConsoleInterface
0741: // The interpreter reflexively implements the console interface that it
0742: // uses. Should clean this up by using an inner class to implement the
0743: // console for us.
0744:
0745: /**
0746: Get the input stream associated with this interpreter.
0747: This may be be stdin or the GUI console.
0748: */
0749: public Reader getIn() {
0750: return in;
0751: }
0752:
0753: /**
0754: Get the outptut stream associated with this interpreter.
0755: This may be be stdout or the GUI console.
0756: */
0757: public PrintStream getOut() {
0758: return out;
0759: }
0760:
0761: /**
0762: Get the error output stream associated with this interpreter.
0763: This may be be stderr or the GUI console.
0764: */
0765: public PrintStream getErr() {
0766: return err;
0767: }
0768:
0769: public final void println(Object o) {
0770: print(String.valueOf(o) + systemLineSeparator);
0771: }
0772:
0773: public final void print(Object o) {
0774: if (console != null) {
0775: console.print(o);
0776: } else {
0777: out.print(o);
0778: out.flush();
0779: }
0780: }
0781:
0782: // End ConsoleInterface
0783:
0784: /**
0785: Print a debug message on debug stream associated with this interpreter
0786: only if debugging is turned on.
0787: */
0788: public final static void debug(String s) {
0789: if (DEBUG)
0790: debug.println("// Debug: " + s);
0791: }
0792:
0793: /*
0794: Primary interpreter set and get variable methods
0795: Note: These are squeltching errors... should they?
0796: */
0797:
0798: /**
0799: Get the value of the name.
0800: name may be any value. e.g. a variable or field
0801: */
0802: public Object get(String name) throws EvalError {
0803: try {
0804: Object ret = globalNameSpace.get(name, this );
0805: return Primitive.unwrap(ret);
0806: } catch (UtilEvalError e) {
0807: throw e.toEvalError(SimpleNode.JAVACODE, new CallStack());
0808: }
0809: }
0810:
0811: /**
0812: Unchecked get for internal use
0813: */
0814: Object getu(String name) {
0815: try {
0816: return get(name);
0817: } catch (EvalError e) {
0818: throw new InterpreterError("set: " + e);
0819: }
0820: }
0821:
0822: /**
0823: Assign the value to the name.
0824: name may evaluate to anything assignable. e.g. a variable or field.
0825: */
0826: public void set(String name, Object value) throws EvalError {
0827: // map null to Primtive.NULL coming in...
0828: if (value == null)
0829: value = Primitive.NULL;
0830:
0831: CallStack callstack = new CallStack();
0832: try {
0833: if (Name.isCompound(name)) {
0834: LHS lhs = globalNameSpace.getNameResolver(name).toLHS(
0835: callstack, this );
0836: lhs.assign(value, false);
0837: } else
0838: // optimization for common case
0839: globalNameSpace.setVariable(name, value, false);
0840: } catch (UtilEvalError e) {
0841: throw e.toEvalError(SimpleNode.JAVACODE, callstack);
0842: }
0843: }
0844:
0845: /**
0846: Unchecked set for internal use
0847: */
0848: void setu(String name, Object value) {
0849: try {
0850: set(name, value);
0851: } catch (EvalError e) {
0852: throw new InterpreterError("set: " + e);
0853: }
0854: }
0855:
0856: public void set(String name, long value) throws EvalError {
0857: set(name, new Primitive(value));
0858: }
0859:
0860: public void set(String name, int value) throws EvalError {
0861: set(name, new Primitive(value));
0862: }
0863:
0864: public void set(String name, double value) throws EvalError {
0865: set(name, new Primitive(value));
0866: }
0867:
0868: public void set(String name, float value) throws EvalError {
0869: set(name, new Primitive(value));
0870: }
0871:
0872: public void set(String name, boolean value) throws EvalError {
0873: set(name, value ? Primitive.TRUE : Primitive.FALSE);
0874: }
0875:
0876: /**
0877: Unassign the variable name.
0878: Name should evaluate to a variable.
0879: */
0880: public void unset(String name) throws EvalError {
0881: /*
0882: We jump through some hoops here to handle arbitrary cases like
0883: unset("bsh.foo");
0884: */
0885: CallStack callstack = new CallStack();
0886: try {
0887: LHS lhs = globalNameSpace.getNameResolver(name).toLHS(
0888: callstack, this );
0889:
0890: if (lhs.type != LHS.VARIABLE)
0891: throw new EvalError("Can't unset, not a variable: "
0892: + name, SimpleNode.JAVACODE, new CallStack());
0893:
0894: //lhs.assign( null, false );
0895: lhs.nameSpace.unsetVariable(name);
0896: } catch (UtilEvalError e) {
0897: throw new EvalError(e.getMessage(), SimpleNode.JAVACODE,
0898: new CallStack());
0899: }
0900: }
0901:
0902: // end primary set and get methods
0903:
0904: /**
0905: Get a reference to the interpreter (global namespace), cast
0906: to the specified interface type. Assuming the appropriate
0907: methods of the interface are defined in the interpreter, then you may
0908: use this interface from Java, just like any other Java object.
0909: <p>
0910:
0911: For example:
0912: <pre>
0913: Interpreter interpreter = new Interpreter();
0914: // define a method called run()
0915: interpreter.eval("run() { ... }");
0916:
0917: // Fetch a reference to the interpreter as a Runnable
0918: Runnable runnable =
0919: (Runnable)interpreter.getInterface( Runnable.class );
0920: </pre>
0921: <p>
0922:
0923: Note that the interpreter does *not* require that any or all of the
0924: methods of the interface be defined at the time the interface is
0925: generated. However if you attempt to invoke one that is not defined
0926: you will get a runtime exception.
0927: <p>
0928:
0929: Note also that this convenience method has exactly the same effect as
0930: evaluating the script:
0931: <pre>
0932: (Type)this;
0933: </pre>
0934: <p>
0935:
0936: For example, the following is identical to the previous example:
0937: <p>
0938:
0939: <pre>
0940: // Fetch a reference to the interpreter as a Runnable
0941: Runnable runnable =
0942: (Runnable)interpreter.eval( "(Runnable)this" );
0943: </pre>
0944: <p>
0945:
0946: <em>Version requirement</em> Although standard Java interface types
0947: are always available, to be used with arbitrary interfaces this
0948: feature requires that you are using Java 1.3 or greater.
0949: <p>
0950:
0951: @throws EvalError if the interface cannot be generated because the
0952: version of Java does not support the proxy mechanism.
0953: */
0954: public Object getInterface(Class interf) throws EvalError {
0955: try {
0956: return globalNameSpace.getThis(this ).getInterface(interf);
0957: } catch (UtilEvalError e) {
0958: throw e.toEvalError(SimpleNode.JAVACODE, new CallStack());
0959: }
0960: }
0961:
0962: /* Methods for interacting with Parser */
0963:
0964: private JJTParserState get_jjtree() {
0965: return parser.jjtree;
0966: }
0967:
0968: private JavaCharStream get_jj_input_stream() {
0969: return parser.jj_input_stream;
0970: }
0971:
0972: private boolean Line() throws ParseException {
0973: return parser.Line();
0974: }
0975:
0976: /* End methods for interacting with Parser */
0977:
0978: void loadRCFiles() {
0979: try {
0980: String rcfile =
0981: // Default is c:\windows under win98, $HOME under Unix
0982: System.getProperty("user.home") + File.separator + ".bshrc";
0983: source(rcfile, globalNameSpace);
0984: } catch (Exception e) {
0985: // squeltch security exception, filenotfoundexception
0986: if (Interpreter.DEBUG)
0987: debug("Could not find rc file: " + e);
0988: }
0989: }
0990:
0991: /**
0992: Localize a path to the file name based on the bsh.cwd interpreter
0993: working directory.
0994: */
0995: public File pathToFile(String fileName) throws IOException {
0996: File file = new File(fileName);
0997:
0998: // if relative, fix up to bsh.cwd
0999: if (!file.isAbsolute()) {
1000: String cwd = (String) getu("bsh.cwd");
1001: file = new File(cwd + File.separator + fileName);
1002: }
1003:
1004: // The canonical file name is also absolute.
1005: // No need for getAbsolutePath() here...
1006: return new File(file.getCanonicalPath());
1007: }
1008:
1009: public static void redirectOutputToFile(String filename) {
1010: try {
1011: PrintStream pout = new PrintStream(new FileOutputStream(
1012: filename));
1013: System.setOut(pout);
1014: System.setErr(pout);
1015: } catch (IOException e) {
1016: System.err.println("Can't redirect output to file: "
1017: + filename);
1018: }
1019: }
1020:
1021: /**
1022: Set an external class loader to be used as the base classloader
1023: for BeanShell. The base classloader is used for all classloading
1024: unless/until the addClasspath()/setClasspath()/reloadClasses()
1025: commands are called to modify the interpreter's classpath. At that
1026: time the new paths /updated paths are added on top of the base
1027: classloader.
1028: <p>
1029:
1030: BeanShell will use this at the same point it would otherwise use the
1031: plain Class.forName().
1032: i.e. if no explicit classpath management is done from the script
1033: (addClassPath(), setClassPath(), reloadClasses()) then BeanShell will
1034: only use the supplied classloader. If additional classpath management
1035: is done then BeanShell will perform that in addition to the supplied
1036: external classloader.
1037: However BeanShell is not currently able to reload
1038: classes supplied through the external classloader.
1039: <p>
1040:
1041: @see BshClassManager#setClassLoader( ClassLoader )
1042: */
1043: public void setClassLoader(ClassLoader externalCL) {
1044: getClassManager().setClassLoader(externalCL);
1045: }
1046:
1047: /**
1048: Get the class manager associated with this interpreter
1049: (the BshClassManager of this interpreter's global namespace).
1050: This is primarily a convenience method.
1051: */
1052: public BshClassManager getClassManager() {
1053: return getNameSpace().getClassManager();
1054: }
1055:
1056: /**
1057: Set strict Java mode on or off.
1058: This mode attempts to make BeanShell syntax behave as Java
1059: syntax, eliminating conveniences like loose variables, etc.
1060: When enabled, variables are required to be declared or initialized
1061: before use and method arguments are reqired to have types.
1062: <p>
1063:
1064: This mode will become more strict in a future release when
1065: classes are interpreted and there is an alternative to scripting
1066: objects as method closures.
1067: */
1068: public void setStrictJava(boolean b) {
1069: this .strictJava = b;
1070: }
1071:
1072: /**
1073: @see #setStrictJava( boolean )
1074: */
1075: public boolean getStrictJava() {
1076: return this .strictJava;
1077: }
1078:
1079: static void staticInit() {
1080: /*
1081: Apparently in some environments you can't catch the security exception
1082: at all... e.g. as an applet in IE ... will probably have to work
1083: around
1084: */
1085: try {
1086: systemLineSeparator = System.getProperty("line.separator");
1087: debug = System.err;
1088: DEBUG = Boolean.getBoolean("debug");
1089: TRACE = Boolean.getBoolean("trace");
1090: LOCALSCOPING = Boolean.getBoolean("localscoping");
1091: String outfilename = System.getProperty("outfile");
1092: if (outfilename != null)
1093: redirectOutputToFile(outfilename);
1094: } catch (SecurityException e) {
1095: System.err.println("Could not init static:" + e);
1096: } catch (Exception e) {
1097: System.err.println("Could not init static(2):" + e);
1098: } catch (Throwable e) {
1099: System.err.println("Could not init static(3):" + e);
1100: }
1101: }
1102:
1103: /**
1104: Specify the source of the text from which this interpreter is reading.
1105: Note: there is a difference between what file the interrpeter is
1106: sourcing and from what file a method was originally parsed. One
1107: file may call a method sourced from another file. See SimpleNode
1108: for origination file info.
1109: @see bsh.SimpleNode#getSourceFile()
1110: */
1111: public String getSourceFileInfo() {
1112: if (sourceFileInfo != null)
1113: return sourceFileInfo;
1114: else
1115: return "<unknown source>";
1116: }
1117:
1118: /**
1119: Get the parent Interpreter of this interpreter, if any.
1120: Currently this relationship implies the following:
1121: 1) Parent and child share a BshClassManager
1122: 2) Children indicate the parent's source file information in error
1123: reporting.
1124: When created as part of a source() / eval() the child also shares
1125: the parent's namespace. But that is not necessary in general.
1126: */
1127: public Interpreter getParent() {
1128: return parent;
1129: }
1130:
1131: public void setOut(PrintStream out) {
1132: this .out = out;
1133: }
1134:
1135: public void setErr(PrintStream err) {
1136: this .err = err;
1137: }
1138:
1139: /**
1140: De-serialization setup.
1141: Default out and err streams to stdout, stderr if they are null.
1142: */
1143: private void readObject(ObjectInputStream stream)
1144: throws java.io.IOException, ClassNotFoundException {
1145: stream.defaultReadObject();
1146:
1147: // set transient fields
1148: if (console != null) {
1149: setOut(console.getOut());
1150: setErr(console.getErr());
1151: } else {
1152: setOut(System.out);
1153: setErr(System.err);
1154: }
1155: }
1156:
1157: /**
1158: Get the prompt string defined by the getBshPrompt() method in the
1159: global namespace. This may be from the getBshPrompt() command or may
1160: be defined by the user as with any other method.
1161: Defaults to "bsh % " if the method is not defined or there is an error.
1162: */
1163: private String getBshPrompt() {
1164: try {
1165: return (String) eval("getBshPrompt()");
1166: } catch (Exception e) {
1167: return "bsh % ";
1168: }
1169: }
1170:
1171: /**
1172: Specify whether, in interactive mode, the interpreter exits Java upon
1173: end of input. If true, when in interactive mode the interpreter will
1174: issue a System.exit(0) upon eof. If false the interpreter no
1175: System.exit() will be done.
1176: <p/>
1177: Note: if you wish to cause an EOF externally you can try closing the
1178: input stream. This is not guaranteed to work in older versions of Java
1179: due to Java limitations, but should work in newer JDK/JREs. (That was
1180: the motivation for the Java NIO package).
1181: */
1182: public void setExitOnEOF(boolean value) {
1183: exitOnEOF = value; // ug
1184: }
1185:
1186: /**
1187: Turn on/off the verbose printing of results as for the show()
1188: command.
1189: If this interpreter has a parent the call is delegated.
1190: See the BeanShell show() command.
1191: */
1192: public void setShowResults(boolean showResults) {
1193: this .showResults = showResults;
1194: }
1195:
1196: /**
1197: Show on/off verbose printing status for the show() command.
1198: See the BeanShell show() command.
1199: If this interpreter has a parent the call is delegated.
1200: */
1201: public boolean getShowResults() {
1202: return showResults;
1203: }
1204:
1205: public static String getSaveClassesDir() {
1206: return System.getProperty("saveClasses");
1207: }
1208:
1209: public static boolean getSaveClasses() {
1210: return getSaveClassesDir() != null;
1211: }
1212: }
|