760 lines · 26.4 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.io.Serializable;import java.lang.reflect.*;import java.text.ParseException;import javax.swing.*;import javax.swing.text.*; /** * <code>DefaultFormatter</code> formats aribtrary objects. Formatting is done * by invoking the <code>toString</code> method. In order to convert the * value back to a String, your class must provide a constructor that * takes a String argument. If no single argument constructor that takes a * String is found, the returned value will be the String passed into * <code>stringToValue</code>. * <p> * Instances of <code>DefaultFormatter</code> can not be used in multiple * instances of <code>JFormattedTextField</code>. To obtain a copy of * an already configured <code>DefaultFormatter</code>, use the * <code>clone</code> method. * <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 javax.swing.JFormattedTextField.AbstractFormatter * * @since 1.4 */public class DefaultFormatter extends JFormattedTextField.AbstractFormatter                    implements Cloneable, Serializable {    /** Indicates if the value being edited must match the mask. */    private boolean allowsInvalid;     /** If true, editing mode is in overwrite (or strikethough). */    private boolean overwriteMode;     /** If true, any time a valid edit happens commitEdit is invoked. */    private boolean commitOnEdit;     /** Class used to create new instances. */    private Class<?> valueClass;     /** NavigationFilter that forwards calls back to DefaultFormatter. */    private NavigationFilter navigationFilter;     /** DocumentFilter that forwards calls back to DefaultFormatter. */    private DocumentFilter documentFilter;     /** Used during replace to track the region to replace. */    transient ReplaceHolder replaceHolder;      /**     * Creates a DefaultFormatter.     */    public DefaultFormatter() {        overwriteMode = true;        allowsInvalid = true;    }     /**     * 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);        positionCursorAtInitialLocation();    }     /**     * Sets when edits are published back to the     * <code>JFormattedTextField</code>. If true, <code>commitEdit</code>     * is invoked after every valid edit (any time the text is edited). On     * the other hand, if this is false than the <code>DefaultFormatter</code>     * does not publish edits back to the <code>JFormattedTextField</code>.     * As such, the only time the value of the <code>JFormattedTextField</code>     * will change is when <code>commitEdit</code> is invoked on     * <code>JFormattedTextField</code>, typically when enter is pressed     * or focus leaves the <code>JFormattedTextField</code>.     *     * @param commit Used to indicate when edits are commited back to the     *               JTextComponent     */    public void setCommitsOnValidEdit(boolean commit) {        commitOnEdit = commit;    }     /**     * Returns when edits are published back to the     * <code>JFormattedTextField</code>.     *     * @return true if edits are commited after evey valid edit     */    public boolean getCommitsOnValidEdit() {        return commitOnEdit;    }     /**     * Configures the behavior when inserting characters. If     * <code>overwriteMode</code> is true (the default), new characters     * overwrite existing characters in the model.     *     * @param overwriteMode Indicates if overwrite or overstrike mode is used     */    public void setOverwriteMode(boolean overwriteMode) {        this.overwriteMode = overwriteMode;    }     /**     * Returns the behavior when inserting characters.     *     * @return true if newly inserted characters overwrite existing characters     */    public boolean getOverwriteMode() {        return overwriteMode;    }     /**     * Sets whether or not the value being edited is allowed to be invalid     * for a length of time (that is, <code>stringToValue</code> throws     * a <code>ParseException</code>).     * It is often convenient to allow the user to temporarily input an     * invalid value.     *     * @param allowsInvalid Used to indicate if the edited value must always     *        be valid     */    public void setAllowsInvalid(boolean allowsInvalid) {        this.allowsInvalid = allowsInvalid;    }     /**     * Returns whether or not the value being edited is allowed to be invalid     * for a length of time.     *     * @return false if the edited value must always be valid     */    public boolean getAllowsInvalid() {        return allowsInvalid;    }     /**     * Sets that class that is used to create new Objects. If the     * passed in class does not have a single argument constructor that     * takes a String, String values will be used.     *     * @param valueClass Class used to construct return value from     *        stringToValue     */    public void setValueClass(Class<?> valueClass) {        this.valueClass = valueClass;    }     /**     * Returns that class that is used to create new Objects.     *     * @return Class used to constuct return value from stringToValue     */    public Class<?> getValueClass() {        return valueClass;    }     /**     * Converts the passed in String into an instance of     * <code>getValueClass</code> by way of the constructor that     * takes a String argument. If <code>getValueClass</code>     * returns null, the Class of the current value in the     * <code>JFormattedTextField</code> will be used. If this is null, a     * String will be returned. If the constructor thows an exception, a     * <code>ParseException</code> will be thrown. If there is no single     * argument String constructor, <code>string</code> will be returned.     *     * @throws ParseException if there is an error in the conversion     * @param string String to convert     * @return Object representation of text     */    public Object stringToValue(String string) throws ParseException {        Class<?> vc = getValueClass();        JFormattedTextField ftf = getFormattedTextField();         if (vc == null && ftf != null) {            Object value = ftf.getValue();             if (value != null) {                vc = value.getClass();            }        }        if (vc != null) {            Constructor cons;             try {                cons = vc.getConstructor(new Class[] { String.class });             } catch (NoSuchMethodException nsme) {                cons = null;            }             if (cons != null) {                try {                    return cons.newInstance(new Object[] { string });                } catch (Throwable ex) {                    throw new ParseException("Error creating instance", 0);                }            }        }        return string;    }     /**     * Converts the passed in Object into a String by way of the     * <code>toString</code> method.     *     * @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 "";        }        return value.toString();    }     /**     * Returns the <code>DocumentFilter</code> used to restrict the characters     * that can be input into the <code>JFormattedTextField</code>.     *     * @return DocumentFilter to restrict edits     */    protected DocumentFilter getDocumentFilter() {        if (documentFilter == null) {            documentFilter = new DefaultDocumentFilter();        }        return documentFilter;    }     /**     * Returns the <code>NavigationFilter</code> used to restrict where the     * cursor can be placed.     *     * @return NavigationFilter to restrict navigation     */    protected NavigationFilter getNavigationFilter() {        if (navigationFilter == null) {            navigationFilter = new DefaultNavigationFilter();        }        return navigationFilter;    }     /**     * Creates a copy of the DefaultFormatter.     *     * @return copy of the DefaultFormatter     */    public Object clone() throws CloneNotSupportedException {        DefaultFormatter formatter = (DefaultFormatter)super.clone();         formatter.navigationFilter = null;        formatter.documentFilter = null;        formatter.replaceHolder = null;        return formatter;    }      /**     * Positions the cursor at the initial location.     */    void positionCursorAtInitialLocation() {        JFormattedTextField ftf = getFormattedTextField();        if (ftf != null) {            ftf.setCaretPosition(getInitialVisualPosition());        }    }     /**     * Returns the initial location to position the cursor at. This forwards     * the call to <code>getNextNavigatableChar</code>.     */    int getInitialVisualPosition() {        return getNextNavigatableChar(0, 1);    }     /**     * Subclasses should override this if they want cursor navigation     * to skip certain characters. A return value of false indicates     * the character at <code>offset</code> should be skipped when     * navigating throught the field.     */    boolean isNavigatable(int offset) {        return true;    }     /**     * Returns true if the text in <code>text</code> can be inserted.  This     * does not mean the text will ultimately be inserted, it is used if     * text can trivially reject certain characters.     */    boolean isLegalInsertText(String text) {        return true;    }     /**     * Returns the next editable character starting at offset incrementing     * the offset by <code>direction</code>.     */    private int getNextNavigatableChar(int offset, int direction) {        int max = getFormattedTextField().getDocument().getLength();         while (offset >= 0 && offset < max) {            if (isNavigatable(offset)) {                return offset;            }            offset += direction;        }        return offset;    }     /**     * A convenience methods to return the result of deleting     * <code>deleteLength</code> characters at <code>offset</code>     * and inserting <code>replaceString</code> at <code>offset</code>     * in the current text field.     */    String getReplaceString(int offset, int deleteLength,                            String replaceString) {        String string = getFormattedTextField().getText();        String result;         result = string.substring(0, offset);        if (replaceString != null) {            result += replaceString;        }        if (offset + deleteLength < string.length()) {            result += string.substring(offset + deleteLength);        }        return result;    }     /*     * Returns true if the operation described by <code>rh</code> will     * result in a legal edit.  This may set the <code>value</code>     * field of <code>rh</code>.     */    boolean isValidEdit(ReplaceHolder rh) {        if (!getAllowsInvalid()) {            String newString = getReplaceString(rh.offset, rh.length, rh.text);             try {                rh.value = stringToValue(newString);                 return true;            } catch (ParseException pe) {                return false;            }        }        return true;    }     /**     * Invokes <code>commitEdit</code> on the JFormattedTextField.     */    void commitEdit() throws ParseException {        JFormattedTextField ftf = getFormattedTextField();         if (ftf != null) {            ftf.commitEdit();        }    }     /**     * Pushes the value to the JFormattedTextField if the current value     * is valid and invokes <code>setEditValid</code> based on the     * validity of the value.     */    void updateValue() {        updateValue(null);    }     /**     * Pushes the <code>value</code> to the editor if we are to     * commit on edits. If <code>value</code> is null, the current value     * will be obtained from the text component.     */    void updateValue(Object value) {        try {            if (value == null) {                String string = getFormattedTextField().getText();                 value = stringToValue(string);            }             if (getCommitsOnValidEdit()) {                commitEdit();            }            setEditValid(true);        } catch (ParseException pe) {            setEditValid(false);        }    }     /**     * Returns the next cursor position from offset by incrementing     * <code>direction</code>. This uses     * <code>getNextNavigatableChar</code>     * as well as constraining the location to the max position.     */    int getNextCursorPosition(int offset, int direction) {        int newOffset = getNextNavigatableChar(offset, direction);        int max = getFormattedTextField().getDocument().getLength();         if (!getAllowsInvalid()) {            if (direction == -1 && offset == newOffset) {                // Case where hit backspace and only characters before                // offset are fixed.                newOffset = getNextNavigatableChar(newOffset, 1);                if (newOffset >= max) {                    newOffset = offset;                }            }            else if (direction == 1 && newOffset >= max) {                // Don't go beyond last editable character.                newOffset = getNextNavigatableChar(max - 1, -1);                if (newOffset < max) {                    newOffset++;                }            }        }        return newOffset;    }     /**     * Resets the cursor by using getNextCursorPosition.     */    void repositionCursor(int offset, int direction) {        getFormattedTextField().getCaret().setDot(getNextCursorPosition                                                  (offset, direction));    }      /**     * Finds the next navigatable character.     */    int getNextVisualPositionFrom(JTextComponent text, int pos,                                  Position.Bias bias, int direction,

Lines 1–500 of 760.