Basic Concepts of text2gui

Introduction

This document covers the basic concepts used in text2gui library. It attempts to be pedantic, solving problems as they arise, so that the package is re-engineered. It is our hope that this presentation method will provide the user insight into the capabilities of text2gui and how to use them properly.

Although resource bundles can be used as presented, the presented Java code that parses the resource bundle strings is only illustrative. Code that actually works with text2gui is not presented until later.

An Introduction to Resource Bundles

The text2gui library makes object creation from strings simple and dynamic. But  where should these strings be stored? Obviously, not in the source file of the program itself, or else recompilation will be required when these strings change. The answer, as programmers writing internationalized applications well know, is in resource bundles. A typical resource bundle is implemented in a properties file (let's say "MyBundle.properties") which looks like this:

okButton.text=OK
okButton.tooltip=Accept the changes.

cancelButton.text=Cancel
cancelButton.tooltip=Forget everything!

On the left of each = is the resource bundle key name, and on the right is the value mapped to the key. So to create a button with the text for the OK button, one would write:

ResourceBundle bundle = ResourceBundle.getBundle("MyBundle");
...
JButton okButton = new JButton();
String text = bundle.getString("okButton.text");
okButton.setText(text);

... // many more operations with the bundle

Since the resource bundle is not read until runtime, changes to the bundle do not require recompilation. But the power of resource bundles for i18n programmers is that values can be overridden in child bundles based on the desired locale. For example, if an i18n programmer wanted to make the program usable to a Spanish user, he would create a file named "MyBundle_es.properties" ("es" is the language code for Spanish), which would look like:

okButton.text=Acepta
okButton.tooltip=Acepta los modificaciones

cancelButton.text=Cancele
cancelButton.tooltip=Vamos!

Assuming the file is installed in the right place, if the program we to run on a computer where the default language is Spanish, the above code would set the text of the OK button to "Acepta", without changes.

We have converted the basics of resource bundles, but actually the text2gui library comes with its own factory to create resource bundles. This factory significantly enhances the capabilities of resource bundles. So code written with the text2gui library would use a resource bundle factory to create a resource bundle, instead of calling ResourceBundle.getBundle(). For more information, see Resource Bundles in Depth.

The Need for String Converters

Resource bundles backed by properties files are great for retrieving locale-dependent strings dynamically, but what about other objects? Suppose we want to set the border of the button above, dynamically. Unfortunately, if we want to retrieve a border directly from a bundle, the bundle has to be implemented as a class, and thus it needs to be pre-compiled.

The beauty of properties files is that no recompilation is necessary. So our solution is to store the string representation of the border in the properties file, then convert the string to a border at runtime. Our new properties file might look like this:

okButton.text=OK
okButton.tooltip=Accept the changes.
okButton.border=empty left=30 right=20 top=10 bottom=10

cancelButton.text=Cancel
cancelButton.tooltip=Forget everything!
cancelButton.border=etched highlight=yellow shadow=gray

and the code to create the OK button would look like this:

ResourceBundle bundle = ResourceBundle.getBundle("MyBundle");
...
JButton okButton = new JButton();
String text = bundle.getString("okButton.text");
okButton.setText(text);
String tooltip = bundle.getString("okButton.tooltip");
okButton.setTooltipText(tooltip);
String borderString =
bundle.getString("okButton.border");
// Not real text2gui code
Border border = BORDER_CONVERTER.toBorder(borderString);
okButton.setBorder(border);

... // many more operations with the bundle

where BORDER_CONVERTER is an object that knows how to convert strings to borders. Assuming that BORDER_CONVERTER is implemented, the configuration of the OK button, including its border, is determined at runtime. That means if you want to change the border, you just need to change the properties file -- no recompilation is necessary.

The text2gui library provides just such a converter from strings to borders. It also provides converters from strings to many other types. But we are only scratching the surface of the power of text2gui. This had better be the case, because the code listing above looks long and painful. To simplify the code above, we need to introduce the concept of resource bundle key converters.

Resource Bundle Key Converters

Notice that property names in the above example had a structure to them: they all began with the name of the component to configure, were followed by a dot, and finally the name of the property to set. For example, in okButton.text, okButton is the name of the component to configure, and text is the property name of JButton to set. Because we were sensible, we named these keys in this way, although we didn't have to.

But what if we assumed that the keys are named this way? Then, given a resource bundle and a name of a component to configure, we could read all of values for the "subkeys" like okButton.text, okButton.border, okButton.icon, etc., convert all the values to objects, then set the appropriate properties of the button, all in one call.
Furthermore, the button itself may be created by the call.

We call an object that performs this task a resource bundle key converter, because it takes as input a resource bundle and the name of a resource bundle key, and converts them into a configured object. Assuming we had such a converter from resource bundle keys to instances of JButton, we would write:

ResourceBundle bundle = ResourceBundle.getBundle("MyBundle");
...
// Not real text2gui code
JButton okButton = JBUTTON_KEY_CONVERTER.toJButton("okButton", bundle);

which would create an OK button with all of its properties set. Much better, isn't it? The implementation of
JBUTTON_KEY_CONVERTER would itself use the string to border converter, as well as many other converters.

We could also have converted the string "jbutton text="OK" tooltip="Accept the changes" border={empty left=30 right=20 top=10 bottom=10}" with a string converter. Besides being very long, this string is not very amenable to internationalization. If the text for the OK button should be overridden to "Dach!" (in Klingonese), the entire string needs to be rewritten to:
jbutton text="Dach!" tooltip="Hurry up you p'tach!" border={empty left=30 right=20 top=10 bottom=10}
Clearly, splitting a long string into a series of properties is more amenable to internationalization because single properties can be overridden while other properties are inherited.

Let's consider for a minute how JBUTTON_KEY_CONVERTER would be implemented. It needs to know the names of the properties of JButton, and for each property, it needs to know what kind of converter to use to convert the property. For example, JBUTTON_KEY_CONVERTER needs to know that the text property should be converted with a String to String converter, while the border property should be converted with a String to Border converter.

The point is that whenever a key is converted, there is a specific kind of converter being used, which depends on the context in which the key is referenced. The value mapped to the key must conform to the syntax expected by the specific kind of converter.

For a variety of reasons, in the text2gui library, resource bundle key converters are implemented as string converters for the same type as well.

Interpolating Converters

Although we have seen how to create objects dynamically from strings and resource bundle keys, these objects were flat, meaning that each property value could be converted from string literals only. For example, the border of the OK button was represented by the string literal "
empty left=30 right=20 top=10 bottom=10". However, we might want to represent the border with another resource bundle key, eborder:

eborder.dispatchType=empty
eborder.left=30
eborder.right=20
eborder.top=10
eborder.bottom=10

The trouble is that the button converter expects a string literal, mapped to okButton.border, to convert to a border. Thus there needs to be a mechanism for a string literal to reference a resource bundle key. We do this by prefixing the name of the bundle key with a "%". Thus to reference the eborder bundle key, we would use the string literal "%eborder". The properties file would then look like this:

eborder.dispatchType=empty
eborder.left=30
eborder.right=20
eborder.top=10
eborder.bottom=10

okButton.text=OK
okButton.tooltip=Accept the changes.
okButton.border=%eborder

The order of the lines above is immaterial. As directed by our Java code above, the resource bundle key converter starts parsing at the entry point "okButton". It later creates the border referenced by %eborder when it sees that the border property of the button is set.

We call the referencing of other keys interpolation. This is a powerful technique which we can now apply to create component hierarchies. Consider the creation of a JPanel containing the OK and cancel buttons, as well as some space between them. We might use following property file:

size.width=50
size.height=20

eborder
.dispatchType=empty
eborder.left=30
eborder.right=20
eborder.top=10
eborder.bottom=10

okButton.dispatchType=jbutton
okButton.text=OK
okButton.tooltip=Accept the changes
okButton.border=%eborder
okButton.size=%size

cancelButton.dispatchType=jbutton
cancelButton.text=Cancel
cancelButton.tooltip=Forget everything\!
cancelButton.border=%eborder
cancelButton.size=%size


panel.dispatchType=jpanel
panel.layout=box axis=x
panel.contents=[%okButton, {strut length=15}, %cancelButton]

Notice that interpolation can occur within strings multiple times, as in the last line.

Now, the entire panel can be constructed with the one-liner:

// Not real text2gui code
JPanel panel = JPANEL_CONVERTER.toJPanel("panel", bundle);

assuming that JPANEL_CONVERTER is implemented somewhere.

Can you imagine how long it would take to create the same panel using only Java code? It would be a mess of code that needs to be recompiled if say, the size of the strut between the buttons should be 20 instead of 15. If the code were to be internationalized, it would be even more complicated.

We have just discussed resource bundle key interpolation. The basic idea is that a string literal representing an object can be replaced with a reference to a resource bundle key, which is used to create the object. Each time a resource bundle key is referenced, the key is converted using the same converter (recall that string conversion and resource bundle key conversion is performed by the same converter object). Thus, normally, each time a resource bundle is key is referenced, a new object is created, which is independent of the object created when the key was referenced previously.

There are two other types of interpolation available. Argument map interpolation references objects stored in a map passed in by the user to the top-level converter. The syntax for such a reference is '$' followed by the name of the key in the argument map, optionally followed by some flags (see Argument Maps in Detail).
Unless a from map value converter is specified in the flags, argument map interpolation simply returns the associated value in the argument map, without creating a new object.

Global variable interpolation references objects stored in the global namespace. The syntax for such a reference is '&' followed by the name of the global variable. Global variable interpolation always returns the value of the global variable, without creating new objects.

To summarize, the following is the syntax for any convertible string:

   %ResourceBundleKeyName
| $ArgumentMapKeyName
| &GlobalVariableName
| LiteralString

where | means "or" and LiteralString is a string that conforms to the converter-specific syntax, for example the Integer converter expects that LiteralString consists of an optional '-', then a string of digits. 

 Actually, the syntax above is not quite correct. See The Actual Syntax for Any Convertible String for details.

Next: The Real Thing

Now that we understand the concepts of resource bundle key conversion and interpolation, we are ready to describe actual converters in the text2gui library in Core text2gui.