FileDocCategorySizeDatePackage
NumberFormatter.javaAPI DocJava SE 5 API17154Fri Aug 26 14:58:16 BST 2005javax.swing.text

NumberFormatter

public class NumberFormatter extends InternationalFormatter
NumberFormatter subclasses InternationalFormatter adding special behavior for numbers. Among the specializations are (these are only used if the NumberFormatter does not display invalid nubers, eg setAllowsInvalid(false)):
  • Pressing +/- (- is determined from the DecimalFormatSymbols associated with the DecimalFormat) in any field but the exponent field will attempt to change the sign of the number to positive/negative.
  • Pressing +/- (- is determined from the DecimalFormatSymbols associated with the DecimalFormat) in the exponent field will attemp to change the sign of the exponent to positive/negative.

If you are displaying scientific numbers, you may wish to turn on overwrite mode, setOverwriteMode(true). For example:

DecimalFormat decimalFormat = new DecimalFormat("0.000E0");
NumberFormatter textFormatter = new NumberFormatter(decimalFormat);
textFormatter.setOverwriteMode(true);
textFormatter.setAllowsInvalid(false);

If you are going to allow the user to enter decimal values, you should either force the DecimalFormat to contain at least one decimal (#.0###), or allow the value to be invalid setAllowsInvalid(true). Otherwise users may not be able to input decimal values.

NumberFormatter provides slightly different behavior to stringToValue than that of its superclass. If you have specified a Class for values, {@link #setValueClass}, that is one of of Integer, Long, Float, Double, Byte or Short and the Format's parseObject returns an instance of Number, the corresponding instance of the value class will be created using the constructor appropriate for the primitive type the value class represents. For example: setValueClass(Integer.class) will cause the resulting value to be created via new Integer(((Number)formatter.parseObject(string)).intValue()). This is typically useful if you wish to set a min/max value as the various Number implementations are generally not comparable to each other. This is also useful if for some reason you need a specific Number implementation for your values.

Warning: Serialized objects of this class will not be compatible with future Swing releases. The current serialization support is appropriate for short term storage or RMI between applications running the same version of Swing. As of 1.4, support for long term storage of all JavaBeansTM has been added to the java.beans package. Please see {@link java.beans.XMLEncoder}.

version
1.4 03/05/01
since
1.4

Fields Summary
private String
specialChars
The special characters from the Format instance.
Constructors Summary
public NumberFormatter()
Creates a NumberFormatter with the a default NumberFormat instance obtained from NumberFormat.getNumberInstance().

        this(NumberFormat.getNumberInstance());
    
public NumberFormatter(NumberFormat format)
Creates a NumberFormatter with the specified Format instance.

param
format Format used to dictate legal values

        super(format);
        setFormat(format);
        setAllowsInvalid(true);
        setCommitsOnValidEdit(false);
        setOverwriteMode(false);
    
Methods Summary
private java.lang.ObjectconvertValueToValueClass(java.lang.Object value, java.lang.Class valueClass)
Converts the passed in value to the passed in class. This only works if valueClass is one of Integer, Long, Float, Double, Byte or Short and value is an instanceof Number.

        if (valueClass != null && (value instanceof Number)) {
            if (valueClass == Integer.class) {
                return new Integer(((Number)value).intValue());
            }
            else if (valueClass == Long.class) {
                return new Long(((Number)value).longValue());
            }
            else if (valueClass == Float.class) {
                return new Float(((Number)value).floatValue());
            }
            else if (valueClass == Double.class) {
                return new Double(((Number)value).doubleValue());
            }
            else if (valueClass == Byte.class) {
                return new Byte(((Number)value).byteValue());
            }
            else if (valueClass == Short.class) {
                return new Short(((Number)value).shortValue());
            }
        }
        return value;
    
private java.text.DecimalFormatSymbolsgetDecimalFormatSymbols()
Returns the DecimalFormatSymbols from the Format instance.

        Format f = getFormat();

        if (f instanceof DecimalFormat) {
            return ((DecimalFormat)f).getDecimalFormatSymbols();
        }
        return null;
    
private chargetDecimalSeparator()
Returns the character that is used to toggle to negative values.

        DecimalFormatSymbols dfs = getDecimalFormatSymbols();

        if (dfs != null) {
            return dfs.getDecimalSeparator();
        }
        return '.";
    
private java.text.NumberFormat$FieldgetFieldFrom(int index, int direction)
Returns the first NumberFormat.Field starting index incrementing by direction.

        if (isValidMask()) {
            int max = getFormattedTextField().getDocument().getLength();
            AttributedCharacterIterator iterator = getIterator();

            if (index >= max) {
                index += direction;
            }
            while (index >= 0 && index < max) {
                iterator.setIndex(index);

                Map attrs = iterator.getAttributes();

                if (attrs != null && attrs.size() > 0) {
                    Iterator keys = attrs.keySet().iterator();

                    while (keys.hasNext()) {
                        Object key = keys.next();

                        if (key instanceof NumberFormat.Field) {
                            return (NumberFormat.Field)key;
                        }
                    }
                }
                index += direction;
            }
        }
        return null;
    
private chargetMinusSign()
Returns the character that is used to toggle to negative values.

        DecimalFormatSymbols dfs = getDecimalFormatSymbols();

        if (dfs != null) {
            return dfs.getMinusSign();
        }
        return '-";
    
private chargetPositiveSign()
Returns the character that is used to toggle to positive values.

        return '+";
    
booleanisLegalInsertText(java.lang.String text)
Subclassed to return false if text contains in an invalid character to insert, that is, it is not a digit (Character.isDigit()) and not one of the characters defined by the DecimalFormatSymbols.

        if (getAllowsInvalid()) {
            return true;
        }
        for (int counter = text.length() - 1; counter >= 0; counter--) {
            char aChar = text.charAt(counter);

            if (!Character.isDigit(aChar) &&
                           specialChars.indexOf(aChar) == -1){
                return false;
            }
        }
        return true;
    
booleanisLiteral(java.util.Map attrs)
Subclassed to treat the decimal separator, grouping separator, exponent symbol, percent, permille, currency and sign as literals.

        if (!super.isLiteral(attrs)) {
            if (attrs == null) {
                return false;
            }
            int size = attrs.size();

            if (attrs.get(NumberFormat.Field.GROUPING_SEPARATOR) != null) {
                size--;
                if (attrs.get(NumberFormat.Field.INTEGER) != null) {
                    size--;
                }
            }
            if (attrs.get(NumberFormat.Field.EXPONENT_SYMBOL) != null) {
                size--;
            }
            if (attrs.get(NumberFormat.Field.PERCENT) != null) {
                size--;
            }
            if (attrs.get(NumberFormat.Field.PERMILLE) != null) {
                size--;
            }
            if (attrs.get(NumberFormat.Field.CURRENCY) != null) {
                size--;
            }
            if (attrs.get(NumberFormat.Field.SIGN) != null) {
                size--;
            }
            if (size == 0) {
                return true;
            }
            return false;
        }
        return true;
    
booleanisNavigatable(int index)
Subclassed to make the decimal separator navigatable, as well as making the character between the integer field and the next field navigatable.

        if (!super.isNavigatable(index)) {
            // Don't skip the decimal, it causes wierd behavior
            if (getBufferedChar(index) == getDecimalSeparator()) {
                return true;
            }
            return false;
        }
        return true;
    
private booleanisOnlyIntegerField(int offset, int length)
Returns true if the range offset to length identifies the only integer field.

        if (isValidMask()) {
            int start = getAttributeStart(NumberFormat.Field.INTEGER);

            if (start != -1) {
                AttributedCharacterIterator iterator = getIterator();

                iterator.setIndex(start);
                if (offset > start || iterator.getRunLimit(
                    NumberFormat.Field.INTEGER) > (offset + length)) {
                    return false;
                }
                return true;
            }
        }
        return false;
    
private booleanisValidInsertionCharacter(char aChar)

        return (Character.isDigit(aChar) || specialChars.indexOf(aChar) != -1);
    
voidreplace(javax.swing.text.DocumentFilter$FilterBypass fb, int offset, int length, java.lang.String string, javax.swing.text.AttributeSet attr)
Overriden to toggle the value if the positive/minus sign is inserted.

        if (!getAllowsInvalid() && length == 0 && string != null &&
            string.length() == 1 &&
            toggleSignIfNecessary(fb, offset, string.charAt(0))) {
            return;
        }
        super.replace(fb, offset, length, string, attr);
    
public voidsetFormat(java.text.Format format)
Sets the format that dictates the legal values that can be edited and displayed.

If you have used the nullary constructor the value of this property will be determined for the current locale by way of the NumberFormat.getNumberInstance() method.

param
format NumberFormat instance used to dictate legal values

        super.setFormat(format);

        DecimalFormatSymbols dfs = getDecimalFormatSymbols();

        if (dfs != null) {
            StringBuffer sb = new StringBuffer();

            sb.append(dfs.getCurrencySymbol());
            sb.append(dfs.getDecimalSeparator());
            sb.append(dfs.getGroupingSeparator());
            sb.append(dfs.getInfinity());
            sb.append(dfs.getInternationalCurrencySymbol());
            sb.append(dfs.getMinusSign());
            sb.append(dfs.getMonetaryDecimalSeparator());
            sb.append(dfs.getNaN());
            sb.append(dfs.getPercent());
            sb.append('+");
            specialChars = sb.toString();
        }
        else {
            specialChars = "";
        }
    
java.lang.ObjectstringToValue(java.lang.String text, java.text.Format f)
Invokes parseObject on f, returning its value.

        if (f == null) {
            return text;
        }
        Object value = f.parseObject(text);

        return convertValueToValueClass(value, getValueClass());
    
private java.lang.ObjecttoggleExponentSign(int offset, char aChar)
Invoked to toggle the sign of the exponent (for scientific numbers).

        String string = getFormattedTextField().getText();
        int replaceLength = 0;
        int loc = getAttributeStart(NumberFormat.Field.EXPONENT_SIGN);

        if (loc >= 0) {
            replaceLength = 1;
            offset = loc;
        }
        if (aChar == getPositiveSign()) {
            string = getReplaceString(offset, replaceLength, null);
        }
        else {
            string = getReplaceString(offset, replaceLength,
                                      new String(new char[] { aChar }));
        }
        return stringToValue(string);
    
private java.lang.ObjecttoggleSign(boolean positive)
Invoked to toggle the sign. For this to work the value class must have a single arg constructor that takes a String.

        Object value = stringToValue(getFormattedTextField().getText());

        if (value != null) {
            // toString isn't localized, so that using +/- should work
            // correctly.
            String string = value.toString();

            if (string != null && string.length() > 0) {
                if (positive) {
                    if (string.charAt(0) == '-") {
                        string = string.substring(1);
                    }
                }
                else { 
                    if (string.charAt(0) == '+") {
                        string = string.substring(1);
                    }
                    if (string.length() > 0 && string.charAt(0) != '-") {
                        string = "-" + string;
                    }
                }
                if (string != null) {
                    Class valueClass = getValueClass();

                    if (valueClass == null) {
                        valueClass = value.getClass();
                    }
                    try {
                        Constructor cons = valueClass.getConstructor(
                                              new Class[] { String.class });

                        if (cons != null) {
                            return cons.newInstance(new Object[]{string});
                        }
                    } catch (Throwable ex) { }
                }
            }
        }
        return null;
    
private booleantoggleSignIfNecessary(javax.swing.text.DocumentFilter$FilterBypass fb, int offset, char aChar)
Will change the sign of the integer or exponent field if aChar is the positive or minus sign. Returns true if a sign change was attempted.

        if (aChar == getMinusSign() || aChar == getPositiveSign()) {
            NumberFormat.Field field = getFieldFrom(offset, -1);
            Object newValue;

            try {
                if (field == null ||
                    (field != NumberFormat.Field.EXPONENT &&
                     field != NumberFormat.Field.EXPONENT_SYMBOL &&
                     field != NumberFormat.Field.EXPONENT_SIGN)) {
                    newValue = toggleSign((aChar == getPositiveSign()));
                }
                else {
                    // exponent
                    newValue = toggleExponentSign(offset, aChar);
                }
                if (newValue != null && isValidValue(newValue, false)) {
                    int lc = getLiteralCountTo(offset);
                    String string = valueToString(newValue);

                    fb.remove(0, fb.getDocument().getLength());
                    fb.insertString(0, string, null);
                    updateValue(newValue);
                    repositionCursor(getLiteralCountTo(offset) -
                                     lc + offset, 1);
                    return true;
                }
            } catch (ParseException pe) {
                invalidEdit();
            }
        }
        return false;