summaryrefslogtreecommitdiff
path: root/javax/swing/JFormattedTextField.java
diff options
context:
space:
mode:
Diffstat (limited to 'javax/swing/JFormattedTextField.java')
-rw-r--r--javax/swing/JFormattedTextField.java427
1 files changed, 354 insertions, 73 deletions
diff --git a/javax/swing/JFormattedTextField.java b/javax/swing/JFormattedTextField.java
index 9890df200..761955d6d 100644
--- a/javax/swing/JFormattedTextField.java
+++ b/javax/swing/JFormattedTextField.java
@@ -40,15 +40,21 @@ package javax.swing;
import java.awt.event.FocusEvent;
import java.io.Serializable;
+import java.text.DateFormat;
import java.text.Format;
+import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Date;
+import javax.swing.text.AbstractDocument;
import javax.swing.text.DateFormatter;
import javax.swing.text.DefaultFormatter;
+import javax.swing.text.DefaultFormatterFactory;
import javax.swing.text.Document;
import javax.swing.text.DocumentFilter;
+import javax.swing.text.InternationalFormatter;
import javax.swing.text.NavigationFilter;
+import javax.swing.text.NumberFormatter;
/**
* A text field that makes use of a formatter to display and edit a specific
@@ -62,6 +68,7 @@ import javax.swing.text.NavigationFilter;
* formatting of the value of the JFormattedTextField.
*
* @author Michael Koch
+ * @author Anthony Balkissoon abalkiss at redhat dot com
*
* @since 1.4
*/
@@ -85,58 +92,184 @@ public class JFormattedTextField extends JTextField
//Do nothing here.
}
+ /**
+ * Clones the AbstractFormatter and removes the association to any
+ * particular JFormattedTextField.
+ *
+ * @return a clone of this formatter with no association to any particular
+ * JFormattedTextField
+ * @throws CloneNotSupportedException if the Object's class doesn't support
+ * the {@link Cloneable} interface
+ */
protected Object clone ()
throws CloneNotSupportedException
{
- throw new InternalError ("not implemented");
+ // Clone this formatter.
+ AbstractFormatter newFormatter = (AbstractFormatter)super.clone();
+
+ // And remove the association to the JFormattedTextField.
+ newFormatter.textField = null;
+ return newFormatter;
}
+ /**
+ * Returns a custom set of Actions that this formatter supports. Should
+ * be subclassed by formatters that have a custom set of Actions.
+ *
+ * @return <code>null</code>. Should be subclassed by formatters that want
+ * to install custom Actions on the JFormattedTextField.
+ */
protected Action[] getActions ()
{
- return textField.getActions();
+ return null;
}
+ /**
+ * Gets the DocumentFilter for this formatter. Should be subclassed
+ * by formatters wishing to install a filter that oversees Document
+ * mutations.
+ *
+ * @return <code>null</code>. Should be subclassed by formatters
+ * that want to restrict Document mutations.
+ */
protected DocumentFilter getDocumentFilter ()
{
- throw new InternalError ("not implemented");
+ // Subclasses should override this if they want to install a
+ // DocumentFilter.
+ return null;
}
+ /**
+ * Returns the JFormattedTextField on which this formatter is
+ * currently installed.
+ *
+ * @return the JFormattedTextField on which this formatter is currently
+ * installed
+ */
protected JFormattedTextField getFormattedTextField ()
{
return textField;
}
+ /**
+ * Gets the NavigationFilter for this formatter. Should be subclassed
+ * by formatters (such as {@link DefaultFormatter}) that wish to
+ * restrict where the cursor can be placed within the text field.
+ *
+ * @return <code>null</code>. Subclassed by formatters that want to restrict
+ * cursor location within the JFormattedTextField.
+ */
protected NavigationFilter getNavigationFilter ()
{
- return textField.getNavigationFilter();
+ // This should be subclassed if the formatter wants to install
+ // a NavigationFilter on the JFormattedTextField.
+ return null;
}
+ /**
+ * Installs this formatter on the specified JFormattedTextField. This
+ * converts the current value to a displayable String and displays it,
+ * and installs formatter specific Actions from <code>getActions</code>.
+ * It also installs a DocumentFilter and NavigationFilter on the
+ * JFormattedTextField.
+ * <p>
+ * If there is a <code>ParseException</code> this sets the text to an
+ * empty String and marks the text field in an invalid state.
+ *
+ * @param textField the JFormattedTextField on which to install this
+ * formatter
+ */
public void install(JFormattedTextField textField)
{
+ // Uninstall the current textfield.
if (this.textField != null)
- uninstall();
+ uninstall();
this.textField = textField;
+
+ // Install some state on the text field, including display text,
+ // DocumentFilter, NavigationFilter, and formatter specific Actions.
+ if (textField != null)
+ {
+ try
+ {
+ // Set the text of the field.
+ textField.setText(valueToString(textField.getValue()));
+ Document doc = textField.getDocument();
+
+ // Set the DocumentFilter for the field's Document.
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument)doc).setDocumentFilter(getDocumentFilter());
+
+ // Set the NavigationFilter.
+ textField.setNavigationFilter(getNavigationFilter());
+
+ // Set the Formatter Actions
+ // FIXME: Have to add the actions from getActions()
+ }
+ catch (ParseException pe)
+ {
+ // Set the text to an empty String and mark the field as invalid.
+ textField.setText("");
+ setEditValid(false);
+ }
+ }
}
+ /**
+ * Clears the state installed on the JFormattedTextField by the formatter.
+ * This resets the DocumentFilter, NavigationFilter, and any additional
+ * Actions (returned by <code>getActions()</code>).
+ */
public void uninstall ()
{
+ // Set the DocumentFilter for the field's Document.
+ Document doc = textField.getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument)doc).setDocumentFilter(null);
+ textField.setNavigationFilter(null);
+ // FIXME: Have to remove the Actions from getActions()
this.textField = null;
}
+ /**
+ * Invoke this method when invalid values are entered. This forwards the
+ * call to the JFormattedTextField.
+ */
protected void invalidEdit ()
{
textField.invalidEdit();
}
+ /**
+ * This method updates the <code>editValid</code> property of
+ * JFormattedTextField.
+ *
+ * @param valid the new state for the <code>editValid</code> property
+ */
protected void setEditValid (boolean valid)
{
textField.editValid = valid;
}
+ /**
+ * Parses <code>text</code> to return a corresponding Object.
+ *
+ * @param text the String to parse
+ * @return an Object that <code>text</code> represented
+ * @throws ParseException if there is an error in the conversion
+ */
public abstract Object stringToValue (String text)
throws ParseException;
+ /**
+ * Returns a String to be displayed, based on the Object
+ * <code>value</code>.
+ *
+ * @param value the Object from which to generate a String
+ * @return a String to be displayed
+ * @throws ParseException if there is an error in the conversion
+ */
public abstract String valueToString (Object value)
throws ParseException;
}
@@ -155,88 +288,177 @@ public class JFormattedTextField extends JTextField
public abstract AbstractFormatter getFormatter (JFormattedTextField tf);
}
- static class FormatterFactoryWrapper extends AbstractFormatterFactory
- {
- AbstractFormatter formatter;
-
- public FormatterFactoryWrapper(AbstractFormatter formatter)
- {
- this.formatter = formatter;
- }
-
- public AbstractFormatter getFormatter(JFormattedTextField tf)
- {
- return formatter;
- }
- }
-
+ /** The possible focusLostBehavior options **/
public static final int COMMIT = 0;
public static final int COMMIT_OR_REVERT = 1;
public static final int REVERT = 2;
public static final int PERSIST = 3;
+ /** The most recent valid and committed value **/
private Object value;
+
+ /** The behaviour for when this text field loses focus **/
private int focusLostBehavior = COMMIT_OR_REVERT;
+
+ /** The formatter factory currently being used **/
private AbstractFormatterFactory formatterFactory;
+
+ /** The formatter currently being used **/
+ private AbstractFormatter formatter;
+
// Package-private to avoid an accessor method.
boolean editValid = true;
+ /**
+ * Creates a JFormattedTextField with no formatter factory.
+ * <code>setValue</code> or <code>setFormatterFactory</code> will
+ * properly configure this text field to edit a particular type
+ * of value.
+ */
public JFormattedTextField ()
{
this((AbstractFormatterFactory) null, null);
}
+ /**
+ * Creates a JFormattedTextField that can handle the specified Format.
+ * An appopriate AbstractFormatter and AbstractFormatterFactory will
+ * be created for the specified Format.
+ *
+ * @param format the Format that this JFormattedTextField should be able
+ * to handle
+ */
public JFormattedTextField (Format format)
{
- throw new InternalError ("not implemented");
+ this ();
+ setFormatterFactory(getAppropriateFormatterFactory(format));
}
+ /**
+ * Creates a JFormattedTextField with the specified formatter. This will
+ * create a {@link DefaultFormatterFactory} with this formatter as the default
+ * formatter.
+ *
+ * @param formatter the formatter to use for this JFormattedTextField
+ */
public JFormattedTextField (AbstractFormatter formatter)
{
- this(new FormatterFactoryWrapper(formatter), null);
+ this(new DefaultFormatterFactory (formatter));
}
+ /**
+ * Creates a JFormattedTextField with the specified formatter factory.
+ *
+ * @param factory the formatter factory to use for this JFormattedTextField
+ */
public JFormattedTextField (AbstractFormatterFactory factory)
{
- this(factory, null);
+ setFormatterFactory(factory);
}
+ /**
+ * Creates a JFormattedTextField with the specified formatter factory and
+ * initial value.
+ *
+ * @param factory the initial formatter factory for this JFormattedTextField
+ * @param value the initial value for the text field
+ */
public JFormattedTextField (AbstractFormatterFactory factory, Object value)
- {
- this.formatterFactory = factory;
- this.value = value;
+ {
+ setFormatterFactory(factory);
+ setValue(value);
}
+ /**
+ * Creates a JFormattedTextField with the specified value. This creates a
+ * formatter and formatterFactory that are appropriate for the value.
+ *
+ * @param value the initial value for this JFormattedTextField
+ */
public JFormattedTextField (Object value)
{
- this.value = value;
+ setValue(value);
+ }
+
+ /**
+ * Returns an AbstractFormatterFactory that will give an appropriate
+ * AbstractFormatter for the given Format.
+ * @param format the Format to match with an AbstractFormatter.
+ * @return a DefaultFormatterFactory whose defaultFormatter is appropriate
+ * for the given Format.
+ */
+ private AbstractFormatterFactory getAppropriateFormatterFactory (Format format)
+ {
+ AbstractFormatter newFormatter;
+ if (format instanceof DateFormat)
+ newFormatter = new DateFormatter((DateFormat)format);
+ else if (format instanceof NumberFormat)
+ newFormatter = new NumberFormatter ((NumberFormat)format);
+ else
+ newFormatter = new InternationalFormatter(format);
+
+ return new DefaultFormatterFactory(newFormatter);
}
+ /**
+ * Forces the current value from the editor to be set as the current
+ * value. If there is no current formatted this has no effect.
+ *
+ * @throws ParseException if the formatter cannot format the current value
+ */
public void commitEdit ()
throws ParseException
{
- throw new InternalError ("not implemented");
+ if (formatter == null)
+ return;
+ // Note: this code is a lot like setValue except that we don't want
+ // to create a new formatter.
+ Object oldValue = this.value;
+
+ this.value = formatter.stringToValue(getText());;
+ editValid = true;
+
+ firePropertyChange("value", oldValue, this.value);
}
+ /**
+ * Gets the command list supplied by the UI augmented by the specific
+ * Actions for JFormattedTextField.
+ *
+ * @return an array of Actions that this text field supports
+ */
public Action[] getActions ()
{
// FIXME: Add JFormattedTextField specific actions
+ // These are related to committing or cancelling edits.
return super.getActions();
}
+ /**
+ * Returns the behaviour of this JFormattedTextField upon losing focus. This
+ * is one of <code>COMMIT</code>, <code>COMMIT_OR_REVERT</code>,
+ * <code>PERSIST</code>, or <code>REVERT</code>.
+ * @return the behaviour upon losing focus
+ */
public int getFocusLostBehavior()
{
return focusLostBehavior;
}
+ /**
+ * Returns the current formatter used for this JFormattedTextField.
+ * @return the current formatter used for this JFormattedTextField
+ */
public AbstractFormatter getFormatter ()
{
- if (formatterFactory == null)
- return null;
-
- return formatterFactory.getFormatter(this);
+ return formatter;
}
-
+
+ /**
+ * Returns the factory currently used to generate formatters for this
+ * JFormattedTextField.
+ * @return the factory currently used to generate formatters
+ */
public AbstractFormatterFactory getFormatterFactory ()
{
return formatterFactory;
@@ -247,31 +469,61 @@ public class JFormattedTextField extends JTextField
return "FormattedTextFieldUI";
}
+ /**
+ * Returns the last valid value. This may not be the value currently shown
+ * in the text field depending on whether or not the formatter commits on
+ * valid edits and allows invalid input to be temporarily displayed.
+ * @return the last committed valid value
+ */
public Object getValue ()
{
return value;
}
+ /**
+ * This method is used to provide feedback to the user when an invalid value
+ * is input during editing.
+ */
protected void invalidEdit ()
{
UIManager.getLookAndFeel().provideErrorFeedback(this);
}
+ /**
+ * Returns true if the current value being edited is valid. This property is
+ * managed by the current formatted.
+ * @return true if the value being edited is valid.
+ */
public boolean isEditValid ()
{
return editValid;
}
+ /**
+ * Processes focus events. This is overridden because we may want to
+ * change the formatted depending on whether or not this field has
+ * focus.
+ *
+ * @param evt the FocusEvent
+ */
protected void processFocusEvent (FocusEvent evt)
{
- // it's safe to simply call super for now, until it gets clear
- // what this method is supposed to do
- // throw new InternalError ("not implemented");
super.processFocusEvent(evt);
+ // Let the formatterFactory change the formatter for this text field
+ // based on whether or not it has focus.
+ setFormatter (formatterFactory.getFormatter(this));
}
-
+
+ /**
+ * Associates this JFormattedTextField with a Document and propagates
+ * a PropertyChange event to each listener.
+ *
+ * @param newDocument the Document to associate with this text field
+ */
public void setDocument(Document newDocument)
{
+ // FIXME: This method should do more than this. Must do some handling
+ // of the DocumentListeners.
Document oldDocument = getDocument();
if (oldDocument == newDocument)
@@ -280,6 +532,16 @@ public class JFormattedTextField extends JTextField
super.setDocument(newDocument);
}
+ /**
+ * Sets the behaviour of this JFormattedTextField upon losing focus.
+ * This must be <code>COMMIT</code>, <code>COMMIT_OR_REVERT</code>,
+ * <code>PERSIST</code>, or <code>REVERT</code> or an
+ * IllegalArgumentException will be thrown.
+ *
+ * @param behavior
+ * @throws IllegalArgumentException if <code>behaviour</code> is not
+ * one of the above
+ */
public void setFocusLostBehavior(int behavior)
{
if (behavior != COMMIT
@@ -291,20 +553,38 @@ public class JFormattedTextField extends JTextField
this.focusLostBehavior = behavior;
}
+ /**
+ * Sets the formatter for this JFormattedTextField. Normally the formatter
+ * factory will take care of this, or calls to setValue will also make sure
+ * that the formatter is set appropriately.
+ *
+ * @param formatter the AbstractFormatter to use for formatting the value for
+ * this JFormattedTextField
+ */
protected void setFormatter (AbstractFormatter formatter)
{
AbstractFormatter oldFormatter = null;
- if (formatterFactory != null)
- oldFormatter = formatterFactory.getFormatter(this);
+ oldFormatter = this.formatter;
- if (oldFormatter == formatter)
- return;
+ if (oldFormatter != null)
+ oldFormatter.uninstall();
+
+ this.formatter = formatter;
+
+ if (formatter != null)
+ formatter.install(this);
- setFormatterFactory(new FormatterFactoryWrapper(formatter));
firePropertyChange("formatter", oldFormatter, formatter);
}
+ /**
+ * Sets the factory from which this JFormattedTextField should obtain
+ * its formatters.
+ *
+ * @param factory the AbstractFormatterFactory that will be used to generate
+ * formatters for this JFormattedTextField
+ */
public void setFormatterFactory (AbstractFormatterFactory factory)
{
if (formatterFactory == factory)
@@ -313,55 +593,56 @@ public class JFormattedTextField extends JTextField
AbstractFormatterFactory oldFactory = formatterFactory;
formatterFactory = factory;
firePropertyChange("formatterFactory", oldFactory, factory);
+
+ // Now set the formatter according to our new factory.
+ if (formatterFactory != null)
+ setFormatter(formatterFactory.getFormatter(this));
+ else
+ setFormatter(null);
}
+ /**
+ * Sets the value that will be formatted and displayed.
+ *
+ * @param newValue the value to be formatted and displayed
+ */
public void setValue (Object newValue)
{
if (value == newValue)
return;
- // format value
- AbstractFormatter formatter = createFormatter(newValue);
- try
- {
- setText(formatter.valueToString(newValue));
- }
- catch (ParseException ex)
- {
- // TODO: what should we do with this?
- }
-
Object oldValue = value;
value = newValue;
+
+ // If there is no formatterFactory then make one.
+ if (formatterFactory == null)
+ setFormatterFactory(createFormatterFactory(newValue));
+
+ // Set the formatter appropriately. This is because there may be a new
+ // formatterFactory from the line above, or we may want a new formatter
+ // depending on the type of newValue (or if newValue is null).
+ setFormatter (formatterFactory.getFormatter(this));
firePropertyChange("value", oldValue, newValue);
}
/**
- * A helper method that attempts to create a formatter that is suitable
- * to format objects of the type like <code>value</code>.
+ * A helper method that attempts to create a formatter factory that is
+ * suitable to format objects of the type like <code>value</code>.
*
- * If <code>formatterFactory</code> is not null and the returned formatter
- * is also not <code>null</code> then this formatter is used. Otherwise we
- * try to create one based on the type of <code>value</code>.
+ * @param value an object which should be formatted by the formatter factory.
*
- * @param value an object which should be formatted by the formatter
- *
- * @return a formatter able to format objects of the class of
+ * @return a formatter factory able to format objects of the class of
* <code>value</code>
*/
- AbstractFormatter createFormatter(Object value)
+ AbstractFormatterFactory createFormatterFactory(Object value)
{
AbstractFormatter formatter = null;
- if (formatterFactory != null
- && formatterFactory.getFormatter(this) != null)
- formatter = formatterFactory.getFormatter(this);
- else
- {
- if (value instanceof Date)
- formatter = new DateFormatter();
- else
- formatter = new DefaultFormatter();
- }
- return formatter;
+ if (value instanceof Date)
+ formatter = new DateFormatter();
+ else if (value instanceof Number)
+ formatter = new NumberFormatter();
+ else
+ formatter = new DefaultFormatter();
+ return new DefaultFormatterFactory(formatter);
}
}