KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > JFormattedTextField


1 /*
2  * @(#)JFormattedTextField.java 1.22 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing;
8
9 import java.awt.*;
10 import java.awt.event.*;
11 import java.awt.im.InputContext JavaDoc;
12 import java.io.*;
13 import java.text.*;
14 import java.util.*;
15 import javax.swing.UIManager JavaDoc;
16 import javax.swing.event.*;
17 import javax.swing.plaf.UIResource JavaDoc;
18 import javax.swing.text.*;
19
20 /**
21  * <code>JFormattedTextField</code> extends <code>JTextField</code> adding
22  * support for formatting arbitrary values, as well as retrieving a particular
23  * object once the user has edited the text. The following illustrates
24  * configuring a <code>JFormattedTextField</code> to edit dates:
25  * <pre>
26  * JFormattedTextField ftf = new JFormattedTextField();
27  * ftf.setValue(new Date());
28  * </pre>
29  * <p>
30  * Once a <code>JFormattedTextField</code> has been created, you can
31  * listen for editing changes by way of adding
32  * a <code>PropertyChangeListener</code> and listening for
33  * <code>PropertyChangeEvent</code>s with the property name <code>value</code>.
34  * <p>
35  * <code>JFormattedTextField</code> allows
36  * configuring what action should be taken when focus is lost. The possible
37  * configurations are:
38  * <table summary="Possible JFormattedTextField configurations and their descriptions">
39  * <tr><th><p align="left">Value</p></th><th><p align="left">Description</p></th></tr>
40  * <tr><td>JFormattedTextField.REVERT
41  * <td>Revert the display to match that of <code>getValue</code>,
42  * possibly losing the current edit.
43  * <tr><td>JFormattedTextField.COMMIT
44  * <td>Commits the current value. If the value being edited
45  * isn't considered a legal value by the
46  * <code>AbstractFormatter</code> that is, a
47  * <code>ParseException</code> is thrown, then the value
48  * will not change, and then edited value will persist.
49  * <tr><td>JFormattedTextField.COMMIT_OR_REVERT
50  * <td>Similar to <code>COMMIT</code>, but if the value isn't
51  * legal, behave like <code>REVERT</code>.
52  * <tr><td>JFormattedTextField.PERSIST
53  * <td>Do nothing, don't obtain a new
54  * <code>AbstractFormatter</code>, and don't update the value.
55  * </table>
56  * The default is <code>JFormattedTextField.COMMIT_OR_REVERT</code>,
57  * refer to {@link #setFocusLostBehavior} for more information on this.
58  * <p>
59  * <code>JFormattedTextField</code> allows the focus to leave, even if
60  * the currently edited value is invalid. To lock the focus down while the
61  * <code>JFormattedTextField</code> is an invalid edit state
62  * you can attach an <code>InputVerifier</code>. The following code snippet
63  * shows a potential implementation of such an <code>InputVerifier</code>:
64  * <pre>
65  * public class FormattedTextFieldVerifier extends InputVerifier {
66  * public boolean verify(JComponent input) {
67  * if (input instanceof JFormattedTextField) {
68  * JFormattedTextField ftf = (JFormattedTextField)input;
69  * AbstractFormatter formatter = ftf.getFormatter();
70  * if (formatter != null) {
71  * String text = ftf.getText();
72  * try {
73  * formatter.stringToValue(text);
74  * return true;
75  * } catch (ParseException pe) {
76  * return false;
77  * }
78  * }
79  * }
80  * return true;
81  * }
82  * public boolean shouldYieldFocus(JComponent input) {
83  * return verify(input);
84  * }
85  * }
86  * </pre>
87  * <p>
88  * Alternatively, you could invoke <code>commitEdit</code>, which would also
89  * commit the value.
90  * <p>
91  * <code>JFormattedTextField</code> does not do the formatting it self,
92  * rather formatting is done through an instance of
93  * <code>JFormattedTextField.AbstractFormatter</code> which is obtained from
94  * an instance of <code>JFormattedTextField.AbstractFormatterFactory</code>.
95  * Instances of <code>JFormattedTextField.AbstractFormatter</code> are
96  * notified when they become active by way of the
97  * <code>install</code> method, at which point the
98  * <code>JFormattedTextField.AbstractFormatter</code> can install whatever
99  * it needs to, typically a <code>DocumentFilter</code>. Similarly when
100  * <code>JFormattedTextField</code> no longer
101  * needs the <code>AbstractFormatter</code>, it will invoke
102  * <code>uninstall</code>.
103  * <p>
104  * <code>JFormattedTextField</code> typically
105  * queries the <code>AbstractFormatterFactory</code> for an
106  * <code>AbstractFormat</code> when it gains or loses focus. Although this
107  * can change based on the focus lost policy. If the focus lost
108  * policy is <code>JFormattedTextField.PERSIST</code>
109  * and the <code>JFormattedTextField</code> has been edited, the
110  * <code>AbstractFormatterFactory</code> will not be queried until the
111  * value has been commited. Similarly if the focus lost policy is
112  * <code>JFormattedTextField.COMMIT</code> and an exception
113  * is thrown from <code>stringToValue</code>, the
114  * <code>AbstractFormatterFactory</code> will not be querired when focus is
115  * lost or gained.
116  * <p>
117  * <code>JFormattedTextField.AbstractFormatter</code>
118  * is also responsible for determining when values are commited to
119  * the <code>JFormattedTextField</code>. Some
120  * <code>JFormattedTextField.AbstractFormatter</code>s will make new values
121  * available on every edit, and others will never commit the value. You can
122  * force the current value to be obtained
123  * from the current <code>JFormattedTextField.AbstractFormatter</code>
124  * by way of invoking <code>commitEdit</code>. <code>commitEdit</code> will
125  * be invoked whenever return is pressed in the
126  * <code>JFormattedTextField</code>.
127  * <p>
128  * If an <code>AbstractFormatterFactory</code> has not been explicitly
129  * set, one will be set based on the <code>Class</code> of the value type after
130  * <code>setValue</code> has been invoked (assuming value is non-null).
131  * For example, in the following code an appropriate
132  * <code>AbstractFormatterFactory</code> and <code>AbstractFormatter</code>
133  * will be created to handle formatting of numbers:
134  * <pre>
135  * JFormattedTextField tf = new JFormattedTextField();
136  * tf.setValue(new Number(100));
137  * </pre>
138  * <p>
139  * <strong>Warning:</strong> As the <code>AbstractFormatter</code> will
140  * typically install a <code>DocumentFilter</code> on the
141  * <code>Document</code>, and a <code>NavigationFilter</code> on the
142  * <code>JFormattedTextField</code> you should not install your own. If you do,
143  * you are likely to see odd behavior in that the editing policy of the
144  * <code>AbstractFormatter</code> will not be enforced.
145  * <p>
146  * <strong>Warning:</strong>
147  * Serialized objects of this class will not be compatible with
148  * future Swing releases. The current serialization support is
149  * appropriate for short term storage or RMI between applications running
150  * the same version of Swing. As of 1.4, support for long term storage
151  * of all JavaBeans<sup><font size="-2">TM</font></sup>
152  * has been added to the <code>java.beans</code> package.
153  * Please see {@link java.beans.XMLEncoder}.
154  *
155  * @version 1.22 12/19/03
156  * @since 1.4
157  */

