RoKlib Library Documentation

Roland Krüger

1.0

This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.


1. Conditional Engine
2. Component Group Visibility and Enablement
2.1. Managing Visibility Conditions in a Single Place
2.2. RoKlib's VisibilityGroupManager
2.2.1. Target Components: IVisibilityEnablingConfigurable
2.2.2. Boolean Expressions
2.2.3. VisibilityGroupManager
3. Data Structures
3.1. Introduction
3.2. The Ternary Search Tree Map
3.2.1. Prefix Matching
3.2.2. Spell Checking
3.2.3. Case Insensitivity
3.3. Ternary Search Tree Set
4. URL Action Handling Framework
4.1. Overview
4.2. Introduction

Chapter 1. Conditional Engine

Chapter 2. Component Group Visibility and Enablement

When writing user interface driven applications, in all but the most simple scenarios you will have parts of the application or of the user interface which will need to be deactivated or made invisible when some predefined condition holds true. There are a lot of instances you can think of when this will be necessary. For example, a web application may have some GUI elements which are only visible to administrators. A logout button will only be visible to logged-in users. Another application may have buttons which are only enabled under certain conditions, such as a Print Document menu item which is disabled if there is no document available to be printed.

Handling this type of conditions is quite common for most applications. They affect whether a component is visible or invisible, or likewise if it gets enabled or disabled. Visibility and enablement of components are both very similar concepts. For the sake of simplicity, the following text will therefore only refer to the visibility of components which does not exclude the applicability to the concept of enablement.

While mostly relatively simple, conditions that affect the visibility of components can become arbitrarily complex. In the simplest case, there is one variable of a system that decides whether a component is visible or not. A typical use case is the role system for an application where the role of a user directly affects an element's visibility in the user interface. Has the user an administrative role, for instance, she will be provided with corresponding components for administration purposes in her user interface.

Such conditions, however, can be a lot more involved. It is not uncommon that a condition controlling a component's visibility is formed of a number of sub-conditions which are interconnected by Boolean operators. Those sub-conditions themselves can be recursively assembled by further sub-conditions. Thus, as a generalization visibility conditions are composed of a tree of sub-conditions.

Let's clarify that with an example. Say you have a desktop application using user authorization for controlling the access to different functionalities. One such functionality may be the option to print documents on a network printer. The menu item for this function is only enabled when different preconditions hold. Firstly, a document to be printed must be opened in the application. Secondly, the document to be printed must not be locked since locked documents are not allowed to be printed. Thirdly, the current user must be eligible for printing. She is authorized to print documents if she is member of either the administrator or editor group. The decision whether or not the printing option is enabled can be expressed with a Boolean function . Here, the Boolean variables x1, x2, x3, x4 represent the individual conditions which are relevant for the print option's enablement. These are as follows: x1 defines whether a printable document is loaded. x2 is true if the document is locked. x3 is true if the current user has the administrator role and x4 is true if she has the editor role, respectively. The Boolean function above now becomes true if x1 is true and x2 is false and one of x3 and x4 or both are true. In that case the print option can be enabled.

In a typical application, conditions of this type are controlled by different parts of the application. User roles and authorization are usually handled in a user administration module, whereas the state of a loaded document is monitored by a document manager. A simplistic implementation of the application could now allow each of these modules to directly access the print option's user interface component so that they can enable or disable this component a required. Additionally, each module would need to have a reference to the other modules so that they can query the status of all other involved conditions. Only then an individual part of the application could decide whether it can safely enable or disable the print option's user interface component without violating the rule given by the Boolean function f.

It is easy to see that such a design would lead to a very tight coupling between components yielding a system which will be very hard to maintain. An architecture where different unrelated modules have to know of each other only to be able to manage the visibility of components originating from yet another module should decidedly be avoided.

2.1. Managing Visibility Conditions in a Single Place

Managing the set of conditions responsible for the visibility of components is a cross-cutting concern which should be organized in one central place. The conditions involved usually refer to a lot of different parts of an application. So, instead of spreading visibility management over the whole application, it should be concentrated in a single manager component. Each part of the application which influences one of the Boolean variables xi should exclusively be responsible for setting the value of this variable and nothing else. In the example above, for instance, the part of the application which loads printable documents should only need to set the values of the two Boolean variables refering to whether a document is loaded and whether the current document is locked. After a document has been loaded by the user, variable x1 can be set to true indicating that a document is available for printing. If that document is locked, variable x2 is set to true as well, otherwise it is set to false. However, the decision whether the print option now must be enabled or disabled has to be made elsewhere.

2.2. RoKlib's VisibilityGroupManager

RoKlib supports you with the kind of problem described above with its package info.rolandkrueger.roklib.util.groupvisibility. This package offers you an API which directly aims at solving the problem of managing the visibility of an application's components centrally. It provides methods for organizing a set of Boolean conditions which refer to different parts of an application. You can assemble these conditions into composites of conditions and manage the visibility of component groups with a central manager class using these composite conditions as a basis.

