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