1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
|
<?xml version="1.0" encoding="UTF-8"?>
<html><head><title>Adding BeanShell Commands</title></head><body bgcolor="ffffff"><table cellspacing="10"><tr><td align="center"><a href="http://www.beanshell.org/"><img src="../images/homebutton.gif"/><br/>Home</a></td><td><a href="commands.html#BeanShell_Commands"><img src="../images/backbutton.gif"/><br/>Back
</a></td><td align="center"><a href="contents.html"><img src="../images/upbutton.gif"/><br/>Contents</a></td><td align="center"><a href="strictjava.html#Strict_Java_Mode"><img src="../images/forwardbutton.gif"/><br/>Next
</a></td></tr></table><h1>Adding BeanShell Commands</h1>
BeanShell Commands are scripted methods or compiled Java classes which are
dynamically loaded from the classpath to implement a method.
All of the standard commands we discuss in this manual live in the BeanShell
JAR file under the path /bsh/commands.
<p CLEAR="ALL"/>
Adding to the set of "prefab" commands supplied with BeanShell is as easy as
writing any other BeanShell methods. You simply have to place your
script into a file named with the same name as the command and place the
file in the classpath. You may then "import" the commands with the
importCommands() method.
<p CLEAR="ALL"/>
Command files can be placed anywhere in the BeanShell classpath.
You can use even use the addClassPath() or setClassPath() commands to add new
command directories or JARs containing commands to your script at any time.
<p CLEAR="ALL"/>
<h2><a name="Hello_World">Hello World</a></h2>
For example, let's make a helloWorld() command:
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
// File: helloWorld.bsh
helloWorld() {
print("Hello World!");
}
</pre></td></tr></table></center><p/>
Place the command file helloWorld.bsh in a directory or JAR in your
classpath and import it with the importCommands() command.
You can either set the classpath
externally for Java or inside of BeanShell with the addClassPath() command.
For example, suppose we have placed
the file in the path: /home/pat/mycommands/helloWorld.bsh.
We could then do:
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
addClassPath("/home/pat"); // If it's not already in our classpath
importCommands("/mycommands");
</pre></td></tr></table></center><p/>
We can now use helloWorld() just like any other BeanShell command.
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
helloWorld(); // prints "Hello World!"
</pre></td></tr></table></center><p/>
importCommands() will accept either a "resource path" style path name or a
Java package name. Either one is simply converted to a resource path or
Java package name as required to load scripts or compiled BeanShell command
classes. A relative path (e.g. "mycommands") is turned into an absolute path
by prepending "/". You may import "loose" commands (like unpackaged classes)
at the top of the classpath by importing "/".
<p CLEAR="ALL"/>
If for example you have placed BeanShell commands along with your other
classes in a Java package called com.xyz.utils in your classpath, you can
import those commands with:
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
// equivalent
importCommands("com.xyz.utils");
importCommands("/com/xyz/utils");
</pre></td></tr></table></center><p/>
Imported commands are scoped just like imported classes. So if you import
commands in a method or object they are local to that scope.
<h3>Overloaded Commands</h3>
BeanShell command scripts can contain any number of overloaded forms of the
command method, e.g.:
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
// File: helloWorld.bsh
helloWorld() {
print("Hello World!");
}
helloWorld( String msg ) {
print("Hello World: "+msg);
}
</pre></td></tr></table></center><p/>
BeanShell will select the appropriate method based on the
usual rules for methods selection.
<h2><a name="Compiled_Commands">Compiled Commands</a></h2>
You can also implement BeanShell commands as compiled classes instead of
scripts if you wish. Your class name must simply be the name of the command
(matching case as well) and it must implement one or more static invoke()
methods who's signatures match a pattern. The first two
arguments of the invoke() method must be the bsh.Interpreter
and bsh.CallStack objects that provide context to all BeanShell scripts.
Then any number (possibly zero) of arguments, which are the arguments of the
command may follow.
BeanShell will select the appropriate method based on the
usual rules for methods selection.
<p CLEAR="ALL"/>
The dir() command is an example of a BeanShell command that is implemented in
Java. Let's look at a snippet from it to see how it implements a pair of
invoke() methods for the dir() and dir(path) commands.
<p CLEAR="ALL"/>
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
/**
Implement dir() command.
*/
public static void invoke( Interpreter env, CallStack callstack )
{
String dir = ".";
invoke( env, callstack, dir );
}
/**
Implement dir( String directory ) command.
*/
public static void invoke(
Interpreter env, CallStack callstack, String dir )
{
...
}
</pre></td></tr></table></center><p/>
<h2><a name="User_Defined_Commands_with_invoke()">User Defined Commands with invoke()</a></h2>
It is useful to note that the invoke() meta-method which we described in the
section "Scripting Interfaces" can be used directly in scope as well as through
an object reference and one could use this to load arbitrary commands or
implement arbitrary behavior for commands (undefined method calls). For
example:
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
invoke( String methodName, Object [] arguments ) {
print("You invoked the method: "+ methodName );
}
// invoke() will be called to handle noSuchMethod()
noSuchMethod("foo");
</pre></td></tr></table></center><p/>
invoke() is called to handle any method invocations for undefined methods
within its scope. In this case we have declared it at the global scope.
<p CLEAR="ALL"/>
<h2><a name="Commands_Scope">Commands Scope</a></h2>
Scripted BeanShell commands are loaded when no existing method matches
the command name.
When a command script is loaded it is sourced (evaluated) in the 'global' scope
of the interpreter. This means that once the command is loaded the methods
declared in the command script are then defined in the interpreter's global
scope and subsequent calls to the command are simply handled by
the those methods as any other scripted method.
<p CLEAR="ALL"/>
<p/><center><table cellpadding="5" border="1" width="90%"><tr><td bgcolor="#eeeebb"><strong>Note:</strong><br CLEAR="ALL"/>
Note that this means that currently scripted commands
may only be loaded once and then they are effectively cached.
</td></tr></table></center><p/>
<p CLEAR="ALL"/>
<h2><a name="Getting_the_Caller_Context">Getting the Caller Context</a></h2>
A useful feature of BeanShell for command writers is the 'this.caller'
reference, which allows you to create side effects (set or modify variables) in
the method caller's scope. For example:
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
fooSetter() {
this.caller.foo=42;
}
</pre></td></tr></table></center><p/>
The above command has the effect that after running it the variable 'foo'
will be set in the caller's scope. e.g.:
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
fooSetter();
print( foo ); // 42
</pre></td></tr></table></center><p/>
It may appear that we could simply have used the 'super' modifier to accomplish
this and in this case it would have worked. However it would not have been
correct in general because the 'super' of fooSetter() always points to the same
location - the scope in which it was defined. We would like fooSetter() to
set the variable in whatever scope it was called from.
<p CLEAR="ALL"/>
To reiterate:
The 'super' of a method is always
the context in which the method was defined. But the caller may be any context
in which the method is used. In the following example,
the parent context of foo() and the caller context of foo() are the same:
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
foo() { ... }
foo();
</pre></td></tr></table></center><p/>
But this is not always the case, as for bar() in the following example:
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
foo() {
bar() { ... }
...
}
// somewhere
fooObject.bar();
</pre></td></tr></table></center><p/>
The special "magic" field reference: 'this.caller' makes it possible
to reach the context of whomever called bar(). The 'this.caller' reference
always refers to the calling context of the current method context.
<p CLEAR="ALL"/>
<img src="../images/caller.gif"/>
<p CLEAR="ALL"/>
The diagram above shows the foo() and bar() scopes, along with the caller's
scope access via 'this.caller'.
<p CLEAR="ALL"/>
This is very useful in writing BeanShell commands.
BeanShell command methods are always loaded into the global
scope. If you refer to 'super' from your command you will simply
get 'global'. Often it is desirable to write commands that explicitly have
side effects in the caller's scope. The ability to do so makes it possible to
write new kinds of commands that have the appearance of being "built-in"
to the language.
<p CLEAR="ALL"/>
A good example of this is the eval() BeanShell command. eval() evaluates
a string as if it were typed in the current context. To do this, it sends
the string to an instance of the BeanShell interpreter. But when it does
so it tells the interpreter to evaluate the string in a specific namespace:
the namespace of the caller; using this.caller.
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
eval("a=5");
print( a ); // 5
</pre></td></tr></table></center><p/>
The eval() command is implemented simply as:
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
eval( String text ) {
this.interpreter.eval( text, this.caller.namespace );
}
</pre></td></tr></table></center><p/>
As a novelty, you can follow the call chain further back if you want to
by chaining the '.caller' reference, like so:
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
this.caller.caller...;
</pre></td></tr></table></center><p/>
Or, more generally, another magic reference 'this.callstack' returns an
array of bsh.NameSpace objects representing the full call "stack". This is
an advanced topic for developers that we'll discuss in another location.
<h2><a name="setNameSpace()">setNameSpace()</a></h2>
In the previous discussion we used the this.caller reference to allow us to
write commands that have side effects in the caller's context. This is a
powerful tool. But what happens when one command calls another command that
intends to do this? That would leave the side effects in the first command's
context, not it's original caller. Fortunately this doesn't come up all that
often. But there is a general way to solve this problem.
That is to use the powerful
setNameSpace() method to "step into" the caller's context. After that we may
set variables and call methods exactly as if we were in the caller's context
(because we are). If all commands did this there would be no need to use the
this.caller reference explicitly (indeed, we may make it idiomatic for all
commands to do this in the future).
<p CLEAR="ALL"/>
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
myCommand() {
// "Step into" the caller's namespace.
setNameSpace( this.caller.namespace );
// work as if we were in the caller's namespace.
}
</pre></td></tr></table></center><p/>
You can try out the setNameSpace() command with arbitrary object scope's
as well. For example:
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
object = object();
// save our namespace
savedNameSpace = this.namespace;
// step into object's namespace
setNameSpace( object.namespace );
// Work in the object's scope
a=1;
b=2;
// step back
setNameSpace( savedNameSpace );
print( object.a ); // 1
print( object.b ); // 2
print( a ); // ERROR! undefined
</pre></td></tr></table></center><p/>
<h2><a name="Getting_the_Invocation_Text">Getting the Invocation Text</a></h2>
You can get specific information about the invocation of a method
using namespace.getInvocationLine() and namespace.getInvocationText().
The most important use for this is in support of the ability to write an
assert() method for unit tests that automatically prints the assertion text.
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
assert( boolean condition )
{
if ( condition )
print( "Test Passed..." );
else {
print(
"Test FAILED: "
+"Line: "+ this.namespace.getInvocationLine()
+" : "+this.namespace.getInvocationText()
+" : while evaluating file: "+getSourceFileInfo()
);
super.test_failed = true;
}
}
</pre></td></tr></table></center><p/>
<h2><a name="Working_with_Dirctories_and_Paths">Working with Dirctories and Paths</a></h2>
BeanShell supports the notion of a <em>current working directory</em> for
commands that work with files. The cd() command can be used to change the
working directory and pwd() can be used to display the current value.
The BeanShell current working directory is stored in the variable bsh.cwd.
<p CLEAR="ALL"/>
All commands that work with files respect the working directory, including
the following:
<p CLEAR="ALL"/>
<ul>
<li>dir()</li>
<li>source()</li>
<li>run(),</li>
<li>cat()</li>
<li>load()</li>
<li>save()</li>
<li>mv()</li>
<li>rm()</li>
<li>addClassPath()</li>
</ul>
<h3>pathToFile()</h3>
As a convenience for writing your own scripts and commands you can use
the pathToFile() command to translate a relative file path to an absolute
one relative to the current working directory. Absolute paths are unmodified.
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
absfilename = pathToFile( filename );
</pre></td></tr></table></center><p/>
<h3>Path Names and Slashes</h3>
When working with path names you can generally just use forward slashes
in BeanShell. Java localizes forward slashes to the appropriate value
under Windows environments. If you must use backslashes remember to
escape them by doubling them:
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
dir("c:/Windows"); // ok
dir("c:\\Windows"); // ok
</pre></td></tr></table></center><p/>
<h2><a name="Working_With_Class_Identifiers">Working With Class Identifiers</a></h2>
You may have noticed that certain BeanShell commands such as javap(),
which(), and browseClass() which take a class as an argument can accept any
type of argument, including a plain Java class identifier. For example,
all of the following are legal:
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
javap( Date.class ); // use a class type directly
javap( new Date() ); // uses class of object
javap( "java.util.Date" ); // Uses string name of class
javap( java.util.Date ); // Use plain class identifier
</pre></td></tr></table></center><p/>
In the last case above we used the plain Java class identifier
java.util.Date. In Beanshell this resolves to a bsh.ClassIdentifier
reference. You can get the class represented by a ClassIdentifier using the
Name.identifierToClass() method. Here is an example of how to work
with all of the above, converting the argument to a class type:
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
import bsh.ClassIdentifier;
if ( o instanceof ClassIdentifier )
clas = this.namespace.identifierToClass(o);
if ( o instanceof String)
clas = this.namespace.getClass((String)o);
else if ( o instanceof Class )
clas = o;
else
clas = o.getClass();
</pre></td></tr></table></center><p/>
<h2><a name="Working_with_Iterable_Types">Working with Iterable Types</a></h2>
In conjunction with the enhanced for-loop added in BeanShell version 1.3 a
unified API was added to provide support for iteration over composite types.
The bsh.BshIterator interface provides the standard hasNext() and next()
methods of the java.util.Iterator interface, but is available in all versions
of Java and can be created for all composite types including arrays.
<p CLEAR="ALL"/>
The BeanShell CollectionManager is used to get a BshIterator for an interable
object or array. It is a dynamically loaded extension, so it provides support
for the java.util.Collections API when available, but does not break
compatability for Java 1.1 applications.
You can use this in the implementation of BeanShell commands to iterate
over Enumeration, arrays, Vector, String, StringBuffer and
(when the java.util.collections API is present) Collections and Iterator.
<p CLEAR="ALL"/>
<p/><center><table border="1" cellpadding="5" width="100%"><tr><td bgcolor="#dfdfdc"><pre>
cm = CollectionManager.getCollectionManager();
if ( cm.isBshIterable( myObject ) )
{
BshIterator iterator = cm.getBshIterator( myObject );
while ( iterator.hasNext() )
i = iterator.next();
}
</pre></td></tr></table></center><p/>
<table cellspacing="10"><tr><td align="center"><a href="http://www.beanshell.org/"><img src="../images/homebutton.gif"/><br/>Home</a></td><td><a href="commands.html#BeanShell_Commands"><img src="../images/backbutton.gif"/><br/>Back
</a></td><td align="center"><a href="contents.html"><img src="../images/upbutton.gif"/><br/>Contents</a></td><td align="center"><a href="strictjava.html#Strict_Java_Mode"><img src="../images/forwardbutton.gif"/><br/>Next
</a></td></tr></table></body></html>
|