This manager class is implemented by class VisibilityGroupManager in package info.rolandkrueger.roklib.util.groupvisibility. This is the central class for organizing visibility conditions for an application. As its name indicates it manages groups of related components which can be set visible or invisible - and likewise enabled or disabled - based on a particular predefined condition. Switching the state from visible to invisible or from enabled to disabled and vice versa is done automatically by the manager. This does not need to be done manually. You as an application designer only need to declaratively define the conditions for such state transitions and to set the corresponding Boolean variables accordingly when the application state changes.

There are three constituents which take part in centralizing component visibility management. These are in particular the components that can be made visible/invisible or enabled/disabled, a Boolean expression which defines when a component's state has to be switched, and the manager component itself. We're going to look at each of these parts in the following subsections and will discuss how they will be applied, respectively.

2.2.1. Target Components: IVisibilityEnablingConfigurable

VisibilityGroupManager can manage arbitrary objects which can be set visible or invisible or enabled and disabled. These may be graphical user interface components, such as menu items or buttons, or anything else. The only requirement for these components is that they offer methods for making them visible and invisible and for enabling and disabling them. VisibilityGroupManager therefore accepts as components all classes which implement the interface info.rolandkrueger.roklib.util.groupvisibility.IVisibilityEnablingConfigurable. This interface is defined as follows.

public interface IVisibilityEnablingConfigurable extends Serializable {
  void setVisible (boolean visible);
  void setEnabled (boolean enabled);
  boolean isVisible ();
  boolean isEnabled ();
}

Classes which implement the methods of IVisibilityEnablingConfigurable can be managed by the VisibilityGroupManager. For instance, if you want to manage Java Swing user interface components with this manager, you can write corresponding proxy classes for the respective GUI elements. These proxies then delegate calls to IVisibilityEnablingConfigurable's methods to the corresponding methods of the GUI classes. We'll make that clear with a concrete example shortly.

If you have more than one component whose states shall be switched under the same conditions, you can combine them as a group of components. This is what gave the manager class for RoKlib's enablement feature its name.

2.2.2. Boolean Expressions

The next important element for managing visibility and enablement of components is the Boolean expression which defines when the state of a IVisibilityEnablingConfigurable component can be toggled. Such an expression may be very simple, but it can become arbitrarily complex. Above we have seen an example of such an expression given as the Boolean function f.

In order to define a Boolean expression for the VisibilityGroupManager, the classes of RoKlib's conditional engine (see Chapter 1, Conditional Engine) are used. You create these expressions as objects of class info.rolandkrueger.roklib.util.conditionalengine.BooleanExpression. There are two Boolean expressions you can define for each group of components. One expression defines the condition under which all components of a particular group shall be made visible. As soon as the expression resolves to true, all components of the corresponding group are made visible. The same applies to the second expression defined for enablement. If this condition becomes true, all components of the group are enabled. Likewise, the reverse applies as well. If the condition becomes false, all components are disabled or are made invisible, respectively.

2.2.3. VisibilityGroupManager

The VisibilityGroupManager is the central component for enablement and visibility management. It accepts IVisibilityEnablingConfigurable components and their corresponding BooleanExpression objects and takes care of correctly switching the components' state according to the conditions given by these expressions.

Components implementing the IVisibilityEnablingConfigurable interface can be arranged in visibility groups. Each group is uniquely identified by a String ID which can be freely chosen by implementers. These IDs only serve the purpose of adding individual components into the same group. Additionally, these IDs are used to refer to a particular visibility group.

Let us look at a simple example to make clear the usage of a VisibilityGroupManager. We will first define a class JButtonEnablementProxy which acts as a delegate between a javax.swing.JButton object and the IVisibilityEnablingConfigurable interface. This class represents our component to be managed by a VisibilityGroupManager object.

public class JButtonEnablementProxy implements IVisibilityEnablingConfigurable {
  private JButton button;

  public JButtonEnablementProxy (JButton pButton) {
    button = pButton;
  }

  public void setVisible (boolean visible) {                                  (1)
    button.setVisible (visible);
  }

  // implement all other methods of IVisibilityEnablingConfigurable accordingly
}

(1)

Method of IVisibilityEnablingConfigurable implemented as a delegate that simply passes each call to it on to a corresponding method of JButton.

Objects of this class can be added to a VisibilityGroupManager as the target objects for visibility management and enablement. We can now define the concrete instances which shall be managed by the VisibilityGroupManager. Let's define two buttons for printing documents in an application. One button can be used for immediately printing out a document, the second print button will first show a printing dialog. By this we get two components which should be enabled and disabled under the same condition. Therefore, they can be put into the same group.

