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