All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bsh.Name Maven / Gradle / Ivy

The newest version!
/*
 * #%L
 * The AIBench Shell Plugin
 * %%
 * Copyright (C) 2006 - 2017 Daniel Glez-Peña and Florentino Fdez-Riverola
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */
/*****************************************************************************
 *                                                                           *
 *  This file is part of the BeanShell Java Scripting distribution.          *
 *  Documentation and updates may be found at http://www.beanshell.org/      *
 *                                                                           *
 *  Sun Public License Notice:                                               *
 *                                                                           *
 *  The contents of this file are subject to the Sun Public License Version  *
 *  1.0 (the "License"); you may not use this file except in compliance with *
 *  the License. A copy of the License is available at http://www.sun.com    * 
 *                                                                           *
 *  The Original Code is BeanShell. The Initial Developer of the Original    *
 *  Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright     *
 *  (C) 2000.  All Rights Reserved.                                          *
 *                                                                           *
 *  GNU Public License Notice:                                               *
 *                                                                           *
 *  Alternatively, the contents of this file may be used under the terms of  *
 *  the GNU Lesser General Public License (the "LGPL"), in which case the    *
 *  provisions of LGPL are applicable instead of those above. If you wish to *
 *  allow use of your version of this file only under the  terms of the LGPL *
 *  and not to allow others to use your version of this file under the SPL,  *
 *  indicate your decision by deleting the provisions above and replace      *
 *  them with the notice and other provisions required by the LGPL.  If you  *
 *  do not delete the provisions above, a recipient may use your version of  *
 *  this file under either the SPL or the LGPL.                              *
 *                                                                           *
 *  Patrick Niemeyer ([email protected])                                           *
 *  Author of Learning Java, O'Reilly & Associates                           *
 *  http://www.pat.net/~pat/                                                 *
 *                                                                           *
 *****************************************************************************/

package bsh;

import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;

