Welcome!

Java Authors: Walter H. Pinson, III, Maureen O'Gara, Yakov Werde, Tony Bishop, Kevin Jackson

Related Topics: Java

Java: Article

BPP: The Beanshell Preprocessor

Increase your productivity benefits

The Beanshell preprocessor, or BPP for short, is intended to be a convenient and powerful preprocessing tool for Java developers. It's convenient because the preprocessor is based on Beanshell, which is essentially interpreted Java. This means that Java or Beanshell programmers can quickly use all of BPP's features. It's powerful for the same reasons: all the power of the Java SDK with the convenience of the Beanshell scripting language is available as double payment: once as a development language and once as a preprocessing language.

In the current world of sexy words for new concepts in software development, a new preprocessor is sure to draw a yawn. But for Java developers, this preprocessor can revolutionize core software development. The reason is that it isn't actually just a new processor, but an entirely different kind of preprocessor: a symmetric preprocessor. A language with a symmetric preprocessor gives the full power (and syntax) of the language as a preprocessor, including the provision for a preprocessor, and so on.

Getting BPP and Beanshell
BPP is available as an executable JAR file from http://bpp.sourceforge.net. Beanshell is available from http://beanshell.org. BPP uses Beanshell under the hood and so must find its JAR file in the class path. Putting the bsh-2.0b1.jar in your classpath ensures it will be found. Once this has been set up correctly, use the command:

java bsh.Console

from a command shell to start up the Beanshell desktop. A windowed Beanshell desktop should appear. In the window, type:

you="Jay R. EE";
print("Welcome, " + you);

After seeing the expected message, add a sticky note to your monitor with the words: "Learn Beanshell now; save time later." For now, just close the Beanshell desktop (see Figure 1). BPP does not use the Beanshell desktop; instead it uses the bsh.Interpreter class internally as a lightweight Java interpreter.

Show Me How Useful BPP Is!
BPP allows you to write Java code that writes Java code in a convenient way. The preprocessor lines begin with a # sign and are executed at preprocess time, while normal lines are undecorated. $id and $(expr) on otherwise normal lines are translated in a natural way. For example, the following BPP source file will create the traditional "Hello World" Java program, but with the message appearing in four different languages.

 
#
# greetings=new String[] {
#  "hola mundo",
#  "ciao mondo",
#  "hello world",
#  "\u043F\u0440\u0438\u0432\u0435\u0442 \u043C\u0438\u0440",
# };
#
public class Xample1 {
  public static void main(String[] args) {
#for (i=0;i<greetings.length; ++i) {
    System.out.println("$(greetings[i])");
#}
  }
}

If you save the above code in a file named Xample1.java.bpp, then running BPP with the line

  java -jar bp.jar Xample1.java.bpp

  will produce the following text in Xample1.java:

 

The last line is "hello world" in Russian, and may appear strangely on systems that don't understand UTF-8 encoded unicode. Point is, the #'ed lines are executed at "preprocess time" by BPP. This results in a Java source file with, in compiler optimization parlance, an "unwound loop."

Admittedly, unwinding the above loop at preprocess time will provide no particular advantage over executing the loop at runtime. There are places where such unwinding could make a great deal of difference. Similar preprocessor code could write substantial "boiler plate" code that you might otherwise use a more traditional "copy/paste/edit" approach on, but we will leave that to the reader's imagination (and online tutorials on the BPP Web site). The next example takes on a loftier software engineering goal: compile-time versus runtime safety.