// name idendifier for the component group
private final static String PRINT_GROUP = "PRINT_GROUP";                                      (1)

// create the button objects
private JButton printNow    =  new JButton ("Print");                                         (2)
private JButton printDialog =  new JButton ("Print...");

// create the proxies
private JButtonEnablementProxy printNowProxy    = new JButtonEnablementProxy (printNow);      (3)
private JButtonEnablementProxy printDialogProxy = new JButtonEnablementProxy (printDialog);

// create a VisibilityGroupManager
private VisibilityGroupManager vgManager = new VisibilityGroupManager ();

// add the component proxies to the manager
vgManager.addGroupMember (PRINT_GROUP, printNowProxy);                                        (4)
vgManager.addGroupMember (PRINT_GROUP, printDialogProxy);

(1)

Define a constant for the identifier of a group of components which shall all be enabled and disabled under the same condition. You can choose an arbitrary name here. This name is only internally used as a handle on the individual component groups.

(2)

Create the GUI elements which shall eventually be enabled and disabled automatically by the VisibilityGroupManager.

(3)

Create the corresponding proxy objects for these GUI components. They are only needed by the VisibilityGroupManager in order to be able to access the components in a standardized way.

(4)

Finally, add the component proxies to the VisibilityGroupManager using the same group ID. By the both proxy objects belong to the same group and will be enabled/disabled and set visible/invisible under the same conditions.

We have now created a group of components which is managed by the VisibilityGroupManager. What is missing now is the Boolean expression which decides whether this group must be enabled or disabled.

Chapter 3. Data Structures

3.1. Introduction

Currently, RoKlib offers only one data structure, which is an implementation of a Ternary Search Tree. This is a map with some interesting properties making it most suitable for very fast prefix matching and spell checking operations.

3.2. The Ternary Search Tree Map

RoKlib's implementation of a ternary search tree comes as a class implementing the java.util.SortedMap interface with one particularity. As opposed to common Java maps, RoKlib's ternary search tree map does only have one generic class parameter which defines the type of the map's value object. The data type of the keys is predefined as having to be a subclass of java.lang.CharSequence. This is due to the nature of ternary search trees which don't use integer hash values to store their map values but instead split up a key's String representation into single characters to form a sort of a character path to the value. Hence, the only sensible key type for a ternary search tree is a textual type.

There are three similar classes available based on the ternary search tree data structure. Two of them are the aforementioned map in a case sensitive and case insensitive version (TernarySearchTreeMap and TernarySearchTreeMapCaseInsensitive). The third one is simply an implementation of java.util.SortedSet<CharSequence> that uses a ternary search tree map as its internal data model. This class TernarySearchTreeSet offers the same features as the ternary search tree map except that it doesn't offer to associate a value with the String data. Thus, a TernarySearchTreeSet is best used when you only need the prefix matching or spell checking feature of a ternary search tree.

Let's have a look at the class structure of these map and set classes.

Class diagram for the ternary search tree data classes

Figure 3.1. Class diagram for the ternary search tree data classes


You can see that the functionality specific to ternary search trees is declared in two interfaces ITernarySearchTreeQuery and ITernarySearchTreeMap<V>.

3.2.1. Prefix Matching

One of the most notable feature that a ternary search tree has to offer is that it allows for a very fast prefix matching. That is, given some prefix string, the data structure will provide you all keys stored in the map that start with this prefix.

There are two methods available for a TernarySearchTreeMap to do prefix matching.

Iterable<CharSequence> getPrefixMatch (CharSequence prefix);
Iterator<Entry<CharSequence, V>> getPrefixSubtreeIterator (CharSequence prefix);

3.2.2. Spell Checking

3.2.3. Case Insensitivity

3.3. Ternary Search Tree Set

Chapter 4. URL Action Handling Framework

4.1. Overview

Even though most modern web application frameworks go to great lengths to hide the context of the web from the programmer and make writing a web application as natural as writing a desktop application, it often cannot be avoided to also address the particularities of the web. One of these areas is dealing with URLs. In spite of modern web technologies such as AJAX, which abstract away the request-response model of the web, you will often want to handle URLs visited by the user directly. RoKlib assists you in doing so in a structured, accurate and well maintainable way. It lets you define action handlers for every URL that can be visited by the user. To these handlers you add action commands which will be executed when a corresponding URL has been visited. These action handlers are arranged in a tree-like structure which mimics the directory strucure of a website's URL structure.

4.2. Introduction

Before we talk about the classes making up the URL action handling framework of RoKlib, we take a look at the general structure of URLs. We're doing this to gain a better understanding of how the library classes work and why they are handling URLs in their particular way.

The general URL structure of a website is inherently hierarchical. A URL can be seen as an equivalent to a file system path where the first part points to a specific directory and the last part selects one particular file in that directory. We can outline the structure of a website similar to a file system. Let's take a contrived website www.example.com as an example.