You are receiving this e-mail because you elected to receive e-mail from Sun Microsystems, Inc. To update your communications preferences, please see the link at the bottom of this message. We respect your privacy and post our privacy policy prominently on our Web site http://sun.com/privacy/
  Welcome to the Core Java Technologies Tech Tips.
Core Java Technologies
TECHNICAL TIPS
May 18, 2005
View this issue as simple text
In this Issue
 
Here you'll get tips on using core Java technologies and APIs, such as those in Java 2 Platform, Standard Edition (J2SE).

This issue covers:

» Validating Text and Filtering Documents
» Accessibility and the Java Access Bridge

These tips were developed using the Java 2 Platform Standard Edition Development Kit 5.0 (JDK 5.0). You can download JDK 5.0 at http://java.sun.com/j2se/1.5.0/download.jsp.

This issue of the Core Java Technologies Tech Tips is written by John Zukowski, president of JZ Ventures, Inc.

See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms.

For more Java technology content, visit these sites:

java.sun.com - The latest Java platform releases, tutorials, and newsletters.

java.net - A web forum where enthusiasts of Java technology can collaborate and build solutions together.

java.com - Hot games, cool apps -- Experience the power of Java technology.

VALIDATING TEXT AND FILTERING DOCUMENTS
 
Validating and constraining text input has always been an interesting problem for those creating user interfaces for the Java platform. Depending on the version of J2SE you work with, there are different validation options available. Typically, older options are still available with each release, but more often than not, you probably don't want to use them because the new options are better. This tip presents several options that have been available in past releases of J2SE to validate text input, including the DocumentFilter class added in the 1.4 release.

Validating With Key and Focus Listeners

Early versions of the Swing component set didn't offer any direct or simple ways of validating input. If you wanted to limit input to a text field at the key level, you needed to add a KeyListener to the component, and consume() the input event (that is, if it wasn't to be added to the text field). This allowed you to have things like numeric-only input fields. For field level validation, you needed to attach a focus listener. If the input wasn't valid, you would reject a request to move the focus when the focus left the field. Neither of these options scaled well, but they did provide a way to validate input. Neither of these options are recommended today.

Validating with Input Verifiers

The 1.3 release of J2SE added the abstract InputVerifier class. You could attach a verifier to any component with the setInputVerifier() method of JComponent, and override the boolean verify(JComponent input) method of the InputVerifier. If the current input for the component is valid, and you press tab or shift-tab, verify() returns true. Focus then moves to the next/previous component in the traversal order. If verify() returns false, you would be stuck in the current field. JComponent also provides a getVerifyInputWhenFocusTarget() method. You could use this method to disable verifying before selecting a menu item such as Help.

To demonstrate this approach to validation, the following program, NumericVerifier, includes three text fields. The top and bottom text fields require a valid numeric value before you can tab out of either field. The middle text field has no input restrictions.
   import java.awt.*;
   import java.awt.event.*;
   import javax.swing.*;

   public class NumericVerifier{
     public static void main(String args[]) {
       Runnable runner = new Runnable() {
         public void run() {
           JFrame frame = new JFrame("Numeric Verifier");
           frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

           JPanel panel1 = new JPanel(new BorderLayout());
           JLabel label1 = new JLabel("Numeric-only");
           JTextField textField1 = new JTextField();
           panel1.add(label1, BorderLayout.WEST);
           panel1.add(textField1, BorderLayout.CENTER);

           JPanel panel2 = new JPanel(new BorderLayout());
           JLabel label2 = new JLabel("Anything");
           JTextField textField2 = new JTextField();
           panel2.add(label2, BorderLayout.WEST);
           panel2.add(textField2, BorderLayout.CENTER);

           JPanel panel3 = new JPanel(new BorderLayout());
           JLabel label3 = new JLabel("Numeric-only");
           JTextField textField3 = new JTextField();
           panel3.add(label3, BorderLayout.WEST);
           panel3.add(textField3, BorderLayout.CENTER);

           InputVerifier verifier = new InputVerifier() {
             public boolean verify(JComponent comp) {
               boolean returnValue;
               JTextField textField = (JTextField)comp;
               try {
                 Integer.parseInt(textField.getText());
                 returnValue = true;
               } catch (NumberFormatException e) {
                 Toolkit.getDefaultToolkit().beep();
                 returnValue = false;
               }
            return returnValue;
             }
           };

           textField1.setInputVerifier(verifier);
           textField3.setInputVerifier(verifier);

           frame.add(panel1, BorderLayout.NORTH);
           frame.add(panel2, BorderLayout.CENTER);
           frame.add(panel3, BorderLayout.SOUTH);
           frame.setSize(300, 95);
          frame.setVisible(true);
         }
       };
       EventQueue.invokeLater(runner);
     }
   }
