20.3 The DefaultFormatter Class


DefaultFormatter is a concrete implementation of AbstractFormatter that provides enough functionality for many purposes. It can be used for classes with a constructor that takes a single String. To support other classes, you may have to override DefaultFormatter's stringToValue( ) method and its valueToString( ) method if the class's toString( ) method is not adequate.

DefaultFormatter maintains the field's editValid property, checking to see if the current edit is valid after every user keystroke and calling setEditValid( ) as appropriate.

20.3.1 Properties

Table 20-3 shows the properties defined by DefaultFormatter.

Table 20-3. DefaultFormatter properties

Property

Data type

get

is

set

Default value

allowsInvalid

boolean

·

 

·

true

commitsOnValidEdit

boolean

·

 

·

false

overwriteMode

boolean

·

 

·

true

valueClass

Class

·

 

·

null

The allowsInvalid property controls whether the field's content may be temporarily invalid during an edit. Consider an integer field with a current value of 25 that the user wants to change to 19. The user may decide to do this by pressing the Backspace key twice, then the 1 key and the 9 key. After the first backspace, the content of the field is 2. After the second backspace, the content of the field is the empty string, but if allowsInvalid were false, this would not be allowed (since the empty string is not a valid integer), and the field would refuse to allow the 2 to be deleted. Setting this property to false can be effective in certain cases but should be done only after careful consideration.

commitsOnValidEdit controls how often a field's value is set during an edit. If this property is true, then the field attempts to commit its content after every keystroke. The default value is false, which means the field does not commit until something special happens, such as when the field loses focus or the user presses the Enter key. Consider again the situation from the previous paragraph. After the first backspace, the field shows 2. If commitsOnValidEdit is false, nothing is committed, and the field's value remains 25. If the user later cancels the edit (using the Escape key, for example), the content of the field reverts to 25. If commitsOnValidEdit is true, the 2 is committed immediately, and the field's value becomes 2.

When the overwriteMode property is true, characters entered by the user (and even pastes from the clipboard) replace characters in the field. When it is false (insert mode), new characters are inserted without deleting any of the existing characters. The default value is true, which is often not what you want. Subclasses of DefaultFormatter often call setOverwriteMode(false) in their constructors.

The valueClass property determines the type of object that the stringToValue( ) method attempts to return. It is usually fine for this property to remain null, in which case stringToValue( ) attempts to return an object of the same type[6] as getFormattedTextField.getValue( ). (If you override the stringToValue( ) method, the valueClass property is ignored unless you handle it manually or invoke super.stringToValue( ).)

[6] Numerics (Float, Double, Integer, Long, Short, and Byte) are an exception. This might be a good reason to call setValueClass( ). See Section 20.2, earlier in this chapter.

20.3.2 Constructor

public DefaultFormatter( )

Create a new DefaultFormatter with default property values.

20.3.3 Public Methods

public abstract Object stringToValue(String text) throws java.text.ParseException

This method uses reflection to attempt to instantiate an object using a constructor that takes a single String argument. The object instantiated is of the type specified by the formatter's valueClass property or, if null, of the same type as the field's current value. If that class doesn't have a public String constructor, or if the constructor throws an exception, this method throws a ParseException. Subclasses frequently override this behavior.

public abstract String valueToString(Object value) throws java.text.ParseException

This method simply returns value.toString( ). Subclasses may override this behavior.

public void install(JFormattedTextField ftf)

This method is overridden to move the caret to the beginning of the field. Except for that, it delegates to the superclass.

20.3.4 Example

Here, we extend DefaultFormatter to create a formatter that can edit combinations such as those used by combination locks. The String representation of a combination is something like 15-45-22 or 35-30-11-19. The Object representation is an int[] array.[7]

[7] We could have chosen any object type, including java.util.Vector or a custom Combination class, instead of int[].

We override stringToValue( ) and valueToString( ) to convert between these representations, and we override getActions( ) so that the number under the caret can be incremented or decremented from the keyboard. We also provide a sample main( ) method that shows how CombinationFormatter could be used. (See Figure 20-3.)