Safe Sets
It's a basic principle of software engineering that the earlier you find an error, the less expensive it is. The collections framework in Java is a good example of something that's runtime safe (because you can't put in an apple and treat it like an orange without a ClassCastException), but it isn't compile-time safe, since you can add any object to any collection, even if you only really wanted oranges in it.

BPP can quickly create compile-time type-safe wrapper classes for collections (or wherever else you need them). Listing 1 provides a snippet of the type-safe template for Collection.

To use the code in Listing 1, save it in a file called "makeTypedCollection.bpp" and generate the equivalent Beanshell script with:

java -jar bpp.jar -b makeTypedCollection.bpp

The -b option tells BPP to create the script, but not execute it. The script is written in this case to makeTypedCollection.bsh. With this handy "template" around, creating a compile-time type-safe collection is a snap. For example, a string collection would be the following smidgen of lines in StringCollection.java.bp:

  #source("makeTypedCollection.bsh");
#makeTypedCollection("StringCollection","java.lang.String");

  Notice that I source the Beanshell script generated by BPP, not the BPP script itself.

There is a philosophical point here: judicious use of BPP can move many checks from runtime to compile time. This can make an order-of-magnitude difference in how long it takes (and expensive it is) to find and correct mistakes. Now let's look at how BPP does its magic.

You is What?

Beanshell is a get-to-the-point Java. Typing:

 
you="Jay R. EE";
print("Welcome, " + you);

  in Beanshell is equivalent to compiling and executing the following Java code:  


public class SomeClass
{
  public static void main(String[] args)
  {
    String you="Jay R. EE";
    System.out.println("Welcome, " + you);
  }
}

Which one would you rather type? See www.beanshell.org for many useful resources on Beanshell. For now, here is what I claim to be the shortest tutorial of a production programming language in history:


Beanshell is an interpreted version of Java with optional types. 

Types are great for safety, but they can turn the quick and dirty into the slow and tedious. Beanshell adheres to types you specify, but also allows unspecified types so you can choose your safety level. As of version 2.0, Beanshell has seamless integration with JDK 1.3 and above. Thus Beanshell is a flexible superset of Java that I encourage any Java developer to get to know.

How Does It Work?
BPP is similar in nature to servlets and php (and a very similar Perl-based preprocessor, perlpp). It works by translating the BPP code into Beanshell code under the following rules:

  • Lines with a # (pound) in column one have the remainder of the line copied exactly to the Beanshell script. For example:

    
        #n=10;
        #for(i=0; i<n; ++i) {
    
         becomes 
    
        n=10;
        for(i=0; i<n; ++i) {
    

    in the Beanshell script.

  • Lines with a " (double quote) in column one have the remainder of the line "quoted magically." This means it becomes a print statement with $IDENTIFIER and $(EXPRESSION) patterns concatenated in. Two compromises were made in the magic translation:
    -$ is a legal start and part of a Java identifier. If you need to have such a value in a BP script use $(my$-strange$id).
    -When $ is not succeeded by an identifier, a left parenthesis, or another $, it simply represents a single $. $$ on a magically quoted line represents a single $. For example:

    
    #static import bpp.Format.*;
    "Dear $title $lastName;
    "You owe $$$(N(blnc,"#,###.00")).
    

    becomes

    
    static import bpp.Format.*;
    print("Dear "+title+" "+lastName+";");
    print("You owe $"+(N(blnc,"#,###.00"))+".");
    

    in the Beanshell script. For those new to Beanshell, print() is equivalent to System.out.println(). The N(Number n,String f) method is a static member of the bpp.Format class as a convenience for formatting numbers.

  • Lines with a ' (single quote) in column one have the remainder of the line 'quoted exactly.' This generates a print statement in the Beanshell script that will faithfully reproduce the line of text. For example:

    
    '#static import bpp.Format.*;
    'Dear $title $lastName;
    'You owe $(N(blnc,"$#,###.00")).
    

    becomes

    	
    print("#static import bpp.Format.*;");
    print("Dear $title $lastName;");
    print("You owe $(N(blnc,\"$#,###.00\")).");
    

    in the Beanshell script.

  • Lines with none of the above "translation codes" have the default translation applied to them. This is "quoted magically" unless the -q ('quote exactly by default') option is passed to BPP.

    Putting these together for the first example, the first BPP source file, Xample1.java.bpp, produces the Beanshell script:

    
     greetings=new String[] {
      "hola mundo",
      "ciao mondo",
      "hello world",
      "\u043F\u0440\u0438\u0432\u0435\u0442 \u043C\u0438\u0440",
     };
     
    print("");
    print("public class Xample1 {");
    print("  public static void main(String[] args) {");
    for (i=0;i<greetings.length; ++i) {
    print("    System.out.println(\""+(greetings[i])+"\");");
    }
    print("  }");
    print("}");
    

      Executing this script with Beanshell generates the promised pure Java source file shown above as Xample1.java.

    Conclusion
    BPP facilitates versioning, templates, macros, optimizations, and compile-time type safety. These things are a normal expectation of preprocessors. However, because the preprocessor is essentially the same full-featured language as the target language, including the fact that it has a preprocessor, these features are much more accessible than, say, C++ templates are to C programmers. This provides big productivity benefits.

    Here are a few other gains.

    Have Fun!
    Writing code over and over that's just a little different from the last time is boring. After the third time, you usually see the part that's staying the same and what is changing. With BPP you can codify that and stick to the fun (new) stuff.

    Big Tools Make Little Ones
    You can use BPP in development and take advantage of the latest and greatest JDKs in your development environment to produce solutions in any target language/ architecture. BPP is used in this way to support a multilanguage environment called FRAMES.

    Dr. Piet Jonas in his "Type Safe Collections" article also addresses type safety, but in another manner.

    Jonas' idea is to verify the types for collections at insertion time using runtime type information. This causes an exception to be thrown early (at insertion time) rather than late (at extraction time). In either case, the exception is thrown at runtime.

    The type-safe wrappers we suggest using here allow for compile-time safety.

    Protecting IP
    The JavaBean model reduces an uber-model to one that solves a particular problem. Giving such a bean to someone else allows them to reverse-engineer your IP. BPP can generate a specific solution to a problem without revealing any general techniques on how that specific solution was constructed. Karl Castleton is writing a BPP-based tool to generate 80% of the boiler plate code for a Java servlet/ MySQL Web site based on an XML description of the database architecture. This includes the SQL initialization, basic form pages, and compile-time-safe SQL access classes.

    Simplification
    In both the above cases, the code that BPP writes is as readable as what a specific programmer would have written to solve the same problem. For example, the above SQL framework generates code that a Java programmer who knows nothing about BP, XML, or even SQL can easily use. In the FRAMES application, BPP writes specific documentation appropriate for each supported target language based on a single document root.

    Enjoy BPP!

    References

  • Beanshell: www.beanshell.org
  • BPP: http://bpp.sourceforge.net
  • FRAMES (click on the "FRAMES" link): http://mepas.pnl.gov/earth
  • BPPSql: http://home.mesastate.edu/˜kcastlet/BPPSql.html
  • perlpp: www.linuxgazette.com/issue44/macevoy/macevoy.html
  • More Stories By Warren MacEvoy

    Warren D. MacEvoy is Asst Professor of Comp Science in the department of Computer Science, Mathematics & Statistics at Mesa State College, Grand Junction, Colorado.

    Comments (1) View Comments

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


    Most Recent Comments
    Karl Castleton 06/16/04 03:09:35 PM EDT

    The URL for BPPSQL is missing the ~ character. The URL should read. http://home.mesastate.edu/~kcastlet/BPPSQL.html

    Karl Castleton