Numeric Verifier

Validating with a Custom Document

Instead of validating input at the field level after all input is entered, you can validate as the text component content is entered or removed. You can do this using the Document interface. Following the Model-View-Controller (MVC) design pattern, the model part of Swing text components is managed by implementations of the Document interface. Prior to the 5.0 release of J2SE, you had to create a custom Document implementation to restrict input to what you deemed valid.

Swing includes standard Document implementations for the text components in the form of the PlainDocument, DefaultStyledDocument, and HTMLDocument classes. If extending one of these implementations doesn't give you what you need, you can subclass the AbstractDocument (which provides the basis for custom implementations). The PlainDocument class is used with text components that work with single attributed (same font/same color) text, as in JTextField and JTextArea. The DefaultStyledDocument class is useful with JTextPane. The HTMLDocument class models content of type text/html in a JEditorPane. This is only a high-level look at where and how these Document implementations are used. The document types are also used elsewhere.

If you wanted to limit a JTextField to only numeric input, you would subclass PlainDocument, and then customize the behavior of its insertString() and remove() methods. The insertString() method tries to insert a String, not just a char, so you have to test if the result is valid. More than one character would be inserted in the case of a paste operation. Here's an example:
   import javax.swing.text.*;

   public class IntegerDocument extends PlainDocument {

     int currentValue = 0;

     public IntegerDocument() {
     }

     public int getValue() {
       return currentValue;
     }

     public void insertString(int offset, String string,
         AttributeSet attributes) throws BadLocationException {

       if (string == null) {
         return;
       } else {
         String newValue;
         int length = getLength();
         if (length == 0) {
           newValue = string;
         } else {
           String currentContent = getText(0, length);
           StringBuffer currentBuffer = 
                new StringBuffer(currentContent);
           currentBuffer.insert(offset, string);
           newValue = currentBuffer.toString();
         }
         currentValue = checkInput(newValue, offset);
         super.insertString(offset, string, attributes);
       }
     }
     public void remove(int offset, int length)
         throws BadLocationException {
       int currentLength = getLength();
       String currentContent = getText(0, currentLength);
       String before = currentContent.substring(0, offset);
       String after = currentContent.substring(length+offset,
         currentLength);
       String newValue = before + after;
       currentValue = checkInput(newValue, offset);
       super.remove(offset, length);
     }
     public int checkInput(String proposedValue, int offset)
         throws BadLocationException {
       if (proposedValue.length() > 0) {
         try {
           int newValue = Integer.parseInt(proposedValue);
           return newValue;
         } catch (NumberFormatException e) {
           throw new BadLocationException(proposedValue, offset);
         }
       } else {
         return 0;
       }
     }
   }
To test the new IntegerDocument, you would attach it to a JTextField for which you want to restrict input. To demonstrate, the following program, NumericInput, presents two text fields. The one on the top accepts only numeric input. The one below accepts anything. One important difference between this example and the previous example is that the program in the prior example only validates the input field when focus is lost. This example never lets you see an invalid value in the field.
   import javax.swing.*;
   import javax.swing.text.*;
   import java.awt.*;
   import java.awt.event.*;

   public class NumericInput {
     public static void main(String args[]) {
       Runnable runner = new Runnable() {
         public void run() {
           JFrame frame = new JFrame("Numeric Input");
           frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
           frame.setLayout(new GridLayout(2, 2));

           frame.add(new JLabel("Number"));
           JTextField textFieldOne = new JTextField();
           Document textDocOne = new IntegerDocument();
           textFieldOne.setDocument(textDocOne);
           frame.add(textFieldOne);

           frame.add(new JLabel("All"));
           JTextField textFieldTwo = new JTextField();
           frame.add(textFieldTwo);

           frame.setSize(250, 90);
           frame.setVisible(true);
         }
       };
       EventQueue.invokeLater(runner);
     }
   }
