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.*;
0035:
0036: import java.io.InputStream;
0037: import java.io.BufferedReader;
0038: import java.io.InputStreamReader;
0039: import java.io.IOException;
0040:
0041: import java.lang.reflect.Method;
0042: import java.lang.reflect.Field;
0043:
0044: /**
0045: A namespace in which methods, variables, and imports (class names) live.
0046: This is package public because it is used in the implementation of some
0047: bsh commands. However for normal use you should be using methods on
0048: bsh.Interpreter to interact with your scripts.
0049: <p>
0050:
0051: A bsh.This object is a thin layer over a NameSpace that associates it with
0052: an Interpreter instance. Together they comprise a Bsh scripted object
0053: context.
0054: <p>
0055:
0056: Note: I'd really like to use collections here, but we have to keep this
0057: compatible with JDK1.1
0058: */
0059: /*
0060: Thanks to Slava Pestov (of jEdit fame) for import caching enhancements.
0061: Note: This class has gotten too big. It should be broken down a bit.
0062: */
0063: public class NameSpace implements java.io.Serializable,
0064: BshClassManager.Listener, NameSource {
0065: public static final NameSpace JAVACODE = new NameSpace(
0066: (BshClassManager) null, "Called from compiled Java code.");
0067: static {
0068: JAVACODE.isMethod = true;
0069: }
0070:
0071: // Begin instance data
0072: // Note: if we add something here we should reset it in the clear() method.
0073:
0074: /**
0075: The name of this namespace. If the namespace is a method body
0076: namespace then this is the name of the method. If it's a class or
0077: class instance then it's the name of the class.
0078: */
0079: private String nsName;
0080: private NameSpace parent;
0081: private Hashtable variables;
0082: private Hashtable methods;
0083:
0084: protected Hashtable importedClasses;
0085: private Vector importedPackages;
0086: private Vector importedCommands;
0087: private Vector importedObjects;
0088: private Vector importedStatic;
0089: private String packageName;
0090:
0091: transient private BshClassManager classManager;
0092:
0093: // See notes in getThis()
0094: private This this Reference;
0095:
0096: /** Name resolver objects */
0097: private Hashtable names;
0098:
0099: /** The node associated with the creation of this namespace.
0100: This is used support getInvocationLine() and getInvocationText(). */
0101: SimpleNode callerInfoNode;
0102:
0103: /**
0104: Note that the namespace is a method body namespace. This is used for
0105: printing stack traces in exceptions.
0106: */
0107: boolean isMethod;
0108:
0109: /**
0110: Note that the namespace is a class body or class instance namespace.
0111: This is used for controlling static/object import precedence, etc.
0112: */
0113: /*
0114: Note: We can move this class related behavior out to a subclass of
0115: NameSpace, but we'll start here.
0116: */
0117: boolean isClass;
0118: Class classStatic;
0119: Object classInstance;
0120:
0121: void setClassStatic(Class clas) {
0122: this .classStatic = clas;
0123: importStatic(clas);
0124: }
0125:
0126: void setClassInstance(Object instance) {
0127: this .classInstance = instance;
0128: importObject(instance);
0129: }
0130:
0131: Object getClassInstance() throws UtilEvalError {
0132: if (classInstance != null)
0133: return classInstance;
0134:
0135: if (classStatic != null
0136: //|| ( getParent()!=null && getParent().classStatic != null )
0137: )
0138: throw new UtilEvalError(
0139: "Can't refer to class instance from static context.");
0140: else
0141: throw new InterpreterError(
0142: "Can't resolve class instance 'this' in: " + this );
0143: }
0144:
0145: /**
0146: Local class cache for classes resolved through this namespace using
0147: getClass() (taking into account imports). Only unqualified class names
0148: are cached here (those which might be imported). Qualified names are
0149: always absolute and are cached by BshClassManager.
0150: */
0151: transient private Hashtable classCache;
0152:
0153: // End instance data
0154:
0155: // Begin constructors
0156:
0157: /**
0158: @parent the parent namespace of this namespace. Child namespaces
0159: inherit all variables and methods of their parent and can (of course)
0160: override / shadow them.
0161: */
0162: public NameSpace(NameSpace parent, String name) {
0163: // Note: in this case parent must have a class manager.
0164: this (parent, null, name);
0165: }
0166:
0167: public NameSpace(BshClassManager classManager, String name) {
0168: this (null, classManager, name);
0169: }
0170:
0171: public NameSpace(NameSpace parent, BshClassManager classManager,
0172: String name) {
0173: // We might want to do this here rather than explicitly in Interpreter
0174: // for global (see also prune())
0175: //if ( classManager == null && (parent == null ) )
0176: // create our own class manager?
0177:
0178: setName(name);
0179: setParent(parent);
0180: setClassManager(classManager);
0181:
0182: // Register for notification of classloader change
0183: if (classManager != null)
0184: classManager.addListener(this );
0185: }
0186:
0187: // End constructors
0188:
0189: public void setName(String name) {
0190: this .nsName = name;
0191: }
0192:
0193: /**
0194: The name of this namespace. If the namespace is a method body
0195: namespace then this is the name of the method. If it's a class or
0196: class instance then it's the name of the class.
0197: */
0198: public String getName() {
0199: return this .nsName;
0200: }
0201:
0202: /**
0203: Set the node associated with the creation of this namespace.
0204: This is used in debugging and to support the getInvocationLine()
0205: and getInvocationText() methods.
0206: */
0207: void setNode(SimpleNode node) {
0208: callerInfoNode = node;
0209: }
0210:
0211: /**
0212: */
0213: SimpleNode getNode() {
0214: if (callerInfoNode != null)
0215: return callerInfoNode;
0216: if (parent != null)
0217: return parent.getNode();
0218: else
0219: return null;
0220: }
0221:
0222: /**
0223: Resolve name to an object through this namespace.
0224: */
0225: public Object get(String name, Interpreter interpreter)
0226: throws UtilEvalError {
0227: CallStack callstack = new CallStack(this );
0228: return getNameResolver(name).toObject(callstack, interpreter);
0229: }
0230:
0231: /**
0232: Set the variable through this namespace.
0233: This method obeys the LOCALSCOPING property to determine how variables
0234: are set.
0235: <p>
0236: Note: this method is primarily intended for use internally. If you use
0237: this method outside of the bsh package and wish to set variables with
0238: primitive values you will have to wrap them using bsh.Primitive.
0239: @see bsh.Primitive
0240: <p>
0241: Setting a new variable (which didn't exist before) or removing
0242: a variable causes a namespace change.
0243:
0244: @param strictJava specifies whether strict java rules are applied.
0245: */
0246: public void setVariable(String name, Object value,
0247: boolean strictJava) throws UtilEvalError {
0248: // if localscoping switch follow strictJava, else recurse
0249: boolean recurse = Interpreter.LOCALSCOPING ? strictJava : true;
0250: setVariable(name, value, strictJava, recurse);
0251: }
0252:
0253: /**
0254: Set a variable explicitly in the local scope.
0255: */
0256: void setLocalVariable(String name, Object value, boolean strictJava)
0257: throws UtilEvalError {
0258: setVariable(name, value, strictJava, false/*recurse*/);
0259: }
0260:
0261: /**
0262: Set the value of a the variable 'name' through this namespace.
0263: The variable may be an existing or non-existing variable.
0264: It may live in this namespace or in a parent namespace if recurse is
0265: true.
0266: <p>
0267: Note: This method is not public and does *not* know about LOCALSCOPING.
0268: Its caller methods must set recurse intelligently in all situations
0269: (perhaps based on LOCALSCOPING).
0270:
0271: <p>
0272: Note: this method is primarily intended for use internally. If you use
0273: this method outside of the bsh package and wish to set variables with
0274: primitive values you will have to wrap them using bsh.Primitive.
0275: @see bsh.Primitive
0276: <p>
0277: Setting a new variable (which didn't exist before) or removing
0278: a variable causes a namespace change.
0279:
0280: @param strictJava specifies whether strict java rules are applied.
0281: @param recurse determines whether we will search for the variable in
0282: our parent's scope before assigning locally.
0283: */
0284: void setVariable(String name, Object value, boolean strictJava,
0285: boolean recurse) throws UtilEvalError {
0286: if (variables == null)
0287: variables = new Hashtable();
0288:
0289: // primitives should have been wrapped
0290: if (value == null)
0291: throw new InterpreterError("null variable value");
0292:
0293: // Locate the variable definition if it exists.
0294: Variable existing = getVariableImpl(name, recurse);
0295:
0296: // Found an existing variable here (or above if recurse allowed)
0297: if (existing != null) {
0298: try {
0299: existing.setValue(value, Variable.ASSIGNMENT);
0300: } catch (UtilEvalError e) {
0301: throw new UtilEvalError("Variable assignment: " + name
0302: + ": " + e.getMessage());
0303: }
0304: } else
0305: // No previous variable definition found here (or above if recurse)
0306: {
0307: if (strictJava)
0308: throw new UtilEvalError(
0309: "(Strict Java mode) Assignment to undeclared variable: "
0310: + name);
0311:
0312: // If recurse, set global untyped var, else set it here.
0313: //NameSpace varScope = recurse ? getGlobal() : this;
0314: // This modification makes default allocation local
0315: NameSpace varScope = this ;
0316:
0317: varScope.variables.put(name, createVariable(name, value,
0318: null/*modifiers*/));
0319:
0320: // nameSpaceChanged() on new variable addition
0321: nameSpaceChanged();
0322: }
0323: }
0324:
0325: ////////////////////////////////////////////////////////////////////////////
0326:
0327: /**
0328: <p>
0329: Sets a variable or property. See "setVariable" for rules regarding
0330: scoping.
0331: </p>
0332:
0333: <p>
0334: We first check for the existence of the variable. If it exists, we set
0335: it.
0336: If the variable does not exist we look for a property. If the property
0337: exists and is writable we set it.
0338: Finally, if neither the variable or the property exist, we create a new
0339: variable.
0340: </p>
0341:
0342: @param strictJava specifies whether strict java rules are applied.
0343: */
0344: public void setVariableOrProperty(String name, Object value,
0345: boolean strictJava) throws UtilEvalError {
0346: // if localscoping switch follow strictJava, else recurse
0347: boolean recurse = Interpreter.LOCALSCOPING ? strictJava : true;
0348: setVariableOrProperty(name, value, strictJava, recurse);
0349: }
0350:
0351: /**
0352: Set a variable or property explicitly in the local scope.
0353:
0354: <p>
0355: Sets a variable or property. See "setLocalVariable" for rules regarding
0356: scoping.
0357: </p>
0358:
0359: <p>
0360: We first check for the existence of the variable. If it exists, we set
0361: it.
0362: If the variable does not exist we look for a property. If the property
0363: exists and is writable we set it.
0364: Finally, if neither the variable or the property exist, we create a new
0365: variable.
0366: </p>
0367: */
0368: void setLocalVariableOrProperty(String name, Object value,
0369: boolean strictJava) throws UtilEvalError {
0370: setVariableOrProperty(name, value, strictJava, false/*recurse*/);
0371: }
0372:
0373: /**
0374: Set the value of a the variable or property 'name' through this
0375: namespace.
0376:
0377: <p>
0378: Sets a variable or property. See "setVariableOrProperty" for rules
0379: regarding scope.
0380: </p>
0381:
0382: <p>
0383: We first check for the existence of the variable. If it exists, we set
0384: it.
0385: If the variable does not exist we look for a property. If the property
0386: exists and is writable we set it.
0387: Finally, if neither the variable or the property exist, we create a new
0388: variable.
0389: </p>
0390:
0391: @param strictJava specifies whether strict java rules are applied.
0392: @param recurse determines whether we will search for the variable in
0393: our parent's scope before assigning locally.
0394: */
0395: void setVariableOrProperty(String name, Object value,
0396: boolean strictJava, boolean recurse) throws UtilEvalError {
0397: if (variables == null)
0398: variables = new Hashtable();
0399:
0400: // primitives should have been wrapped
0401: if (value == null)
0402: throw new InterpreterError("null variable value");
0403:
0404: // Locate the variable definition if it exists.
0405: Variable existing = getVariableImpl(name, recurse);
0406:
0407: // Found an existing variable here (or above if recurse allowed)
0408: if (existing != null) {
0409: try {
0410: existing.setValue(value, Variable.ASSIGNMENT);
0411: } catch (UtilEvalError e) {
0412: throw new UtilEvalError("Variable assignment: " + name
0413: + ": " + e.getMessage());
0414: }
0415: } else
0416: // No previous variable definition found here (or above if recurse)
0417: {
0418: if (strictJava)
0419: throw new UtilEvalError(
0420: "(Strict Java mode) Assignment to undeclared variable: "
0421: + name);
0422:
0423: boolean setProp = attemptSetPropertyValue(name, value, null);
0424: if (setProp)
0425: return;
0426:
0427: // If recurse, set global untyped var, else set it here.
0428: //NameSpace varScope = recurse ? getGlobal() : this;
0429: // This modification makes default allocation local
0430: NameSpace varScope = this ;
0431:
0432: varScope.variables.put(name, createVariable(name, value,
0433: null/*modifiers*/));
0434:
0435: // nameSpaceChanged() on new variable addition
0436: nameSpaceChanged();
0437: }
0438: }
0439:
0440: protected Variable createVariable(String name, Object value,
0441: Modifiers mods) throws UtilEvalError {
0442: return createVariable(name, null/*type*/, value, mods);
0443: }
0444:
0445: protected Variable createVariable(String name, Class type,
0446: Object value, Modifiers mods) throws UtilEvalError {
0447: return new Variable(name, type, value, mods);
0448: }
0449:
0450: protected Variable createVariable(String name, Class type, LHS lhs)
0451: throws UtilEvalError {
0452: return new Variable(name, type, lhs);
0453: }
0454:
0455: /**
0456: Remove the variable from the namespace.
0457: */
0458: public void unsetVariable(String name) {
0459: if (variables != null) {
0460: variables.remove(name);
0461: nameSpaceChanged();
0462: }
0463: }
0464:
0465: /**
0466: Get the names of variables defined in this namespace.
0467: (This does not show variables in parent namespaces).
0468: */
0469: public String[] getVariableNames() {
0470: if (variables == null)
0471: return new String[0];
0472: else
0473: return enumerationToStringArray(variables.keys());
0474: }
0475:
0476: /**
0477: Get the names of methods declared in this namespace.
0478: (This does not include methods in parent namespaces).
0479: */
0480: public String[] getMethodNames() {
0481: if (methods == null)
0482: return new String[0];
0483: else
0484: return enumerationToStringArray(methods.keys());
0485: }
0486:
0487: /**
0488: Get the methods defined in this namespace.
0489: (This does not show methods in parent namespaces).
0490: Note: This will probably be renamed getDeclaredMethods()
0491: */
0492: public BshMethod[] getMethods() {
0493: if (methods == null)
0494: return new BshMethod[0];
0495: else
0496: return flattenMethodCollection(methods.elements());
0497: }
0498:
0499: private String[] enumerationToStringArray(Enumeration e) {
0500: Vector v = new Vector();
0501: while (e.hasMoreElements())
0502: v.addElement(e.nextElement());
0503: String[] sa = new String[v.size()];
0504: v.copyInto(sa);
0505: return sa;
0506: }
0507:
0508: /**
0509: Flatten the vectors of overloaded methods to a single array.
0510: @see #getMethods()
0511: */
0512: private BshMethod[] flattenMethodCollection(Enumeration e) {
0513: Vector v = new Vector();
0514: while (e.hasMoreElements()) {
0515: Object o = e.nextElement();
0516: if (o instanceof BshMethod)
0517: v.addElement(o);
0518: else {
0519: Vector ov = (Vector) o;
0520: for (int i = 0; i < ov.size(); i++)
0521: v.addElement(ov.elementAt(i));
0522: }
0523: }
0524: BshMethod[] bma = new BshMethod[v.size()];
0525: v.copyInto(bma);
0526: return bma;
0527: }
0528:
0529: /**
0530: Get the parent namespace.
0531: Note: this isn't quite the same as getSuper().
0532: getSuper() returns 'this' if we are at the root namespace.
0533: */
0534: public NameSpace getParent() {
0535: return parent;
0536: }
0537:
0538: /**
0539: Get the parent namespace' This reference or this namespace' This
0540: reference if we are the top.
0541: */
0542: public This getSuper(Interpreter declaringInterpreter) {
0543: if (parent != null)
0544: return parent.getThis(declaringInterpreter);
0545: else
0546: return getThis(declaringInterpreter);
0547: }
0548:
0549: /**
0550: Get the top level namespace or this namespace if we are the top.
0551: Note: this method should probably return type bsh.This to be consistent
0552: with getThis();
0553: */
0554: public This getGlobal(Interpreter declaringInterpreter) {
0555: if (parent != null)
0556: return parent.getGlobal(declaringInterpreter);
0557: else
0558: return getThis(declaringInterpreter);
0559: }
0560:
0561: /**
0562: A This object is a thin layer over a namespace, comprising a bsh object
0563: context. It handles things like the interface types the bsh object
0564: supports and aspects of method invocation on it.
0565: <p>
0566:
0567: The declaringInterpreter is here to support callbacks from Java through
0568: generated proxies. The scripted object "remembers" who created it for
0569: things like printing messages and other per-interpreter phenomenon
0570: when called externally from Java.
0571: */
0572: /*
0573: Note: we need a singleton here so that things like 'this == this' work
0574: (and probably a good idea for speed).
0575:
0576: Caching a single instance here seems technically incorrect,
0577: considering the declaringInterpreter could be different under some
0578: circumstances. (Case: a child interpreter running a source() / eval()
0579: command ). However the effect is just that the main interpreter that
0580: executes your script should be the one involved in call-backs from Java.
0581:
0582: I do not know if there are corner cases where a child interpreter would
0583: be the first to use a This reference in a namespace or if that would
0584: even cause any problems if it did... We could do some experiments
0585: to find out... and if necessary we could cache on a per interpreter
0586: basis if we had weak references... We might also look at skipping
0587: over child interpreters and going to the parent for the declaring
0588: interpreter, so we'd be sure to get the top interpreter.
0589: */
0590: public This getThis(Interpreter declaringInterpreter) {
0591: if (this Reference == null)
0592: this Reference = This.getThis(this , declaringInterpreter);
0593:
0594: return this Reference;
0595: }
0596:
0597: public BshClassManager getClassManager() {
0598: if (classManager != null)
0599: return classManager;
0600: if (parent != null && parent != JAVACODE)
0601: return parent.getClassManager();
0602:
0603: //System.out.println("experiment: creating class manager");
0604: classManager = BshClassManager
0605: .createClassManager(null/*interp*/);
0606:
0607: //Interpreter.debug("No class manager namespace:" +this);
0608: return classManager;
0609: }
0610:
0611: void setClassManager(BshClassManager classManager) {
0612: this .classManager = classManager;
0613: }
0614:
0615: /**
0616: Used for serialization
0617: */
0618: public void prune() {
0619: // Cut off from parent, we must have our own class manager.
0620: // Can't do this in the run() command (needs to resolve stuff)
0621: // Should we do it by default when we create a namespace will no
0622: // parent of class manager?
0623:
0624: if (this .classManager == null)
0625: // XXX if we keep the createClassManager in getClassManager then we can axe
0626: // this?
0627: setClassManager(BshClassManager
0628: .createClassManager(null/*interp*/));
0629:
0630: setParent(null);
0631: }
0632:
0633: public void setParent(NameSpace parent) {
0634: this .parent = parent;
0635:
0636: // If we are disconnected from root we need to handle the def imports
0637: if (parent == null)
0638: loadDefaultImports();
0639: }
0640:
0641: /**
0642: <p>
0643: Get the specified variable or property in this namespace or a parent
0644: namespace.
0645: </p>
0646:
0647: <p>
0648: We first search for a variable name, and then a property.
0649: </p>
0650:
0651: @return The variable or property value or Primitive.VOID if neither is
0652: defined.
0653: */
0654: public Object getVariableOrProperty(String name, Interpreter interp)
0655: throws UtilEvalError {
0656: Object val = getVariable(name, true);
0657: return (val == Primitive.VOID) ? getPropertyValue(name, interp)
0658: : val;
0659: }
0660:
0661: /**
0662: Get the specified variable in this namespace or a parent namespace.
0663: <p>
0664: Note: this method is primarily intended for use internally. If you use
0665: this method outside of the bsh package you will have to use
0666: Primitive.unwrap() to get primitive values.
0667: @see Primitive#unwrap( Object )
0668:
0669: @return The variable value or Primitive.VOID if it is not defined.
0670: */
0671: public Object getVariable(String name) throws UtilEvalError {
0672: return getVariable(name, true);
0673: }
0674:
0675: /**
0676: Get the specified variable in this namespace.
0677: @param recurse If recurse is true then we recursively search through
0678: parent namespaces for the variable.
0679: <p>
0680: Note: this method is primarily intended for use internally. If you use
0681: this method outside of the bsh package you will have to use
0682: Primitive.unwrap() to get primitive values.
0683: @see Primitive#unwrap( Object )
0684:
0685: @return The variable value or Primitive.VOID if it is not defined.
0686: */
0687: public Object getVariable(String name, boolean recurse)
0688: throws UtilEvalError {
0689: Variable var = getVariableImpl(name, recurse);
0690: return unwrapVariable(var);
0691: }
0692:
0693: /**
0694: Locate a variable and return the Variable object with optional
0695: recursion through parent name spaces.
0696: <p/>
0697: If this namespace is static, return only static variables.
0698:
0699: @return the Variable value or null if it is not defined
0700: */
0701: protected Variable getVariableImpl(String name, boolean recurse)
0702: throws UtilEvalError {
0703: Variable var = null;
0704:
0705: // Change import precedence if we are a class body/instance
0706: // Get imported first.
0707: if (var == null && isClass)
0708: var = getImportedVar(name);
0709:
0710: if (var == null && variables != null)
0711: var = (Variable) variables.get(name);
0712:
0713: // Change import precedence if we are a class body/instance
0714: if (var == null && !isClass)
0715: var = getImportedVar(name);
0716:
0717: // try parent
0718: if (recurse && (var == null) && (parent != null))
0719: var = parent.getVariableImpl(name, recurse);
0720:
0721: return var;
0722: }
0723:
0724: /*
0725: Get variables declared in this namespace.
0726: */
0727: public Variable[] getDeclaredVariables() {
0728: if (variables == null)
0729: return new Variable[0];
0730: Variable[] vars = new Variable[variables.size()];
0731: int i = 0;
0732: for (Enumeration e = variables.elements(); e.hasMoreElements();)
0733: vars[i++] = (Variable) e.nextElement();
0734: return vars;
0735: }
0736:
0737: /**
0738: Unwrap a variable to its value.
0739: @return return the variable value. A null var is mapped to
0740: Primitive.VOID
0741: */
0742: protected Object unwrapVariable(Variable var) throws UtilEvalError {
0743: return (var == null) ? Primitive.VOID : var.getValue();
0744: }
0745:
0746: /**
0747: @deprecated See #setTypedVariable( String, Class, Object, Modifiers )
0748: */
0749: public void setTypedVariable(String name, Class type, Object value,
0750: boolean isFinal) throws UtilEvalError {
0751: Modifiers modifiers = new Modifiers();
0752: if (isFinal)
0753: modifiers.addModifier(Modifiers.FIELD, "final");
0754: setTypedVariable(name, type, value, modifiers);
0755: }
0756:
0757: /**
0758: Declare a variable in the local scope and set its initial value.
0759: Value may be null to indicate that we would like the default value
0760: for the variable type. (e.g. 0 for integer types, null for object
0761: types). An existing typed variable may only be set to the same type.
0762: If an untyped variable of the same name exists it will be overridden
0763: with the new typed var.
0764: The set will perform a Types.getAssignableForm() on the value if
0765: necessary.
0766:
0767: <p>
0768: Note: this method is primarily intended for use internally. If you use
0769: this method outside of the bsh package and wish to set variables with
0770: primitive values you will have to wrap them using bsh.Primitive.
0771: @see bsh.Primitive
0772:
0773: @param value If value is null, you'll get the default value for the type
0774: @param modifiers may be null
0775: */
0776: public void setTypedVariable(String name, Class type, Object value,
0777: Modifiers modifiers) throws UtilEvalError {
0778: //checkVariableModifiers( name, modifiers );
0779:
0780: if (variables == null)
0781: variables = new Hashtable();
0782:
0783: // Setting a typed variable is always a local operation.
0784: Variable existing = getVariableImpl(name, false/*recurse*/);
0785:
0786: // Null value is just a declaration
0787: // Note: we might want to keep any existing value here instead of reset
0788:
0789: // does the variable already exist?
0790: if (existing != null) {
0791: // Is it typed?
0792: if (existing.getType() != null) {
0793: // If it had a different type throw error.
0794: // This allows declaring the same var again, but not with
0795: // a different (even if assignable) type.
0796: if (existing.getType() != type) {
0797: throw new UtilEvalError("Typed variable: " + name
0798: + " was previously declared with type: "
0799: + existing.getType());
0800: } else {
0801: // else set it and return
0802: existing.setValue(value, Variable.DECLARATION);
0803: return;
0804: }
0805: }
0806: // Careful here:
0807: // else fall through to override and install the new typed version
0808: }
0809:
0810: // Add the new typed var
0811: variables.put(name,
0812: createVariable(name, type, value, modifiers));
0813: }
0814:
0815: /**
0816: Dissallow static vars outside of a class
0817: @param name is here just to allow the error message to use it
0818: protected void checkVariableModifiers( String name, Modifiers modifiers )
0819: throws UtilEvalError
0820: {
0821: if ( modifiers!=null && modifiers.hasModifier("static") )
0822: throw new UtilEvalError(
0823: "Can't declare static variable outside of class: "+name );
0824: }
0825: */
0826:
0827: /**
0828: Note: this is primarily for internal use.
0829: @see Interpreter#source( String )
0830: @see Interpreter#eval( String )
0831: */
0832: public void setMethod(String name, BshMethod method)
0833: throws UtilEvalError {
0834: //checkMethodModifiers( method );
0835:
0836: if (methods == null)
0837: methods = new Hashtable();
0838:
0839: Object m = methods.get(name);
0840:
0841: if (m == null)
0842: methods.put(name, method);
0843: else if (m instanceof BshMethod) {
0844: Vector v = new Vector();
0845: v.addElement(m);
0846: v.addElement(method);
0847: methods.put(name, v);
0848: } else
0849: // Vector
0850: ((Vector) m).addElement(method);
0851: }
0852:
0853: /**
0854: @see #getMethod( String, Class [], boolean )
0855: @see #getMethod( String, Class [] )
0856: */
0857: public BshMethod getMethod(String name, Class[] sig)
0858: throws UtilEvalError {
0859: return getMethod(name, sig, false/*declaredOnly*/);
0860: }
0861:
0862: /**
0863: Get the bsh method matching the specified signature declared in
0864: this name space or a parent.
0865: <p>
0866: Note: this method is primarily intended for use internally. If you use
0867: this method outside of the bsh package you will have to be familiar
0868: with BeanShell's use of the Primitive wrapper class.
0869: @see bsh.Primitive
0870: @return the BshMethod or null if not found
0871: @param declaredOnly if true then only methods declared directly in this
0872: namespace will be found and no inherited or imported methods will
0873: be visible.
0874: */
0875: public BshMethod getMethod(String name, Class[] sig,
0876: boolean declaredOnly) throws UtilEvalError {
0877: BshMethod method = null;
0878:
0879: // Change import precedence if we are a class body/instance
0880: // Get import first.
0881: if (method == null && isClass && !declaredOnly)
0882: method = getImportedMethod(name, sig);
0883:
0884: Object m = null;
0885: if (method == null && methods != null) {
0886: m = methods.get(name);
0887:
0888: // m contains either BshMethod or Vector of BshMethod
0889: if (m != null) {
0890: // unwrap
0891: BshMethod[] ma;
0892: if (m instanceof Vector) {
0893: Vector vm = (Vector) m;
0894: ma = new BshMethod[vm.size()];
0895: vm.copyInto(ma);
0896: } else
0897: ma = new BshMethod[] { (BshMethod) m };
0898:
0899: // Apply most specific signature matching
0900: Class[][] candidates = new Class[ma.length][];
0901: for (int i = 0; i < ma.length; i++)
0902: candidates[i] = ma[i].getParameterTypes();
0903:
0904: int match = Reflect.findMostSpecificSignature(sig,
0905: candidates);
0906: if (match != -1)
0907: method = ma[match];
0908: }
0909: }
0910:
0911: if (method == null && !isClass && !declaredOnly)
0912: method = getImportedMethod(name, sig);
0913:
0914: // try parent
0915: if (!declaredOnly && (method == null) && (parent != null))
0916: return parent.getMethod(name, sig);
0917:
0918: return method;
0919: }
0920:
0921: /**
0922: Import a class name.
0923: Subsequent imports override earlier ones
0924: */
0925: public void importClass(String name) {
0926: if (importedClasses == null)
0927: importedClasses = new Hashtable();
0928:
0929: importedClasses.put(Name.suffix(name, 1), name);
0930: nameSpaceChanged();
0931: }
0932:
0933: /**
0934: subsequent imports override earlier ones
0935: */
0936: public void importPackage(String name) {
0937: if (importedPackages == null)
0938: importedPackages = new Vector();
0939:
0940: // If it exists, remove it and add it at the end (avoid memory leak)
0941: if (importedPackages.contains(name))
0942: importedPackages.remove(name);
0943:
0944: importedPackages.addElement(name);
0945: nameSpaceChanged();
0946: }
0947:
0948: /**
0949: Import scripted or compiled BeanShell commands in the following package
0950: in the classpath. You may use either "/" path or "." package notation.
0951: e.g. importCommands("/bsh/commands") or importCommands("bsh.commands")
0952: are equivalent. If a relative path style specifier is used then it is
0953: made into an absolute path by prepending "/".
0954: */
0955: public void importCommands(String name) {
0956: if (importedCommands == null)
0957: importedCommands = new Vector();
0958:
0959: // dots to slashes
0960: name = name.replace('.', '/');
0961: // absolute
0962: if (!name.startsWith("/"))
0963: name = "/" + name;
0964: // remove trailing (but preserve case of simple "/")
0965: if (name.length() > 1 && name.endsWith("/"))
0966: name = name.substring(0, name.length() - 1);
0967:
0968: // If it exists, remove it and add it at the end (avoid memory leak)
0969: if (importedCommands.contains(name))
0970: importedCommands.remove(name);
0971:
0972: importedCommands.addElement(name);
0973: nameSpaceChanged();
0974: }
0975:
0976: /**
0977: A command is a scripted method or compiled command class implementing a
0978: specified method signature. Commands are loaded from the classpath
0979: and may be imported using the importCommands() method.
0980: <p/>
0981:
0982: This method searches the imported commands packages for a script or
0983: command object corresponding to the name of the method. If it is a
0984: script the script is sourced into this namespace and the BshMethod for
0985: the requested signature is returned. If it is a compiled class the
0986: class is returned. (Compiled command classes implement static invoke()
0987: methods).
0988: <p/>
0989:
0990: The imported packages are searched in reverse order, so that later
0991: imports take priority.
0992: Currently only the first object (script or class) with the appropriate
0993: name is checked. If another, overloaded form, is located in another
0994: package it will not currently be found. This could be fixed.
0995: <p/>
0996:
0997: @return a BshMethod, Class, or null if no such command is found.
0998: @param name is the name of the desired command method
0999: @param argTypes is the signature of the desired command method.
1000: @throws UtilEvalError if loadScriptedCommand throws UtilEvalError
1001: i.e. on errors loading a script that was found
1002: */
1003: public Object getCommand(String name, Class[] argTypes,
1004: Interpreter interpreter) throws UtilEvalError {
1005: if (Interpreter.DEBUG)
1006: Interpreter.debug("getCommand: " + name);
1007: BshClassManager bcm = interpreter.getClassManager();
1008:
1009: if (importedCommands != null) {
1010: // loop backwards for precedence
1011: for (int i = importedCommands.size() - 1; i >= 0; i--) {
1012: String path = (String) importedCommands.elementAt(i);
1013:
1014: String scriptPath;
1015: if (path.equals("/"))
1016: scriptPath = path + name + ".bsh";
1017: else
1018: scriptPath = path + "/" + name + ".bsh";
1019:
1020: Interpreter
1021: .debug("searching for script: " + scriptPath);
1022:
1023: InputStream in = bcm.getResourceAsStream(scriptPath);
1024:
1025: if (in != null)
1026: return loadScriptedCommand(in, name, argTypes,
1027: scriptPath, interpreter);
1028:
1029: // Chop leading "/" and change "/" to "."
1030: String className;
1031: if (path.equals("/"))
1032: className = name;
1033: else
1034: className = path.substring(1).replace('/', '.')
1035: + "." + name;
1036:
1037: Interpreter.debug("searching for class: " + className);
1038: Class clas = bcm.classForName(className);
1039: if (clas != null)
1040: return clas;
1041: }
1042: }
1043:
1044: if (parent != null)
1045: return parent.getCommand(name, argTypes, interpreter);
1046: else
1047: return null;
1048: }
1049:
1050: protected BshMethod getImportedMethod(String name, Class[] sig)
1051: throws UtilEvalError {
1052: // Try object imports
1053: if (importedObjects != null)
1054: for (int i = 0; i < importedObjects.size(); i++) {
1055: Object object = importedObjects.elementAt(i);
1056: Class clas = object.getClass();
1057: Method method = Reflect
1058: .resolveJavaMethod(getClassManager(), clas,
1059: name, sig, false/*onlyStatic*/);
1060: if (method != null)
1061: return new BshMethod(method, object);
1062: }
1063:
1064: // Try static imports
1065: if (importedStatic != null)
1066: for (int i = 0; i < importedStatic.size(); i++) {
1067: Class clas = (Class) importedStatic.elementAt(i);
1068: Method method = Reflect
1069: .resolveJavaMethod(getClassManager(), clas,
1070: name, sig, true/*onlyStatic*/);
1071: if (method != null)
1072: return new BshMethod(method, null/*object*/);
1073: }
1074:
1075: return null;
1076: }
1077:
1078: protected Variable getImportedVar(String name) throws UtilEvalError {
1079: // Try object imports
1080: if (importedObjects != null)
1081: for (int i = 0; i < importedObjects.size(); i++) {
1082: Object object = importedObjects.elementAt(i);
1083: Class clas = object.getClass();
1084: Field field = Reflect.resolveJavaField(clas, name,
1085: false/*onlyStatic*/);
1086: if (field != null)
1087: return createVariable(name, field.getType(),
1088: new LHS(object, field));
1089: }
1090:
1091: // Try static imports
1092: if (importedStatic != null)
1093: for (int i = 0; i < importedStatic.size(); i++) {
1094: Class clas = (Class) importedStatic.elementAt(i);
1095: Field field = Reflect
1096: .resolveJavaField(clas, name, true/*onlyStatic*/);
1097: if (field != null)
1098: return createVariable(name, field.getType(),
1099: new LHS(field));
1100: }
1101:
1102: return null;
1103: }
1104:
1105: /**
1106: Load a command script from the input stream and find the BshMethod in
1107: the target namespace.
1108: @throws UtilEvalError on error in parsing the script or if the the
1109: method is not found after parsing the script.
1110: */
1111: /*
1112: If we want to support multiple commands in the command path we need to
1113: change this to not throw the exception.
1114: */
1115: private BshMethod loadScriptedCommand(InputStream in, String name,
1116: Class[] argTypes, String resourcePath,
1117: Interpreter interpreter) throws UtilEvalError {
1118: try {
1119: interpreter.eval(new InputStreamReader(in), this ,
1120: resourcePath);
1121: } catch (EvalError e) {
1122: /*
1123: Here we catch any EvalError from the interpreter because we are
1124: using it as a tool to load the command, not as part of the
1125: execution path.
1126: */
1127: Interpreter.debug(e.toString());
1128: throw new UtilEvalError("Error loading script: "
1129: + e.getMessage());
1130: }
1131:
1132: // Look for the loaded command
1133: BshMethod meth = getMethod(name, argTypes);
1134: /*
1135: if ( meth == null )
1136: throw new UtilEvalError("Loaded resource: " + resourcePath +
1137: "had an error or did not contain the correct method" );
1138: */
1139:
1140: return meth;
1141: }
1142:
1143: /**
1144: Helper that caches class.
1145: */
1146: void cacheClass(String name, Class c) {
1147: if (classCache == null) {
1148: classCache = new Hashtable();
1149: //cacheCount++; // debug
1150: }
1151:
1152: classCache.put(name, c);
1153: }
1154:
1155: /**
1156: Load a class through this namespace taking into account imports.
1157: The class search will proceed through the parent namespaces if
1158: necessary.
1159:
1160: @return null if not found.
1161: */
1162: public Class getClass(String name) throws UtilEvalError {
1163: Class c = getClassImpl(name);
1164: if (c != null)
1165: return c;
1166: else
1167: // implement the recursion for getClassImpl()
1168: if (parent != null)
1169: return parent.getClass(name);
1170: else
1171: return null;
1172: }
1173:
1174: /**
1175: Implementation of getClass()
1176:
1177: Load a class through this namespace taking into account imports.
1178: <p>
1179:
1180: Check the cache first. If an unqualified name look for imported
1181: class or package. Else try to load absolute name.
1182: <p>
1183:
1184: This method implements caching of unqualified names (normally imports).
1185: Qualified names are cached by the BshClassManager.
1186: Unqualified absolute class names (e.g. unpackaged Foo) are cached too
1187: so that we don't go searching through the imports for them each time.
1188:
1189: @return null if not found.
1190: */
1191: private Class getClassImpl(String name) throws UtilEvalError {
1192: Class c = null;
1193:
1194: // Check the cache
1195: if (classCache != null) {
1196: c = (Class) classCache.get(name);
1197:
1198: if (c != null)
1199: return c;
1200: }
1201:
1202: // Unqualified (simple, non-compound) name
1203: boolean unqualifiedName = !Name.isCompound(name);
1204:
1205: // Unqualified name check imported
1206: if (unqualifiedName) {
1207: // Try imported class
1208: if (c == null)
1209: c = getImportedClassImpl(name);
1210:
1211: // if found as imported also cache it
1212: if (c != null) {
1213: cacheClass(name, c);
1214: return c;
1215: }
1216: }
1217:
1218: // Try absolute
1219: c = classForName(name);
1220: if (c != null) {
1221: // Cache unqualified names to prevent import check again
1222: if (unqualifiedName)
1223: cacheClass(name, c);
1224: return c;
1225: }
1226:
1227: // Not found
1228: if (Interpreter.DEBUG)
1229: Interpreter.debug("getClass(): " + name + " not found in "
1230: + this );
1231: return null;
1232: }
1233:
1234: /**
1235: Try to make the name into an imported class.
1236: This method takes into account only imports (class or package)
1237: found directly in this NameSpace (no parent chain).
1238: */
1239: private Class getImportedClassImpl(String name)
1240: throws UtilEvalError {
1241: // Try explicitly imported class, e.g. import foo.Bar;
1242: String fullname = null;
1243: if (importedClasses != null)
1244: fullname = (String) importedClasses.get(name);
1245:
1246: // not sure if we should really recurse here for explicitly imported
1247: // class in parent...
1248:
1249: if (fullname != null) {
1250: /*
1251: Found the full name in imported classes.
1252: */
1253: // Try to make the full imported name
1254: Class clas = classForName(fullname);
1255:
1256: // Handle imported inner class case
1257: if (clas == null) {
1258: // Imported full name wasn't found as an absolute class
1259: // If it is compound, try to resolve to an inner class.
1260: // (maybe this should happen in the BshClassManager?)
1261:
1262: if (Name.isCompound(fullname))
1263: try {
1264: clas = getNameResolver(fullname).toClass();
1265: } catch (ClassNotFoundException e) { /* not a class */
1266: }
1267: else if (Interpreter.DEBUG)
1268: Interpreter
1269: .debug("imported unpackaged name not found:"
1270: + fullname);
1271:
1272: // If found cache the full name in the BshClassManager
1273: if (clas != null) {
1274: // (should we cache info in not a class case too?)
1275: getClassManager().cacheClassInfo(fullname, clas);
1276: return clas;
1277: }
1278: } else
1279: return clas;
1280:
1281: // It was explicitly imported, but we don't know what it is.
1282: // should we throw an error here??
1283: return null;
1284: }
1285:
1286: /*
1287: Try imported packages, e.g. "import foo.bar.*;"
1288: in reverse order of import...
1289: (give later imports precedence...)
1290: */
1291: if (importedPackages != null)
1292: for (int i = importedPackages.size() - 1; i >= 0; i--) {
1293: String s = ((String) importedPackages.elementAt(i))
1294: + "." + name;
1295: Class c = classForName(s);
1296: if (c != null)
1297: return c;
1298: }
1299:
1300: BshClassManager bcm = getClassManager();
1301: /*
1302: Try super import if available
1303: Note: we do this last to allow explicitly imported classes
1304: and packages to take priority. This method will also throw an
1305: error indicating ambiguity if it exists...
1306: */
1307: if (bcm.hasSuperImport()) {
1308: String s = bcm.getClassNameByUnqName(name);
1309: if (s != null)
1310: return classForName(s);
1311: }
1312:
1313: return null;
1314: }
1315:
1316: private Class classForName(String name) {
1317: return getClassManager().classForName(name);
1318: }
1319:
1320: /**
1321: Implements NameSource
1322: @return all variable and method names in this and all parent
1323: namespaces
1324: */
1325: public String[] getAllNames() {
1326: Vector vec = new Vector();
1327: getAllNamesAux(vec);
1328: String[] names = new String[vec.size()];
1329: vec.copyInto(names);
1330: return names;
1331: }
1332:
1333: /**
1334: Helper for implementing NameSource
1335: */
1336: protected void getAllNamesAux(Vector vec) {
1337: Enumeration varNames = variables.keys();
1338: while (varNames.hasMoreElements())
1339: vec.addElement(varNames.nextElement());
1340:
1341: Enumeration methodNames = methods.keys();
1342: while (methodNames.hasMoreElements())
1343: vec.addElement(methodNames.nextElement());
1344:
1345: if (parent != null)
1346: parent.getAllNamesAux(vec);
1347: }
1348:
1349: Vector nameSourceListeners;
1350:
1351: /**
1352: Implements NameSource
1353: Add a listener who is notified upon changes to names in this space.
1354: */
1355: public void addNameSourceListener(NameSource.Listener listener) {
1356: if (nameSourceListeners == null)
1357: nameSourceListeners = new Vector();
1358: nameSourceListeners.addElement(listener);
1359: }
1360:
1361: /**
1362: Perform "import *;" causing the entire classpath to be mapped.
1363: This can take a while.
1364: */
1365: public void doSuperImport() throws UtilEvalError {
1366: getClassManager().doSuperImport();
1367: }
1368:
1369: public String toString() {
1370: return "NameSpace: "
1371: + (nsName == null ? super .toString() : nsName + " ("
1372: + super .toString() + ")")
1373: + (isClass ? " (isClass) " : "")
1374: + (isMethod ? " (method) " : "")
1375: + (classStatic != null ? " (class static) " : "")
1376: + (classInstance != null ? " (class instance) " : "");
1377: }
1378:
1379: /*
1380: For serialization.
1381: Don't serialize non-serializable objects.
1382: */
1383: private synchronized void writeObject(java.io.ObjectOutputStream s)
1384: throws IOException {
1385: // clear name resolvers... don't know if this is necessary.
1386: names = null;
1387:
1388: s.defaultWriteObject();
1389: }
1390:
1391: /**
1392: Invoke a method in this namespace with the specified args and
1393: interpreter reference. No caller information or call stack is
1394: required. The method will appear as if called externally from Java.
1395: <p>
1396:
1397: @see bsh.This.invokeMethod(
1398: String methodName, Object [] args, Interpreter interpreter,
1399: CallStack callstack, SimpleNode callerInfo, boolean )
1400: */
1401: public Object invokeMethod(String methodName, Object[] args,
1402: Interpreter interpreter) throws EvalError {
1403: return invokeMethod(methodName, args, interpreter, null, null);
1404: }
1405:
1406: /**
1407: This method simply delegates to This.invokeMethod();
1408: <p>
1409: @see bsh.This.invokeMethod(
1410: String methodName, Object [] args, Interpreter interpreter,
1411: CallStack callstack, SimpleNode callerInfo )
1412: */
1413: public Object invokeMethod(String methodName, Object[] args,
1414: Interpreter interpreter, CallStack callstack,
1415: SimpleNode callerInfo) throws EvalError {
1416: return getThis(interpreter)
1417: .invokeMethod(methodName, args, interpreter, callstack,
1418: callerInfo, false/*declaredOnly*/);
1419: }
1420:
1421: /**
1422: Clear all cached classes and names
1423: */
1424: public void classLoaderChanged() {
1425: nameSpaceChanged();
1426: }
1427:
1428: /**
1429: Clear all cached classes and names
1430: */
1431: public void nameSpaceChanged() {
1432: classCache = null;
1433: names = null;
1434: }
1435:
1436: /**
1437: Import standard packages. Currently:
1438: <pre>
1439: importClass("bsh.EvalError");
1440: importClass("bsh.Interpreter");
1441: importPackage("javax.swing.event");
1442: importPackage("javax.swing");
1443: importPackage("java.awt.event");
1444: importPackage("java.awt");
1445: importPackage("java.net");
1446: importPackage("java.util");
1447: importPackage("java.io");
1448: importPackage("java.lang");
1449: importCommands("/bsh/commands");
1450: </pre>
1451: */
1452: public void loadDefaultImports() {
1453: /**
1454: Note: the resolver looks through these in reverse order, per
1455: precedence rules... so for max efficiency put the most common
1456: ones later.
1457: */
1458: importClass("bsh.EvalError");
1459: importClass("bsh.Interpreter");
1460: importPackage("javax.swing.event");
1461: importPackage("javax.swing");
1462: importPackage("java.awt.event");
1463: importPackage("java.awt");
1464: importPackage("java.net");
1465: importPackage("java.util");
1466: importPackage("java.io");
1467: importPackage("java.lang");
1468: importCommands("/bsh/commands");
1469: }
1470:
1471: /**
1472: This is the factory for Name objects which resolve names within
1473: this namespace (e.g. toObject(), toClass(), toLHS()).
1474: <p>
1475:
1476: This was intended to support name resolver caching, allowing
1477: Name objects to cache info about the resolution of names for
1478: performance reasons. However this not proven useful yet.
1479: <p>
1480:
1481: We'll leave the caching as it will at least minimize Name object
1482: creation.
1483: <p>
1484:
1485: (This method would be called getName() if it weren't already used for
1486: the simple name of the NameSpace)
1487: <p>
1488:
1489: This method was public for a time, which was a mistake.
1490: Use get() instead.
1491: */
1492: Name getNameResolver(String ambigname) {
1493: if (names == null)
1494: names = new Hashtable();
1495:
1496: Name name = (Name) names.get(ambigname);
1497:
1498: if (name == null) {
1499: name = new Name(this , ambigname);
1500: names.put(ambigname, name);
1501: }
1502:
1503: return name;
1504: }
1505:
1506: public int getInvocationLine() {
1507: SimpleNode node = getNode();
1508: if (node != null)
1509: return node.getLineNumber();
1510: else
1511: return -1;
1512: }
1513:
1514: public String getInvocationText() {
1515: SimpleNode node = getNode();
1516: if (node != null)
1517: return node.getText();
1518: else
1519: return "<invoked from Java code>";
1520: }
1521:
1522: /**
1523: This is a helper method for working inside of bsh scripts and commands.
1524: In that context it is impossible to see a ClassIdentifier object
1525: for what it is. Attempting to access a method on a ClassIdentifier
1526: will look like a static method invocation.
1527:
1528: This method is in NameSpace for convenience (you don't have to import
1529: bsh.ClassIdentifier to use it );
1530: */
1531: public static Class identifierToClass(ClassIdentifier ci) {
1532: return ci.getTargetClass();
1533: }
1534:
1535: /**
1536: Clear all variables, methods, and imports from this namespace.
1537: If this namespace is the root, it will be reset to the default
1538: imports.
1539: @see #loadDefaultImports()
1540: */
1541: public void clear() {
1542: variables = null;
1543: methods = null;
1544: importedClasses = null;
1545: importedPackages = null;
1546: importedCommands = null;
1547: importedObjects = null;
1548: if (parent == null)
1549: loadDefaultImports();
1550: classCache = null;
1551: names = null;
1552: }
1553:
1554: /**
1555: Import a compiled Java object's methods and variables into this
1556: namespace. When no scripted method / command or variable is found
1557: locally in this namespace method / fields of the object will be
1558: checked. Objects are checked in the order of import with later imports
1559: taking precedence.
1560: <p/>
1561: */
1562: /*
1563: Note: this impor pattern is becoming common... could factor it out into
1564: an importedObject Vector class.
1565: */
1566: public void importObject(Object obj) {
1567: if (importedObjects == null)
1568: importedObjects = new Vector();
1569:
1570: // If it exists, remove it and add it at the end (avoid memory leak)
1571: if (importedObjects.contains(obj))
1572: importedObjects.remove(obj);
1573:
1574: importedObjects.addElement(obj);
1575: nameSpaceChanged();
1576:
1577: }
1578:
1579: /**
1580: */
1581: public void importStatic(Class clas) {
1582: if (importedStatic == null)
1583: importedStatic = new Vector();
1584:
1585: // If it exists, remove it and add it at the end (avoid memory leak)
1586: if (importedStatic.contains(clas))
1587: importedStatic.remove(clas);
1588:
1589: importedStatic.addElement(clas);
1590: nameSpaceChanged();
1591: }
1592:
1593: /**
1594: Set the package name for classes defined in this namespace.
1595: Subsequent sets override the package.
1596: */
1597: void setPackage(String packageName) {
1598: this .packageName = packageName;
1599: }
1600:
1601: String getPackage() {
1602: if (packageName != null)
1603: return packageName;
1604:
1605: if (parent != null)
1606: return parent.getPackage();
1607:
1608: return null;
1609: }
1610:
1611: /**
1612: * If a writable property exists for the given name, set it and return true,
1613: * otherwise do nothing and return false.
1614: */
1615: boolean attemptSetPropertyValue(String propName, Object value,
1616: Interpreter interp) throws UtilEvalError {
1617:
1618: String accessorName = Reflect.accessorName("set", propName);
1619:
1620: Class[] classArray = new Class[] { value == null ? null : value
1621: .getClass() };
1622:
1623: BshMethod m = getMethod(accessorName, classArray);
1624:
1625: if (m != null) {
1626: try {
1627: invokeMethod(accessorName, new Object[] { value },
1628: interp);
1629: // m.invoke(new Object[] {value}, interp);
1630: return true;
1631: } catch (EvalError ee) {
1632: throw new UtilEvalError(
1633: "'This' property accessor threw exception: "
1634: + ee.getMessage());
1635: }
1636: }
1637:
1638: return false;
1639: }
1640:
1641: /**
1642: * Get a property from a scripted object or Primitive.VOID if no such
1643: * property exists.
1644: * @throws UtilEvalError
1645: * @throws UtilEvalError
1646: */
1647: Object getPropertyValue(String propName, Interpreter interp)
1648: throws UtilEvalError {
1649:
1650: String accessorName = Reflect.accessorName("get", propName);
1651: Class[] classArray = new Class[0];
1652:
1653: BshMethod m = getMethod(accessorName, classArray);
1654:
1655: try {
1656: if (m != null)
1657: return m.invoke((Object[]) null, interp);
1658:
1659: accessorName = Reflect.accessorName("is", propName);
1660: m = getMethod(accessorName, classArray);
1661: if (m != null)
1662: return m.invoke((Object[]) null, interp);
1663:
1664: return Primitive.VOID;
1665: } catch (EvalError ee) {
1666: throw new UtilEvalError(
1667: "'This' property accessor threw exception: "
1668: + ee.getMessage());
1669: }
1670: }
1671: }
|