Developer's Daily Visual Cafe Education
  front page | java | perl | unix | DevDirectory
   
Front Page
Java
Education
Visual Cafe
Articles
   
 

A FinancialTextField component for commerce applets


Introduction

Have you ever noticed that when you withdraw money from an ATM machine, most ATM's require you to enter values like "10.00" to get a $10 bill?  I think this is pretty funny, because every ATM machine I've seen can only give out money in increments of ten-dollar bills.  If this is the case, what's the purpose of entering the ".00" after the "10"?  What happens at your ATM if you type "$1.76" or "$10.01" - do you still get ten dollars?


Problems with TextField's in financial applets

I make this point about ATM's because in a recent survey of Java applets used in financial applications, I found that almost all of these applets let me type things like "Give me all of your money" in their financial transaction prompts.  Whether the applet was prompting me for a deposit, withdrawal, or other cash value, I could type anything I wanted in the input field.

I began wondering, as programmers, why do we let people type anything but numeric digits and decimals when we prompt them for financial information?  The answer soon became apparent - it's not that easy.

In this article we'll begin to demonstrate the steps necessary to create a robust, customized TextField component that only lets end-users enter numeric digits and decimals into the data entry field.  As we do this, we'll gain insight into Java's AWT Event Model, which we'll use to provide the desired behavior for our component.

As a final note before we dive in, because we're creating this component for Java applets, and most browsers on the Internet currently support Java 1.0 (and not necessarily Java 1.1), we'll focus on making this method work with the event model of Java 1.0.
 

Failed attempts

Before embarking on the approach of creating a custom FinancialTextField class, I tried several other approaches that simply proved to be inadequate.  I'll describe those attempts here briefly, because it's important to understand the need to derive a new class, as opposed to using the tools that already exist.


First approach

My first approach was to use a simple TextField component for financial data input, just like everyone else was using.  I'd let the user type anything they wanted, and then I'd get their input using getText() and convert the text to a float value with the Float class.

Because Float.valueOf() throws a NumberFormatException on conversion failures, it's pretty easy to catch this exception, and throw an error dialog at the user.  In my estimation this was easy, but a bad solution to the problem.  In short - why allow the error to happen if you can stop it in the first place?


Second attempt

My second approach was to use a Visual Cafe FormattedTextField component from the component panel named Additional.  I dragged this component onto my applet, and opened the Properties dialog.  In the Properties dialog, I applied a mask of 9999\.99 in the Mask field.  If you're not familiar with masks in Cafe's FormattedTextField object, the 9's in the mask are placeholders for any numeric digit (0-9), and the backslash-decimal combination represents a placeholder for a decimal.  When combined as 9999\.99, this mask forces a user to enter a sequence of four numeric digits (0-9), followed by a single decimal, followed by two more numeric digits.

At first glance this seemed like the proper approach, but it suffered from major shortcomings.  The most important shortcoming occurred when the user need to enter a value like $1.  To enter one dollar, they had to type 0001.00.  In short, they had to fill the left-most field positions (thousands, hundreds, tens) before they could enter anything into the one-dollar field position.  In short, the mask approach of the FormattedTextField was not flexible enough for my purposes.

I tried other approaches, but wasn't satisfied with any of them.  The most straight-forward solution to the problem was to create a customized TextField component to prompt for user input in commerce applets.
 

Building the FinancialTextField class

In the remainder of this article, we'll walk through the steps of creating a new FinancialTextField class by inheriting from Java's TextField class.  In this new class, we'll create a customized keyDown() method that will be called by the Java event handler.  This custom keyDown() method will restrict the user's input options, so they can only enter numeric digits and the decimal character, along with other cursor-control keys such as [Del], [Backspace], [End], [Home], [Tab], [Enter], and the left/right/up/down arrow keys.

Due to space constraints and the complexity of this subject, our approach will still suffer from a few problems by the end of this article.  While we'll successfully limit the end-user to entering only numeric digits and decimal characters, they'll unfortunately be able to enter any number of decimals, and they'll be able to enter any number of numeric digits to the right of any decimal.  We'll fix these problems in the second article in this series.
 

Creating the FinancialTextField code

The first step in creating the FinancialTextField class is to open a new Visual Cafe project.  Because you'll eventually want to test this new class within an applet, you can save some time by creating an empty applet project to start with.  To create an empty applet project within Visual Cafe, select File | New Project, and then select Basic Applet.

Before proceeding further, save the applet project to a directory of your choice by selecting File | Save As.  As an example, I saved my project in a directory named C:\Pgmg\Java\Applets\ FinanceApplet with the name FinanceProj.vpj, by entering C:\Pgmg\Java\Applets\FinanceApplet\FinanceProj.vpj at the filename prompt.