Numeric Input

Validating with a Document Filter

With the J2SE 1.4 release, a change was made to the way document filtering worked. Instead of having to subclass the Document implementation to restrict the contents of the model, you can use the new DocumentFilter class. You simply attach the DocumentFilter to the AbstractDocument through the setDocumentFilter method.

The DocumentFilter class has three methods available to override. In addition to the two shown with Document, there is a replace method. The replace method is called almost always now. You can think of a paste operation done through replace, instead of separate calls to remove() and insertString(). Here are the three methods:
   public void insertString(DocumentFilter.FilterBypass fb,
                         int offset,
                         String string,
                         AttributeSet attr)
                  throws BadLocationException
                  
   public void remove(DocumentFilter.FilterBypass fb,
                   int offset,
                   int length)
            throws BadLocationException
            
   public void replace(DocumentFilter.FilterBypass fb,
                    int offset,
                    int length,
                    String text,
                    AttributeSet attrs)
             throws BadLocationException
Here is an example of a DocumentFilter that provides the same functionality as the IntegerDocument shown previously. The bulk of the work is wrapped into the replace() method.
   import javax.swing.text.*;
   import java.awt.Toolkit;   
   
   public class IntegerDocumentFilter extends DocumentFilter {
   
   
      int currentValue = 0;   
   
      public IntegerDocumentFilter() {
      }
      
      public void insertString(DocumentFilter.FilterBypass fb, 
       int offset, String string, AttributeSet attr) 
       throws BadLocationException {   
   
        if (string == null) {
          return;
        } else {
          replace(fb, offset, 0, string, attr);
        }
       }   
   
      public void remove(DocumentFilter.FilterBypass fb, 
       int offset, int length)
       throws BadLocationException {
   
        replace(fb, offset, length, "", null);
      }
      
      public void replace(DocumentFilter.FilterBypass fb, 
       int offset, int length, String text, AttributeSet attrs) 
       throws BadLocationException {   
   
        Document doc = fb.getDocument();
        int currentLength = doc.getLength();
        String currentContent = doc.getText(0, currentLength);
        String before = currentContent.substring(0, offset);
        String after = currentContent.substring(
             length+offset, currentLength);
        String newValue = before + 
             (text == null ? "" : text) + after;
        currentValue = checkInput(newValue, offset);
        fb.replace(offset, length, text, attrs);
      }
      
      private int checkInput(String proposedValue, int offset)
          throws BadLocationException {
        int newValue = 0;
        if (proposedValue.length() > 0) {
          try {
            newValue = Integer.parseInt(proposedValue);
          } catch (NumberFormatException e) {
            throw new BadLocationException(
              proposedValue, offset);
          }
        }
        return newValue;
      }
   }
The updated test program, NumericInputFilter, is practically the same as the previous test program, NumericInput. The only differences in the updated test program are changes in the window title and the association of the filter to document the text component.
   import javax.swing.*;
   import javax.swing.text.*;
   import java.awt.*;

   public class NumericInputFilter {
     public static void main(String args[]) {
       Runnable runner = new Runnable() {
         public void run() {
           JFrame frame = new JFrame("Numeric Input Filter");
           frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
           frame.setLayout(new GridLayout(2, 2));

           frame.add(new JLabel("Number"));
           JTextField textFieldOne = new JTextField();
           Document textDocOne = textFieldOne.getDocument();
           DocumentFilter filterOne = 
                new IntegerDocumentFilter();
           ((AbstractDocument)
                textDocOne).setDocumentFilter(filterOne);
           textFieldOne.setDocument(textDocOne);
           frame.add(textFieldOne);

           frame.add(new JLabel("All"));
           JTextField textFieldTwo = new JTextField();
           frame.add(textFieldTwo);

           frame.setSize(250, 90);
           frame.setVisible(true);
         }
       };
       EventQueue.invokeLater(runner);
     }
   }
Numeric Input Filter

Conclusion

