WTKX is an XML-based markup language used for building Apache Pivot applications. Though it is most commonly used for defining the structure of an application's user interface, it can be used to declaratively construct any type of Java object hierarchy.
This section introduces WTKX and explains how it can be used to create and configure a collection of Java objects. It assumes some familiarity with Pivot and Java programming in general. For an introduction to Pivot, please see the Pivot Platform Overview.
In WTKX, an XML element may represent one of the following:
A class instance
A property of a class instance
A processing directive to the WTKX serializer
If an element's tag name begins with an uppercase letter, it is considered a class instance. Otherwise, it is treated as an instance property, unless the tag name begins with the reserved "wtkx" namespace prefix, in which case it is considered a serialization directive. These are discussed in more detail in later sections.
When the WTKX serializer (an instance of org.apache.pivot.wtkx.WTKXSerializer, discussed below) encounters an element whose tag name begins with an uppercase letter, it considers the tag to be the name of a Java class and creates an instance of that class. The element's namespace is assumed to contain the name of the package that the class belongs to.
For example, the following WTKX would produce an instance of the org.apache.pivot.wtk.Label class populated with the text "Hello, World!" (like property elements, attributes can also be used to set property values, and are discussed in the next section):
<Label text="Hello, World!" xmlns="org.apache.pivot.wtk" />Note that the default namespace is defined as org.apache.pivot.wtk. This is fairly common practice in Pivot development, since WTKX is often used to construct user interfaces using components defined in this package.
More complex examples may also use classes defined in other packages and will require multiple namespaces. Namespace prefixes can be used for this purpose. For example, the following WTKX assigns the org.apache.pivot.wtk.charts package to the charts namespace prefix, and sets an instance of org.apache.pivot.wtk.charts.BarChartView as the content of a Window:
<Window xmlns="org.apache.pivot.wtk" xmlns:charts="org.apache.pivot.wtk.charts"> <content> <charts:BarChartView/> </content> </Window>In general, uppercase elements in a WTKX file will represent instances of JavaBean classes. Internally, the WTKX serializer uses an instance of org.apache.pivot.beans.BeanDictionary to wrap the instantiated class and invoke its setter methods. However, if the class name represents an object that already implements the org.apache.pivot.collections.Dictionary interface (such as org.apache.pivot.collections.HashMap), it is not wrapped, and its dictionary methods are accessed directly. For example, the following WTKX creates an instance of org.apache.pivot.collections.HashMap and sets its "foo" and "bar" values to "123" and "456", respectively:
<HashMap foo="123" bar="456" xmlns="org.apache.pivot.collections"/>Elements whose tag names begin with a lowercase letter represent instance properties. A property element may represent one of the following:
A standard JavaBean property setter
A read-only sequence
A read-only dictionary
An event listener list
The WTKX serializer uses a bean dictionary to obtain information about the type of the property so that it can process the element's contents correctly.
If the element represents a JavaBean property setter, the contents of the element (which must be either a text node or a class instance element) are passed as the value to the setter for the property. For example, the following WTKX creates an instance of the Label class and sets the value of the label's "text" property to "Hello, World!":
<Label xmlns="org.apache.pivot.wtk"> <text>Hello, World!</text> </Label>
It produces the same result as the earlier example which used an attribute to set the "text" property:
<Label text="Hello, World!" xmlns="org.apache.pivot.wtk"/>
This example creates an instance of ListView and sets the value of its "listData" property to an instance of org.apache.pivot.collections.ArrayList that has been populated with several instances of org.apache.pivot.wtk.content.ListItem (a data class recognized by the default list item renderer):
<ListView xmlns="org.apache.pivot.wtk" xmlns:collections="org.apache.pivot.collections" xmlns:content="org.apache.pivot.wtk.content"> <listData> <collections:ArrayList> <content:ListItem text="A"/> <content:ListItem text="B"/> <content:ListItem text="C"/> </collections:ArrayList> </listData> </ListView>
If the property represents a read-only sequence (a bean property whose getter returns an instance of org.apache.pivot.collections.Sequence and has no corresponding setter method), the contents of the element are added to the sequence. For example, the "tabs" property of the org.apache.pivot.wtk.TabPane class returns an instance of TabSequence, which implements Sequence<Component>; tabs are added to a TabPane in WTKX as follows:
<TabPane xmlns="org.apache.pivot.wtk"> <tabs> <Label text="Foo"/> <Label text="Bar"/> </tabs> </TabPane>
A property element may also represent a read-only dictionary (a bean property whose getter returns an instance of org.apache.pivot.collections.Dictionary but has no corresponding setter method). For example, the "userData" property of org.apache.pivot.wtk.Component (which the Label class in this example extends) represents a read-only dictionary:
<Label text="Hello, World!" xmlns="org.apache.pivot.wtk"> <userData foo="123" bar="456"/> </Label>
The attribute values are put into the dictionary using the attribute names as keys.
Finally, the property may represent an event listener list (an instance of org.apache.pivot.util.ListenerList). If so, the sub-elements represent listeners of the appropriate type and are added to the listener list. This is discussed in more detail in the Scripting section.
An attribute in WTKX may represent one of the following:
A standard JavaBean property setter
A "static" property setter (explained below)
If an attribute represents a bean property setter, the attribute value is passed as the argument to the setter method. If the type of the property is a string, the value is passed as-is; however, if it is one of the simple types (boolean, char, byte, short, int, long, float, or double) or one of their wrapper equivalents, it is converted the appropriate type before invoking the setter. Whenever possible, WTKXSerializer will use BeanDictionary to determine what, if any, type conversion needs to take place. For example, given the following simple bean class:
package com.foo; public class MyBean { public String getFoo() { ... } public void setFoo(String foo) { ... } public int getBar() { ... } public void setBar(int bar) { ... } }
the following WTKX would instantiate the bean and invoke the "foo" and "bar" setters, passing a string to setFoo() and an int to setBar():
<MyBean foo="hello" bar="123" xmlns="com.foo"/>
However, if the element represents a class that already implements the Dictionary interface (such as HashMap), the type of the attribute cannot be determined, no conversion takes place - the values are simply passed as strings.
Attributes may also represent "static setters" (sometimes called "attached properties"). Attached properties are properties that only make sense in a particular context. They are not intrinsic to the class on which they are invoked, but are defined by another class (generally the parent container of a component).
The following WTKX invokes the static setter for the TabPane class's "label" property:
<TabPane xmlns="org.apache.pivot.wtk"> <tabs> <Label TabPane.label="First Tab" text="Tab 1"/> <tabs> </TabPane>
This translates roughly to the following in Java:
TabPane tabPane = new TabPane(); Label label = new Label(); label.setText("Tab 1"); tabPane.getTabs().add(label); TabPane.setLabel(label, "First Tab");
The call to TabPane.setLabel() attaches the "name" property to the Label instance. The tab pane then uses the value of this property as the button data for the label's tab in the tab panes button bar. TabPane also defines a static setter for a tab's icon. Other containers, including Accordion and TablePane, define similar setters.
Note that, although the static setter attribute is declared first in the WTKX, it is actually invoked after the setter for the "text" property (as well as after the Label instance has been added to the tab pane). This is because static setters often cannot be called until after the object on which they are invoked has been added to an instance of the parent class that defines the setter. The act of adding the object to the parent effectively makes the attached properties available. Conversely, removing the object from the parent also removes any previously attached properties.
Setter attributes (either bean or static) in WTKX support several resolution operators that extend their type handling capabilities:
Object dereference
Resource resolution
URL resolution
The object deference operator allows a caller to replace an attribute value with an instance of a named object before the corresponding setter method is invoked. Any attribute whose value begins with the "$" is considered an object reference.
For example, a table view header must be associated with an instance of TableView; in Java, this is done via the setTableView() method. In WTKX, the object dereference operator is used. The following WTKX defines an instance of ScrollPane, setting a TableView as its view component and a TableViewHeader as the column header. The table view is associated with the header via the "tableView" attribute:
<ScrollPane xmlns="org.apache.pivot.wtk" xmlns:wtkx="http://pivot.apache.org/wtkx"> <view> <TableView wtkx:id="tableView"> ... </TableView> </view> <columnHeader> <TableViewHeader tableView="$tableView"/> </columnHeader> </ScrollPane>
Note the use of the "wtkx" namespace prefix in the preceding example. This is a special namespace reserved by the WTKX serializer. As shown above, it defines an "id" element that is used to assign a name to a class instance. In addition to the object dereference operator, this ID can also be used to obtain a reference to the instantiated element in the Java code that processes the WTKX or by script code defined or included by the WTKX file.
Other special elements are also defined by the wtkx namespace. Each are discussed in more detail in later sections.
In WTKX, resource substitution can be performed at load time for localization purposes. When given an instance of org.apache.pivot.util.Resources, WTKXSerializer will replace instances of resource names with their locale-specific values. Resource names are identified by a "%" prefix, as shown below:
<Label text="%myText"/ xmlns="org.apache.pivot.wtk">
The associated localized resource file might contain something like the following:
{ myText:"This is my text!" }
producing a label displaying the text "This is my text!".
WTKXSerializer is discussed in more detail below.
Attributes can also be used to specify URLs. An attribute that begins with the "@" character is converted to a URL whose path is interpreted as relative to the location of the WTKX source file. For example, the following WTKX would load an image from the same directory as the WTKX file into an ImageView component. This WTKX translates to a call to the ImageView#setImage(java.net.URL) method:
<ImageView image="@foo.png" xmlns="org.apache.pivot.wtk"/>
Without the "@" operator, bean properties would have no context by which to determine the path to such a resource.
The <wtkx:include> tag allows a WTKX file to embed content defined in an external WTKX file as if it was defined in the source file itself. This is useful for partitioning content into manageable pieces (for example, when working on large applications or with multiple developers, or when defining reusable content templates).
The following WTKX defines a Window whose content is defined in an external file named "content.wtkx":
<Window xmlns="org.apache.pivot.wtk" xmlns:wtkx="http://pivot.apache.org/wtkx"> <content> <wtkx:include src="content.wtkx"/> </content> </Window>
The contents of the included file are loaded using a nested instance of WTKXSerializer. If the include tag is given an ID, the objects defined in the included file will be accessible by name using WTKXSerializer#get(), discussed in more detail below. For example, given the following WTKX, a caller can later retrieve the Label instance from the serializer by the name "content.label":
<-- window.wtkx --> <Window xmlns="org.apache.pivot.wtk" xmlns:wtkx="http://pivot.apache.org/wtkx"> <content> <wtkx:include wtkx:id="content" src="content.wtkx"/> </content> </Window>
<-- content.wtkx --> <Label xmlns="org.apache.pivot.wtk" xmlns:wtkx="http://pivot.apache.org/wtkx" wtkx:id="label" text="Hello, World!"/>
Java code:
Label label = (Label)wtkxSerializer.get("content.label");
In general, class instance elements declared in WTKX are expected to have a parent tag that represents a sequence of some sort (generally, either as a parent container or a property of a parent container). However, it is sometimes desirable to declare objects for use in a WTKX file that do not have or need a direct parent. The <wtkx:define> tag can be used for this purpose.
For example, the following WTKX instantiates a login dialog by including a file named "login_dialog.wtkx" within a define block. The dialog instance is assigned an ID, presumably so it can be used by an event handler defined in script or Java code later on:
<Window xmlns:wtkx="http://pivot.apache.org/wtkx" xmlns="org.apache.pivot.wtk"> <wtkx:define> <wtkx:include wtkx:id="loginDialog" src="login_dialog.wtkx"/> </wtkx:define> <content> ... </content> </Window>
The <wtkx:script> tag allows a caller to import scripting code into or embed script within a WTKX file. Any JVM scripting language can be used.
For example, the following WTKX defines a JavaScript block that defines a variable named "foo". The value of this variable is used to populate the Label instance that is declared as the window's content:
<Window xmlns:wtkx="http://pivot.apache.org/wtkx" xmlns="org.apache.pivot.wtk"> <wtkx:script language="javascript"> var foo = "Hello, World!"; </wtkx:script> <content> <Label text="$foo"/> </content> </Window>
The script could also have been defined in an external file:
<Window xmlns:wtkx="http://pivot.apache.org/wtkx" xmlns="org.apache.pivot.wtk"> <wtkx:script src="foo.js"/> <content> <Label text="$foo"/> </content> </Window>
In either case, any global variables declared in a script are added to the WTKX file's namespace, and become available for use by the object dereference operator (as shown) as well as to callers via the WTKXSerializer#get() method discussed below.
Script code can also be used to define event handlers in WTKX. Event handlers can often be defined more succinctly in script than in Java. For example, given the following WTKX:
<PushButton xmlns="org.apache.pivot.wtk" xmlns:wtkx="http://pivot.apache.org/wtkx" wtkx:id="pushButton" buttonData="Click Me!"/>
the Java code to obtain a reference to a PushButton and attach a button press listener to it might look like this:
PushButton pushButton = (PushButton)wtkxSerializer.get("pushButton"); pushButton.getButtonPressListeners().add(new ButtonPressListener() { public void buttonPressed(Button button) { // Handle event } });
While this is simple enough, it can become cumbersome in any non-trivial application where many such event handlers are defined. It also dissociates the event handler from the element to which it applies, making it difficult to track down event handling logic.
A similar event handler might be defined in JavaScript as follows:
<PushButton xmlns="org.apache.pivot.wtk" xmlns:wtkx="http://pivot.apache.org/wtkx" buttonData="Click Me!"> <buttonPressListeners> <wtkx:script> function buttonPressed(button) { // Handle event } </wtkx:script> </buttonPressListeners> </PushButton>
This version is quite a bit easier to read, and creates a much stronger association between the button and the handler. It doesn't even require the button to be given an ID.
When a <wtkx:script> block is declared within a listener list element, WTKXSerializer creates a special scope that is local to the handler. As a result, any variables or functions defined within the script block do not pollute the page's global namespace and are only visible within the block.
Also, 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.
The org.apache.pivot.wtkx.WTKXSerializer class, which has been mentioned in previous sections, is what drives the actual loading and processing of a WTKX file and its associated script and includes. It implements the org.apache.pivot.serialization.Serializer interface, and returns the object hierarchy corresponding to the structure declared within the WTKX file. It defines the following overloads for the readObject() method:
public Object readObject(String resourceName) { ... } public Object readObject(URL location) { ... } public Object readObject(InputStream inputStream) { ... }
The first version loads a WTKX file from a resource specified on the application's classpath. This method delegates to the second version, which loads a resource from an arbitrary URL. Callers must use one of these versions in order to use the URL resolution operator described in the previous section, since the location URL is used as the base URL for any relative URLs specified in the file.
The second method delegates in turn to the third version, which performs the actual processing and returns the deserialized object graph. For example, given the following WTKX, which defines a root Window object and sets its contents to a Label displaying the text "Hello, World!":
<Window xmlns="org.apache.pivot.wtk" xmlns:wtkx="http://pivot.apache.org/wtkx"> <content> <Label wtkx:id="label" text="Hello, World!"/> </content> <Window>
this Java code, taken from an implementation of org.apache.pivot.wtk.Application, would load the WTKX into a local variable and open the window on the application's display:
public void startup(Display display, Map<String, String> properties) throws Exception { WTKXSerializer wtkxSerializer = new WTKXSerializer(); Window window = (Window)wtkxSerializer.readObject(getClass().getResource("window.wtkx")); window.open(display); }
As previously discussed, the WTKXSerializer#get() method allows a caller to retrieve a named object instance from a WTKX file once the root object has been loaded. The readObject() method populates a map of named object IDs to object instances that can later be used to obtain a reference to those objects.
Continuing the previous example, the following code would obtain a reference to the Label instance and change its text to "Welcome to Pivot"!:
public void startup(Display display, Map<String, String> properties) throws Exception { WTKXSerializer wtkxSerializer = new WTKXSerializer(); Window window = (Window)wtkxSerializer.readObject(getClass().getResource("window.wtkx")); Label label = wtkxSerializer.getObjectByID("label"); label.setText("Welcome to Pivot!"); window.open(display); }
WTKXSerializer implements the Dictionary interface, so callers can also use the put() or remove() methods to modify the serializer's namespace before the WTKX file is loaded (effectively "parameterizing" the WTKX).
As mentioned earlier, objects defined in WTKX includes can also be retrieved via get(). The ID of the included file defines the namespace for the include; callers can use a dot-separated namespace path to a nested object to access it:
Label label = (Label)wtkxSerializer.get("content.label")
where "content" is the ID of a
The org.apache.pivot.wtkx package includes an annotation that can be used to simplify the process of mapping named objects into a Java application. The @WTKX annotation can be used to tag a member variable such that it will be automatically mapped to a named object in a WTKX file. The bind() method of WTKXSerializer is used to perform the actual mapping.
For example, given the following WTKX:
<Window xmlns="org.apache.pivot.wtk" xmlns:wtkx="http://pivot.apache.org/wtkx"> <content> <Label wtkx:id="label" text="Hello, World!"/> </content> <Window>
a Java member variable declared as follows will be automatically populated with the declared Label instance when the WTKXSerializer#bind() method is called on an instance of the declaring class:
@WTKX private Label label;
As a result, the @WTKX annotation can significanly simplify the process of working with loaded WTKX data in Java code. However, because it relies on reflection to set the member variables, it can only be used with trusted code, or to set public fields.
WTKX provides a number of features that help simplify the process of building a user interface. It can be used to instantiate objects and set member variables as well as define script logic for working with those objects. It is a powerful and efficient way to construct a Pivot application.