Scripting

Pivot applications can take advantage of the JVM scripting support provided by the javax.script package included with JDK versions 1.6 and later. While these APIs can be used on their own to script Pivot applications, Pivot includes platform support for easily embedding script code in BXML files.

Script blocks can be defined in BXML files in three different ways:

  • Inline in a <bxml:script> tag
  • As an external file included by a <bxml:script> with a src attribute
  • In an event listener attribute
  • In a <bxml:script> tag defined in an event listener element

Though the first and last methods may seem similar, they are actually handled slightly differently. All four methods are discussed in more detail below.

The following application demonstrate's Pivot's scripting support:

This application is completely contrived - it doesn't present any practical example, but instead serves only to demonstrate the various means of using scripting in a BXML file. The Java source code for the application is shown below:

            
            package org.apache.pivot.tutorials.scripting;

            import org.apache.pivot.beans.BXMLSerializer;
            import org.apache.pivot.collections.List;
            import org.apache.pivot.collections.Map;
            import org.apache.pivot.wtk.Application;
            import org.apache.pivot.wtk.Button;
            import org.apache.pivot.wtk.ButtonPressListener;
            import org.apache.pivot.wtk.DesktopApplicationContext;
            import org.apache.pivot.wtk.Display;
            import org.apache.pivot.wtk.Window;

            public class Scripting implements Application {
                public static class MyButtonPressListener implements ButtonPressListener {
                    @Override
                    public void buttonPressed(Button button) {
                        System.out.println("[Java] A button was clicked.");
                    }
                }

                private Window window = null;

                private String foo;
                private List<?> listData;

                @Override
                public void startup(Display display, Map<String, String> properties)
                    throws Exception {
                    BXMLSerializer bxmlSerializer = new BXMLSerializer();
                    bxmlSerializer.getNamespace().put("bar", "12345");

                    window = (Window)bxmlSerializer.readObject(Scripting.class, "scripting.bxml");
                    foo = (String)bxmlSerializer.getNamespace().get("foo");
                    listData = (List<?>)bxmlSerializer.getNamespace().get("listData");

                    System.out.println("foo = " + foo);
                    System.out.println("listData.getLength() = " + listData.getLength());

                    window.open(display);
                }

                @Override
                public boolean shutdown(boolean optional) {
                    if (window != null) {
                        window.close();
                    }

                    return false;
                }

                @Override
                public void suspend() {
                }

                @Override
                public void resume() {
                }

                public static void main(String[] args) {
                    DesktopApplicationContext.main(Scripting.class, args);
                }
            }
            
        

Notice how the startup() method calls put() on the BXMLSerializer instance. BXMLSerializer's dictionary methods allow a caller to manipulate the script namespace before the BXML is loaded and retrieve values from it afterwards. In this example, the "bar" variable is pre-populated with the value "12345", which is later written to the console by script defined in "scripting.bxml". Similarly, the values of "foo" and "listData" are obtained from the serializer and written to the console after the BXML file has been read.

The BXML source is shown below:

            
            <Window title="Scripting Demo" maximized="true"
                WindowStateListener.windowOpened="java.lang.System.out.println('Window opened: ' + x)"
                WindowStateListener.windowClosed="java.lang.System.out.println('Window closed: ' + y)"
                xmlns:bxml="http://pivot.apache.org/bxml"
                xmlns:scripting="org.apache.pivot.demos.scripting"
                xmlns="org.apache.pivot.wtk">
                <bxml:script>
                importClass(java.lang.System);
                importPackage(org.apache.pivot.wtk);
                System.out.println("bar = " + bar);

                var x = 10;
                var y = 20;

                function buttonClicked(button) {
                    Prompt.prompt("y = " + y, button.window);
                }
                </bxml:script>

                <bxml:script src="example.js"/>

                <Border styles="{padding:2}">
                    <BoxPane orientation="vertical" styles="{padding:6}">
                        <PushButton buttonData="Click Me!">
                            <buttonPressListeners>
                                importPackage(org.apache.pivot.wtk);
                                function buttonPressed(button) {
                                    Prompt.prompt("x = " + x, button.getWindow());
                                }
                            </buttonPressListeners>
                        </PushButton>

                        <PushButton buttonData="No, Click Me!"
                            ButtonPressListener.buttonPressed="buttonClicked(arguments[0])"/>

                        <Border styles="{color:10}">
                            <ListView listData="$listData" selectedIndex="0"/>
                        </Border>
                    </BoxPane>
                </Border>
            </Window>
            
        

This code demonstrates the various means by which script code can be used in BXML:

  • Inline in a <bxml:script> tag. The first <bxml:script> tag defines its content inline. This code is executed as it is processed by the BXML serializer. This example first outputs the "bar" value set by the application, then declares two variables (x and y) and a function (buttonClicked(). These values are visible to all other script code declared within the page.

  • As an external file included by a <bxml:script> with a src attribute. The second <bxml:script> tag includes an externally defined script into the page. The contents of the script are defined in "example.js", which is shown below. Any previously defined values will be visible to the script, and any variables declared by the script will be visible to other script blocks declared by the page.

  • In an event listener attribute. Event listeners can be declared in attributes, using a syntax similar to that used for static property setters. The attribute name for an event listener consists of the name of the interface that defines the event plus the name of the event, separated by a period. The BXML source contains several examples of attribute-based listeners:

    • WindowStateListener.windowOpened
    • WindowStateListener.windowClosed
    • ButtonPressListener.buttonPressed
  • In a <bxml:script> tag defined in an event listener element. Attribute-based event handlers are convenient for simple one-line listeners, but are not particuarly well suited to anything much more complex. Pivot also provides the ability to define event listeners within a listener list element. For example, the buttonPressListeners sub-element of the "Click Me!" push button defines a buttonPressed() function that is invoked when the corresponding button press event is fired by the button. Because the script for element-based listeners doesn't need to fit within a single XML attribute, more sophisticated handler code can be defined, while still maintaining proximity to the element to which the handler applies.

    Though it isn't obvious from this simple example, script-based event handlers are not required to provide implementations for every method defined by the listener interface. Any omitted methods are simply processed by a default no-op handler.

Note that a special scope is created for event listener scripts that is local to the listener; although all page-level variables remain visible to the listener code, any variables defined within the listener (including functions) are only visible within that block. This prevents listener script from "polluting" the global page namespace. While this is generally not an issue for attribute-based listeners (which tend to be focused and short), can easily become an issue for element-based listeners, which may declare multiple functions with the same name (for example, when multiple button press handlers are defined within the same page).

The "example.js" file is defined as follows:

            
            importClass(java.lang.System);
            importPackage(org.apache.pivot.collections);

            System.out.println("Executing external script block; x = " + x);

            var foo = "ABCDE";

            var listData = new ArrayList();
            listData.add("One");
            listData.add("Two");
            listData.add("Three");
            
        

This script declares the "foo" value that is later output by the Java application code, and also defines the list data that is used by the example ListView defined by the BXML file.

Next: Summary