Overview

In this section we provide an overview about this library. We will discuss some of the key concepts and show briefly how a developer can benefit from them.

The Main Application Object

In each JGUIraffe application there is a single object, which represents the application as a whole. This object is also the main entry point in the application. It is an instance of the Application class or a class derived from it. (The base class usually works out of the box. If you have special requirements, e.g. for parameter processing or initialization, you can create a custom sub class of this class.)

When your application starts this Application object performs a couple of initialization tasks. Among other things it

  • Reads in the application's configuration data.
  • Initializes the whole framework based on the data obtained from the application's configuration.
  • Sets up a resource manager for accessing data that depends on the user's locale.
  • Creates the application's GUI. This step is discussed in more detail in the next section.

When the application is up and running all interested components can easily obtain a reference to the central Application instance. This instance provides some important services like

  • Access to configuration data.
  • Access to the application's resources, e.g. internationalized texts.
  • A simple means for creating message boxes to be displayed to the user, e.g. for showing info screens or issuing error messages.
  • The Application object holds a reference to an application context. This is an object, in which central data can be stored that is needed by multiple components.
  • From the Application instance a factory object for creating the GUI builder can be obtained. The GUI builder allows the easy creation of GUI elements like windows and dialogs. More on that below.

The typical way of starting a JGUIraffe application is to execute the main() method of the Application class. This will start the initialization phase, in which the configuration files are searched and processed and all necessary initialization steps are performed. If no problems occur, the application's main window is shown.

Defining the GUI

For the definition of the application's graphical user interface JGUIraffe goes a special way: Windows, dialogs, and their content are defined in XML files. These files are interpreted using Apache Commons's Jelly. They can contain elements that correspond to typical user interface controls like labels, text fields, and radio buttons. In addition all of the standard tags provided by Jelly and its tag libraries can be used. We refer to this approach as the GUI builder.

Defining the user interface in separate configuration files instead of hard coding it in Java code has many advantages. It simplyfies a separation between GUI and business logic. Changing the GUI can be done without having to rebuild the code. With this approach even dynamically generated GUIs are possible.

All tags for constructing GUI elements directly support internationalization. All texts to be displayed to the user can be defined as resource identifiers and are then resolved using the application's resource manager. Of course it is also possible to use text literals directly, but for applications that should present themselves in mutliple languages this is not recommended.

It is important to notice that the XML tags do not directly create the corresponding GUI controls. Instead this task is delegated to a factory. It is thinkable to have different implementations of this factory. At the moment a factory implementation is provided that produces Swing controls. But another implementation that uses SWT/JFace would also be feasible. Because the JGUIraffe API hides the underlying GUI toolkit it would be possible to write programs that are indipendent on a concrete GUI toolkit; just by replacing the factory a different toolkit is used (well, in theory; this feature is a bit experimental).

Scripts interpreted by the GUI builder are not limited to the definition of UI elements. In fact, JGUIraffe implements a lean, but fully functional dependency injection framework. This framework allows the definition of arbitrary Java objects in builder scripts with all their dependencies. This feature is especially useful for combining business logic with the user interface. For instance, controller objects can be defined and associated with UI elements like windows or input fields.

Layouts

Complex layouts of GUI controls are always hard to deal with in Java applications. The fact that different GUI toolkits provide different layout managers does not simplify a developer's task either. For instance the layout managers used in SWT distinguish from the ones supported by Swing.

Because JGUIraffe is intended to support a certain level of independence of a specific GUI toolkit a solution is needed to provide a set of powerful layout managers that can easily be ported to multiple target GUI toolkits. This is achieved through the family of the Percent Layout Managers.

The basic idea behind these layout managers is to implement the layouting algorithm in a way independent on a concrete graphics library. To port such a layout to a specific GUI toolkit an adapter class must be provided, which deals with specifics like obtaining the preferred size of a control, setting its bounds, or implementing the appropriate layout manager interface. Again such an adapter exists for Swing so far, others should not be too complicated to implement.

The fundamental layout class implemented in this library is the PercentLayoutBase class. PercentLayoutBase is especially useful for regular layouts, e.g. when multiple successive lines all have the same structure (for instance two or more columns consisting of a label and an input field). It devides the available space into a grid of rows and columns. For each row and column properties can be defined, which determine its size. So the size can be fixed or determined by the minimum or preferred width/height of the contained controls. In addition a weight factor can be provided that controls how additional space is assigned to the rows and columns. So for instance you can specify that when the user enlarges a dialog the text fields grow, but the labels remain their size.

Based on this fundamental and mighty layout manager class there are some other useful layout manager implementations:

  • BorderLayout is a slightly more powerful implementation of AWT's layout manager with the same name. Its main purpose is to bring this easy yet convenient layouting scheme to other GUI toolkits that do not support it (SWT for instance).
  • ButtonLayout is well suited for creating bars that hold the buttons (e.g. Close, Cancel, Apply) of a dialog. It ensures that all buttons have the same size and the spaces between the buttons are equal. The button bar can be aligned to the left, the right, or centered.

