Home

Back

Contents

Next

BeanShell Syntax

BeanShell is, foremost, a Java interpreter. So you probably already know most of what you need to start scripting with BeanShell. This section describes specifically what portion of the Java langauge BeanShell interprets and how BeanShell extends it or "loosens it" to be more scripting-language-like.

Standard Java Syntax

In a BeanShell script (and on the command line) you can type normal Java statements and expressions and display the results. Statements and expressions are the kinds of things you normally find inside of a Java method: variable assignments, method calls, math expressions, for-loops, etc.

Here are some examples:

/*
    Standard Java syntax
*/

// Use a hashtable
Hashtable hashtable = new Hashtable();
Date date = new Date();
hashtable.put( "today", date );

// Print the current clock value
print( System.currentTimeMillis() );

// Loop
for (int i=0; i<5; i++)
    print(i);

// Pop up a frame with a button in it
JButton button = new JButton( "My Button" );
JFrame frame = new JFrame( "My Frame" );
frame.getContentPane().add( button, "Center" );
frame.pack();
frame.setVisible(true);

You can also define your own methods and use them just as you would inside a Java class. We'll get to that in a moment.

Loosely Typed Java Syntax

In the examples above, all of our variables have declared types. e.g. "JButton button". Beanshell will enforce these types, as you will see if you later try to assign something other than a JButton to the variable "button" (you will get an error message). However BeanShell also supports "loose" or dynamically typed variables. That is, you can refer to variables without declaring them first and without specifying any type. In this case BeanShell will do type checking where appropriate at runtime. So, for example, we could have left off the types in the above example and written all of the above as:

/*
    Loosely Typed Java syntax
*/

// Use a hashtable
hashtable = new Hashtable();
date = new Date();
hashtable.put( "today", date );

// Print the current clock value
print( System.currentTimeMillis() );

// Loop
for (i=0; i<5; i++)
    print(i);

// Pop up a frame with a button in it
button = new JButton( "My Button" );
frame = new JFrame( "My Frame" );
frame.getContentPane().add( button, "Center" );
frame.pack();
frame.setVisible(true);

This may not seem like it has saved us a great deal of work. But you will feel the difference when you come to rely on scripting as part of your development and testing process; especially for in interactive use.

When a "loose" variable is used you are free to reassign it to another type of Java object later. Untyped BeanShell variables can also freely hold Java primitive values like int and boolean. Don't worry, BeanShell always knows the real types and only lets you use the values where appropriate. For primitive types this includes doing the correct numeric promotion that the real Java language would do when you use them in an expression.

Exception Handling

Exception handling using try/catch blocks works just as it does in Java. For example:

try {
    int i = 1/0;
} catch ( ArithmeticException e ) {
    print( e );
}

But you can loosely type your catch blocks if you wish:

try {
    ...
} catch ( e ) { 
    ...
}

Scoping of Loose Variables

We'll see in the next section that untyped variables in BeanShell methods default to the local scope. This means that, in general, if you assign a value to a variable without first declaring its type you are creating a new local variable.

However to be consistent with Java syntax, untyped variables used in block statements such as if-else, for-loops, while, try/catch, etc. act like they are part of the enclosing block, just as they would when referring to an existing variable in Java. Typed variable declarations remain local to the block, just as they would be in Java. Here are some examples:

// Arbitrary code block
{
    y = 2;      // Untyped variable assigned
    int x = 1;  // Typed variable assigned
}
print( y ); // 2
print( x ); // Error! x is undefined.

// Same with any block statement: if, while, try/catch, etc.
if ( true ) {
    y = 2;      // Untyped variable assigned
    int x = 1;  // Typed variable assigned
}
print( y ); // 2
print( x ); // Error! x is undefined.

Variables declared in the for-init area of a for-loop follow the same rules as part of the block:

for( int i=0; i<10; i++ ) {  // typed for-init variable
    j=42;
}
print( i ); // Error! 'i' is undefined.
print( j ); // 42

for( z=0; z<10; z++ ) { }   // untyped for-init variable
print( z ); // 10

Convenience Syntax

In BeanShell you may access JavaBean properties as if they were fields:

button = new java.awt.Button();
button.label = "my button";  // Equivalent to: b.setLabel("my button");

JavaBean properties are simply pairs of "setter" and "getter" methods that adhere to a naming convention. In the above example BeanShell located a "setter" method with the name "setLabel()" and used it to assign the string value.

If there is any ambiguity with an actual Java field name of the object (e.g. label in the above example) then the actual field name takes precedence. If you wish to avoid ambiguity BeanShell provides an additional, uniform syntax for accessing Java Bean properties and Hashtable entries. You may use the "{}" curly brace construct with a String identifier as a qualifier on any variable of the appropriate type:

b = new java.awt.Button();
b{"label"} = "my button";  // Equivalent to: b.setLabel("my button");

h = new Hashtable();
h{"foo"} = "bar";          // Equivalent to: h.put("foo", "bar");

This syntax will be refined and broadenend to cover general Java collections in an upcoming release.

Boxing and Unboxing

"Boxing" and "Unboxing" are the terms used to describe automatically wrapping a primitive type in a wrapper class and unwrapping it as necessary. Boxing is reportedly an upcoming feature in the next release of Java (SDK1.5).

BeanShell supports boxing and unboxing of primitive types. For example:

i=5;
iw=new Integer(5);
print( i * iw );  // 25

Importing Classes and Packages

In BeanShell as in Java, you can either refer to classes by their fully qualified names, or you can import one or more classes from a Java package.

import javax.xml.parsers.*;
import mypackage.MyClass;

In BeanShell import statements may appear anywhere - not just at the top of a script. In the event of a conflict, later imports take precedence over earlier ones.

A somewhat experimental feature is the "super import". With it you may automatically import the entire classpath, like so:

import *;

The first time you do this BeanShell will map out your entire classpath; so this is primarily intended for interactive use. Note that importing every class in your classpath can be time consuming. It can also result in a lot of ambiguities. Currently BeanShell will report an error when resolving an an ambiguous import from mapping the entire classpath. You may disambiguate it by importing the class you intend.

Tip:
The BeanShell which() command will use the classpath mapping capability to tell you where exactly in your classpath a specified class is located:
bsh % which( java.lang.String );
Jar: file:/usr/java/j2sdk1.4.0/jre/lib/rt.jar

See "Class Path Management" for more details.

Default Imports

By default, common Java core and extension packages are imported for you. They are:

Document Friendly Entities

BeanShell supports special overloaded text forms of all common operators to make it easier to embed BeanShell scripts inside other kinds of documents (e.g XML).

@gt>
@lt<
@lteq<=
@gteq>=
@or||
@and&&
@bitwise_and&
@bitwise_or|
@left_shift<<
@right_shift>>
@right_unsigned_shift>>>
@and_assign&=
@or_assign|=
@left_shift_assign<<=
@right_shift_assign>>=
@right_unsigned_shift_assign>>>=

Home

Back

Contents

Next