158 public class JFormattedTextField extends JTextField JavaDoc {
159     private static final String JavaDoc uiClassID = "FormattedTextFieldUI";
160     private static final Action JavaDoc[] defaultActions =
161             { new CommitAction(), new CancelAction() };
162
163     /**
164      * Constant identifying that when focus is lost,
165      * <code>commitEdit</code> should be invoked. If in commiting the
166      * new value a <code>ParseException</code> is thrown, the invalid
167      * value will remain.
168      *
169      * @see #setFocusLostBehavior
170      */

171     public static final int COMMIT = 0;
172
173     /**
174      * Constant identifying that when focus is lost,
175      * <code>commitEdit</code> should be invoked. If in commiting the new
176      * value a <code>ParseException</code> is thrown, the value will be
177      * reverted.
178      *
179      * @see #setFocusLostBehavior
180      */

181     public static final int COMMIT_OR_REVERT = 1;
182
183     /**
184      * Constant identifying that when focus is lost, editing value should
185      * be reverted to current value set on the
186      * <code>JFormattedTextField</code>.
187      *
188      * @see #setFocusLostBehavior
189      */

190     public static final int REVERT = 2;
191
192     /**
193      * Constant identifying that when focus is lost, the edited value
194      * should be left.
195      *
196      * @see #setFocusLostBehavior
197      */

198     public static final int PERSIST = 3;
199
200
201     /**
202      * Factory used to obtain an instance of AbstractFormatter.
203      */

204     private AbstractFormatterFactory factory;
205     /**
206      * Object responsible for formatting the current value.
207      */

208     private AbstractFormatter format;
209     /**
210      * Last valid value.
211      */

212     private Object JavaDoc value;
213     /**
214      * True while the value being edited is valid.
215      */

216     private boolean editValid;
217     /**
218      * Behavior when focus is lost.
219      */

220     private int focusLostBehavior;
221     /**
222      * Indicates the current value has been edited.
223      */

224     private boolean edited;
225     /**
226      * Used to set the dirty state.
227      */

228     private DocumentListener documentListener;
229     /**
230      * Masked used to set the AbstractFormatterFactory.
231      */

232     private Object JavaDoc mask;
233     /**
234      * ActionMap that the TextFormatter Actions are added to.
235      */

236     private ActionMap JavaDoc textFormatterActionMap;
237     /**
238      * Indicates the input method composed text is in the document
239      */

240     private boolean composedTextExists = false;
241     /**
242      * A handler for FOCUS_LOST event
243      */

244     private FocusLostHandler focusLostHandler;
245
246
247     /**
248      * Creates a <code>JFormattedTextField</code> with no
249      * <code>AbstractFormatterFactory</code>. Use <code>setMask</code> or
250      * <code>setFormatterFactory</code> to configure the
251      * <code>JFormattedTextField</code> to edit a particular type of
252      * value.
253      */

254     public JFormattedTextField() {
255         super();
256         enableEvents(AWTEvent.FOCUS_EVENT_MASK);
257         setFocusLostBehavior(COMMIT_OR_REVERT);
258     }
259
260     /**
261      * Creates a JFormattedTextField with the specified value. This will
262      * create an <code>AbstractFormatterFactory</code> based on the
263      * type of <code>value</code>.
264      *
265      * @param value Initial value for the JFormattedTextField
266      */

267     public JFormattedTextField(Object JavaDoc value) {
268         this();
269         setValue(value);
270     }
271
272     /**
273      * Creates a <code>JFormattedTextField</code>. <code>format</code> is
274      * wrapped in an appropriate <code>AbstractFormatter</code> which is
275      * then wrapped in an <code>AbstractFormatterFactory</code>.
276      *
277      * @param format Format used to look up an AbstractFormatter
278      */

279     public JFormattedTextField(java.text.Format JavaDoc format) {
280         this();
281         setFormatterFactory(getDefaultFormatterFactory(format));
282     }
283
284     /**
285      * Creates a <code>JFormattedTextField</code> with the specified
286      * <code>AbstractFormatter</code>. The <code>AbstractFormatter</code>
287      * is placed in an <code>AbstractFormatterFactory</code>.
288      *
289      * @param formatter AbstractFormatter to use for formatting.
290      */

291     public JFormattedTextField(AbstractFormatter formatter) {
292         this(new DefaultFormatterFactory(formatter));
293     }
294
295     /**
296      * Creates a <code>JFormattedTextField</code> with the specified
297      * <code>AbstractFormatterFactory</code>.
298      *
299      * @param factory AbstractFormatterFactory used for formatting.
300      */

301     public JFormattedTextField(AbstractFormatterFactory factory) {
302         this();
303         setFormatterFactory(factory);
304     }
305
306     /**
307      * Creates a <code>JFormattedTextField</code> with the specified
308      * <code>AbstractFormatterFactory</code> and initial value.
309      *
310      * @param factory <code>AbstractFormatterFactory</code> used for
311      * formatting.
312      * @param currentValue Initial value to use
313      */

314     public JFormattedTextField(AbstractFormatterFactory factory,
315                                Object JavaDoc currentValue) {
316         this(currentValue);
317         setFormatterFactory(factory);
318     }
319
320     /**
321      * Sets the behavior when focus is lost. This will be one of
322      * <code>JFormattedTextField.COMMIT_OR_REVERT</code>,
323      * <code>JFormattedTextField.REVERT</code>,
324      * <code>JFormattedTextField.COMMIT</code> or
325      * <code>JFormattedTextField.PERSIST</code>
326      * Note that some <code>AbstractFormatter</code>s may push changes as
327      * they occur, so that the value of this will have no effect.
328      * <p>
329      * This will throw an <code>IllegalArgumentException</code> if the object
330      * passed in is not one of the afore mentioned values.
331      * <p>
332      * The default value of this property is
333      * <code>JFormattedTextField.COMMIT_OR_REVERT</code>.
334      *
335      * @param behavior Identifies behavior when focus is lost
336      * @throws IllegalArgumentException if behavior is not one of the known
337      * values
338      * @beaninfo
339      * enum: COMMIT JFormattedTextField.COMMIT
340      * COMMIT_OR_REVERT JFormattedTextField.COMMIT_OR_REVERT
341      * REVERT JFormattedTextField.REVERT
342      * PERSIST JFormattedTextField.PERSIST
343      * description: Behavior when component loses focus
344      */

345     public void setFocusLostBehavior(int behavior) {
346         if (behavior != COMMIT && behavior != COMMIT_OR_REVERT &&
347             behavior != PERSIST && behavior != REVERT) {
348             throw new IllegalArgumentException JavaDoc("setFocusLostBehavior must be one of: JFormattedTextField.COMMIT, JFormattedTextField.COMMIT_OR_REVERT, JFormattedTextField.PERSIST or JFormattedTextField.REVERT");
349         }
350         focusLostBehavior = behavior;
351     }
352
353     /**
354      * Returns the behavior when focus is lost. This will be one of
355      * <code>COMMIT_OR_REVERT</code>,
356      * <code>COMMIT</code>,
357      * <code>REVERT</code> or
358      * <code>PERSIST</code>
359      * Note that some <code>AbstractFormatter</code>s may push changes as
360      * they occur, so that the value of this will have no effect.
361      *
362      * @return returns behavior when focus is lost
363      */

364     public int getFocusLostBehavior() {
365         return focusLostBehavior;
366     }
367
368     /**
369      * Sets the <code>AbstractFormatterFactory</code>.
370      * <code>AbstractFormatterFactory</code> is
371      * able to return an instance of <code>AbstractFormatter</code> that is
372      * used to format a value for display, as well an enforcing an editing
373      * policy.
374      * <p>
375      * If you have not explicitly set an <code>AbstractFormatterFactory</code>
376      * by way of this method (or a constructor) an
377      * <code>AbstractFormatterFactory</code> and consequently an
378      * <code>AbstractFormatter</code> will be used based on the
379      * <code>Class</code> of the value. <code>NumberFormatter</code> will
380      * be used for <code>Number</code>s, <code>DateFormatter</code> will
381      * be used for <code>Dates</code>, otherwise <code>DefaultFormatter</code>
382      * will be used.
383      * <p>
384      * This is a JavaBeans bound property.
385      *
386      * @param tf <code>AbstractFormatterFactory</code> used to lookup
387      * instances of <code>AbstractFormatter</code>
388      * @beaninfo
389      * bound: true
390      * attribute: visualUpdate true
391      * description: AbstractFormatterFactory, responsible for returning an
392      * AbstractFormatter that can format the current value.
393      */

394     public void setFormatterFactory(AbstractFormatterFactory tf) {
395         AbstractFormatterFactory oldFactory = factory;
396
397         factory = tf;
398         firePropertyChange("formatterFactory", oldFactory, tf);
399         setValue(getValue(), true, false);
400     }
401
402     /**
403      * Returns the current <code>AbstractFormatterFactory</code>.
404      *
405      * @see #setFormatterFactory
406      * @return <code>AbstractFormatterFactory</code> used to determine
407      * <code>AbstractFormatter</code>s
408      */

409     public AbstractFormatterFactory getFormatterFactory() {
410         return factory;
411     }
412
413     /**
414      * Sets the current <code>AbstractFormatter</code>.
415      * <p>
416      * You should not normally invoke this, instead set the
417      * <code>AbstractFormatterFactory</code> or set the value.
418      * <code>JFormattedTextField</code> will
419      * invoke this as the state of the <code>JFormattedTextField</code>
420      * changes and requires the value to be reset.
421      * <code>JFormattedTextField</code> passes in the
422      * <code>AbstractFormatter</code> obtained from the
423      * <code>AbstractFormatterFactory</code>.
424      * <p>
425      * This is a JavaBeans bound property.
426      *
427      * @see #setFormatterFactory
428      * @param format AbstractFormatter to use for formatting
429      * @beaninfo
430      * bound: true
431      * attribute: visualUpdate true
432      * description: TextFormatter, responsible for formatting the current value
433      */

434     protected void setFormatter(AbstractFormatter format) {
435         AbstractFormatter oldFormat = this.format;
436
437         if (oldFormat != null) {
438             oldFormat.uninstall();
439         }
440         setEditValid(true);
441         this.format = format;
442         if (format != null) {
443             format.install(this);
444         }
445         setEdited(false);
446         firePropertyChange("textFormatter", oldFormat, format);
447     }
448
449     /**
450      * Returns the <code>AbstractFormatter</code> that is used to format and
451      * parse the current value.
452      *
453      * @return AbstractFormatter used for formatting
454      */

455     public AbstractFormatter getFormatter() {
456         return format;
457     }
458
459     /**
460      * Sets the value that will be formatted by an
461      * <code>AbstractFormatter</code> obtained from the current
462      * <code>AbstractFormatterFactory</code>. If no
463      * <code>AbstractFormatterFactory</code> has been specified, this will
464      * attempt to create one based on the type of <code>value</code>.
465      * <p>
466      * The default value of this property is null.
467      * <p>
468      * This is a JavaBeans bound property.
469      *
470      * @param value Current value to display
471      * @beaninfo
472      * bound: true
473      * attribute: visualUpdate true
474      * description: The value to be formatted.
475      */

476     public void setValue(Object JavaDoc value) {
477         if (value != null && getFormatterFactory() == null) {
478             setFormatterFactory(getDefaultFormatterFactory(value));
479         }
480         setValue(value, true, true);
481     }
482
483     /**
484      * Returns the last valid value. Based on the editing policy of
485      * the <code>AbstractFormatter</code> this may not return the current
486      * value. The currently edited value can be obtained by invoking
487      * <code>commitEdit</code> followed by <code>getValue</code>.
488      *
489      * @return Last valid value
490      */

491     public Object JavaDoc getValue() {
492         return value;
493     }
494
495     /**
496      * Forces the current value to be taken from the
497      * <code>AbstractFormatter</code> and set as the current value.
498      * This has no effect if there is no current
499      * <code>AbstractFormatter</code> installed.
500      *
501      * @throws ParseException if the <code>AbstractFormatter</code> is not able
502      * to format the current value
503      */

504     public void commitEdit() throws ParseException {
505         AbstractFormatter format = getFormatter();
506
507         if (format != null) {
508             setValue(format.stringToValue(getText()), false, true);
509         }
510     }
511
512     /**
513      * Sets the validity of the edit on the receiver. You should not normally
514      * invoke this. This will be invoked by the
515      * <code>AbstractFormatter</code> as the user edits the value.
516      * <p>
517      * Not all formatters will allow the component to get into an invalid
518      * state, and thus this may never be invoked.
519      * <p>
520      * Based on the look and feel this may visually change the state of
521      * the receiver.
522      *
523      * @param isValid boolean indicating if the currently edited value is
524      * valid.
525      * @beaninfo
526      * bound: true
527      * attribute: visualUpdate true
528      * description: True indicates the edited value is valid
529      */

530     private void setEditValid(boolean isValid) {
531         if (isValid != editValid) {
532             editValid = isValid;
533             firePropertyChange("editValid", Boolean.valueOf(!isValid),
534                                Boolean.valueOf(isValid));
535         }
536     }
537
538     /**
539      * Returns true if the current value being edited is valid. The value of
540      * this is managed by the current <code>AbstractFormatter</code>, as such
541      * there is no public setter for it.
542      *
543      * @return true if the current value being edited is valid.
544      */

545     public boolean isEditValid() {
546         return editValid;
547     }
548
549     /**
550      * Invoked when the user inputs an invalid value. This gives the
551      * component a chance to provide feedback. The default
552      * implementation beeps.
553      */

554     protected void invalidEdit() {
555     UIManager.getLookAndFeel().provideErrorFeedback(JFormattedTextField.this);
556     }
557     
558     /**
559      * Processes any input method events, such as
560      * <code>InputMethodEvent.INPUT_METHOD_TEXT_CHANGED</code> or
561      * <code>InputMethodEvent.CARET_POSITION_CHANGED</code>.
562      *
563      * @param e the <code>InputMethodEvent</code>
564      * @see InputMethodEvent
565      */

566     protected void processInputMethodEvent(InputMethodEvent e) {
567     AttributedCharacterIterator text = e.getText();
568     int commitCount = e.getCommittedCharacterCount();
569
570     // Keep track of the composed text
571
if (text != null) {
572         int begin = text.getBeginIndex();
573         int end = text.getEndIndex();
574         composedTextExists = ((end - begin) > commitCount);
575     } else {
576         composedTextExists = false;
577     }
578
579     super.processInputMethodEvent(e);
580     }
581
582     /**
583      * Processes any focus events, such as
584      * <code>FocusEvent.FOCUS_GAINED</code> or
585      * <code>FocusEvent.FOCUS_LOST</code>.
586      *
587      * @param e the <code>FocusEvent</code>
588      * @see FocusEvent
589      */

590     protected void processFocusEvent(FocusEvent e) {
591         super.processFocusEvent(e);
592
593     // ignore temporary focus event
594
if (e.isTemporary()) {
595         return;
596     }
597
598         if (isEdited() && e.getID() == FocusEvent.FOCUS_LOST) {
599         InputContext JavaDoc ic = getInputContext();
600         if (focusLostHandler == null) {
601         focusLostHandler = new FocusLostHandler();
602         }
603         
604         // if there is a composed text, process it first
605
if ((ic != null) && composedTextExists) {
606         ic.endComposition();
607         EventQueue.invokeLater(focusLostHandler);
608         } else {
609         focusLostHandler.run();
610         }
611         }
612         else if (!isEdited()) {
613             // reformat
614
setValue(getValue(), true, true);
615         }
616     }
617
618     /**
619      * FOCUS_LOST behavior implementation
620      */

621     private class FocusLostHandler implements Runnable JavaDoc, Serializable {
622     public void run() {
623             int fb = JFormattedTextField.this.getFocusLostBehavior();
624             if (fb == JFormattedTextField.COMMIT ||
625             fb == JFormattedTextField.COMMIT_OR_REVERT) {
626                 try {
627                     JFormattedTextField.this.commitEdit();
628                     // Give it a chance to reformat.
629
JFormattedTextField.this.setValue(
630                 JFormattedTextField.this.getValue(), true, true);
631                 } catch (ParseException pe) {
632                     if (fb == JFormattedTextField.this.COMMIT_OR_REVERT) {
633                         JFormattedTextField.this.setValue(
634                 JFormattedTextField.this.getValue(), true, true);
635                     }
636                 }
637             }
638             else if (fb == JFormattedTextField.REVERT) {
639                 JFormattedTextField.this.setValue(
640             JFormattedTextField.this.getValue(), true, true);
641             }
642     }
643     }
644
645     /**
646      * Fetches the command list for the editor. This is
647      * the list of commands supported by the plugged-in UI
648      * augmented by the collection of commands that the
649      * editor itself supports. These are useful for binding
650      * to events, such as in a keymap.
651      *
652      * @return the command list
653      */

654     public Action JavaDoc[] getActions() {
655         return TextAction.augmentList(super.getActions(), defaultActions);
656     }
657
658     /**
659      * Gets the class ID for a UI.
660      *
661      * @return the string "FormattedTextFieldUI"
662      * @see JComponent#getUIClassID
663      */

664     public String JavaDoc getUIClassID() {
665         return uiClassID;
666     }
667
668     /**
669      * Associates the editor with a text document.
670      * The currently registered factory is used to build a view for
671      * the document, which gets displayed by the editor after revalidation.
672      * A PropertyChange event ("document") is propagated to each listener.
673      *
674      * @param doc the document to display/edit
675      * @see #getDocument
676      * @beaninfo
677      * description: the text document model
678      * bound: true
679      * expert: true
680      */

681     public void setDocument(Document doc) {
682         if (documentListener != null && getDocument() != null) {
683             getDocument().removeDocumentListener(documentListener);
684         }
685         super.setDocument(doc);
686         if (documentListener == null) {
687             documentListener = new DocumentHandler();
688         }
689         doc.addDocumentListener(documentListener);
690     }
691
692     /*
693      * See readObject and writeObject in JComponent for more
694      * information about serialization in Swing.
695      *
696      * @param s Stream to write to
697      */

698     private void writeObject(ObjectOutputStream s) throws IOException {
699         s.defaultWriteObject();
700         if (getUIClassID().equals(uiClassID)) {
701             byte count = JComponent.getWriteObjCounter(this);
702             JComponent.setWriteObjCounter(this, --count);
703             if (count == 0 && ui != null) {
704                 ui.installUI(this);
705             }
706         }
707     }
708
709     /**
710      * Resets the Actions that come from the TextFormatter to
711      * <code>actions</code>.
712      */

713     private void setFormatterActions(Action JavaDoc[] actions) {
714         if (actions == null) {
715             if (textFormatterActionMap != null) {
716                 textFormatterActionMap.clear();
717             }
718         }
719         else {
720             if (textFormatterActionMap == null) {
721                 ActionMap JavaDoc map = getActionMap();
722
723                 textFormatterActionMap = new ActionMap JavaDoc();
724                 while (map != null) {
725                     ActionMap JavaDoc parent = map.getParent();
726
727                     if (parent instanceof UIResource JavaDoc || parent == null) {
728                         map.setParent(textFormatterActionMap);
729                         textFormatterActionMap.setParent(parent);
730                         break;
731                     }
732                     map = parent;
733                 }
734             }
735             for (int counter = actions.length - 1; counter >= 0;
736                  counter--) {
737                 Object JavaDoc key = actions[counter].getValue(Action.NAME);
738
739                 if (key != null) {
740                     textFormatterActionMap.put(key, actions[counter]);
741                 }
742             }
743         }
744     }
745
746     /**
747      * Does the setting of the value. If <code>createFormat</code> is true,
748      * this will also obtain a new <code>AbstractFormatter</code> from the
749      * current factory. The property change event will be fired if
750      * <code>firePC</code> is true.
751      */

752     private void setValue(Object JavaDoc value, boolean createFormat, boolean firePC) {
753         Object JavaDoc oldValue = this.value;
754
755         this.value = value;
756
757         if (createFormat) {
758             AbstractFormatterFactory factory = getFormatterFactory();
759             AbstractFormatter atf;
760
761             if (factory != null) {
762                 atf = factory.getFormatter(this);
763             }
764             else {
765                 atf = null;
766             }
767             setFormatter(atf);
768         }
769         else {
770             // Assumed to be valid
771
setEditValid(true);
772         }
773
774         setEdited(false);
775
776     if (firePC) {
777         firePropertyChange("value", oldValue, value);
778     }
779     }
780
781     /**
782      * Sets the edited state of the receiver.
783      */

784     private void setEdited(boolean edited) {
785         this.edited = edited;
786     }
787
788     /**
789      * Returns true if the receiver has been edited.
790      */

791     private boolean isEdited() {
792         return edited;
793     }
794
795     /**
796      * Returns an AbstractFormatterFactory suitable for the passed in
797      * Object type.
798      */

799     private AbstractFormatterFactory getDefaultFormatterFactory(Object JavaDoc type) {
800         if (type instanceof DateFormat) {
801             return new DefaultFormatterFactory(new DateFormatter
802                                                ((DateFormat)type));
803         }
804         if (type instanceof NumberFormat) {
805             return new DefaultFormatterFactory(new NumberFormatter(
806                                                (NumberFormat)type));
807         }
808         if (type instanceof Format) {
809             return new DefaultFormatterFactory(new InternationalFormatter(
810                                                (Format)type));
811         }
812         if (type instanceof Date) {
813             return new DefaultFormatterFactory(new DateFormatter());
814         }
815         if (type instanceof Number JavaDoc) {
816             AbstractFormatter displayFormatter = new NumberFormatter();
817         ((NumberFormatter)displayFormatter).setValueClass(type.getClass());
818             AbstractFormatter editFormatter = new NumberFormatter(
819                                   new DecimalFormat("#.#"));
820         ((NumberFormatter)editFormatter).setValueClass(type.getClass());
821
822             return new DefaultFormatterFactory(displayFormatter,
823                                                displayFormatter,editFormatter);
824         }
825         return new DefaultFormatterFactory(new DefaultFormatter());
826     }
827
828
829     /**
830      * Instances of <code>AbstractFormatterFactory</code> are used by
831      * <code>JFormattedTextField</code> to obtain instances of
832      * <code>AbstractFormatter</code> which in turn are used to format values.
833      * <code>AbstractFormatterFactory</code> can return different
834      * <code>AbstractFormatter</code>s based on the state of the
835      * <code>JFormattedTextField</code>, perhaps returning different
836      * <code>AbstractFormatter</code>s when the
837      * <code>JFormattedTextField</code> has focus vs when it
838      * doesn't have focus.
839      */

840     public static abstract class AbstractFormatterFactory {
841         /**
842          * Returns an <code>AbstractFormatter</code> that can handle formatting
843          * of the passed in <code>JFormattedTextField</code>.
844          *
845          * @param tf JFormattedTextField requesting AbstractFormatter
846          * @return AbstractFormatter to handle formatting duties, a null
847          * return value implies the JFormattedTextField should behave
848          * like a normal JTextField
849          */

850         public abstract AbstractFormatter getFormatter(JFormattedTextField JavaDoc tf);
851     }
852
853
854     /**
855      * Instances of <code>AbstractFormatter</code> are used by
856      * <code>JFormattedTextField</code> to handle the conversion both
857      * from an Object to a String, and back from a String to an Object.
858      * <code>AbstractFormatter</code>s can also enfore editing policies,
859      * or navigation policies, or manipulate the
860      * <code>JFormattedTextField</code> in any way it sees fit to
861      * enforce the desired policy.
862      * <p>
863      * An <code>AbstractFormatter</code> can only be active in
864      * one <code>JFormattedTextField</code> at a time.
865      * <code>JFormattedTextField</code> invokes
866      * <code>install</code> when it is ready to use it followed
867      * by <code>uninstall</code> when done. Subclasses
868      * that wish to install additional state should override
869      * <code>install</code> and message super appropriately.
870      * <p>
871      * Subclasses must override the conversion methods
872      * <code>stringToValue</code> and <code>valueToString</code>. Optionally
873      * they can override <code>getActions</code>,
874      * <code>getNavigationFilter</code> and <code>getDocumentFilter</code>
875      * to restrict the <code>JFormattedTextField</code> in a particular
876      * way.
877      * <p>
878      * Subclasses that allow the <code>JFormattedTextField</code> to be in
879      * a temporarily invalid state should invoke <code>setEditValid</code>
880      * at the appropriate times.
881      */

882     public static abstract class AbstractFormatter implements Serializable {
883         private JFormattedTextField JavaDoc ftf;
884
885         /**
886          * Installs the <code>AbstractFormatter</code> onto a particular
887          * <code>JFormattedTextField</code>.
888          * This will invoke <code>valueToString</code> to convert the
889          * current value from the <code>JFormattedTextField</code> to
890          * a String. This will then install the <code>Action</code>s from
891          * <code>getActions</code>, the <code>DocumentFilter</code>
892          * returned from <code>getDocumentFilter</code> and the
893          * <code>NavigationFilter</code> returned from
894          * <code>getNavigationFilter</code> onto the
895          * <code>JFormattedTextField</code>.
896          * <p>
897          * Subclasses will typically only need to override this if they
898          * wish to install additional listeners on the
899          * <code>JFormattedTextField</code>.
900          * <p>
901          * If there is a <code>ParseException</code> in converting the
902          * current value to a String, this will set the text to an empty
903          * String, and mark the <code>JFormattedTextField</code> as being
904          * in an invalid state.
905          * <p>
906          * While this is a public method, this is typically only useful
907          * for subclassers of <code>JFormattedTextField</code>.
908          * <code>JFormattedTextField</code> will invoke this method at
909          * the appropriate times when the value changes, or its internal
910          * state changes. You will only need to invoke this yourself if
911          * you are subclassing <code>JFormattedTextField</code> and
912          * installing/uninstalling <code>AbstractFormatter</code> at a
913          * different time than <code>JFormattedTextField</code> does.
914          *
915          * @param ftf JFormattedTextField to format for, may be null indicating
916          * uninstall from current JFormattedTextField.
917          */

918         public void install(JFormattedTextField JavaDoc ftf) {
919             if (this.ftf != null) {
920                 uninstall();
921             }
922             this.ftf = ftf;
923             if (ftf != null) {
924                 try {
925                     ftf.setText(valueToString(ftf.getValue()));
926                 } catch (ParseException pe) {
927                     ftf.setText("");
928                     setEditValid(false);
929                 }
930                 installDocumentFilter(getDocumentFilter());
931                 ftf.setNavigationFilter(getNavigationFilter());
932                 ftf.setFormatterActions(getActions());
933             }
934         }
935
936         /**
937          * Uninstalls any state the <code>AbstractFormatter</code> may have
938          * installed on the <code>JFormattedTextField</code>. This resets the
939          * <code>DocumentFilter</code>, <code>NavigationFilter</code>
940          * and additional <code>Action</code>s installed on the
941          * <code>JFormattedTextField</code>.
942          */

943         public void uninstall() {
944             if (this.ftf != null) {
945                 installDocumentFilter(null);
946                 this.ftf.setNavigationFilter(null);
947                 this.ftf.setFormatterActions(null);
948             }
949         }
950
951         /**
952          * Parses <code>text</code> returning an arbitrary Object. Some
953          * formatters may return null.
954          *
955          * @throws ParseException if there is an error in the conversion
956          * @param text String to convert
957          * @return Object representation of text
958          */

959         public abstract Object JavaDoc stringToValue(String JavaDoc text) throws
960                                      ParseException;
961
962         /**
963          * Returns the string value to display for <code>value</code>.
964          *
965          * @throws ParseException if there is an error in the conversion
966          * @param value Value to convert
967          * @return String representation of value
968          */

969         public abstract String JavaDoc valueToString(Object JavaDoc value) throws
970                         ParseException;
971
972         /**
973          * Returns the current <code>JFormattedTextField</code> the
974          * <code>AbstractFormatter</code> is installed on.
975          *
976          * @return JFormattedTextField formatting for.
977          */

978         protected JFormattedTextField JavaDoc getFormattedTextField() {
979             return ftf;
980         }
981
982         /**
983          * This should be invoked when the user types an invalid character.
984          * This forwards the call to the current JFormattedTextField.
985          */

986         protected void invalidEdit() {
987             JFormattedTextField JavaDoc ftf = getFormattedTextField();
988
989             if (ftf != null) {
990                 ftf.invalidEdit();
991             }
992         }
993
994         /**
995          * Invoke this to update the <code>editValid</code> property of the
996          * <code>JFormattedTextField</code>. If you an enforce a policy
997          * such that the <code>JFormattedTextField</code> is always in a
998          * valid state, you will never need to invoke this.
999          *
1000         * @param valid Valid state of the JFormattedTextField
1001         */

1002        protected void setEditValid(boolean valid) {
1003            JFormattedTextField JavaDoc ftf = getFormattedTextField();
1004
1005            if (ftf != null) {
1006                ftf.setEditValid(valid);
1007            }
1008        }
1009
1010        /**
1011         * Subclass and override if you wish to provide a custom set of
1012         * <code>Action</code>s. <code>install</code> will install these
1013         * on the <code>JFormattedTextField</code>'s <code>ActionMap</code>.
1014         *
1015         * @return Array of Actions to install on JFormattedTextField
1016         */

1017        protected Action JavaDoc[] getActions() {
1018            return null;
1019        }
1020
1021        /**
1022         * Subclass and override if you wish to provide a
1023         * <code>DocumentFilter</code> to restrict what can be input.
1024         * <code>install</code> will install the returned value onto
1025         * the <code>JFormattedTextField</code>.
1026         *
1027         * @return DocumentFilter to restrict edits
1028         */

1029        protected DocumentFilter getDocumentFilter() {
1030            return null;
1031        }
1032
1033        /**
1034         * Subclass and override if you wish to provide a filter to restrict
1035         * where the user can navigate to.
1036         * <code>install</code> will install the returned value onto
1037         * the <code>JFormattedTextField</code>.
1038         *
1039         * @return NavigationFilter to restrict navigation
1040         */

1041        protected NavigationFilter getNavigationFilter() {
1042            return null;
1043        }
1044
1045        /**
1046         * Clones the <code>AbstractFormatter</code>. The returned instance
1047         * is not associated with a <code>JFormattedTextField</code>.
1048         *
1049         * @return Copy of the AbstractFormatter
1050         */

1051        protected Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
1052            AbstractFormatter formatter = (AbstractFormatter)super.clone();
1053
1054            formatter.ftf = null;
1055            return formatter;
1056        }
1057
1058        /**
1059         * Installs the <code>DocumentFilter</code> <code>filter</code>
1060         * onto the current <code>JFormattedTextField</code>.
1061         *
1062         * @param filter DocumentFilter to install on the Document.
1063         */

1064        private void installDocumentFilter(DocumentFilter filter) {
1065            JFormattedTextField JavaDoc ftf = getFormattedTextField();
1066
1067            if (ftf != null) {
1068                Document doc = ftf.getDocument();
1069
1070                if (doc instanceof AbstractDocument) {
1071                    ((AbstractDocument)doc).setDocumentFilter(filter);
1072                }
1073                doc.putProperty(DocumentFilter.class, null);
1074            }
1075        }
1076    }
1077
1078
1079    /**
1080     * Used to commit the edit. This extends JTextField.NotifyAction
1081     * so that <code>isEnabled</code> is true while a JFormattedTextField
1082     * has focus, and extends <code>actionPerformed</code> to invoke
1083     * commitEdit.
1084     */