// CombinationFormatter.java // import javax.swing.*; import javax.swing.text.*; public class CombinationFormatter extends DefaultFormatter {   public CombinationFormatter( ) {     setOverwriteMode(false);   }   public Object stringToValue(String string) throws java.text.ParseException {     // Input: string of form "15-45-22" (any number of hyphen-delimited numbers)     // Output: int array     String s[] = string.split("-");     int a[] = new int[s.length];     for (int j=0; j<a.length; j+=1)       try {         a[j] = Integer.parseInt(s[j]);       } catch (NumberFormatException nfe) {         throw new java.text.ParseException(s[j] + " is not an int", 0);       }     return a;   }   public String valueToString(Object value) throws java.text.ParseException {     // Input: int array     // Output: string of numerals separated by hyphens     if (value == null) return null;     if (! (value instanceof int[]))       throw new java.text.ParseException("expected int[]", 0);     int a[] = (int[])value;     StringBuffer sb = new StringBuffer( );     for (int j=0; j < a.length; j+=1) {       if (j > 0) sb.append('-');       sb.append(a[j]);     }     return sb.toString( );   }   protected Action[] getActions( ) {     Action[] actions = { new CombinationIncrementer("increment", 1),                          new CombinationIncrementer("decrement", -1) };     return actions;   }   // Begin inner class ----------------------------------------   public class CombinationIncrementer extends AbstractAction {     protected int delta;     public CombinationIncrementer(String name, int delta) { // Constructor       super(name); // 'name' must match something in the component's InputMap or else                    // this Action is not invoked automatically. Valid names include                    // "reset-field-edit", "increment", "decrement", and "unselect"                    // (see Appendix B).       this.delta = delta;     }     public void actionPerformed(java.awt.event.ActionEvent ae) {       JFormattedTextField ftf = getFormattedTextField( ); // From AbstractFormatter       if (ftf == null) return;       String text = ftf.getText( );       if (text == null) return;       int pos = ftf.getCaretPosition( );       int hyphenCount = 0;       for (int j=0; j < pos; j+=1) // How many hyphens precede the caret?         if (text.charAt(j) == '-') hyphenCount += 1;       try {         int a[] = (int[])stringToValue(text);         a[hyphenCount] += delta; // Change the number at caret position.         if (a[hyphenCount] < 0) a[hyphenCount] = 0;         String newText = valueToString(a);         ftf.setText(newText); // Does not retain caret position         if ((text.charAt(pos) == '-') && (newText.length( ) < text.length( )) )           pos -= 1; // Don't let caret move past - when 10 changes to 9.         ftf.setCaretPosition(pos);       } catch (Exception e) { return; }     }   }   // End inner class  ----------------------------------------   public static void main(String argv[]) {     // A demo main( ) method to show how CombinationFormatter could be used     int comb1[] = { 35, 11, 19 };     int comb2[] = { 10, 20, 30 };     final JFormattedTextField field1 =       new JFormattedTextField(new CombinationFormatter( ));     field1.setValue(comb1);     final JFormattedTextField field2 =       new JFormattedTextField(new CombinationFormatter( ));     field2.setValue(comb2);     JPanel pan = new JPanel( );     pan.add(new JLabel("Change the combination from"));     pan.add(field1);     pan.add(new JLabel("to"));     pan.add(field2);     JButton b = new JButton("Submit");     b.addActionListener(new java.awt.event.ActionListener( ) {       public void actionPerformed(java.awt.event.ActionEvent ae) {         try {           field1.commitEdit( ); // Make sure current edit (if any) is committed.           field2.commitEdit( );         } catch (java.text.ParseException pe) { }         int oldc[] = (int[])field1.getValue( );         int newc[] = (int[])field2.getValue( );         //         // Code to validate oldc[] and change to newc[] goes here.         //       }     });     pan.add(b);     JFrame f = new JFrame("CombinationFormatter Demo");     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     f.setContentPane(pan);     f.setSize(360, 100);     f.setVisible(true);   }     }
Figure 20-3. JFormattedTextFields using CombinationFormatters
figs/swng2.2003.gif

Some combination locks require each combination to have exactly three numbers and require each number to be no higher than 59. CombinationFormatter doesn't enforce either of these restrictions, but it wouldn't be hard to incorporate them.



Java Swing
Graphic Java 2: Mastering the Jfc, By Geary, 3Rd Edition, Volume 2: Swing
ISBN: 0130796670
EAN: 2147483647
Year: 2001
Pages: 289
Authors: David Geary

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net