1058 lines · 36.2 KB · Java
RawDownload
/* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing.text; import java.awt.event.ActionEvent;import java.io.*;import java.text.*;import java.text.AttributedCharacterIterator.Attribute;import java.util.*;import javax.swing.*; /** * <code>InternationalFormatter</code> extends <code>DefaultFormatter</code>, * using an instance of <code>java.text.Format</code> to handle the * conversion to a String, and the conversion from a String. * <p> * If <code>getAllowsInvalid()</code> is false, this will ask the * <code>Format</code> to format the current text on every edit. * <p> * You can specify a minimum and maximum value by way of the * <code>setMinimum</code> and <code>setMaximum</code> methods. In order * for this to work the values returned from <code>stringToValue</code> must be * comparable to the min/max values by way of the <code>Comparable</code> * interface. * <p> * Be careful how you configure the <code>Format</code> and the * <code>InternationalFormatter</code>, as it is possible to create a * situation where certain values can not be input. Consider the date * format 'M/d/yy', an <code>InternationalFormatter</code> that is always * valid (<code>setAllowsInvalid(false)</code>), is in overwrite mode * (<code>setOverwriteMode(true)</code>) and the date 7/1/99. In this * case the user will not be able to enter a two digit month or day of * month. To avoid this, the format should be 'MM/dd/yy'. * <p> * If <code>InternationalFormatter</code> is configured to only allow valid * values (<code>setAllowsInvalid(false)</code>), every valid edit will result * in the text of the <code>JFormattedTextField</code> being completely reset * from the <code>Format</code>. * The cursor position will also be adjusted as literal characters are * added/removed from the resulting String. * <p> * <code>InternationalFormatter</code>'s behavior of * <code>stringToValue</code> is  slightly different than that of * <code>DefaultTextFormatter</code>, it does the following: * <ol> *   <li><code>parseObject</code> is invoked on the <code>Format</code> *       specified by <code>setFormat</code> *   <li>If a Class has been set for the values (<code>setValueClass</code>), *       supers implementation is invoked to convert the value returned *       from <code>parseObject</code> to the appropriate class. *   <li>If a <code>ParseException</code> has not been thrown, and the value *       is outside the min/max a <code>ParseException</code> is thrown. *   <li>The value is returned. * </ol> * <code>InternationalFormatter</code> implements <code>stringToValue</code> * in this manner so that you can specify an alternate Class than * <code>Format</code> may return. * <p> * <strong>Warning:</strong> * 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 JavaBeans<sup><font size="-2">TM</font></sup> * has been added to the <code>java.beans</code> package. * Please see {@link java.beans.XMLEncoder}. * * @see java.text.Format * @see java.lang.Comparable * * @since 1.4 */public class InternationalFormatter extends DefaultFormatter {    /**     * Used by <code>getFields</code>.     */    private static final Format.Field[] EMPTY_FIELD_ARRAY =new Format.Field[0];     /**     * Object used to handle the conversion.     */    private Format format;    /**     * Can be used to impose a maximum value.     */    private Comparable max;    /**     * Can be used to impose a minimum value.     */    private Comparable min;     /**     * <code>InternationalFormatter</code>'s behavior is dicatated by a     * <code>AttributedCharacterIterator</code> that is obtained from     * the <code>Format</code>. On every edit, assuming     * allows invalid is false, the <code>Format</code> instance is invoked     * with <code>formatToCharacterIterator</code>. A <code>BitSet</code> is     * also kept upto date with the non-literal characters, that is     * for every index in the <code>AttributedCharacterIterator</code> an     * entry in the bit set is updated based on the return value from     * <code>isLiteral(Map)</code>. <code>isLiteral(int)</code> then uses     * this cached information.     * <p>     * If allowsInvalid is false, every edit results in resetting the complete     * text of the JTextComponent.     * <p>     * InternationalFormatterFilter can also provide two actions suitable for     * incrementing and decrementing. To enable this a subclass must     * override <code>getSupportsIncrement</code> to return true, and     * override <code>adjustValue</code> to handle the changing of the     * value. If you want to support changing the value outside of     * the valid FieldPositions, you will need to override     * <code>canIncrement</code>.     */    /**     * A bit is set for every index identified in the     * AttributedCharacterIterator that is not considered decoration.     * This should only be used if validMask is true.     */    private transient BitSet literalMask;    /**     * Used to iterate over characters.     */    private transient AttributedCharacterIterator iterator;    /**     * True if the Format was able to convert the value to a String and     * back.     */    private transient boolean validMask;    /**     * Current value being displayed.     */    private transient String string;    /**     * If true, DocumentFilter methods are unconditionally allowed,     * and no checking is done on their values. This is used when     * incrementing/decrementing via the actions.     */    private transient boolean ignoreDocumentMutate;      /**     * Creates an <code>InternationalFormatter</code> with no     * <code>Format</code> specified.     */    public InternationalFormatter() {        setOverwriteMode(false);    }     /**     * Creates an <code>InternationalFormatter</code> with the specified     * <code>Format</code> instance.     *     * @param format Format instance used for converting from/to Strings     */    public InternationalFormatter(Format format) {        this();        setFormat(format);    }     /**     * Sets the format that dictates the legal values that can be edited     * and displayed.     *     * @param format <code>Format</code> instance used for converting     * from/to Strings     */    public void setFormat(Format format) {        this.format = format;    }     /**     * Returns the format that dictates the legal values that can be edited     * and displayed.     *     * @return Format instance used for converting from/to Strings     */    public Format getFormat() {        return format;    }     /**     * Sets the minimum permissible value. If the <code>valueClass</code> has     * not been specified, and <code>minimum</code> is non null, the     * <code>valueClass</code> will be set to that of the class of     * <code>minimum</code>.     *     * @param minimum Minimum legal value that can be input     * @see #setValueClass     */    public void setMinimum(Comparable minimum) {        if (getValueClass() == null && minimum != null) {            setValueClass(minimum.getClass());        }        min = minimum;    }     /**     * Returns the minimum permissible value.     *     * @return Minimum legal value that can be input     */    public Comparable getMinimum() {        return min;    }     /**     * Sets the maximum permissible value. If the <code>valueClass</code> has     * not been specified, and <code>max</code> is non null, the     * <code>valueClass</code> will be set to that of the class of     * <code>max</code>.     *     * @param max Maximum legal value that can be input     * @see #setValueClass     */    public void setMaximum(Comparable max) {        if (getValueClass() == null && max != null) {            setValueClass(max.getClass());        }        this.max = max;    }     /**     * Returns the maximum permissible value.     *     * @return Maximum legal value that can be input     */    public Comparable getMaximum() {        return max;    }     /**     * Installs the <code>DefaultFormatter</code> onto a particular     * <code>JFormattedTextField</code>.     * This will invoke <code>valueToString</code> to convert the     * current value from the <code>JFormattedTextField</code> to     * a String. This will then install the <code>Action</code>s from     * <code>getActions</code>, the <code>DocumentFilter</code>     * returned from <code>getDocumentFilter</code> and the     * <code>NavigationFilter</code> returned from     * <code>getNavigationFilter</code> onto the     * <code>JFormattedTextField</code>.     * <p>     * Subclasses will typically only need to override this if they     * wish to install additional listeners on the     * <code>JFormattedTextField</code>.     * <p>     * If there is a <code>ParseException</code> in converting the     * current value to a String, this will set the text to an empty     * String, and mark the <code>JFormattedTextField</code> as being     * in an invalid state.     * <p>     * While this is a public method, this is typically only useful     * for subclassers of <code>JFormattedTextField</code>.     * <code>JFormattedTextField</code> will invoke this method at     * the appropriate times when the value changes, or its internal     * state changes.     *     * @param ftf JFormattedTextField to format for, may be null indicating     *            uninstall from current JFormattedTextField.     */    public void install(JFormattedTextField ftf) {        super.install(ftf);        updateMaskIfNecessary();        // invoked again as the mask should now be valid.        positionCursorAtInitialLocation();    }     /**     * Returns a String representation of the Object <code>value</code>.     * This invokes <code>format</code> on the current <code>Format</code>.     *     * @throws ParseException if there is an error in the conversion     * @param value Value to convert     * @return String representation of value     */    public String valueToString(Object value) throws ParseException {        if (value == null) {            return "";        }        Format f = getFormat();         if (f == null) {            return value.toString();        }        return f.format(value);    }     /**     * Returns the <code>Object</code> representation of the     * <code>String</code> <code>text</code>.     *     * @param text <code>String</code> to convert     * @return <code>Object</code> representation of text     * @throws ParseException if there is an error in the conversion     */    public Object stringToValue(String text) throws ParseException {        Object value = stringToValue(text, getFormat());         // Convert to the value class if the Value returned from the        // Format does not match.        if (value != null && getValueClass() != null &&                             !getValueClass().isInstance(value)) {            value = super.stringToValue(value.toString());        }        try {            if (!isValidValue(value, true)) {                throw new ParseException("Value not within min/max range", 0);            }        } catch (ClassCastException cce) {            throw new ParseException("Class cast exception comparing values: "                                     + cce, 0);        }        return value;    }     /**     * Returns the <code>Format.Field</code> constants associated with     * the text at <code>offset</code>. If <code>offset</code> is not     * a valid location into the current text, this will return an     * empty array.     *     * @param offset offset into text to be examined     * @return Format.Field constants associated with the text at the     *         given position.     */    public Format.Field[] getFields(int offset) {        if (getAllowsInvalid()) {            // This will work if the currently edited value is valid.            updateMask();        }         Map<Attribute, Object> attrs = getAttributes(offset);         if (attrs != null && attrs.size() > 0) {            ArrayList<Attribute> al = new ArrayList<Attribute>();             al.addAll(attrs.keySet());            return al.toArray(EMPTY_FIELD_ARRAY);        }        return EMPTY_FIELD_ARRAY;    }     /**     * Creates a copy of the DefaultFormatter.     *     * @return copy of the DefaultFormatter     */    public Object clone() throws CloneNotSupportedException {        InternationalFormatter formatter = (InternationalFormatter)super.                                           clone();         formatter.literalMask = null;        formatter.iterator = null;        formatter.validMask = false;        formatter.string = null;        return formatter;    }     /**     * If <code>getSupportsIncrement</code> returns true, this returns     * two Actions suitable for incrementing/decrementing the value.     */    protected Action[] getActions() {        if (getSupportsIncrement()) {            return new Action[] { new IncrementAction("increment", 1),                                  new IncrementAction("decrement", -1) };        }        return null;    }     /**     * Invokes <code>parseObject</code> on <code>f</code>, returning     * its value.     */    Object stringToValue(String text, Format f) throws ParseException {        if (f == null) {            return text;        }        return f.parseObject(text);    }     /**     * Returns true if <code>value</code> is between the min/max.     *     * @param wantsCCE If false, and a ClassCastException is thrown in     *                 comparing the values, the exception is consumed and     *                 false is returned.     */    boolean isValidValue(Object value, boolean wantsCCE) {        Comparable min = getMinimum();         try {            if (min != null && min.compareTo(value) > 0) {                return false;            }        } catch (ClassCastException cce) {            if (wantsCCE) {                throw cce;            }            return false;        }         Comparable max = getMaximum();        try {            if (max != null && max.compareTo(value) < 0) {                return false;            }        } catch (ClassCastException cce) {            if (wantsCCE) {                throw cce;            }            return false;        }        return true;    }     /**     * Returns a Set of the attribute identifiers at <code>index</code>.     */    Map<Attribute, Object> getAttributes(int index) {        if (isValidMask()) {            AttributedCharacterIterator iterator = getIterator();             if (index >= 0 && index <= iterator.getEndIndex()) {                iterator.setIndex(index);                return iterator.getAttributes();            }        }        return null;    }      /**     * Returns the start of the first run that contains the attribute     * <code>id</code>. This will return <code>-1</code> if the attribute     * can not be found.     */    int getAttributeStart(AttributedCharacterIterator.Attribute id) {        if (isValidMask()) {            AttributedCharacterIterator iterator = getIterator();             iterator.first();            while (iterator.current() != CharacterIterator.DONE) {                if (iterator.getAttribute(id) != null) {                    return iterator.getIndex();                }                iterator.next();            }        }        return -1;    }     /**     * Returns the <code>AttributedCharacterIterator</code> used to     * format the last value.     */    AttributedCharacterIterator getIterator() {        return iterator;    }     /**     * Updates the AttributedCharacterIterator and bitset, if necessary.     */    void updateMaskIfNecessary() {        if (!getAllowsInvalid() && (getFormat() != null)) {            if (!isValidMask()) {                updateMask();            }            else {                String newString = getFormattedTextField().getText();                 if (!newString.equals(string)) {                    updateMask();                }            }        }    }

Lines 1–500 of 1058.