1085    static class CommitAction extends JTextField.NotifyAction JavaDoc {
1086        public void actionPerformed(ActionEvent e) {
1087            JTextComponent target = getFocusedComponent();
1088
1089            if (target instanceof JFormattedTextField JavaDoc) {
1090                // Attempt to commit the value
1091
try {
1092                    ((JFormattedTextField JavaDoc)target).commitEdit();
1093                } catch (ParseException pe) {
1094            ((JFormattedTextField JavaDoc)target).invalidEdit();
1095                    // value not commited, don't notify ActionListeners
1096
return;
1097                }
1098            }
1099            // Super behavior.
1100
super.actionPerformed(e);
1101        }
1102
1103        public boolean isEnabled() {
1104            JTextComponent target = getFocusedComponent();
1105            if (target instanceof JFormattedTextField JavaDoc) {
1106        JFormattedTextField JavaDoc ftf = (JFormattedTextField JavaDoc)target;
1107        if (!ftf.isEdited()) {
1108            return false;
1109        }
1110                return true;
1111            }
1112            return super.isEnabled();
1113        }
1114    }
1115
1116
1117    /**
1118     * CancelAction will reset the value in the JFormattedTextField when
1119     * <code>actionPerformed</code> is invoked. It will only be
1120     * enabled if the focused component is an instance of
1121     * JFormattedTextField.
1122     */

