| /* 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 = true; |
| 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(); |
| } |
| } |