Once the applet project is open and saved, you'll want to create the FinancialTextField class in a separate file within the project.  Open a separate file by selecting File | New File.  When the new file is displayed, save it to the same directory as your project file, with the name FinancialTextField.java.  You use this named because you'll be defining the FinancialTextField class in this file.

Now that those files are saved, you can begin creating the FinancialTextField class.  Because this class will use the TextField and Event classes of the Java AWT, you'll need to import those classes into this file with the following statements at the top of the file:

import java.awt.Event;
import java.awt.TextField;
Because the new class will inherit it's properties from the TextField class, the next thing to do is to create the code to extend the TextField class.  The basic template to extend the TextField class looks like this:
 public class FinancialTextField extends TextField {

 }

The remainder of the FinancialTextField code will be placed inside of this class framework.  Because much of the behavior of the TextField class is what we already want, our FinancialTextField class is pretty simple.  The class consists of two methods: a constructor method, and a keyDown() method.  The entire code for the FinancialTextField class is shown in Listing 1.
 

import java.awt.Event;
import java.awt.TextField;

/**
 * A customized "FinancialTextField" component that let's user's enter only numeric
 * values (0-9)or decimal characters.
 *
 * @version 1.0.0, Nov 14, 1997
 * @author Kris Kringle
 */

public class FinancialTextField extends TextField
{

    //  Constructor method

 public FinancialTextField(String s) {
     super(s);
 }
 

    //  keyDown method

    public boolean keyDown(Event evt, int key) {

        if ( evt.id == Event.KEY_PRESS ) {

            switch (key) {
                case  8:   // backspace key
                case  9:   // tab key
                case 10:   // return key
                case 46:   // decimal key
                case 48:   // 0
                case 49:   // 1
                case 50:   // 2
                case 51:   // 3
                case 52:   // 4
                case 53:   // 5
                case 54:   // 6
                case 55:   // 7
                case 56:   // 8
                case 57:   // 9
                case 127:  // del key
                    return super.keyDown(evt, key);
                default:
                    return true;
            }
        }

        return super.keyDown(evt, key);
    }

}


 
Listing 1:  The FinancialTextField class, FinancialTextField.java

 

The constructor method

The constructor method for this class, FinancialTextField(), accepts a String object as input from the calling program.  In our case, the calling program will be our applet code.  In turn, the constructor passes the input String directly to it's superclass (TextField) constructor method.

With this String argument, an applet can initialize it's FinancialTextField prompt to "0.00" with a call like this

depositField = new FinancialTextField("0.00");
or they can leave the field blank initially by constructing it this way:
depositField = new FinancialTextField("");
An important note here is that if we had the space in this article, we'd probably offer several additional constructor methods.  However, for the purposes of our example, we'll keep it short and offer only this one method.  For suggestions on additional constructor methods, look at the documentation for the TextField component, which offers four possible constructors.
 

The keyDown method - introduction

Next we'll look at the keyDown() method.  The keyDown() method is a specially-named method that's invoked by the handleEvent() method of our FinancialTextField component.  The keyDown() method is invoked by handleEvent() any time a keyboard event occurs.

We've inherited the handleEvent() method all the way down from the Component superclass.  If you're not familiar with the class hierarchy of the TextField component, you'll see in the documentation that the TextField component is derived from the TextComponent class, which in turn is derived from the Component class.  The full lineage, including our new class, looks like this:

Object -> Component -> TextComponent -> TextField -> FinancialTextField
As mentioned, the keyDown() method is invoked by handleEvent() when a keyboard event occurs.  A primary characteristic of the Java 1.0 event model is that the you must override methods such as keyDown() when you want to achieve custom behavior.  Therefore, to control which characters a user can enter into the FinancialTextField component, we create a custom keyDown() method to process the input characters.
 

The keyDown() method - details

The keyDown() method we'll create is fairly simple.  Before looking at the code, remember that keyDown() is called when a keyboard event occurs.  In the first line of the keyDown() method, shown in Listing 1, we check to see if the keyboard event generated was a KEY_PRESS event.  A KEY_PRESS event occurs when the user presses a key for a normal printing character, a control character, or a non-printing character with a standard ASCII value.  This includes the all of the alphabetic and numeric characters, as well as the [Backspace], [Del], [Escape], [Return], and [Tab] keys.

In a more complex keyDown() method, we might also respond to KEY_ACTION events generated by the user.  KEY_ACTION events are created when the user presses some type of function key, or a key without an ASCII representation.  This includes keys such as the [Home], [End], [PgUp], [PgDn], and the arrow keys on the keyboard.  However, in the case of our FinancialTextField, the default behavior of the KEY_ACTION keys is acceptable, so we won't add any logic to process those keystrokes.