There are various ways to validate input. In addition to the use of InputVerifier, Document, and DocumentFilter shown in this tip, you can also use the JFormattedTextField class. That component was described in a previous tip, Formatting Text Input With JFormattedTextField. One big difference between the InputVerifier and the DocumentFilter (and indirectly JFormattedTextField) is that InputVerifier works with any component. By comparison, the DocumentFilter is limited to text components.

Back to Top

ACCESSIBILITY AND THE JAVA ACCESS BRIDGE
 
Assistive technologies are hardware and software solutions that help people with physical impairments interact with computers. Assistive technologies encompass things like voice input devices, braille display terminals, and text-to-speech screen readers. The Java Foundation Classes provide a standard way to support assistive technologies through the Accessibility API provided by the javax.accessibility package. The API allows well-formed Swing-based programs to be accessed by people who have physical impairments.

Users don't have to have physical impairments to take advantage of the accessibility options that you provide in your programs. These options can include something as simple as tool tip text, which a user can display by resting a mouse over a component. A limited-vision user can take advantage of a device such as a screen reader to speak the tool tip text for the component, or describe table cells that are checkboxes. Other accessibility options include keyboard accelerators and menu mnemonics, that allow a user to navigate the screen and activate actions with minimal hand movement.

Provided you configure your Swing components properly, everything related to the javax.accessibility package happens behind the scenes. Accessibility aids are connected to the Java Virtual Machine (JVM)* on a platform. When a user loads a program through the JVM with an attached aid, the Java Accessibility API provides the necessary information to the device in use. For instance, suppose you want to create an ImageIcon in you program, and make the ImageIcon accessible through an Accessibility API. You can do that by providing a description for the ImageIcon. There are two sets of constructors for an ImageIcon. One set does not have a description argument:
   // Without description argument
   public ImageIcon()
   public ImageIcon(Image image)
   public ImageIcon(String filename)
   public ImageIcon(URL location)
   public ImageIcon(byte imageData[])
The other set has a description argument:
   // With description argument
   public ImageIcon(Image image, String description)
   public ImageIcon(String filename, String description)
   public ImageIcon(URL location, String description)
   public ImageIcon(byte imageData[], String description)
By creating an ImageIcon with a description in your program, the program becomes more accessible because the assistive technology can use the description text to present information to a user who can't see the icon.

What follows are some questions to ask and steps to take when you create applications intended for use with assistive technologies. This is not meant to be an exhaustive list.
  • As a first task, try to use your program without a mouse. Do your menus support accelerators and mnemonics? Do your buttons have mnemonics associated with them? Do your text fields have labels associated with them via the setLabelFor() method, and each label have a mnemonic?

  • Make sure components have a short text string associated with them. If a component, such as an image-only button or a checkbox table cell, doesn't have an associated text string, call the setAccessibleName() method of the AccessibleContext. Remember to localize this name if your program is targeting an international audience.

  • Work with tool-tip text wherever possible. If it is not appropriate to have tool-tip text associated with a component, call setAccessibleDescription().

  • Are your custom components accessible? If you've subclassed JComponent, the class for the component does not implement the Accessible interface. But if you've subclassed JPanel, the class does implement the Accessible interface. Even though JPanel implements the Accessible interface, your component might not provide the necessary accessibility information. Consider having your custom AccessibleContext extend JComponent.AccessibleJComponent when appropriate.

  • Components that are inside non-accessible containers are not accessible. Be sure to use JPanel as your container to keep your screens accessible -- do not use the AWT Container class.
The United States government maintains a series of federal guidelines, known as Section 508 accessibility requirements, to ensure that systems developed for the U.S. government are usable by a wide user audience. Many other countries have similar guidelines. Sun maintains a site that covers IT-related accessibility issues. The site includes references to international resources, as well as those in the United States.

For tips on creating more accessible programs, the Java Tutorial offers a lesson called How to Support Assistive Technologies.

Moving beyond the simple rules of creating accessible programs, Sun also offers a tool for the Microsoft Windows platform called the Java Access Bridge. The Java Access Bridge includes tools that can attach to compatible Java VMs to see how accessible your programs are and to demonstrate assistive technologies.

