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.
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
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
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.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.
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.
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.
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.
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 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:
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.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.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.