Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Regular Expression Based AbstractFormatter

 

Regular Expression Based AbstractFormatter

The 1.4 release of Java 2 Standard Edition introduced the JFormattedTextField component. This is a flavor of JTextField that allows you to constrain the text that may be input. Out of the box, JFormattedTextField provides support for editing:

An additional feature in the Java 2 Standard Edition v 1.4 release is support for regular expressions. Regular expressions, among other things, provide a means to specify the legal values that a set of characters, typically a String, may accept. For example, to specify a U.S. phone number of the format (xxx)xxx-xxxx, you could use the regular expression: \(\d{3}\)\d{3}-\d{4}. A combination of the regular expression support and JFormattedTextField are a natural match, and this article discusses a way to combine the two.

JFormattedTextField uses an AbstractFormatter to enforce the editing policy. As regular expressions are a way of enforcing an editing policy, a subclass of AbstractFormatter needs to be created to interact with the regular expression package. This has been provided with the example class RegexFormatter.

In its simplest terms, a JFormattedTextField is a JTextField that has a value of type Object. The value varies depending upon what is being edited. For example, for dates the value would be an instance of Date; for numbers it might be an instance of Integer. JFormattedTextField uses an AbstractFormatter to convert between this value and a String representation using the valueToString method, and from the String representation back to an Object with the stringToValue method. Since RegexFormatter merely enforces an editing policy and doesn't necessarily provide any mapping between the String and Object, it only needs to override the stringToValue method. To enforce the regular expression, RegexFormatter would override valueToString and use a Pattern to determine if the String is legal, such as the following implementation:

    public Object stringToValue(String text) throws ParseException {
        Pattern pattern = getPattern();

        if (pattern != null) {
            Matcher matcher = pattern.matcher(text);

            if (matcher.matches()) {
                return super.stringToValue(text);
            }
            throw new ParseException("Pattern did not match", 0);
        }
        return text;
    }

If the value is legal, matcher.matches() returns true, and super's implementation of stringToValue converts the String back to an arbitrary Object. If the String does not match the regular expression, an exception is thrown indicating the value isn't legal.

Because RegexFormatter extends DefaultFormatter, it gets the following for free:

  • when to commit edits
  • the Class that stringToValue should create
  • support for overwrite mode

Using RegexFormatter

There are a number of ways to use RegexFormatter with a JFormattedTextField. The simplest is shown in the following code:

    new JFormattedTextField(new RegexFormatter("\(\d{3}\)\d{3}-\d{4}"));

Besides the source for RegexFormatter, a sample program, Controller, is provided. This demo allows you to interactively set the regular expression used by RegexFormatter, as well as try out some of the various options exposed by DefaultFormatter and JFormattedTextField. The demo also includes a couple of example regular expressions that can be viewed by changing the selection in the JComboBox. When you press enter in the text field, or update any of the configuration widgets, the RegexFormatter is recreated. The valid and value labels at the bottom of the Window update as the corresponding properties of the JFormattedTextField change.

What about groupings?

As was previously mentioned, with regular expressions there isn't necessarily a mapping between the String and Object. This is only partially true. The regular expression package provides a means to identify a region of text that matches a particular portion of the regular expression using grouping. For example, the following expression has two groups:

    \(\d{3}\)(\d{3}-\d{4})

Group 0 is defined as the entire expression and group 1, wrapped in parentheses and highlighted in red, is defined as the phone number without the three-digit area code. The string '(415)555-1212' would match the complete regular expression as defined by group 0, and the string '555-1212' would match the subgroup defined as group 1.

Pattern returns an instance of Matcher that may be used to extract the text that matched a particular grouping. RegexFormatter could expose a means to provide a mapping between groupings and the resulting Object, as well as a mapping from the Object to String using a similar regular expression and set of groupings. This exercise is left for a future article, or the ambitious reader.