Actions and Command Objects

So by now you have a generic Application object and can create a GUI using the GUI builder. A question remains: what about the application's logic?

JGUIraffe makes heavy use of the Action concept also known from other GUI toolkits like Swing or SWT. An action contains a piece of code that gets executed when a GUI control was triggered by the user. Usually actions are associated with menu items or toolbar buttons, i.e. the typical controls for representing commands to be executed.

The actions supported by the JGUIraffe library can be defined in the XML scripts, from which the GUI is generated. In addition to more graphical properties like a name, a tooltip, or an icon (which are used to define the controls the action is associated with), an action must be assigned a task object. This is a normal Java object that implements the Runnable interface (which should be familar to everybody who has ever started a thread). The task can directly be defined in the XML file using the dependency injection framework mentioned above. So a typical approach would be to define your application's logic in a set of task classes and associate instances of these classes with actions, which are itself associated with GUI controls.

One problem with GUI oriented Java applications are long running tasks. If an action is triggered, its code is typically executed on the same thread that is also responsible for maintaining the GUI. So if the the action's task needs a while to finish, for this time the GUI will be blocked.

As a solution to this problem JGUIraffe introduces so called Command Objects. Command objects are an implementation of the popular GoF Command pattern. They are similar to an action's task in that they define a single method for executing the command. A command object can be passed to the central Application object. This object will take care that the command object is executed in a background thread, so the GUI will stay responsive. If the command object needs to update the GUI after it has run, it can do so by placing the corresponding code in a special method; this method will be executed on the special GUI thread, which will ensure proper synchronization.

To combine the concept of actions with the concept of command objects there is a special action task class called CommandActionTask. Instances of this class can be used as tasks for actions. They hold a reference to a command object. When they are invoked (because the associated action was triggered by the user) they pass their command object to the central Application object so that it is executed in a background thread. CommandActionTask objects can be fully defined in GUI builder XML files.

Working with Forms

GUI centric applications typically use dialogs or forms for gathering input data from their users. Creating and managing such a form is a tedious task in standard Java programming: When the form's GUI has been set up the developer has to ensure that all input fields are correctly populated with data from the application's model. This may involve some transformations of data types, e.g. if a date value is to be entered in a text field, it must be converted into a proper text format.

After the user has completed the input task and pressed the "OK" button the entered values have to be checked if they are valid. For instance the date the user might have entered into a field might not exist or be syntactically wrong. When all fields are valid their values must be written back into the application's model for further processing, e.g. for storing in a database. Here again data transformations may happen (of course the date object should be stored as a java.util.Date rather than plain text).

If all these tasks have to be performed manually, each form requires a fair amount of boiler-plate code. To simplify the management of forms the JGUIraffe library provides the concept of Form objects. A form is represented by an instance of the Form class. Instances of this class store all the fields the form contains and allow easy access to their current values.

Each field of a form can be associated with a couple of helper objects, which are responsible for correctly managing the field's data. These are the following:

  • A write transformer: This object is responsible for performing necessary transformations of the field's native data so that it can be edited using a specific GUI element. In our example with the date field the write transformer would convert the date value to a string that can be assigned to a standard text field.
  • A read transformer: A read transformer does the opposite transformation than a write transformer. It is invoked when the form's data is to be extracted from the GUI input elements. In the case of a date field the user's input must be converted from text to date again.
  • A field level validator: The task of this object is to check whether the data the user has entered in an input element is syntactically correct. So for instance for date fields it must be checked if the string the user has typed into a text field does indeed represent an existing date value.
  • A form level validator: After syntatic checks of all of the form's input elements have been successfull an additional check on the semantic level can be performed. This is an optional second validation phase, in which for instance relations between multiple input elements can be evaluated. So it could be checked whether the value in the field date of delivery lies at least a week in the future if the express delivery checkbox is unchecked.

A form object has methods for reading and writing form data that automatically invoke these helper objects -- if they are defined -- at the correct points in time. As source and destination of form data an arbitrary Java bean can be used. The bean's properties are mapped to form fields based on their name. This makes handling of forms quite convenient. Let's say for example we have a form for editing the data of a customer. Then we can do the following steps:

  1. We obtain the bean that represents the customer to be edited. In a typical application this would be loaded from a database.
  2. On our form object we call the initFields() method and pass in the customer bean. This will cause all input fields that can be matched to properties of the bean to be automatically populated.
  3. When the user hits the "OK" button the validateFields() method is to be called on the form. This method returns information about all fields that contain invalid data. If there are such fields, we can display a message box with appropriate information and hints.
  4. If all fields are valid, we call the readFields() method and again pass in the customer bean. This will cause the current values of the input fields to be written back to the bean. We could then update the bean in the database.

And now comes the best part: such a form object can be automatically created for you! Every time you use the GUI builder (see above) for generating a GUI a Form object is automatically constructed and for each processed input element a field object is added. In the XML document that controls the GUI building process special tags can be used for defining transformers and validators for the input elements. So you get a fully initialized form object for free.