| /* DefaultFormatter.java -- | |
| Copyright (C) 2005 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath 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 for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package javax.swing.text; | |
| import java.io.Serializable; | |
| import java.lang.reflect.Constructor; | |
| import java.text.ParseException; | |
| import javax.swing.JFormattedTextField; | |
| /** | |
| * The <code>DefaultFormatter</code> is a concrete formatter for use in | |
| * {@link JFormattedTextField}s. | |
| * | |
| * It can format arbitrary values by invoking | |
| * their {@link Object#toString} method. | |
| * | |
| * In order to convert a String back to | |
| * a value, the value class must provide a single argument constructor that | |
| * takes a String object as argument value. If no such constructor is found, | |
| * the String itself is passed back by #stringToValue. | |
| * | |
| * @author Roman Kennke (roman@kennke.org) | |
| */ | |
| public class DefaultFormatter extends JFormattedTextField.AbstractFormatter | |
| implements Cloneable, Serializable | |
| { | |
| /** | |
| * A {@link DocumentFilter} that intercepts modification of the | |
| * JFormattedTextField's Document and commits the value depending | |
| * on the value of the <code>commitsOnValidEdit</code> property. | |
| * | |
| */ | |
| // FIXME: Handle allowsInvalid and overwriteMode properties | |
| private class FormatterDocumentFilter | |
| extends DocumentFilter | |
| { | |
| /** | |
| * Invoked when text is removed from a text component. | |
| * | |
| * @param bypass the FilterBypass to use to mutate the document | |
| * @param offset the start position of the modification | |
| * @param length the length of the removed text | |
| * | |
| * @throws BadLocationException if offset or lenght are invalid in | |
| * the Document | |
| */ | |
| public void remove(DocumentFilter.FilterBypass bypass, int offset, | |
| int length) | |
| throws BadLocationException | |
| { | |
| super.remove(bypass, offset, length); | |
| checkValidInput(); | |
| commitIfAllowed(); | |
| } | |
| /** | |
| * Invoked when text is inserted into a text component. | |
| * | |
| * @param bypass the FilterBypass to use to mutate the document | |
| * @param offset the start position of the modification | |
| * @param text the inserted text | |
| * @param attributes the attributes of the inserted text | |
| * | |
| * @throws BadLocationException if offset or lenght are invalid in | |
| * the Document | |
| */ | |
| public void insertString(DocumentFilter.FilterBypass bypass, int offset, | |
| String text, AttributeSet attributes) | |
| throws BadLocationException | |
| { | |
| if (overwriteMode == true) | |
| replace(bypass, offset, text.length(), text, attributes); | |
| else | |
| super.insertString(bypass, offset, text, attributes); | |
| checkValidInput(); | |
| commitIfAllowed(); | |
| } | |
| /** | |
| * Invoked when text is replaced in a text component. | |
| * | |
| * @param bypass the FilterBypass to use to mutate the document | |
| * @param offset the start position of the modification | |
| * @param length the length of the removed text | |
| * @param text the inserted text | |
| * @param attributes the attributes of the inserted text | |
| * | |
| * @throws BadLocationException if offset or lenght are invalid in | |
| * the Document | |
| */ | |
| public void replace(DocumentFilter.FilterBypass bypass, int offset, | |
| int length, String text, AttributeSet attributes) | |
| throws BadLocationException | |
| { | |
| super.replace(bypass, offset, length, text, attributes); | |
| checkValidInput(); | |
| commitIfAllowed(); | |
| } | |
| /** | |
| * Commits the value to the JTextTextField if the property | |
| * <code>commitsOnValidEdit</code> is set to <code>true</code>. | |
| */ | |
| private void commitIfAllowed() | |
| { | |
| if (commitsOnValidEdit == true) | |
| try | |
| { | |
| getFormattedTextField().commitEdit(); | |
| } | |
| catch (ParseException ex) | |
| { | |
| // ignore invalid edits | |
| } | |
| } | |
| /** | |
| * Checks if the value in the input field is valid. If the | |
| * property allowsInvalid is set to <code>false</code>, then | |
| * the string in the input field is not allowed to be entered. | |
| */ | |
| private void checkValidInput() | |
| { | |
| JFormattedTextField ftf = getFormattedTextField(); | |
| try | |
| { | |
| Object newval = stringToValue(ftf.getText()); | |
| } | |
| catch (ParseException ex) | |
| { | |
| if (!allowsInvalid) | |
| { | |
| // roll back the input if invalid edits are not allowed | |
| try | |
| { | |
| ftf.setText(valueToString(ftf.getValue())); | |
| } | |
| catch (ParseException pe) | |
| { | |
| // if that happens, something serious must be wrong | |
| AssertionError ae; | |
| ae = new AssertionError("values must be parseable"); | |
| ae.initCause(pe); | |
| throw ae; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| /** The serialization UID (compatible with JDK1.5). */ | |
| private static final long serialVersionUID = -355018354457785329L; | |
| /** | |
| * Indicates if the value should be committed after every | |
| * valid modification of the Document. | |
| */ | |
| boolean commitsOnValidEdit; | |
| /** | |
| * If <code>true</code> newly inserted characters overwrite existing | |
| * values, otherwise insertion is done the normal way. | |
| */ | |
| boolean overwriteMode; | |
| /** | |
| * If <code>true</code> invalid edits are allowed for a limited | |
| * time. | |
| */ | |
| boolean allowsInvalid; | |
| /** | |
| * The class that is used for values. | |
| */ | |
| Class valueClass; | |
| /** | |
| * Creates a new instance of <code>DefaultFormatter</code>. | |
| */ | |
| public DefaultFormatter() | |
| { | |
| commitsOnValidEdit = false; | |
| overwriteMode = true; | |
| allowsInvalid = true; | |
| } | |
| /** | |
| * Installs the formatter on the specified {@link JFormattedTextField}. | |
| * | |
| * This method does the following things: | |
| * <ul> | |
| * <li>Display the value of #valueToString in the | |
| * <code>JFormattedTextField</code></li> | |
| * <li>Install the Actions from #getActions on the <code>JTextField</code> | |
| * </li> | |
| * <li>Install the DocumentFilter returned by #getDocumentFilter</li> | |
| * <li>Install the NavigationFilter returned by #getNavigationFilter</li> | |
| * </ul> | |
| * | |
| * This method is typically not overridden by subclasses. Instead override | |
| * one of the mentioned methods in order to customize behaviour. | |
| * | |
| * @param ftf the {@link JFormattedTextField} in which this formatter | |
| * is installed | |
| */ | |
| public void install(JFormattedTextField ftf) | |
| { | |
| super.install(ftf); | |
| } | |
| /** | |
| * Returns <code>true</code> if the value should be committed after | |
| * each valid modification of the input field, <code>false</code> if | |
| * it should never be committed by this formatter. | |
| * | |
| * @return the state of the <code>commitsOnValidEdit</code> property | |
| * | |
| * @see #setCommitsOnValidEdit | |
| */ | |
| public boolean getCommitsOnValidEdit() | |
| { | |
| return commitsOnValidEdit; | |
| } | |
| /** | |
| * Sets the value of the <code>commitsOnValidEdit</code> property. | |
| * | |
| * @param commitsOnValidEdit the new state of the | |
| * <code>commitsOnValidEdit</code> property | |
| * | |
| * @see #getCommitsOnValidEdit | |
| */ | |
| public void setCommitsOnValidEdit(boolean commitsOnValidEdit) | |
| { | |
| this.commitsOnValidEdit = commitsOnValidEdit; | |
| } | |
| /** | |
| * Returns the value of the <code>overwriteMode</code> property. | |
| * If that is set to <code>true</code> then newly inserted characters | |
| * overwrite existing values, otherwise the characters are inserted like | |
| * normal. The default is <code>true</code>. | |
| * | |
| * @return the value of the <code>overwriteMode</code> property | |
| */ | |
| public boolean getOverwriteMode() | |
| { | |
| return overwriteMode; | |
| } | |
| /** | |
| * Sets the value of the <code>overwriteMode</code> property. | |
| * | |
| * If that is set to <code>true</code> then newly inserted characters | |
| * overwrite existing values, otherwise the characters are inserted like | |
| * normal. The default is <code>true</code>. | |
| * | |
| * @param overwriteMode the new value for the <code>overwriteMode</code> | |
| * property | |
| */ | |
| public void setOverwriteMode(boolean overwriteMode) | |
| { | |
| this.overwriteMode = overwriteMode; | |
| } | |
| /** | |
| * Returns whether or not invalid edits are allowed or not. If invalid | |
| * edits are allowed, the JFormattedTextField may temporarily contain invalid | |
| * characters. | |
| * | |
| * @return the value of the allowsInvalid property | |
| */ | |
| public boolean getAllowsInvalid() | |
| { | |
| return allowsInvalid; | |
| } | |
| /** | |
| * Sets the value of the <code>allowsInvalid</code> property. | |
| * | |
| * @param allowsInvalid the new value for the property | |
| * | |
| * @see #getAllowsInvalid() | |
| */ | |
| public void setAllowsInvalid(boolean allowsInvalid) | |
| { | |
| this.allowsInvalid = allowsInvalid; | |
| } | |
| /** | |
| * Returns the class that is used for values. When Strings are converted | |
| * back to values, this class is used to create new value objects. | |
| * | |
| * @return the class that is used for values | |
| */ | |
| public Class<?> getValueClass() | |
| { | |
| return valueClass; | |
| } | |
| /** | |
| * Sets the class that is used for values. | |
| * | |
| * @param valueClass the class that is used for values | |
| * | |
| * @see #getValueClass() | |
| */ | |
| public void setValueClass(Class<?> valueClass) | |
| { | |
| this.valueClass = valueClass; | |
| } | |
| /** | |
| * Converts a String (from the JFormattedTextField input) to a value. | |
| * In order to achieve this, the formatter tries to instantiate an object | |
| * of the class returned by #getValueClass() using a single argument | |
| * constructor that takes a String argument. If such a constructor cannot | |
| * be found, the String itself is returned. | |
| * | |
| * @param string the string to convert | |
| * | |
| * @return the value for the string | |
| * | |
| * @throws ParseException if the string cannot be converted into | |
| * a value object (e.g. invalid input) | |
| */ | |
| public Object stringToValue(String string) | |
| throws ParseException | |
| { | |
| Object value = string; | |
| Class valueClass = getValueClass(); | |
| if (valueClass == null) | |
| { | |
| JFormattedTextField jft = getFormattedTextField(); | |
| if (jft != null) | |
| valueClass = jft.getValue().getClass(); | |
| } | |
| if (valueClass != null) | |
| try | |
| { | |
| Constructor constr = valueClass.getConstructor | |
| (new Class[]{String.class}); | |
| value = constr.newInstance(new Object[]{ string }); | |
| } | |
| catch (NoSuchMethodException ex) | |
| { | |
| // leave value as string | |
| } | |
| catch (Exception ex) | |
| { | |
| throw new ParseException(string, 0); | |
| } | |
| return value; | |
| } | |
| /** | |
| * Converts a value object into a String. This is done by invoking the | |
| * {@link Object#toString()} method on the value. | |
| * | |
| * @param value the value to be converted | |
| * | |
| * @return the string representation of the value | |
| * | |
| * @throws ParseException if the value cannot be converted | |
| */ | |
| public String valueToString(Object value) | |
| throws ParseException | |
| { | |
| if (value == null) | |
| return ""; | |
| return value.toString(); | |
| } | |
| /** | |
| * Creates and returns a clone of this DefaultFormatter. | |
| * | |
| * @return a clone of this object | |
| * | |
| * @throws CloneNotSupportedException not thrown here | |
| */ | |
| public Object clone() | |
| throws CloneNotSupportedException | |
| { | |
| return super.clone(); | |
| } | |
| /** | |
| * Returns the DocumentFilter that is used to restrict input. | |
| * | |
| * @return the DocumentFilter that is used to restrict input | |
| */ | |
| protected DocumentFilter getDocumentFilter() | |
| { | |
| return new FormatterDocumentFilter(); | |
| } | |
| } |