The keyDown() method consists of a case statement inside of an if-then statement.  If the keyboard event is a KEY_PRESS event, the code is handled in the case statement.  If the keyboard event is not a KEY_PRESS event - say it's a KEY_ACTION event - the if-then statement is bypassed, and the keyDown() method of our superclass (the TextField component) is invoked in the last line of code:

return super.keyDown(evt, key);
It's important to pass keyboard events like this on to our superclass, because even though we're not interested in the event, another container may be very interested in it.  For instance, even though we don't want to do anything special with the [Tab] key in our keyDown() method, other containers - the KeyPressManagerPanel for instance - rely on knowing that the [Tab] key was pressed.  In fact, that's one of the reasons the KeyPressManagerPanel is used later in our test applet - to make sure the [Tab] key event is correctly handled in the keyDown() code.

Inside of the if-then statement, we handle the keyboard events we're concerned with by using a case statement.  If the key the user hits is a [Backspace], [Tab], [Return], decimal, numeric digit, or the [Del] key, the event is passed to the TextField superclass with this statement following the "case 127:" test:

return super.keyDown(evt, key);
In essence, we let these "good" characters pass through our filter and we pass them on to our superclass.
If the KEY_PRESS event is any other printable character, such as the letter 'a', control flows to the default statement, which invokes this command:
return true;
By returning a value of true to the event handler, we're indicating that we've seen the keyboard event and we've handled it, and no other action is necessary by the handler.  When the handler receives a value of true, it moves on and waits for the next event to occur.  Therefore, our case statement says "ignore any KEY_PRESS characters except for those we've specified.  If the user enters an 'a', or a '%' symbol, or anything else like that, just ignore it.  We've seen it, and it has no place in our FinancialTextField."
 

An applet in action

Listing 2 shows the code for an applet designed to test the FinancialTextField component.  In this applet two FinancialTextField components (debitTextField, creditTextField) are added to a KeyPressManagerPanel component.  As mentioned before, this is a good test, because the KeyPressManagerPanel lets the end-user use the [Tab] key to move between components.  If the [Tab] key works in this applet, we can rest assured that we're handling that KEY_PRESS event properly.


 
/*
    A basic extension of the java.applet.Applet class
 */

import java.awt.*;
import java.applet.*;

public class TesterApplet extends Applet {

public void init() {

     super.init();

     //{{INIT_CONTROLS
     setLayout(null);
     addNotify();
     resize(180,100);
     keyPressManagerPanel1 = new  symantec.itools.awt.KeyPressManagerPanel();
     keyPressManagerPanel1.setLayout(null);
     keyPressManagerPanel1.reshape(0,0,180,100);
     add(keyPressManagerPanel1);
     //}}

     debitTextField = new FinancialTextField("0.00");
     debitTextField.reshape(40,20,100,20);
     keyPressManagerPanel1.add(debitTextField);

     creditTextField = new FinancialTextField("");
     creditTextField.reshape(40,50,100,20);
     keyPressManagerPanel1.add(creditTextField);
}

public boolean handleEvent(Event event) {
     return super.handleEvent(event);
}

//  FinancialTextField components
FinancialTextField debitTextField;
FinancialTextField creditTextField;

//{{DECLARE_CONTROLS
symantec.itools.awt.KeyPressManagerPanel keyPressManagerPanel1;
//}}

}

Listing 2:  The code for our test applet.  

 

Our sample applet is shown running in the AppletViewer in Figure 1.

Figure 1:  The sample applet running in the AppletViewer. Users will only be able to enter numeric digits and decimals in the two data fields.  

 

The future

So far we've achieved success in limiting the user input to numeric digits and decimal characters, but the FinancialTextField class still suffers from a few problems, specifically:

  1. The user can still enter more than one decimal character.
  2. The user can enter more than two characters to the right of a decimal.
These events are tougher to handle, and there isn't space to cover the entire topic this month.  If you're anxious to get started, here's a hint:  the best way to handle these problems is to call a special method from keyDown() with the logic necessary to handle these keystrokes.  Therefore, instead of passing control to the superclass like this inside of the case statement
return super.keyDown(evt, key);
you'll write something like this instead:
return myKeyPressHandler(key);
Here, the new method named myKeyPressHandler() contains the logic needed to handle the two problems noted above.  Warning - it's not as easy as you might think.  (Hmm, maybe that's why all these financial applets just use simple TextField components ... )
 

Conclusion

This article has demonstrated the steps necessary to create a customized FinancialTextField component for your commerce-oriented Internet applets.  In the next article in this series, we'll improve the FinancialTextField component by allowing only one decimal in the user input, and allowing only two numeric digits to the right of the decimal.


[Note - This article first appeared in ZD Journals Visual Cafe Developer's Journal. The article is reprinted here with their permission. The author now works for Developer's Daily.]



Do the BLOG

Copyright © 1998 DevDaily Interactive, Inc.
All Rights Reserved.