/**
 * What's in a name? I'll tell you... Name() is a somewhat ambiguous thing in
 * the grammar and so is this.
 * 

* * This class is a name resolver. It holds a possibly ambiguous dot separated * name and reference to a namespace in which it allegedly lives. It provides * methods that attempt to resolve the name to various types of entities: e.g. * an Object, a Class, a declared scripted BeanShell method. *

* * Name objects are created by the factory method NameSpace getNameResolver(), * which caches them subject to a class namespace change. This means that we can * cache information about various types of resolution here. Currently very * little if any information is cached. However with a future "optimize" setting * that defeats certain dynamic behavior we might be able to cache quite a bit. */ /* * Implementation notes

 Thread safety: all of the work
 * methods in this class must be synchronized because they share the internal
 * intermediate evaluation state.
 * 
 * Note about invokeMethod(): We could simply use resolveMethod and return the
 * MethodInvoker (BshMethod or JavaMethod) however there is no easy way for the
 * AST (BSHMehodInvocation) to use this as it doesn't have type information
 * about the target to resolve overloaded methods. (In Java, overloaded methods
 * are resolved at compile time... here they are, of necessity, dynamic). So it
 * would have to do what we do here and cache by signature. We now do that for
 * the client in Reflect.java.
 * 
 * Note on this.caller resolution: Although references like these do work:
 * 
 * this.caller.caller.caller... // works
 * 
 * the equivalent using successive calls: // does *not* work for(
 * caller=this.caller; caller != null; caller = caller.caller );
 * 
 * is prohibited by the restriction that you can only call .caller on a literal
 * this or caller reference. The effect is that magic caller reference only
 * works through the current 'this' reference. The real explanation is that This
 * referernces do not really know anything about their depth on the call stack.
 * It might even be hard to define such a thing...
 * 
 * For those purposes we provide :
 * 
 * this.callstack
 * 
 * 
*/ class Name implements java.io.Serializable { // These do not change during evaluation public NameSpace namespace; String value = null; // --------------------------------------------------------- // The following instance variables mutate during evaluation and should // be reset by the reset() method where necessary // For evaluation /** Remaining text to evaluate */ private String evalName; /** * The last part of the name evaluated. This is really only used for * this, caller, and super resolution. */ private String lastEvalName; private static String FINISHED = null; // null evalname and we're // finished private Object evalBaseObject; // base object for current eval private int callstackDepth; // number of times eval hit // 'this.caller' // // End mutable instance variables. // --------------------------------------------------------- // Begin Cached result structures // These are optimizations // Note: it's ok to cache class resolution here because when the class // space changes the namespace will discard cached names. /** * The result is a class */ Class asClass; /** * The result is a static method call on the following class */ Class classOfStaticMethod; // End Cached result structures private void reset() { evalName = value; evalBaseObject = null; callstackDepth = 0; } /** * This constructor should *not* be used in general. Use NameSpace * getNameResolver() which supports caching. * * @see NameSpace getNameResolver(). */ // I wish I could make this "friendly" to only NameSpace Name(NameSpace namespace, String s) { this.namespace = namespace; value = s; } /** * Resolve possibly complex name to an object value. * * Throws EvalError on various failures. A null object value is * indicated by a Primitive.NULL. A return type of Primitive.VOID comes * from attempting to access an undefined variable. * * Some cases: myVariable myVariable.foo myVariable.foo.bar * java.awt.GridBagConstraints.BOTH * my.package.stuff.MyClass.someField.someField... * * Interpreter reference is necessary to allow resolution of * "this.interpreter" magic field. CallStack reference is necessary to * allow resolution of "this.caller" magic field. "this.callstack" magic * field. */ public Object toObject(CallStack callstack, Interpreter interpreter) throws UtilEvalError { return toObject(callstack, interpreter, false); } /** * @see toObject() * @param forceClass * if true then resolution will only produce a class. * This is necessary to disambiguate in cases where the * grammar knows that we want a class; where in general * the var path may be taken. */ synchronized public Object toObject(CallStack callstack, Interpreter interpreter, boolean forceClass) throws UtilEvalError { reset(); Object obj = null; while (evalName != null) obj = consumeNextObjectField(callstack, interpreter, forceClass, false/* autoalloc */); if (obj == null) throw new InterpreterError("null value in toObject()"); return obj; } private Object completeRound(String lastEvalName, String nextEvalName, Object returnObject) { if (returnObject == null) throw new InterpreterError("lastEvalName = " + lastEvalName); this.lastEvalName = lastEvalName; this.evalName = nextEvalName; this.evalBaseObject = returnObject; return returnObject; } /** * Get the next object by consuming one or more components of evalName. * Often this consumes just one component, but if the name is a * classname it will consume all of the components necessary to make the * class identifier. */ private Object consumeNextObjectField(CallStack callstack, Interpreter interpreter, boolean forceClass, boolean autoAllocateThis) throws UtilEvalError { /* * Is it a simple variable name? Doing this first gives the * correct Java precedence for vars vs. imported class names (at * least in the simple case - see tests/precedence1.bsh). It * should also speed things up a bit. */ if ((evalBaseObject == null && !isCompound(evalName)) && !forceClass) { Object obj = resolveThisFieldReference(callstack, namespace, interpreter, evalName, false); if (obj != Primitive.VOID) return completeRound(evalName, FINISHED, obj); } /* * Is it a bsh script variable reference? If we're just starting * the eval of name (no base object) or we're evaluating * relative to a This type reference check. */ String varName = prefix(evalName, 1); if ((evalBaseObject == null || evalBaseObject instanceof This) && !forceClass) { if (Interpreter.DEBUG) Interpreter.debug("trying to resolve variable: " + varName); Object obj; // switch namespace and special var visibility if (evalBaseObject == null) { obj = resolveThisFieldReference(callstack, namespace, interpreter, varName, false); } else { obj = resolveThisFieldReference(callstack, ((This) evalBaseObject).namespace, interpreter, varName, true); } if (obj != Primitive.VOID) { // Resolved the variable if (Interpreter.DEBUG) Interpreter.debug("resolved variable: " + varName + " in namespace: " + namespace); return completeRound(varName, suffix(evalName), obj); } } /* * Is it a class name? If we're just starting eval of name try * to make it, else fail. */ if (evalBaseObject == null) { if (Interpreter.DEBUG) Interpreter.debug("trying class: " + evalName); /* * Keep adding parts until we have a class */ Class clas = null; int i = 1; String className = null; for (; i <= countParts(evalName); i++) { className = prefix(evalName, i); if ((clas = namespace.getClass(className)) != null) break; } if (clas != null) { return completeRound(className, suffix(evalName, countParts(evalName) - i), new ClassIdentifier(clas)); } // not a class (or variable per above) if (Interpreter.DEBUG) Interpreter.debug("not a class, trying var prefix " + evalName); } // No variable or class found in 'this' type ref. // if autoAllocateThis then create one; a child 'this'. if ((evalBaseObject == null || evalBaseObject instanceof This) && !forceClass && autoAllocateThis) { NameSpace targetNameSpace = (evalBaseObject == null) ? namespace : ((This) evalBaseObject).namespace; Object obj = new NameSpace(targetNameSpace, "auto: " + varName).getThis(interpreter); targetNameSpace.setVariable(varName, obj, false); return completeRound(varName, suffix(evalName), obj); } /* * If we didn't find a class or variable name (or prefix) above * there are two possibilities: - If we are a simple name then * we can pass as a void variable reference. - If we are * compound then we must fail at this point. */ if (evalBaseObject == null) { if (!isCompound(evalName)) { return completeRound(evalName, FINISHED, Primitive.VOID); } else throw new UtilEvalError("Class or variable not found: " + evalName); } /* * -------------------------------------------------------- * After this point we're definitely evaluating relative to a * base object. * -------------------------------------------------------- */ /* * Do some basic validity checks. */ if (evalBaseObject == Primitive.NULL) // previous round // produced null throw new UtilTargetError(new NullPointerException("Null Pointer while evaluating: " + value)); if (evalBaseObject == Primitive.VOID) // previous round // produced void throw new UtilEvalError("Undefined variable or class name while evaluating: " + value); if (evalBaseObject instanceof Primitive) throw new UtilEvalError("Can't treat primitive like an object. " + "Error while evaluating: " + value); /* * Resolve relative to a class type static field, inner class, ? */ if (evalBaseObject instanceof ClassIdentifier) { Class clas = ((ClassIdentifier) evalBaseObject).getTargetClass(); String field = prefix(evalName, 1); // Class qualified 'this' reference from inner class. // e.g. 'MyOuterClass.this' if (field.equals("this")) { // find the enclosing class instance space of // the class name NameSpace ns = namespace; while (ns != null) { // getClassInstance() throws exception // if not there if (ns.classInstance != null && ns.classInstance.getClass() == clas) return completeRound(field, suffix(evalName), ns.classInstance); ns = ns.getParent(); } throw new UtilEvalError("Can't find enclosing 'this' instance of class: " + clas); } Object obj = null; // static field? try { if (Interpreter.DEBUG) Interpreter.debug("Name call to getStaticFieldValue, class: " + clas + ", field:" + field); obj = Reflect.getStaticFieldValue(clas, field); } catch (ReflectError e) { if (Interpreter.DEBUG) Interpreter.debug("field reflect error: " + e); } // inner class? if (obj == null) { String iclass = clas.getName() + "$" + field; Class c = namespace.getClass(iclass); if (c != null) obj = new ClassIdentifier(c); } if (obj == null) throw new UtilEvalError("No static field or inner class: " + field + " of " + clas); return completeRound(field, suffix(evalName), obj); } /* * If we've fallen through here we are no longer resolving to a * class type. */ if (forceClass) throw new UtilEvalError(value + " does not resolve to a class name."); /* * Some kind of field access? */ String field = prefix(evalName, 1); // length access on array? if (field.equals("length") && evalBaseObject.getClass().isArray()) { Object obj = new Primitive(Array.getLength(evalBaseObject)); return completeRound(field, suffix(evalName), obj); } // Check for field on object // Note: could eliminate throwing the exception somehow try { Object obj = Reflect.getObjectFieldValue(evalBaseObject, field); return completeRound(field, suffix(evalName), obj); } catch (ReflectError e) { /* not a field */ } // if we get here we have failed throw new UtilEvalError("Cannot access field: " + field + ", on object: " + evalBaseObject); } /** * Resolve a variable relative to a This reference. * * This is the general variable resolution method, accomodating special * fields from the This context. Together the namespace and interpreter * comprise the This context. The callstack, if available allows for the * this.caller construct. Optionally interpret special "magic" field * names: e.g. interpreter.

* * @param callstack * may be null, but this is only legitimate in special * cases where we are sure resolution will not involve * this.caller. * * @param namespace * the namespace of the this reference (should be the * same as the top of the stack? */ Object resolveThisFieldReference(CallStack callstack, NameSpace thisNameSpace, Interpreter interpreter, String varName, boolean specialFieldsVisible) throws UtilEvalError { if (varName.equals("this")) { /* * Somewhat of a hack. If the special fields are visible * (we're operating relative to a 'this' type already) * dissallow further .this references to prevent user * from skipping to things like super.this.caller */ if (specialFieldsVisible) throw new UtilEvalError("Redundant to call .this on This type"); // Allow getThis() to work through BlockNameSpace to the // method // namespace // XXX re-eval this... do we need it? This ths = thisNameSpace.getThis(interpreter); thisNameSpace = ths.getNameSpace(); Object result = ths; NameSpace classNameSpace = getClassNameSpace(thisNameSpace); if (classNameSpace != null) { if (isCompound(evalName)) result = classNameSpace.getThis(interpreter); else result = classNameSpace.getClassInstance(); } return result; } /* * Some duplication for "super". See notes for "this" above If * we're in an enclsing class instance and have a superclass * instance our super is the superclass instance. */ if (varName.equals("super")) { // if ( specialFieldsVisible ) // throw new UtilEvalError("Redundant to call .this on // This type"); // Allow getSuper() to through BlockNameSpace to the // method's super This ths = thisNameSpace.getSuper(interpreter); thisNameSpace = ths.getNameSpace(); // super is now the closure's super or class instance // XXXX re-evaluate this // can getSuper work by itself now? // If we're a class instance and the parent is also a // class instance // then super means our parent. if (thisNameSpace.getParent() != null && thisNameSpace.getParent().isClass) ths = thisNameSpace.getParent().getThis(interpreter); return ths; } Object obj = null; if (varName.equals("global")) obj = thisNameSpace.getGlobal(interpreter); if (obj == null && specialFieldsVisible) { if (varName.equals("namespace")) obj = thisNameSpace; else if (varName.equals("variables")) obj = thisNameSpace.getVariableNames(); else if (varName.equals("methods")) obj = thisNameSpace.getMethodNames(); else if (varName.equals("interpreter")) if (lastEvalName.equals("this")) obj = interpreter; else throw new UtilEvalError("Can only call .interpreter on literal 'this'"); } if (obj == null && specialFieldsVisible && varName.equals("caller")) { if (lastEvalName.equals("this") || lastEvalName.equals("caller")) { // get the previous context (see notes for this // class) if (callstack == null) throw new InterpreterError("no callstack"); obj = callstack.get(++callstackDepth).getThis(interpreter); } else throw new UtilEvalError("Can only call .caller on literal 'this' or literal '.caller'"); // early return return obj; } if (obj == null && specialFieldsVisible && varName.equals("callstack")) { if (lastEvalName.equals("this")) { // get the previous context (see notes for this // class) if (callstack == null) throw new InterpreterError("no callstack"); obj = callstack; } else throw new UtilEvalError("Can only call .callstack on literal 'this'"); } if (obj == null) obj = thisNameSpace.getVariable(varName); if (obj == null) throw new InterpreterError("null this field ref:" + varName); return obj; } /** * @return the enclosing class body namespace or null if not in a class. */ static NameSpace getClassNameSpace(NameSpace thisNameSpace) { // is a class instance // if ( thisNameSpace.classInstance != null ) if (thisNameSpace.isClass) return thisNameSpace; if (thisNameSpace.isMethod && thisNameSpace.getParent() != null // && thisNameSpace.getParent().classInstance != null && thisNameSpace.getParent().isClass) return thisNameSpace.getParent(); return null; } /** * Check the cache, else use toObject() to try to resolve to a class * identifier. * * @throws ClassNotFoundException * on class not found. * @throws ClassPathException * (type of EvalError) on special case of ambiguous * unqualified name after super import. */ synchronized public Class toClass() throws ClassNotFoundException, UtilEvalError { if (asClass != null) return asClass; reset(); // "var" means untyped, return null class if (evalName.equals("var")) return asClass = null; /* Try straightforward class name first */ Class clas = namespace.getClass(evalName); if (clas == null) { /* * Try toObject() which knows how to work through inner * classes and see what we end up with */ Object obj = null; try { // Null interpreter and callstack references. // class only resolution should not require // them. obj = toObject(null, null, true); } catch (UtilEvalError e) { } ; // couldn't resolve it if (obj instanceof ClassIdentifier) clas = ((ClassIdentifier) obj).getTargetClass(); } if (clas == null) throw new ClassNotFoundException("Class: " + value + " not found in namespace"); asClass = clas; return asClass; } /* */ synchronized public LHS toLHS(CallStack callstack, Interpreter interpreter) throws UtilEvalError { // Should clean this up to a single return statement reset(); LHS lhs; // Simple (non-compound) variable assignment e.g. x=5; if (!isCompound(evalName)) { if (evalName.equals("this")) throw new UtilEvalError("Can't assign to 'this'."); // Interpreter.debug("Simple var LHS..."); lhs = new LHS(namespace, evalName, false/* * bubble up if * allowed */); return lhs; } // Field e.g. foo.bar=5; Object obj = null; try { while (evalName != null && isCompound(evalName)) { obj = consumeNextObjectField(callstack, interpreter, false/* forcclass */, true/* autoallocthis */); } } catch (UtilEvalError e) { throw new UtilEvalError("LHS evaluation: " + e.getMessage()); } // Finished eval and its a class. if (evalName == null && obj instanceof ClassIdentifier) throw new UtilEvalError("Can't assign to class: " + value); if (obj == null) throw new UtilEvalError("Error in LHS: " + value); // e.g. this.x=5; or someThisType.x=5; if (obj instanceof This) { // dissallow assignment to magic fields if (evalName.equals("namespace") || evalName.equals("variables") || evalName.equals("methods") || evalName.equals("caller")) throw new UtilEvalError("Can't assign to special variable: " + evalName); Interpreter.debug("found This reference evaluating LHS"); /* * If this was a literal "super" reference then we allow * recursion in setting the variable to get the normal * effect of finding the nearest definition starting at * the super scope. On any other resolution qualified by * a 'this' type reference we want to set the variable * directly in that scope. e.g. this.x=5; or * someThisType.x=5; * * In the old scoping rules super didn't do this. */ boolean localVar = !lastEvalName.equals("super"); return new LHS(((This) obj).namespace, evalName, localVar); } if (evalName != null) { try { if (obj instanceof ClassIdentifier) { Class clas = ((ClassIdentifier) obj).getTargetClass(); lhs = Reflect.getLHSStaticField(clas, evalName); return lhs; } else { lhs = Reflect.getLHSObjectField(obj, evalName); return lhs; } } catch (ReflectError e) { throw new UtilEvalError("Field access: " + e); } } throw new InterpreterError("Internal error in lhs..."); } /** * Invoke the method identified by this name. Performs caching of method * resolution using SignatureKey. *

* * Name contains a wholely unqualfied messy name; resolve it to ( object | * static prefix ) + method name and invoke. *

* * The interpreter is necessary to support 'this.interpreter' references * in the called code. (e.g. debug()); *

* *

         *           Some cases:
         *  
         *           // dynamic
         *           local();
         *           myVariable.foo();
         *           myVariable.bar.blah.foo();
         *           // static
         *           java.lang.Integer.getInteger("foo");
         * 
*/ public Object invokeMethod(Interpreter interpreter, Object[] args, CallStack callstack, SimpleNode callerInfo) throws UtilEvalError, EvalError, ReflectError, InvocationTargetException { String methodName = Name.suffix(value, 1); BshClassManager bcm = interpreter.getClassManager(); NameSpace namespace = callstack.top(); // Optimization - If classOfStaticMethod is set then we have // already // been here and determined that this is a static method // invocation. // Note: maybe factor this out with path below... clean up. if (classOfStaticMethod != null) { return Reflect.invokeStaticMethod(bcm, classOfStaticMethod, methodName, args); } if (!Name.isCompound(value)) return invokeLocalMethod(interpreter, args, callstack, callerInfo); // Note: if we want methods declared inside blocks to be // accessible via // this.methodname() inside the block we could handle it here as // a // special case. See also resolveThisFieldReference() special // handling // for BlockNameSpace case. They currently work via the direct // name // e.g. methodName(). String prefix = Name.prefix(value); // Superclass method invocation? (e.g. super.foo()) if (prefix.equals("super") && Name.countParts(value) == 2) { // Allow getThis() to work through block namespaces // first This ths = namespace.getThis(interpreter); NameSpace thisNameSpace = ths.getNameSpace(); NameSpace classNameSpace = getClassNameSpace(thisNameSpace); if (classNameSpace != null) { Object instance = classNameSpace.getClassInstance(); return ClassGenerator.getClassGenerator().invokeSuperclassMethod(bcm, instance, methodName, args); } } // Find target object or class identifier Name targetName = namespace.getNameResolver(prefix); Object obj = targetName.toObject(callstack, interpreter); if (obj == Primitive.VOID) throw new UtilEvalError("Attempt to resolve method: " + methodName + "() on undefined variable or class name: " + targetName); // if we've got an object, resolve the method if (!(obj instanceof ClassIdentifier)) { if (obj instanceof Primitive) { if (obj == Primitive.NULL) throw new UtilTargetError(new NullPointerException("Null Pointer in Method Invocation")); // some other primitive // should avoid calling methods on primitive, as // we do // in Name (can't treat primitive like an object // message) // but the hole is useful right now. if (Interpreter.DEBUG) interpreter.debug("Attempt to access method on primitive..." + " allowing bsh.Primitive to peek through for debugging"); } // found an object and it's not an undefined variable return Reflect.invokeObjectMethod(obj, methodName, args, interpreter, callstack, callerInfo); } // It's a class // try static method if (Interpreter.DEBUG) Interpreter.debug("invokeMethod: trying static - " + targetName); Class clas = ((ClassIdentifier) obj).getTargetClass(); // cache the fact that this is a static method invocation on // this class classOfStaticMethod = clas; if (clas != null) return Reflect.invokeStaticMethod(bcm, clas, methodName, args); // return null; ??? throw new UtilEvalError("invokeMethod: unknown target: " + targetName); } /** * Invoke a locally declared method or a bsh command. If the method is * not already declared in the namespace then try to load it as a * resource from the imported command path (e.g. /bsh/commands) */ /* * Note: the bsh command code should probably not be here... we need to * scope it by the namespace that imported the command... so it probably * needs to be integrated into NameSpace. */ private Object invokeLocalMethod(Interpreter interpreter, Object[] args, CallStack callstack, SimpleNode callerInfo) throws EvalError/* * , * ReflectError, * InvocationTargetException */ { if (Interpreter.DEBUG) Interpreter.debug("invokeLocalMethod: " + value); if (interpreter == null) throw new InterpreterError("invokeLocalMethod: interpreter = null"); String commandName = value; Class[] argTypes = Types.getTypes(args); // Check for existing method BshMethod meth = null; try { meth = namespace.getMethod(commandName, argTypes); } catch (UtilEvalError e) { throw e.toEvalError("Local method invocation", callerInfo, callstack); } // If defined, invoke it if (meth != null) return meth.invoke(args, interpreter, callstack, callerInfo); BshClassManager bcm = interpreter.getClassManager(); // Look for a BeanShell command Object commandObject; try { commandObject = namespace.getCommand(commandName, argTypes, interpreter); } catch (UtilEvalError e) { throw e.toEvalError("Error loading command: ", callerInfo, callstack); } // should try to print usage here if nothing found if (commandObject == null) { // Look for a default invoke() handler method in the // namespace // Note: this code duplicates that in This.java... // should it? // Call on 'This' can never be a command BshMethod invokeMethod = null; try { invokeMethod = namespace.getMethod("invoke", new Class[] { null, null }); } catch (UtilEvalError e) { throw e.toEvalError("Local method invocation", callerInfo, callstack); } if (invokeMethod != null) return invokeMethod.invoke(new Object[] { commandName, args }, interpreter, callstack, callerInfo); throw new EvalError("Command not found: " + StringUtil.methodString(commandName, argTypes), callerInfo, callstack); } if (commandObject instanceof BshMethod) return ((BshMethod) commandObject).invoke(args, interpreter, callstack, callerInfo); if (commandObject instanceof Class) try { return Reflect.invokeCompiledCommand(((Class) commandObject), args, interpreter, callstack); } catch (UtilEvalError e) { throw e.toEvalError("Error invoking compiled command: ", callerInfo, callstack); } throw new InterpreterError("invalid command type"); } /* * private String getHelp( String name ) throws UtilEvalError { try { // * should check for null namespace here return get( "bsh.help."+name, * null/interpreter/ ); } catch ( Exception e ) { return "usage: "+name; } } * * private String getHelp( Class commandClass ) throws UtilEvalError { * try { return (String)Reflect.invokeStaticMethod( null/bcm/, * commandClass, "usage", null ); } catch( Exception e ) return "usage: * "+name; } } */ // Static methods that operate on compound ('.' separated) names // I guess we could move these to StringUtil someday public static boolean isCompound(String value) { return value.indexOf('.') != -1; // return countParts(value) > 1; } static int countParts(String value) { if (value == null) return 0; int count = 0; int index = -1; while ((index = value.indexOf('.', index + 1)) != -1) count++; return count + 1; } static String prefix(String value) { if (!isCompound(value)) return null; return prefix(value, countParts(value) - 1); } static String prefix(String value, int parts) { if (parts < 1) return null; int count = 0; int index = -1; while (((index = value.indexOf('.', index + 1)) != -1) && (++count < parts)) { ; } return (index == -1) ? value : value.substring(0, index); } static String suffix(String name) { if (!isCompound(name)) return null; return suffix(name, countParts(name) - 1); } public static String suffix(String value, int parts) { if (parts < 1) return null; int count = 0; int index = value.length() + 1; while (((index = value.lastIndexOf('.', index - 1)) != -1) && (++count < parts)) ; return (index == -1) ? value : value.substring(index + 1); } // end compound name routines public String toString() { return value; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy