History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/JFormattedTextField.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/JFormattedTextField.java+1187−0
@@ -0,0 +1,1187 @@
1+/*
2+ * Copyright (c) 2000, 2006, 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 align="left">Value</p></th><th><p 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 commited. 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 querired when focus is
133+ * lost or gained.
134+ * <p>
135+ * <code>JFormattedTextField.AbstractFormatter</code>
136+ * is also responsible for determining when values are commited 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<sup><font size="-2">TM</font></sup>
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 commiting 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 commiting 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 enfore 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 commited, 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+}