Home

Back

Contents

Next

Basic 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 language 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 see 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 ) { 
    print( "caught exception: "+e );
}

Basic Scoping of Variables

Note:
As of BeanShell version 1.3 the default scoping of loosely typed variables was changed to be more consistent with Java. BeanShell still supports an alternate scoping used in earlier versions. This mode can be enabled for legacy code by setting the system property "localscoping" to true. See appendix "Local Scoping".

Variable scoping in BeanShell behaves, wherever possible, just like that in Java. Ordinary Java, however, does not offer "loose" variables (variables that can be used without being declared first). So we must define their behavior within BeanShell. We'll see in the next section that untyped variables - variables that are not declared and not assigned a value elsewhere - default to the local scope. This means that, in general, if you assign a value to a variable without first declaring it, you are creating a new local variable in the current scope.

Blocks

Blocks are statements between curly braces {}. In BeanShell, as in Java, blocks define a level of scope for typed variables: typed variables declared within a block are local to the block. Other assignments within the block occur, as always, wherever the variable was defined.

Untyped variables in BeanShell, however, are not constrained by blocks. Instead they act as if they were declared at the outer (enclosing) scope's level. With this in mind, BeanShell code looks just like Java code. In BeanShell if you declare a typed variable within a block it is local to the block. But if you use an untyped variable (which looks just like an ordinary assignment in Java) it behaves as an assignment to the enclosing scope.

This will make sense with a few 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

Variable Modifiers

The standard Java variable modifiers may be used on typed variables: private / protected / public, final, transient, volatile, static. Only 'final' is currently implemented. The others are currently ignored.

Modifiers may not be applied to untyped variables.

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");
print( button.label );       // Equivalent to print( b.getLabel() );

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. It then found the method named getLabel() to retrieve the value.

Boolean properties may optionally use the syntax "is" for their "getter". e.g.

Float f = new Float(42f);
print( f.infinite );  // Equivalent to print( f.isInfinite() ); // false

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 any ambiguity BeanShell provides an additional, uniform syntax for accessing both Java Bean properties and Hashtable or Map 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");

Where the java.util.Collections API is available, Maps are also supported.

Enhanced 'for' Loop

BeanShell supports the Java 1.5 style enhanced for-loop for iterating over collections and array types. (Note that you do not have to be running Java 1.5 to use this feature).

List foo = getSomeList();

for ( untypedElement : foo ) 
	print( untypedElement ); 

for ( Object typedElement: foo ) 
	print( typedElement );

int [] array = new int [] { 1, 2, 3 };

for( i : array ) 
	print(i);

for( char c : "a string" )
	print( c ); 

Supported iterable types include all the obvious things.

See also the BshIterator API which supports the ehanced for-loop and allows iteration over these types using the dynamically loaded BeanShell Collection manager.

Switch Statements

In BeanShell, the switch statement may be used not only with numeric types but with objects. For example, you may switch on Dates and Strings which are compared for equality with their equals() methods:

dateobj = new Date();

switch( dateobj ) 
{
	case newYears:
		break;
	case christmas:
		break;
	default:
}

Auto 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 a feature of Java (SDK1.5) and has been supported in BeanShell for many years.

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

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

Vector v = new Vector();
v.put(1); 
int x = v.getFirstElement();

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.

// Standard Java
import javax.xml.parsers.*;
import mypackage.MyClass;

In BeanShell import statements may appear anywhere, even inside a method, not just at the top of a file. 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 information about modifying the BeanShell classpath at run-time with the addClassPath() or setClassPath() commands.

Also see "BeanShell Commands" for information about importing new BeanShell commands from the classpath.

Default Imports

By default, common Java core and extension packages are imported for you. They are, in the order in which they are imported:

Two BeanShell package classes are also imported by default:

Finally, we should mention that BeanShell commands may be imported from the classpath. The default commands are imported in the following way:

importCommands("/bsh/commands");

We will discuss how to import your own commands in a later section.

Tip:
The classes java.awt.List and java.util.List are both imported by default. Because java.util.List is imported later, as part of the java.util package, it takes precedence. To access java.awt.List simply import it in, or the java.awt package again your script. Later imports take precedence.

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