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