1123    private static class CancelAction extends TextAction {
1124        public CancelAction() {
1125            super("reset-field-edit");
1126        }
1127
1128        public void actionPerformed(ActionEvent e) {
1129            JTextComponent target = getFocusedComponent();
1130
1131            if (target instanceof JFormattedTextField JavaDoc) {
1132                JFormattedTextField JavaDoc ftf = (JFormattedTextField JavaDoc)target;
1133                ftf.setValue(ftf.getValue());
1134            }
1135        }
1136
1137        public boolean isEnabled() {
1138            JTextComponent target = getFocusedComponent();
1139            if (target instanceof JFormattedTextField JavaDoc) {
1140        JFormattedTextField JavaDoc ftf = (JFormattedTextField JavaDoc)target;
1141        if (!ftf.isEdited()) {
1142            return false;
1143        }
1144                return true;
1145            }
1146            return super.isEnabled();
1147        }
1148    }
1149
1150
1151    /**
1152     * Sets the dirty state as the document changes.
1153     */

1154    private class DocumentHandler implements DocumentListener, Serializable {
1155        public void insertUpdate(DocumentEvent e) {
1156            setEdited(true);
1157        }
1158        public void removeUpdate(DocumentEvent e) {
1159            setEdited(true);
1160        }
1161        public void changedUpdate(DocumentEvent e) {}
1162    }
1163}
1164
Popular Tags