To download the Java Access Bridge, go to the Java Access Bridge page. Click on the Download link. Read the License Agreement, and if you agree, click on Accept and Continue. Save the Java Access Bridge download, accessbridge-1_2.zip. Then unzip the .zip file, preserving the directory structure. This will create a directory named AccessBridge_1.2_GA under your installation directory.

Next, run the Install program, found in the installer subdirectory. The Installer checks your entire system for compatible JVMs. It prompts for which of the compatible JVMs you want the bridge installed. Pick the JVMs you are most likely to use and leave the rest alone. You might also be prompted to remove some of the older JVM versions on your machine. The directories that begin with c:\program files\java are the different versions of Java Plug-in for the browser.

Available Java virtual machines

After pressing Install (or Install All), you'll get a success window indicating that the installation is complete.

Installation Completed

Now you can run two sample programs that are packaged with Access Bridge: Java Monkey and Java Ferret. Once running, these programs attach themselves to any Java programs that run in the same JVM, and use a number of Java Access Bridge features. To use Java Monkey, first start any Java application. For instance, you can run the NumericVerifier program from the Validating Text and Filtering Documents tip. In a second window, run JavaMonkey. When you start Java Monkey, you should see a window that displays a component tree of all the Java applications running in all the JVMs in your system. If you don't immediately see the component tree, select the Refresh Tree option under the File menu.

Java Monkey

If you select the Accessibility API Panel option under the Panels menu, you get information about a specific component. Select a component in the component tree and you see information such as the component's name, description, and role.

Java Accessibility API view

Java Ferret is more of a tracking program, where you can enable the tracking of different events, such as mouse, focus, and menu selection. You run Java Ferret much like you run Java Monkey. Start any Java application such as the Numeric Verifier program in one window. Then start Java Ferret in a second window.

When you start Java Ferret, you should see a window with menus that include UpdateSettings, JavaEvents, and AccessibilityEvents. As an example of what you can do with JavaFerret, select TrackMouseEvents in the JavaEventsMenu. This registers all "mouse entered" events with the Java Access Bridge, and displays information about each mouse event.

Java Ferret

Try the Java Monkey and Java Ferret programs with some of your programs to see how accessible your programs are.

In addition to Monkey and Ferret, the Accessibility Developer Corner at netBeans.org offers a testing tool called a11y (the "11" in the name represents the 11 characters between the "a" and "y" of accessibility). You can find out more about this tool at the a11y project home page.

UNIX also provides assistive technologies called Gnopernicus and GOK. These are built into Solaris 10, which is available for free.

For more information about accessibility, see How to Support Assistive Technologies in the Java Tutorial, and State, Federal, and International Legal Resources for IT related Accessibility Issues. Also see the book "Accessibility for Everybody: Understanding the Section 508 Accessibility Requirements" by John Paul Mueller, ISBN 1-59059-086-4.

Back to Top

Rate and Review
Tell us what you think of the content of this page.
Excellent   Good   Fair   Poor  
Comments:
If you would like a reply to your comment, please submit your email address:
Note: We may not respond to all submitted comments.
Comments? Send your feedback on the Tech Tips: http://developers.sun.com/contact/feedback.jsp?category=sdn

Subscribe to the following newsletters for the latest information about technologies and products in other Java platforms:
  • Enterprise Java Technologies Tech Tips. Get tips on using enterprise Java technologies and APIs, such as those in the Java 2 Platform, Enterprise Edition (J2EE).
  • Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME).
You can subscribe to these and other Java technology developer newsletters or manage your current newsletter subscriptions on the Sun Developer Network Subscriptions page

IMPORTANT: Please read our Terms of Use, Privacy, and Licensing policies:
http://www.sun.com/share/text/termsofuse.html
http://www.sun.com/privacy/
http://developer.java.sun.com/berkeley_license.html

© 2005 Sun Microsystems, Inc. All Rights Reserved. For information on Sun's trademarks see: http://sun.com/suntrademarks
Java, J2EE, J2SE, J2ME, and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.

Sun Microsystems, Inc. 10 Network Circle, MPK10-209 Menlo Park, CA 94025 US

As used on the web site, the terms "Java Virtual Machine" and "JVM" mean a virtual machine for the Java platform.