Chapter 1: Java Power Tools
An Overview By Examples
Introduction
The fundamental purpose of the Java Power Tools is to enable extremely rapid development of graphical user interfaces in Java. In this overview, using a sequence of examples, we will demonstrate how the encapsulation of Java Power Tools makes it easy to create GUIs. We will present the highlights and postpone a full presentation of the details until later chapters.
Example 1: Adder
In the Adder example, four text fields labeled A, B, C, D are used to enter double precision values and a fifth text field labeled Sum is used to present the sum of these four values. The three actions in the GUI are to sum the values, clear all text fields to blank, and reset all text fields to their initial state. Below is a snapshot of the initial state of the Adder GUI.
Here is another snapshot of the Adder GUI with some sample values entered and the sum calculated.
The Adder GUI contains 5 text fields each with labels that are organized into a 2-dimensional table. The Adder GUI also contains 3 buttons spread out horizontally. Finally, the text field table and the button panel are organized within an enclosing vertical table in the Adder GUI. The GUI is in turn presented in a window frame on the screen. We will now show how all of these elements are put together rapidly using the Java Power Tools (JPT).
The first step is the creation of the 5 text fields.
protected TextFieldView aTFV = new TextFieldView("1", 200);
protected TextFieldView bTFV = new TextFieldView("2", 200);
protected TextFieldView cTFV = new TextFieldView("3", 200);
protected TextFieldView dTFV = new TextFieldView("4", 200);
protected TextFieldView sumTFV = new TextFieldView("", 200);
The JPT class TextFieldView extends the Java Swing class JTextField and incorporates methods for fully error-checked input. In the constructors above, the String parameter provides the initial value in the text field and the numeric parameter provides the preferred width for the text field. As you see, all text fields will have preferred width of 200 pixels.
The next step is to create the 2-dimensional table with the 5 text fields and their labels.
protected TablePanel tfvPanel
= new TablePanel(
new Object[][] {
{ "A:", aTFV },
{ "B:", bTFV },
{ "C:", cTFV },
{ "D:", dTFV },
{ "Sum:", sumTFV }
},
5, 5, EAST);
The JPT class TablePanel extends the base JPT panel class DisplayPanel which in turn extends the base Java Swing panel class JPanel. The TablePanel class is an expert on creating tables of components in 1-dimensional (horizontal or vertical) or 2-dimensional layouts. In the constructor above, a 2-dimensional array of Object is provided to define the contents of the table. In each row, we have one label (a String) and one TextFieldView. Notice how the structure of this Object array exactly parallels what is visible in the GUI with labels on the left and text fields on the right. Notice also that the class String is not a Java Component. A TablePanel constructor will convert appropriate Object’s into Component’s as needed. The final three parameters 5, 5, EAST set the horizontal and vertical spacing between cells in the table and set the default alignment of cell contents which, in this case, is to align contents to the EAST. This is what leads to the right alignment of the labels next to the text fields.
Now that we have the upper half of the GUI, we must construct the 3 buttons. What is important about the buttons is the actions they perform. Hence, we will focus on defining the 3 corresponding actions. It will then be easy to create the button panel and the buttons.
We will define the 3 actions as objects that satisfy the Java Action interface. Each definition will follow the same pattern. Define the action as a JPT SimpleAction which requires that the perform() method be instantiated. Define the perform() method by calling a method in the class (often the method has the same name as the action). This design pattern converts a Java method that may not be passed directly as a parameter into an Action object that is first class and may be used anywhere in Java. We call this pattern Encapsulate action as object.
The 3 actions sum, clear, and reset are defined as follows:
protected Action sum
= new SimpleAction("Sum") {
public void perform() { sum(); }
};
protected Action clear
= new SimpleAction("Clear") {
public void perform() { clear(); }
};
protected Action reset
= new SimpleAction("Reset") {
public void perform() { reset(); }
};
The 3 methods sum(), clear(), and reset() will be discussed below. Once we have the actions defined, it is easy to create the panel with the corresponding buttons.
protected ActionsPanel buttonPanel
= new ActionsPanel(new Action[] { sum, clear, reset });
The JPT ActionsPanel constructor uses the Action array to create a panel with one button for each action. These buttons are organized by default using a Java FlowLayout.
The next step is to stack the text field panel and the button panel within a vertical table to create the main GUI panel. We do this as follows:
protected TablePanel mainPanel =
new TablePanel(
new Object[] { tfvPanel, buttonPanel },
VERTICAL, 10, 10, CENTER);
In this TablePanel constructor, we pass only a 1-dimensional Object array so we must specify the orientation of the panel as HORIZONTAL or VERTICAL. The spacing between the text field panel and the button panel is 10 pixels and each panel is centered in its table cell.
So far, we have defined the member data of what will become the Java class Adder that will instantiate our example. We now describe the overall class structure, the constructor of the Adder class, and the main program.
public class Adder extends DisplayPanel implements JPTConstants {
// member data ...
// member functions ...
//constructor
public Adder() { add(mainPanel); }
// main program
public static void main(String[] args) {
JPTFrame.createQuickJPTFrame("Adder", new Adder());
}
}
It is common practice in Java design to have one class that instantiates the panel that is placed as the content pane within the window frame of the application. Since this simple example has only one class, Adder, we arrange that the Adder class itself extends the JPT panel class DisplayPanel. The constructor for Adder has one statement, namely, add(mainPanel). This places the mainPanel panel object into the Adder panel object as its only Component. In turn, if you examine the main program, you see that a new Adder panel is inserted as the content pane of a new frame. The static method createQuickJPTFrame creates the frame and encapsulates many steps such as packing and centering. Note that the Adder class implements the interface JPTConstants. This makes available the constants such as HORIZONTAL, VERTICAL, CENTER, and EAST that are used in this example.
It remains to discuss the member functions sum(), clear(), and reset() that implement the actions sum, clear, and reset. Since Adder extends DisplayPanel, the function reset() is already defined and will call itself recursively on all subcomponents defined using JPT. Thus, we only need to define the methods sum() and clear().
protected void sum() {
double a = aTFV.demandDouble();
double b = bTFV.demandDouble();
double c = cTFV.demandDouble();
double d = dTFV.demandDouble();
sumTFV.setViewState("" + (a + b + c + d));
}
protected void clear() {
aTFV.setViewState("");
bTFV.setViewState("");
cTFV.setViewState("");
dTFV.setViewState("");
sumTFV.setViewState("");
}
To perform the sum() operation, the text data in the top 4 text fields must be extracted and parsed into data of type double. This is the work of the method demandDouble(). If there is an error in the data of any text field, an error dialog will automatically handle the error.
Once the four numeric values have been extracted, the values are summed, converted back to a String, and inserted into sumTFV using the setViewState method.
To perform the clear() operation, the setViewState method is used to insert an empty String into all 5 text fields.
It is worth making some additional remarks on the TextFieldView class even though a detailed discussion will take place in a later chapter. A TextFieldView object is capable of providing input for any data type representable by a text string that can be entered on a single line by a user. The method demandDouble() utilizes this general capability to extract and parse what has been entered by the user and to convert this information to type double. The parser does more than simply parse numeric constants. The parser actually parses general arithmetic expressions and supports standard mathematical functions. The snapshot below illustrates this capability.
The TextFieldView method setViewState and its partner getViewState are actually methods specified in the JPT Displayable interface and are provided by all JPT views. These methods set or get the user input state of a view using information encapsulated in a text string.
We have now provided all of the code needed for this example except for the import statements at the top of the Adder.java file. These import statements are:
import edu.neu.ccs.gui.*;
import edu.neu.ccs.util.*;
import javax.swing.*;
Since the Java Power Tools were created at the College of Computer Science at Northeastern University (see http://www.ccs.neu.edu/jpt/), the first two import statements represent packages in the JPT library jpt.jar. The final import statement represents the Java Swing package. If the file Adder.java is compiled and linked with jpt.jar then it will work as described. Henceforth, we will assume that the reader can create the necessary import statements using the standard information in the javadocs for Java itself and for JPT.
Let us now discuss in more detail how TextFieldView’s handle input errors and then describe some of the options that are available to the programmer to customize this behavior.
When a TextFieldView is asked to deliver input that is more complex than a pure String, then it will call on parsing utilities to interpret what the user has entered in order to create a value of the appropriate data type. If an error is detected, an automatic error dialog will be displayed that indicates the nature of the error. For example, if the user types “nonsense” as the entry into the first text field then the following dialog will be displayed:
This dialog will persist until the user has typed a valid String representation of a double. Since the JPT parser accepts arithmetic expressions, an error might occur in the midst of an expression. For example, if the user types “(1 + sqrt(5)/2” which has a missing parenthesis, the following dialog will be displayed:
In both of the above cases, the user must supply valid data in order to escape from the modal dialog box. In some situations, it is preferable to permit the user to cancel an operation rather than to insist that valid data be provided. To accomplish this, we replace the use of the method demandDouble with the “more polite” method requestDouble. If this is done, then the error dialog after the second error will now be displayed as follows
Of course, if the user decides to press cancel then the error dialog will disappear but the caller will be left without a valid data value. To deal with this, a CancelledException is thrown to signal to the caller that the user has decided to cancel and that the caller must behave accordingly. In the Adder example, the following code for sum() handles this situation:
protected void sum() {
try {
double a = aTFV.requestDouble();
double b = bTFV.requestDouble();
double c = cTFV.requestDouble();
double d = dTFV.requestDouble();
sumTFV.setViewState("" + (a + b + c + d));
}
catch (CancelledException exception) {
sumTFV.setViewState("");
}
}
Since the requestDouble method may throw a CancelledException, we must execute the call in the try-block of a try-catch construct. In this case, the contents of the try-block parallels the original code in the sum() method. If the user does cancel, then the code in the catch-block simply sets sumTFV to blank to indicate that no sum was calculated. This example of the use of a try-catch construct is a particularly simple way to introduce exception handling to students since it clear to everyone that a cancelled calculation may not continue with bogus data.
In some situations, it may be helpful for the user if the error dialog provides a suggestion for valid input so that the user knows what valid input looks like. It may also be helpful if the error prompt and dialog title are made specific to the particular text field. This can all be accomplished by using a more elaborate constructor for TextFieldView. We illustrate this with the text field aTFV.
protected TextFieldView aTFV = new TextFieldView
("1", "Correct the value in field A:", "Double Input Error", "0", 200);
This constructor sets the error prompt to “Correct the value in field A:”, the dialog title to “Double Input Error”, and the suggestion to “0”. The error dialog then looks as follows:
If the user presses the “Suggest” button then the suggestion value of “0” will be placed in the error text field. If the user wishes to go back to the original error and edit this error value, this can be accomplished by pressing the “Reset” button.
If the user drags the error dialog on the screen so that the original text field is visible then the user will see that whatever is typed into the error dialog is echoed immediately in the original text field. Thus the user is given additional feedback as to which text field is being modified.
The error handler also highlights the smallest component containing the text field. This is useful but can also be problematic. In the Adder program, for example, the smallest component containing any of the five text fields is the table panel tfvPanel. The error highlighting rule means that this entire panel will be highlighted if an error occurs in any of its text fields. This feedback is not useful. To focus the error highlighting on a single text field, we use a simple wrapper class called a Halo that puts a 4 pixel boundary around its contents. We do this easily by making the following modification to the tfvPanel definition: