View Javadoc
1   /*
2    * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  package javax.swing;
26  
27  import java.awt.*;
28  import java.awt.event.*;
29  import java.awt.im.InputContext;
30  import java.io.*;
31  import java.text.*;
32  import java.util.*;
33  import javax.swing.UIManager;
34  import javax.swing.event.*;
35  import javax.swing.plaf.UIResource;
36  import javax.swing.text.*;
37  
38  /**
39   * <code>JFormattedTextField</code> extends <code>JTextField</code> adding
40   * support for formatting arbitrary values, as well as retrieving a particular
41   * object once the user has edited the text. The following illustrates
42   * configuring a <code>JFormattedTextField</code> to edit dates:
43   * <pre>
44   *   JFormattedTextField ftf = new JFormattedTextField();
45   *   ftf.setValue(new Date());
46   * </pre>
47   * <p>
48   * Once a <code>JFormattedTextField</code> has been created, you can
49   * listen for editing changes by way of adding
50   * a <code>PropertyChangeListener</code> and listening for
51   * <code>PropertyChangeEvent</code>s with the property name <code>value</code>.
52   * <p>
53   * <code>JFormattedTextField</code> allows
54   * configuring what action should be taken when focus is lost. The possible
55   * configurations are:
56   * <table summary="Possible JFormattedTextField configurations and their descriptions">
57   * <tr><th><p style="text-align:left">Value</p></th><th><p style="text-align:left">Description</p></th></tr>
58   * <tr><td>JFormattedTextField.REVERT
59   *            <td>Revert the display to match that of <code>getValue</code>,
60   *                possibly losing the current edit.
61   *        <tr><td>JFormattedTextField.COMMIT
62   *            <td>Commits the current value. If the value being edited
63   *                isn't considered a legal value by the
64   *                <code>AbstractFormatter</code> that is, a
65   *                <code>ParseException</code> is thrown, then the value
66   *                will not change, and then edited value will persist.
67   *        <tr><td>JFormattedTextField.COMMIT_OR_REVERT
68   *            <td>Similar to <code>COMMIT</code>, but if the value isn't
69   *                legal, behave like <code>REVERT</code>.
70   *        <tr><td>JFormattedTextField.PERSIST
71   *            <td>Do nothing, don't obtain a new
72   *                <code>AbstractFormatter</code>, and don't update the value.
73   * </table>
74   * The default is <code>JFormattedTextField.COMMIT_OR_REVERT</code>,
75   * refer to {@link #setFocusLostBehavior} for more information on this.
76   * <p>
77   * <code>JFormattedTextField</code> allows the focus to leave, even if
78   * the currently edited value is invalid. To lock the focus down while the
79   * <code>JFormattedTextField</code> is an invalid edit state
80   * you can attach an <code>InputVerifier</code>. The following code snippet
81   * shows a potential implementation of such an <code>InputVerifier</code>:
82   * <pre>
83   * public class FormattedTextFieldVerifier extends InputVerifier {
84   *     public boolean verify(JComponent input) {
85   *         if (input instanceof JFormattedTextField) {
86   *             JFormattedTextField ftf = (JFormattedTextField)input;
87   *             AbstractFormatter formatter = ftf.getFormatter();
88   *             if (formatter != null) {
89   *                 String text = ftf.getText();
90   *                 try {
91   *                      formatter.stringToValue(text);
92   *                      return true;
93   *                  } catch (ParseException pe) {
94   *                      return false;
95   *                  }
96   *              }
97   *          }
98   *          return true;
99   *      }
100  *      public boolean shouldYieldFocus(JComponent input) {
101  *          return verify(input);
102  *      }
103  *  }
104  * </pre>
105  * <p>
106  * Alternatively, you could invoke <code>commitEdit</code>, which would also
107  * commit the value.
108  * <p>
109  * <code>JFormattedTextField</code> does not do the formatting it self,
110  * rather formatting is done through an instance of
111  * <code>JFormattedTextField.AbstractFormatter</code> which is obtained from
112  * an instance of <code>JFormattedTextField.AbstractFormatterFactory</code>.
113  * Instances of <code>JFormattedTextField.AbstractFormatter</code> are
114  * notified when they become active by way of the
115  * <code>install</code> method, at which point the
116  * <code>JFormattedTextField.AbstractFormatter</code> can install whatever
117  * it needs to, typically a <code>DocumentFilter</code>. Similarly when
118  * <code>JFormattedTextField</code> no longer
119  * needs the <code>AbstractFormatter</code>, it will invoke
120  * <code>uninstall</code>.
121  * <p>
122  * <code>JFormattedTextField</code> typically
123  * queries the <code>AbstractFormatterFactory</code> for an
124  * <code>AbstractFormat</code> when it gains or loses focus. Although this
125  * can change based on the focus lost policy. If the focus lost
126  * policy is <code>JFormattedTextField.PERSIST</code>
127  * and the <code>JFormattedTextField</code> has been edited, the
128  * <code>AbstractFormatterFactory</code> will not be queried until the
129  * value has been committed. Similarly if the focus lost policy is
130  * <code>JFormattedTextField.COMMIT</code> and an exception
131  * is thrown from <code>stringToValue</code>, the
132  * <code>AbstractFormatterFactory</code> will not be queried when focus is
133  * lost or gained.
134  * <p>
135  * <code>JFormattedTextField.AbstractFormatter</code>
136  * is also responsible for determining when values are committed to
137  * the <code>JFormattedTextField</code>. Some
138  * <code>JFormattedTextField.AbstractFormatter</code>s will make new values
139  * available on every edit, and others will never commit the value. You can
140  * force the current value to be obtained
141  * from the current <code>JFormattedTextField.AbstractFormatter</code>
142  * by way of invoking <code>commitEdit</code>. <code>commitEdit</code> will
143  * be invoked whenever return is pressed in the
144  * <code>JFormattedTextField</code>.
145  * <p>
146  * If an <code>AbstractFormatterFactory</code> has not been explicitly
147  * set, one will be set based on the <code>Class</code> of the value type after
148  * <code>setValue</code> has been invoked (assuming value is non-null).
149  * For example, in the following code an appropriate
150  * <code>AbstractFormatterFactory</code> and <code>AbstractFormatter</code>
151  * will be created to handle formatting of numbers:
152  * <pre>
153  *   JFormattedTextField tf = new JFormattedTextField();
154  *   tf.setValue(new Number(100));
155  * </pre>
156  * <p>
157  * <strong>Warning:</strong> As the <code>AbstractFormatter</code> will
158  * typically install a <code>DocumentFilter</code> on the
159  * <code>Document</code>, and a <code>NavigationFilter</code> on the
160  * <code>JFormattedTextField</code> you should not install your own. If you do,
161  * you are likely to see odd behavior in that the editing policy of the
162  * <code>AbstractFormatter</code> will not be enforced.
163  * <p>
164  * <strong>Warning:</strong> Swing is not thread safe. For more
165  * information see <a
166  * href="package-summary.html#threading">Swing's Threading
167  * Policy</a>.
168  * <p>
169  * <strong>Warning:</strong>
170  * Serialized objects of this class will not be compatible with
171  * future Swing releases. The current serialization support is
172  * appropriate for short term storage or RMI between applications running
173  * the same version of Swing.  As of 1.4, support for long term storage
174  * of all JavaBeans&trade;
175  * has been added to the <code>java.beans</code> package.
176  * Please see {@link java.beans.XMLEncoder}.
177  *
178  * @since 1.4
179  */
180 public class JFormattedTextField extends JTextField {
181     private static final String uiClassID = "FormattedTextFieldUI";
182     private static final Action[] defaultActions =
183             { new CommitAction(), new CancelAction() };
184 
185     /**
186      * Constant identifying that when focus is lost,
187      * <code>commitEdit</code> should be invoked. If in committing the
188      * new value a <code>ParseException</code> is thrown, the invalid
189      * value will remain.
190      *
191      * @see #setFocusLostBehavior
192      */
193     public static final int COMMIT = 0;
194 
195     /**
196      * Constant identifying that when focus is lost,
197      * <code>commitEdit</code> should be invoked. If in committing the new
198      * value a <code>ParseException</code> is thrown, the value will be
199      * reverted.
200      *
201      * @see #setFocusLostBehavior
202      */
203     public static final int COMMIT_OR_REVERT = 1;
204 
205     /**
206      * Constant identifying that when focus is lost, editing value should
207      * be reverted to current value set on the
208      * <code>JFormattedTextField</code>.
209      *
210      * @see #setFocusLostBehavior
211      */
212     public static final int REVERT = 2;
213 
214     /**
215      * Constant identifying that when focus is lost, the edited value
216      * should be left.
217      *
218      * @see #setFocusLostBehavior
219      */
220     public static final int PERSIST = 3;
221 
222 
223     /**
224      * Factory used to obtain an instance of AbstractFormatter.
225      */
226     private AbstractFormatterFactory factory;
227     /**
228      * Object responsible for formatting the current value.
229      */
230     private AbstractFormatter format;
231     /**
232      * Last valid value.
233      */
234     private Object value;
235     /**
236      * True while the value being edited is valid.
237      */
238     private boolean editValid;
239     /**
240      * Behavior when focus is lost.
241      */
242     private int focusLostBehavior;
243     /**
244      * Indicates the current value has been edited.
245      */
246     private boolean edited;
247     /**
248      * Used to set the dirty state.
249      */
250     private DocumentListener documentListener;
251     /**
252      * Masked used to set the AbstractFormatterFactory.
253      */
254     private Object mask;
255     /**
256      * ActionMap that the TextFormatter Actions are added to.
257      */
258     private ActionMap textFormatterActionMap;
259     /**
260      * Indicates the input method composed text is in the document
261      */
262     private boolean composedTextExists = false;
263     /**
264      * A handler for FOCUS_LOST event
265      */
266     private FocusLostHandler focusLostHandler;
267 
268 
269     /**
270      * Creates a <code>JFormattedTextField</code> with no
271      * <code>AbstractFormatterFactory</code>. Use <code>setMask</code> or
272      * <code>setFormatterFactory</code> to configure the
273      * <code>JFormattedTextField</code> to edit a particular type of
274      * value.
275      */
276     public JFormattedTextField() {
277         super();
278         enableEvents(AWTEvent.FOCUS_EVENT_MASK);
279         setFocusLostBehavior(COMMIT_OR_REVERT);
280     }
281 
282     /**
283      * Creates a JFormattedTextField with the specified value. This will
284      * create an <code>AbstractFormatterFactory</code> based on the
285      * type of <code>value</code>.
286      *
287      * @param value Initial value for the JFormattedTextField
288      */
289     public JFormattedTextField(Object value) {
290         this();
291         setValue(value);
292     }
293 
294     /**
295      * Creates a <code>JFormattedTextField</code>. <code>format</code> is
296      * wrapped in an appropriate <code>AbstractFormatter</code> which is
297      * then wrapped in an <code>AbstractFormatterFactory</code>.
298      *
299      * @param format Format used to look up an AbstractFormatter
300      */
301     public JFormattedTextField(java.text.Format format) {
302         this();
303         setFormatterFactory(getDefaultFormatterFactory(format));
304     }
305 
306     /**
307      * Creates a <code>JFormattedTextField</code> with the specified
308      * <code>AbstractFormatter</code>. The <code>AbstractFormatter</code>
309      * is placed in an <code>AbstractFormatterFactory</code>.
310      *
311      * @param formatter AbstractFormatter to use for formatting.
312      */
313     public JFormattedTextField(AbstractFormatter formatter) {
314         this(new DefaultFormatterFactory(formatter));
315     }
316 
317     /**
318      * Creates a <code>JFormattedTextField</code> with the specified
319      * <code>AbstractFormatterFactory</code>.
320      *
321      * @param factory AbstractFormatterFactory used for formatting.
322      */
323     public JFormattedTextField(AbstractFormatterFactory factory) {
324         this();
325         setFormatterFactory(factory);
326     }
327 
328     /**
329      * Creates a <code>JFormattedTextField</code> with the specified
330      * <code>AbstractFormatterFactory</code> and initial value.
331      *
332      * @param factory <code>AbstractFormatterFactory</code> used for
333      *        formatting.
334      * @param currentValue Initial value to use
335      */
336     public JFormattedTextField(AbstractFormatterFactory factory,
337                                Object currentValue) {
338         this(currentValue);
339         setFormatterFactory(factory);
340     }
341 
342     /**
343      * Sets the behavior when focus is lost. This will be one of
344      * <code>JFormattedTextField.COMMIT_OR_REVERT</code>,
345      * <code>JFormattedTextField.REVERT</code>,
346      * <code>JFormattedTextField.COMMIT</code> or
347      * <code>JFormattedTextField.PERSIST</code>
348      * Note that some <code>AbstractFormatter</code>s may push changes as
349      * they occur, so that the value of this will have no effect.
350      * <p>
351      * This will throw an <code>IllegalArgumentException</code> if the object
352      * passed in is not one of the afore mentioned values.
353      * <p>
354      * The default value of this property is
355      * <code>JFormattedTextField.COMMIT_OR_REVERT</code>.
356      *
357      * @param behavior Identifies behavior when focus is lost
358      * @throws IllegalArgumentException if behavior is not one of the known
359      *         values
360      * @beaninfo
361      *  enum: COMMIT         JFormattedTextField.COMMIT
362      *        COMMIT_OR_REVERT JFormattedTextField.COMMIT_OR_REVERT
363      *        REVERT         JFormattedTextField.REVERT
364      *        PERSIST        JFormattedTextField.PERSIST
365      *  description: Behavior when component loses focus
366      */
367     public void setFocusLostBehavior(int behavior) {
368         if (behavior != COMMIT && behavior != COMMIT_OR_REVERT &&
369             behavior != PERSIST && behavior != REVERT) {
370             throw new IllegalArgumentException("setFocusLostBehavior must be one of: JFormattedTextField.COMMIT, JFormattedTextField.COMMIT_OR_REVERT, JFormattedTextField.PERSIST or JFormattedTextField.REVERT");
371         }
372         focusLostBehavior = behavior;
373     }
374 
375     /**
376      * Returns the behavior when focus is lost. This will be one of
377      * <code>COMMIT_OR_REVERT</code>,
378      * <code>COMMIT</code>,
379      * <code>REVERT</code> or
380      * <code>PERSIST</code>
381      * Note that some <code>AbstractFormatter</code>s may push changes as
382      * they occur, so that the value of this will have no effect.
383      *
384      * @return returns behavior when focus is lost
385      */
386     public int getFocusLostBehavior() {
387         return focusLostBehavior;
388     }
389 
390     /**
391      * Sets the <code>AbstractFormatterFactory</code>.
392      * <code>AbstractFormatterFactory</code> is
393      * able to return an instance of <code>AbstractFormatter</code> that is
394      * used to format a value for display, as well an enforcing an editing
395      * policy.
396      * <p>
397      * If you have not explicitly set an <code>AbstractFormatterFactory</code>
398      * by way of this method (or a constructor) an
399      * <code>AbstractFormatterFactory</code> and consequently an
400      * <code>AbstractFormatter</code> will be used based on the
401      * <code>Class</code> of the value. <code>NumberFormatter</code> will
402      * be used for <code>Number</code>s, <code>DateFormatter</code> will
403      * be used for <code>Dates</code>, otherwise <code>DefaultFormatter</code>
404      * will be used.
405      * <p>
406      * This is a JavaBeans bound property.
407      *
408      * @param tf <code>AbstractFormatterFactory</code> used to lookup
409      *          instances of <code>AbstractFormatter</code>
410      * @beaninfo
411      *       bound: true
412      *   attribute: visualUpdate true
413      * description: AbstractFormatterFactory, responsible for returning an
414      *              AbstractFormatter that can format the current value.
415      */
416     public void setFormatterFactory(AbstractFormatterFactory tf) {
417         AbstractFormatterFactory oldFactory = factory;
418 
419         factory = tf;
420         firePropertyChange("formatterFactory", oldFactory, tf);
421         setValue(getValue(), true, false);
422     }
423 
424     /**
425      * Returns the current <code>AbstractFormatterFactory</code>.
426      *
427      * @see #setFormatterFactory
428      * @return <code>AbstractFormatterFactory</code> used to determine
429      *         <code>AbstractFormatter</code>s
430      */
431     public AbstractFormatterFactory getFormatterFactory() {
432         return factory;
433     }
434 
435     /**
436      * Sets the current <code>AbstractFormatter</code>.
437      * <p>
438      * You should not normally invoke this, instead set the
439      * <code>AbstractFormatterFactory</code> or set the value.
440      * <code>JFormattedTextField</code> will
441      * invoke this as the state of the <code>JFormattedTextField</code>
442      * changes and requires the value to be reset.
443      * <code>JFormattedTextField</code> passes in the
444      * <code>AbstractFormatter</code> obtained from the
445      * <code>AbstractFormatterFactory</code>.
446      * <p>
447      * This is a JavaBeans bound property.
448      *
449      * @see #setFormatterFactory
450      * @param format AbstractFormatter to use for formatting
451      * @beaninfo
452      *       bound: true
453      *   attribute: visualUpdate true
454      * description: TextFormatter, responsible for formatting the current value
455      */
456     protected void setFormatter(AbstractFormatter format) {
457         AbstractFormatter oldFormat = this.format;
458 
459         if (oldFormat != null) {
460             oldFormat.uninstall();
461         }
462         setEditValid(true);
463         this.format = format;
464         if (format != null) {
465             format.install(this);
466         }
467         setEdited(false);
468         firePropertyChange("textFormatter", oldFormat, format);
469     }
470 
471     /**
472      * Returns the <code>AbstractFormatter</code> that is used to format and
473      * parse the current value.
474      *
475      * @return AbstractFormatter used for formatting
476      */
477     public AbstractFormatter getFormatter() {
478         return format;
479     }
480 
481     /**
482      * Sets the value that will be formatted by an
483      * <code>AbstractFormatter</code> obtained from the current
484      * <code>AbstractFormatterFactory</code>. If no
485      * <code>AbstractFormatterFactory</code> has been specified, this will
486      * attempt to create one based on the type of <code>value</code>.
487      * <p>
488      * The default value of this property is null.
489      * <p>
490      * This is a JavaBeans bound property.
491      *
492      * @param value Current value to display
493      * @beaninfo
494      *       bound: true
495      *   attribute: visualUpdate true
496      * description: The value to be formatted.
497      */
498     public void setValue(Object value) {
499         if (value != null && getFormatterFactory() == null) {
500             setFormatterFactory(getDefaultFormatterFactory(value));
501         }
502         setValue(value, true, true);
503     }
504 
505     /**
506      * Returns the last valid value. Based on the editing policy of
507      * the <code>AbstractFormatter</code> this may not return the current
508      * value. The currently edited value can be obtained by invoking
509      * <code>commitEdit</code> followed by <code>getValue</code>.
510      *
511      * @return Last valid value
512      */
513     public Object getValue() {
514         return value;
515     }
516 
517     /**
518      * Forces the current value to be taken from the
519      * <code>AbstractFormatter</code> and set as the current value.
520      * This has no effect if there is no current
521      * <code>AbstractFormatter</code> installed.
522      *
523      * @throws ParseException if the <code>AbstractFormatter</code> is not able
524      *         to format the current value
525      */
526     public void commitEdit() throws ParseException {
527         AbstractFormatter format = getFormatter();
528 
529         if (format != null) {
530             setValue(format.stringToValue(getText()), false, true);
531         }
532     }
533 
534     /**
535      * Sets the validity of the edit on the receiver. You should not normally
536      * invoke this. This will be invoked by the
537      * <code>AbstractFormatter</code> as the user edits the value.
538      * <p>
539      * Not all formatters will allow the component to get into an invalid
540      * state, and thus this may never be invoked.
541      * <p>
542      * Based on the look and feel this may visually change the state of
543      * the receiver.
544      *
545      * @param isValid boolean indicating if the currently edited value is
546      *        valid.
547      * @beaninfo
548      *       bound: true
549      *   attribute: visualUpdate true
550      * description: True indicates the edited value is valid
551      */
552     private void setEditValid(boolean isValid) {
553         if (isValid != editValid) {
554             editValid = isValid;
555             firePropertyChange("editValid", Boolean.valueOf(!isValid),
556                                Boolean.valueOf(isValid));
557         }
558     }
559 
560     /**
561      * Returns true if the current value being edited is valid. The value of
562      * this is managed by the current <code>AbstractFormatter</code>, as such
563      * there is no public setter for it.
564      *
565      * @return true if the current value being edited is valid.
566      */
567     public boolean isEditValid() {
568         return editValid;
569     }
570 
571     /**
572      * Invoked when the user inputs an invalid value. This gives the
573      * component a chance to provide feedback. The default
574      * implementation beeps.
575      */
576     protected void invalidEdit() {
577         UIManager.getLookAndFeel().provideErrorFeedback(JFormattedTextField.this);
578     }
579 
580     /**
581      * Processes any input method events, such as
582      * <code>InputMethodEvent.INPUT_METHOD_TEXT_CHANGED</code> or
583      * <code>InputMethodEvent.CARET_POSITION_CHANGED</code>.
584      *
585      * @param e the <code>InputMethodEvent</code>
586      * @see InputMethodEvent
587      */
588     protected void processInputMethodEvent(InputMethodEvent e) {
589         AttributedCharacterIterator text = e.getText();
590         int commitCount = e.getCommittedCharacterCount();
591 
592         // Keep track of the composed text
593         if (text != null) {
594             int begin = text.getBeginIndex();
595             int end = text.getEndIndex();
596             composedTextExists = ((end - begin) > commitCount);
597         } else {
598             composedTextExists = false;
599         }
600 
601         super.processInputMethodEvent(e);
602     }
603 
604     /**
605      * Processes any focus events, such as
606      * <code>FocusEvent.FOCUS_GAINED</code> or
607      * <code>FocusEvent.FOCUS_LOST</code>.
608      *
609      * @param e the <code>FocusEvent</code>
610      * @see FocusEvent
611      */
612     protected void processFocusEvent(FocusEvent e) {
613         super.processFocusEvent(e);
614 
615         // ignore temporary focus event
616         if (e.isTemporary()) {
617             return;
618         }
619 
620         if (isEdited() && e.getID() == FocusEvent.FOCUS_LOST) {
621             InputContext ic = getInputContext();
622             if (focusLostHandler == null) {
623                 focusLostHandler = new FocusLostHandler();
624             }
625 
626             // if there is a composed text, process it first
627             if ((ic != null) && composedTextExists) {
628                 ic.endComposition();
629                 EventQueue.invokeLater(focusLostHandler);
630             } else {
631                 focusLostHandler.run();
632             }
633         }
634         else if (!isEdited()) {
635             // reformat
636             setValue(getValue(), true, true);
637         }
638     }
639 
640     /**
641      * FOCUS_LOST behavior implementation
642      */
643     private class FocusLostHandler implements Runnable, Serializable {
644         public void run() {
645             int fb = JFormattedTextField.this.getFocusLostBehavior();
646             if (fb == JFormattedTextField.COMMIT ||
647                 fb == JFormattedTextField.COMMIT_OR_REVERT) {
648                 try {
649                     JFormattedTextField.this.commitEdit();
650                     // Give it a chance to reformat.
651                     JFormattedTextField.this.setValue(
652                         JFormattedTextField.this.getValue(), true, true);
653                 } catch (ParseException pe) {
654                     if (fb == JFormattedTextField.this.COMMIT_OR_REVERT) {
655                         JFormattedTextField.this.setValue(
656                             JFormattedTextField.this.getValue(), true, true);
657                     }
658                 }
659             }
660             else if (fb == JFormattedTextField.REVERT) {
661                 JFormattedTextField.this.setValue(
662                     JFormattedTextField.this.getValue(), true, true);
663             }
664         }
665     }
666 
667     /**
668      * Fetches the command list for the editor.  This is
669      * the list of commands supported by the plugged-in UI
670      * augmented by the collection of commands that the
671      * editor itself supports.  These are useful for binding
672      * to events, such as in a keymap.
673      *
674      * @return the command list
675      */
676     public Action[] getActions() {
677         return TextAction.augmentList(super.getActions(), defaultActions);
678     }
679 
680     /**
681      * Gets the class ID for a UI.
682      *
683      * @return the string "FormattedTextFieldUI"
684      * @see JComponent#getUIClassID
685      */
686     public String getUIClassID() {
687         return uiClassID;
688     }
689 
690     /**
691      * Associates the editor with a text document.
692      * The currently registered factory is used to build a view for
693      * the document, which gets displayed by the editor after revalidation.
694      * A PropertyChange event ("document") is propagated to each listener.
695      *
696      * @param doc  the document to display/edit
697      * @see #getDocument
698      * @beaninfo
699      *  description: the text document model
700      *        bound: true
701      *       expert: true
702      */
703     public void setDocument(Document doc) {
704         if (documentListener != null && getDocument() != null) {
705             getDocument().removeDocumentListener(documentListener);
706         }
707         super.setDocument(doc);
708         if (documentListener == null) {
709             documentListener = new DocumentHandler();
710         }
711         doc.addDocumentListener(documentListener);
712     }
713 
714     /*
715      * See readObject and writeObject in JComponent for more
716      * information about serialization in Swing.
717      *
718      * @param s Stream to write to
719      */
720     private void writeObject(ObjectOutputStream s) throws IOException {
721         s.defaultWriteObject();
722         if (getUIClassID().equals(uiClassID)) {
723             byte count = JComponent.getWriteObjCounter(this);
724             JComponent.setWriteObjCounter(this, --count);
725             if (count == 0 && ui != null) {
726                 ui.installUI(this);
727             }
728         }
729     }
730 
731     /**
732      * Resets the Actions that come from the TextFormatter to
733      * <code>actions</code>.
734      */
735     private void setFormatterActions(Action[] actions) {
736         if (actions == null) {
737             if (textFormatterActionMap != null) {
738                 textFormatterActionMap.clear();
739             }
740         }
741         else {
742             if (textFormatterActionMap == null) {
743                 ActionMap map = getActionMap();
744 
745                 textFormatterActionMap = new ActionMap();
746                 while (map != null) {
747                     ActionMap parent = map.getParent();
748 
749                     if (parent instanceof UIResource || parent == null) {
750                         map.setParent(textFormatterActionMap);
751                         textFormatterActionMap.setParent(parent);
752                         break;
753                     }
754                     map = parent;
755                 }
756             }
757             for (int counter = actions.length - 1; counter >= 0;
758                  counter--) {
759                 Object key = actions[counter].getValue(Action.NAME);
760 
761                 if (key != null) {
762                     textFormatterActionMap.put(key, actions[counter]);
763                 }
764             }
765         }
766     }
767 
768     /**
769      * Does the setting of the value. If <code>createFormat</code> is true,
770      * this will also obtain a new <code>AbstractFormatter</code> from the
771      * current factory. The property change event will be fired if
772      * <code>firePC</code> is true.
773      */
774     private void setValue(Object value, boolean createFormat, boolean firePC) {
775         Object oldValue = this.value;
776 
777         this.value = value;
778 
779         if (createFormat) {
780             AbstractFormatterFactory factory = getFormatterFactory();
781             AbstractFormatter atf;
782 
783             if (factory != null) {
784                 atf = factory.getFormatter(this);
785             }
786             else {
787                 atf = null;
788             }
789             setFormatter(atf);
790         }
791         else {
792             // Assumed to be valid
793             setEditValid(true);
794         }
795 
796         setEdited(false);
797 
798         if (firePC) {
799             firePropertyChange("value", oldValue, value);
800         }
801     }
802 
803     /**
804      * Sets the edited state of the receiver.
805      */
806     private void setEdited(boolean edited) {
807         this.edited = edited;
808     }
809 
810     /**
811      * Returns true if the receiver has been edited.
812      */
813     private boolean isEdited() {
814         return edited;
815     }
816 
817     /**
818      * Returns an AbstractFormatterFactory suitable for the passed in
819      * Object type.
820      */
821     private AbstractFormatterFactory getDefaultFormatterFactory(Object type) {
822         if (type instanceof DateFormat) {
823             return new DefaultFormatterFactory(new DateFormatter
824                                                ((DateFormat)type));
825         }
826         if (type instanceof NumberFormat) {
827             return new DefaultFormatterFactory(new NumberFormatter(
828                                                (NumberFormat)type));
829         }
830         if (type instanceof Format) {
831             return new DefaultFormatterFactory(new InternationalFormatter(
832                                                (Format)type));
833         }
834         if (type instanceof Date) {
835             return new DefaultFormatterFactory(new DateFormatter());
836         }
837         if (type instanceof Number) {
838             AbstractFormatter displayFormatter = new NumberFormatter();
839             ((NumberFormatter)displayFormatter).setValueClass(type.getClass());
840             AbstractFormatter editFormatter = new NumberFormatter(
841                                   new DecimalFormat("#.#"));
842             ((NumberFormatter)editFormatter).setValueClass(type.getClass());
843 
844             return new DefaultFormatterFactory(displayFormatter,
845                                                displayFormatter,editFormatter);
846         }
847         return new DefaultFormatterFactory(new DefaultFormatter());
848     }
849 
850 
851     /**
852      * Instances of <code>AbstractFormatterFactory</code> are used by
853      * <code>JFormattedTextField</code> to obtain instances of
854      * <code>AbstractFormatter</code> which in turn are used to format values.
855      * <code>AbstractFormatterFactory</code> can return different
856      * <code>AbstractFormatter</code>s based on the state of the
857      * <code>JFormattedTextField</code>, perhaps returning different
858      * <code>AbstractFormatter</code>s when the
859      * <code>JFormattedTextField</code> has focus vs when it
860      * doesn't have focus.
861      * @since 1.4
862      */
863     public static abstract class AbstractFormatterFactory {
864         /**
865          * Returns an <code>AbstractFormatter</code> that can handle formatting
866          * of the passed in <code>JFormattedTextField</code>.
867          *
868          * @param tf JFormattedTextField requesting AbstractFormatter
869          * @return AbstractFormatter to handle formatting duties, a null
870          *         return value implies the JFormattedTextField should behave
871          *         like a normal JTextField
872          */
873         public abstract AbstractFormatter getFormatter(JFormattedTextField tf);
874     }
875 
876 
877     /**
878      * Instances of <code>AbstractFormatter</code> are used by
879      * <code>JFormattedTextField</code> to handle the conversion both
880      * from an Object to a String, and back from a String to an Object.
881      * <code>AbstractFormatter</code>s can also enforce editing policies,
882      * or navigation policies, or manipulate the
883      * <code>JFormattedTextField</code> in any way it sees fit to
884      * enforce the desired policy.
885      * <p>
886      * An <code>AbstractFormatter</code> can only be active in
887      * one <code>JFormattedTextField</code> at a time.
888      * <code>JFormattedTextField</code> invokes
889      * <code>install</code> when it is ready to use it followed
890      * by <code>uninstall</code> when done. Subclasses
891      * that wish to install additional state should override
892      * <code>install</code> and message super appropriately.
893      * <p>
894      * Subclasses must override the conversion methods
895      * <code>stringToValue</code> and <code>valueToString</code>. Optionally
896      * they can override <code>getActions</code>,
897      * <code>getNavigationFilter</code> and <code>getDocumentFilter</code>
898      * to restrict the <code>JFormattedTextField</code> in a particular
899      * way.
900      * <p>
901      * Subclasses that allow the <code>JFormattedTextField</code> to be in
902      * a temporarily invalid state should invoke <code>setEditValid</code>
903      * at the appropriate times.
904      * @since 1.4
905      */
906     public static abstract class AbstractFormatter implements Serializable {
907         private JFormattedTextField ftf;
908 
909         /**
910          * Installs the <code>AbstractFormatter</code> onto a particular
911          * <code>JFormattedTextField</code>.
912          * This will invoke <code>valueToString</code> to convert the
913          * current value from the <code>JFormattedTextField</code> to
914          * a String. This will then install the <code>Action</code>s from
915          * <code>getActions</code>, the <code>DocumentFilter</code>
916          * returned from <code>getDocumentFilter</code> and the
917          * <code>NavigationFilter</code> returned from
918          * <code>getNavigationFilter</code> onto the
919          * <code>JFormattedTextField</code>.
920          * <p>
921          * Subclasses will typically only need to override this if they
922          * wish to install additional listeners on the
923          * <code>JFormattedTextField</code>.
924          * <p>
925          * If there is a <code>ParseException</code> in converting the
926          * current value to a String, this will set the text to an empty
927          * String, and mark the <code>JFormattedTextField</code> as being
928          * in an invalid state.
929          * <p>
930          * While this is a public method, this is typically only useful
931          * for subclassers of <code>JFormattedTextField</code>.
932          * <code>JFormattedTextField</code> will invoke this method at
933          * the appropriate times when the value changes, or its internal
934          * state changes.  You will only need to invoke this yourself if
935          * you are subclassing <code>JFormattedTextField</code> and
936          * installing/uninstalling <code>AbstractFormatter</code> at a
937          * different time than <code>JFormattedTextField</code> does.
938          *
939          * @param ftf JFormattedTextField to format for, may be null indicating
940          *            uninstall from current JFormattedTextField.
941          */
942         public void install(JFormattedTextField ftf) {
943             if (this.ftf != null) {
944                 uninstall();
945             }
946             this.ftf = ftf;
947             if (ftf != null) {
948                 try {
949                     ftf.setText(valueToString(ftf.getValue()));
950                 } catch (ParseException pe) {
951                     ftf.setText("");
952                     setEditValid(false);
953                 }
954                 installDocumentFilter(getDocumentFilter());
955                 ftf.setNavigationFilter(getNavigationFilter());
956                 ftf.setFormatterActions(getActions());
957             }
958         }
959 
960         /**
961          * Uninstalls any state the <code>AbstractFormatter</code> may have
962          * installed on the <code>JFormattedTextField</code>. This resets the
963          * <code>DocumentFilter</code>, <code>NavigationFilter</code>
964          * and additional <code>Action</code>s installed on the
965          * <code>JFormattedTextField</code>.
966          */
967         public void uninstall() {
968             if (this.ftf != null) {
969                 installDocumentFilter(null);
970                 this.ftf.setNavigationFilter(null);
971                 this.ftf.setFormatterActions(null);
972             }
973         }
974 
975         /**
976          * Parses <code>text</code> returning an arbitrary Object. Some
977          * formatters may return null.
978          *
979          * @throws ParseException if there is an error in the conversion
980          * @param text String to convert
981          * @return Object representation of text
982          */
983         public abstract Object stringToValue(String text) throws
984                                      ParseException;
985 
986         /**
987          * Returns the string value to display for <code>value</code>.
988          *
989          * @throws ParseException if there is an error in the conversion
990          * @param value Value to convert
991          * @return String representation of value
992          */
993         public abstract String valueToString(Object value) throws
994                         ParseException;
995 
996         /**
997          * Returns the current <code>JFormattedTextField</code> the
998          * <code>AbstractFormatter</code> is installed on.
999          *
1000          * @return JFormattedTextField formatting for.
1001          */
1002         protected JFormattedTextField getFormattedTextField() {
1003             return ftf;
1004         }
1005 
1006         /**
1007          * This should be invoked when the user types an invalid character.
1008          * This forwards the call to the current JFormattedTextField.
1009          */
1010         protected void invalidEdit() {
1011             JFormattedTextField ftf = getFormattedTextField();
1012 
1013             if (ftf != null) {
1014                 ftf.invalidEdit();
1015             }
1016         }
1017 
1018         /**
1019          * Invoke this to update the <code>editValid</code> property of the
1020          * <code>JFormattedTextField</code>. If you an enforce a policy
1021          * such that the <code>JFormattedTextField</code> is always in a
1022          * valid state, you will never need to invoke this.
1023          *
1024          * @param valid Valid state of the JFormattedTextField
1025          */
1026         protected void setEditValid(boolean valid) {
1027             JFormattedTextField ftf = getFormattedTextField();
1028 
1029             if (ftf != null) {
1030                 ftf.setEditValid(valid);
1031             }
1032         }
1033 
1034         /**
1035          * Subclass and override if you wish to provide a custom set of
1036          * <code>Action</code>s. <code>install</code> will install these
1037          * on the <code>JFormattedTextField</code>'s <code>ActionMap</code>.
1038          *
1039          * @return Array of Actions to install on JFormattedTextField
1040          */
1041         protected Action[] getActions() {
1042             return null;
1043         }
1044 
1045         /**
1046          * Subclass and override if you wish to provide a
1047          * <code>DocumentFilter</code> to restrict what can be input.
1048          * <code>install</code> will install the returned value onto
1049          * the <code>JFormattedTextField</code>.
1050          *
1051          * @return DocumentFilter to restrict edits
1052          */
1053         protected DocumentFilter getDocumentFilter() {
1054             return null;
1055         }
1056 
1057         /**
1058          * Subclass and override if you wish to provide a filter to restrict
1059          * where the user can navigate to.
1060          * <code>install</code> will install the returned value onto
1061          * the <code>JFormattedTextField</code>.
1062          *
1063          * @return NavigationFilter to restrict navigation
1064          */
1065         protected NavigationFilter getNavigationFilter() {
1066             return null;
1067         }
1068 
1069         /**
1070          * Clones the <code>AbstractFormatter</code>. The returned instance
1071          * is not associated with a <code>JFormattedTextField</code>.
1072          *
1073          * @return Copy of the AbstractFormatter
1074          */
1075         protected Object clone() throws CloneNotSupportedException {
1076             AbstractFormatter formatter = (AbstractFormatter)super.clone();
1077 
1078             formatter.ftf = null;
1079             return formatter;
1080         }
1081 
1082         /**
1083          * Installs the <code>DocumentFilter</code> <code>filter</code>
1084          * onto the current <code>JFormattedTextField</code>.
1085          *
1086          * @param filter DocumentFilter to install on the Document.
1087          */
1088         private void installDocumentFilter(DocumentFilter filter) {
1089             JFormattedTextField ftf = getFormattedTextField();
1090 
1091             if (ftf != null) {
1092                 Document doc = ftf.getDocument();
1093 
1094                 if (doc instanceof AbstractDocument) {
1095                     ((AbstractDocument)doc).setDocumentFilter(filter);
1096                 }
1097                 doc.putProperty(DocumentFilter.class, null);
1098             }
1099         }
1100     }
1101 
1102 
1103     /**
1104      * Used to commit the edit. This extends JTextField.NotifyAction
1105      * so that <code>isEnabled</code> is true while a JFormattedTextField
1106      * has focus, and extends <code>actionPerformed</code> to invoke
1107      * commitEdit.
1108      */
1109     static class CommitAction extends JTextField.NotifyAction {
1110         public void actionPerformed(ActionEvent e) {
1111             JTextComponent target = getFocusedComponent();
1112 
1113             if (target instanceof JFormattedTextField) {
1114                 // Attempt to commit the value
1115                 try {
1116                     ((JFormattedTextField)target).commitEdit();
1117                 } catch (ParseException pe) {
1118                     ((JFormattedTextField)target).invalidEdit();
1119                     // value not committed, don't notify ActionListeners
1120                     return;
1121                 }
1122             }
1123             // Super behavior.
1124             super.actionPerformed(e);
1125         }
1126 
1127         public boolean isEnabled() {
1128             JTextComponent target = getFocusedComponent();
1129             if (target instanceof JFormattedTextField) {
1130                 JFormattedTextField ftf = (JFormattedTextField)target;
1131                 if (!ftf.isEdited()) {
1132                     return false;
1133                 }
1134                 return true;
1135             }
1136             return super.isEnabled();
1137         }
1138     }
1139 
1140 
1141     /**
1142      * CancelAction will reset the value in the JFormattedTextField when
1143      * <code>actionPerformed</code> is invoked. It will only be
1144      * enabled if the focused component is an instance of
1145      * JFormattedTextField.
1146      */
1147     private static class CancelAction extends TextAction {
1148         public CancelAction() {
1149             super("reset-field-edit");
1150         }
1151 
1152         public void actionPerformed(ActionEvent e) {
1153             JTextComponent target = getFocusedComponent();
1154 
1155             if (target instanceof JFormattedTextField) {
1156                 JFormattedTextField ftf = (JFormattedTextField)target;
1157                 ftf.setValue(ftf.getValue());
1158             }
1159         }
1160 
1161         public boolean isEnabled() {
1162             JTextComponent target = getFocusedComponent();
1163             if (target instanceof JFormattedTextField) {
1164                 JFormattedTextField ftf = (JFormattedTextField)target;
1165                 if (!ftf.isEdited()) {
1166                     return false;
1167                 }
1168                 return true;
1169             }
1170             return super.isEnabled();
1171         }
1172     }
1173 
1174 
1175     /**
1176      * Sets the dirty state as the document changes.
1177      */
1178     private class DocumentHandler implements DocumentListener, Serializable {
1179         public void insertUpdate(DocumentEvent e) {
1180             setEdited(true);
1181         }
1182         public void removeUpdate(DocumentEvent e) {
1183             setEdited(true);
1184         }
1185         public void changedUpdate(DocumentEvent e) {}
1186     }
1187 }