1 /* 2 * Copyright (c) 1997, 2018, 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 javax.swing.text.*; 28 import javax.accessibility.*; 29 30 import java.beans.JavaBean; 31 import java.beans.BeanProperty; 32 import java.io.ObjectOutputStream; 33 import java.io.IOException; 34 import java.util.Arrays; 35 36 /** 37 * <code>JPasswordField</code> is a lightweight component that allows 38 * the editing of a single line of text where the view indicates 39 * something was typed, but does not show the original characters. 40 * You can find further information and examples in 41 * <a href="https://docs.oracle.com/javase/tutorial/uiswing/components/textfield.html">How to Use Text Fields</a>, 42 * a section in <em>The Java Tutorial.</em> 43 * <p> 44 * <code>JPasswordField</code> is intended 45 * to be source-compatible with <code>java.awt.TextField</code> 46 * used with <code>echoChar</code> set. It is provided separately 47 * to make it easier to safely change the UI for the 48 * <code>JTextField</code> without affecting password entries. 49 * <p> 50 * <strong>NOTE:</strong> 51 * By default, JPasswordField disables input methods; otherwise, input 52 * characters could be visible while they were composed using input methods. 53 * If an application needs the input methods support, please use the 54 * inherited method, <code>enableInputMethods(true)</code>. 55 * <p> 56 * <strong>Warning:</strong> Swing is not thread safe. For more 57 * information see <a 58 * href="package-summary.html#threading">Swing's Threading 59 * Policy</a>. 60 * <p> 61 * <strong>Warning:</strong> 62 * Serialized objects of this class will not be compatible with 63 * future Swing releases. The current serialization support is 64 * appropriate for short term storage or RMI between applications running 65 * the same version of Swing. As of 1.4, support for long term storage 66 * of all JavaBeans™ 67 * has been added to the <code>java.beans</code> package. 68 * Please see {@link java.beans.XMLEncoder}. 69 * 70 * @author Timothy Prinzing 71 * @since 1.2 72 */ 73 @JavaBean(description = "Allows the editing of a line of text but doesn't show the characters.") 74 @SwingContainer(false) 75 @SuppressWarnings("serial") // Same-version serialization only 76 public class JPasswordField extends JTextField { 77 78 /** 79 * Constructs a new <code>JPasswordField</code>, 80 * with a default document, <code>null</code> starting 81 * text string, and 0 column width. 82 */ 83 public JPasswordField() { 84 this(null,null,0); 85 } 86 87 /** 88 * Constructs a new <code>JPasswordField</code> initialized 89 * with the specified text. The document model is set to the 90 * default, and the number of columns to 0. 91 * 92 * @param text the text to be displayed, <code>null</code> if none 93 */ 94 public JPasswordField(String text) { 95 this(null, text, 0); 96 } 97 98 /** 99 * Constructs a new empty <code>JPasswordField</code> with the specified 100 * number of columns. A default model is created, and the initial string 101 * is set to <code>null</code>. 102 * 103 * @param columns the number of columns >= 0 104 */ 105 public JPasswordField(int columns) { 106 this(null, null, columns); 107 } 108 109 /** 110 * Constructs a new <code>JPasswordField</code> initialized with 111 * the specified text and columns. The document model is set to 112 * the default. 113 * 114 * @param text the text to be displayed, <code>null</code> if none 115 * @param columns the number of columns >= 0 116 */ 117 public JPasswordField(String text, int columns) { 118 this(null, text, columns); 119 } 120 121 /** 122 * Constructs a new <code>JPasswordField</code> that uses the 123 * given text storage model and the given number of columns. 124 * This is the constructor through which the other constructors feed. 125 * The echo character is set to '*', but may be changed by the current 126 * Look and Feel. If the document model is 127 * <code>null</code>, a default one will be created. 128 * 129 * @param doc the text storage to use 130 * @param txt the text to be displayed, <code>null</code> if none 131 * @param columns the number of columns to use to calculate 132 * the preferred width >= 0; if columns is set to zero, the 133 * preferred width will be whatever naturally results from 134 * the component implementation 135 */ 136 public JPasswordField(Document doc, String txt, int columns) { 137 super(doc, txt, columns); 138 // We could either leave this on, which wouldn't be secure, 139 // or obscure the composted text, which essentially makes displaying 140 // it useless. Therefore, we turn off input methods. 141 enableInputMethods(false); 142 } 143 144 /** 145 * Returns the name of the L&F class that renders this component. 146 * 147 * @return the string "PasswordFieldUI" 148 * @see JComponent#getUIClassID 149 * @see UIDefaults#getUI 150 */ 151 @BeanProperty(bound = false) 152 public String getUIClassID() { 153 return uiClassID; 154 } 155 156 157 /** 158 * {@inheritDoc} 159 * @since 1.6 160 */ 161 public void updateUI() { 162 if(!echoCharSet) { 163 echoChar = '*'; 164 } 165 super.updateUI(); 166 } 167 168 /** 169 * Returns the character to be used for echoing. The default is '*'. 170 * The default may be different depending on the currently running Look 171 * and Feel. For example, Metal/Ocean's default is a bullet character. 172 * 173 * @return the echo character, 0 if unset 174 * @see #setEchoChar 175 * @see #echoCharIsSet 176 */ 177 public char getEchoChar() { 178 return echoChar; 179 } 180 181 /** 182 * Sets the echo character for this <code>JPasswordField</code>. 183 * Note that this is largely a suggestion, since the 184 * view that gets installed can use whatever graphic techniques 185 * it desires to represent the field. Setting a value of 0 indicates 186 * that you wish to see the text as it is typed, similar to 187 * the behavior of a standard <code>JTextField</code>. 188 * 189 * @param c the echo character to display 190 * @see #echoCharIsSet 191 * @see #getEchoChar 192 */ 193 @BeanProperty(bound = false, visualUpdate = true, description 194 = "character to display in place of the real characters") 195 public void setEchoChar(char c) { 196 echoChar = c; 197 echoCharSet = true; 198 repaint(); 199 revalidate(); 200 } 201 202 /** 203 * Returns true if this <code>JPasswordField</code> has a character 204 * set for echoing. A character is considered to be set if the echo 205 * character is not 0. 206 * 207 * @return true if a character is set for echoing 208 * @see #setEchoChar 209 * @see #getEchoChar 210 */ 211 public boolean echoCharIsSet() { 212 return echoChar != 0; 213 } 214 215 // --- JTextComponent methods ---------------------------------- 216 217 /** 218 * Invokes <code>provideErrorFeedback</code> on the current 219 * look and feel, which typically initiates an error beep. 220 * The normal behavior of transferring the 221 * currently selected range in the associated text model 222 * to the system clipboard, and removing the contents from 223 * the model, is not acceptable for a password field. 224 */ 225 public void cut() { 226 if (getClientProperty("JPasswordField.cutCopyAllowed") != Boolean.TRUE) { 227 UIManager.getLookAndFeel().provideErrorFeedback(this); 228 } else { 229 super.cut(); 230 } 231 } 232 233 /** 234 * Invokes <code>provideErrorFeedback</code> on the current 235 * look and feel, which typically initiates an error beep. 236 * The normal behavior of transferring the 237 * currently selected range in the associated text model 238 * to the system clipboard, and leaving the contents from 239 * the model, is not acceptable for a password field. 240 */ 241 public void copy() { 242 if (getClientProperty("JPasswordField.cutCopyAllowed") != Boolean.TRUE) { 243 UIManager.getLookAndFeel().provideErrorFeedback(this); 244 } else { 245 super.copy(); 246 } 247 } 248 249 /** 250 * Returns the text contained in this <code>TextComponent</code>. 251 * If the underlying document is <code>null</code>, will give a 252 * <code>NullPointerException</code>. 253 * <p> 254 * For security reasons, this method is deprecated. Use the 255 <code>* getPassword</code> method instead. 256 * @deprecated As of Java 2 platform v1.2, 257 * replaced by <code>getPassword</code>. 258 * @return the text 259 */ 260 @Deprecated 261 public String getText() { 262 return super.getText(); 263 } 264 265 /** 266 * Fetches a portion of the text represented by the 267 * component. Returns an empty string if length is 0. 268 * <p> 269 * For security reasons, this method is deprecated. Use the 270 * <code>getPassword</code> method instead. 271 * @deprecated As of Java 2 platform v1.2, 272 * replaced by <code>getPassword</code>. 273 * @param offs the offset >= 0 274 * @param len the length >= 0 275 * @return the text 276 * @exception BadLocationException if the offset or length are invalid 277 */ 278 @Deprecated 279 public String getText(int offs, int len) throws BadLocationException { 280 return super.getText(offs, len); 281 } 282 283 /** 284 * Returns the text contained in this <code>TextComponent</code>. 285 * If the underlying document is <code>null</code>, will give a 286 * <code>NullPointerException</code>. For stronger 287 * security, it is recommended that the returned character array be 288 * cleared after use by setting each character to zero. 289 * 290 * @return the text 291 */ 292 @BeanProperty(bound = false) 293 public char[] getPassword() { 294 Document doc = getDocument(); 295 Segment txt = new Segment(); 296 try { 297 doc.getText(0, doc.getLength(), txt); // use the non-String API 298 } catch (BadLocationException e) { 299 return null; 300 } 301 char[] retValue = new char[txt.count]; 302 System.arraycopy(txt.array, txt.offset, retValue, 0, txt.count); 303 return retValue; 304 } 305 306 /** 307 * See readObject() and writeObject() in JComponent for more 308 * information about serialization in Swing. 309 */ 310 private void writeObject(ObjectOutputStream s) throws IOException { 311 s.defaultWriteObject(); 312 if (getUIClassID().equals(uiClassID)) { 313 byte count = JComponent.getWriteObjCounter(this); 314 JComponent.setWriteObjCounter(this, --count); 315 if (count == 0 && ui != null) { 316 ui.installUI(this); 317 } 318 } 319 } 320 321 // --- variables ----------------------------------------------- 322 323 /** 324 * @see #getUIClassID 325 * @see #readObject 326 */ 327 private static final String uiClassID = "PasswordFieldUI"; 328 329 private char echoChar; 330 331 private boolean echoCharSet = false; 332 333 334 /** 335 * Returns a string representation of this <code>JPasswordField</code>. 336 * This method is intended to be used only for debugging purposes, and the 337 * content and format of the returned string may vary between 338 * implementations. The returned string may be empty but may not 339 * be <code>null</code>. 340 * 341 * @return a string representation of this <code>JPasswordField</code> 342 */ 343 protected String paramString() { 344 return super.paramString() + 345 ",echoChar=" + echoChar; 346 } 347 348 349 /** 350 * This method is a hack to get around the fact that we cannot 351 * directly override setUIProperty because part of the inheritance hierarchy 352 * goes outside of the javax.swing package, and therefore calling a package 353 * private method isn't allowed. This method should return true if the property 354 * was handled, and false otherwise. 355 */ 356 boolean customSetUIProperty(String propertyName, Object value) { 357 if (propertyName == "echoChar") { 358 if (!echoCharSet) { 359 setEchoChar((Character)value); 360 echoCharSet = false; 361 } 362 return true; 363 } 364 return false; 365 } 366 367 ///////////////// 368 // Accessibility support 369 //////////////// 370 371 372 /** 373 * Returns the <code>AccessibleContext</code> associated with this 374 * <code>JPasswordField</code>. For password fields, the 375 * <code>AccessibleContext</code> takes the form of an 376 * <code>AccessibleJPasswordField</code>. 377 * A new <code>AccessibleJPasswordField</code> instance is created 378 * if necessary. 379 * 380 * @return an <code>AccessibleJPasswordField</code> that serves as the 381 * <code>AccessibleContext</code> of this 382 * <code>JPasswordField</code> 383 */ 384 @BeanProperty(bound = false) 385 public AccessibleContext getAccessibleContext() { 386 if (accessibleContext == null) { 387 accessibleContext = new AccessibleJPasswordField(); 388 } 389 return accessibleContext; 390 } 391 392 /** 393 * This class implements accessibility support for the 394 * <code>JPasswordField</code> class. It provides an implementation of the 395 * Java Accessibility API appropriate to password field user-interface 396 * elements. 397 * <p> 398 * <strong>Warning:</strong> 399 * Serialized objects of this class will not be compatible with 400 * future Swing releases. The current serialization support is 401 * appropriate for short term storage or RMI between applications running 402 * the same version of Swing. As of 1.4, support for long term storage 403 * of all JavaBeans™ 404 * has been added to the <code>java.beans</code> package. 405 * Please see {@link java.beans.XMLEncoder}. 406 */ 407 protected class AccessibleJPasswordField extends AccessibleJTextField { 408 409 /** 410 * Gets the role of this object. 411 * 412 * @return an instance of AccessibleRole describing the role of the 413 * object (AccessibleRole.PASSWORD_TEXT) 414 * @see AccessibleRole 415 */ 416 public AccessibleRole getAccessibleRole() { 417 return AccessibleRole.PASSWORD_TEXT; 418 } 419 420 /** 421 * Gets the <code>AccessibleText</code> for the <code>JPasswordField</code>. 422 * The returned object also implements the 423 * <code>AccessibleExtendedText</code> interface. 424 * 425 * @return <code>AccessibleText</code> for the JPasswordField 426 * @see javax.accessibility.AccessibleContext 427 * @see javax.accessibility.AccessibleContext#getAccessibleText 428 * @see javax.accessibility.AccessibleText 429 * @see javax.accessibility.AccessibleExtendedText 430 * 431 * @since 1.6 432 */ 433 public AccessibleText getAccessibleText() { 434 return this; 435 } 436 437 /* 438 * Returns a String filled with password echo characters. The String 439 * contains one echo character for each character (including whitespace) 440 * that the user entered in the JPasswordField. 441 */ 442 private String getEchoString(String str) { 443 if (str == null) { 444 return null; 445 } 446 char[] buffer = new char[str.length()]; 447 Arrays.fill(buffer, getEchoChar()); 448 return new String(buffer); 449 } 450 451 /** 452 * Returns the <code>String</code> at a given <code>index</code>. 453 * 454 * @param part the <code>CHARACTER</code>, <code>WORD</code> or 455 * <code>SENTENCE</code> to retrieve 456 * @param index an index within the text 457 * @return a <code>String</code> if <code>part</code> and 458 * <code>index</code> are valid. 459 * Otherwise, <code>null</code> is returned 460 * 461 * @see javax.accessibility.AccessibleText#CHARACTER 462 * @see javax.accessibility.AccessibleText#WORD 463 * @see javax.accessibility.AccessibleText#SENTENCE 464 * 465 * @since 1.6 466 */ 467 public String getAtIndex(int part, int index) { 468 String str = null; 469 if (part == AccessibleText.CHARACTER) { 470 str = super.getAtIndex(part, index); 471 } else { 472 // Treat the text displayed in the JPasswordField 473 // as one word and sentence. 474 char[] password = getPassword(); 475 if (password == null || 476 index < 0 || index >= password.length) { 477 return null; 478 } 479 str = new String(password); 480 } 481 return getEchoString(str); 482 } 483 484 /** 485 * Returns the <code>String</code> after a given <code>index</code>. 486 * 487 * @param part the <code>CHARACTER</code>, <code>WORD</code> or 488 * <code>SENTENCE</code> to retrieve 489 * @param index an index within the text 490 * @return a <code>String</code> if <code>part</code> and 491 * <code>index</code> are valid. 492 * Otherwise, <code>null</code> is returned 493 * 494 * @see javax.accessibility.AccessibleText#CHARACTER 495 * @see javax.accessibility.AccessibleText#WORD 496 * @see javax.accessibility.AccessibleText#SENTENCE 497 * 498 * @since 1.6 499 */ 500 public String getAfterIndex(int part, int index) { 501 if (part == AccessibleText.CHARACTER) { 502 String str = super.getAfterIndex(part, index); 503 return getEchoString(str); 504 } else { 505 // There is no word or sentence after the text 506 // displayed in the JPasswordField. 507 return null; 508 } 509 } 510 511 /** 512 * Returns the <code>String</code> before a given <code>index</code>. 513 * 514 * @param part the <code>CHARACTER</code>, <code>WORD</code> or 515 * <code>SENTENCE</code> to retrieve 516 * @param index an index within the text 517 * @return a <code>String</code> if <code>part</code> and 518 * <code>index</code> are valid. 519 * Otherwise, <code>null</code> is returned 520 * 521 * @see javax.accessibility.AccessibleText#CHARACTER 522 * @see javax.accessibility.AccessibleText#WORD 523 * @see javax.accessibility.AccessibleText#SENTENCE 524 * 525 * @since 1.6 526 */ 527 public String getBeforeIndex(int part, int index) { 528 if (part == AccessibleText.CHARACTER) { 529 String str = super.getBeforeIndex(part, index); 530 return getEchoString(str); 531 } else { 532 // There is no word or sentence before the text 533 // displayed in the JPasswordField. 534 return null; 535 } 536 } 537 538 /** 539 * Returns the text between two <code>indices</code>. 540 * 541 * @param startIndex the start index in the text 542 * @param endIndex the end index in the text 543 * @return the text string if the indices are valid. 544 * Otherwise, <code>null</code> is returned 545 * 546 * @since 1.6 547 */ 548 public String getTextRange(int startIndex, int endIndex) { 549 String str = super.getTextRange(startIndex, endIndex); 550 return getEchoString(str); 551 } 552 553 554 /** 555 * Returns the <code>AccessibleTextSequence</code> at a given 556 * <code>index</code>. 557 * 558 * @param part the <code>CHARACTER</code>, <code>WORD</code>, 559 * <code>SENTENCE</code>, <code>LINE</code> or <code>ATTRIBUTE_RUN</code> to 560 * retrieve 561 * @param index an index within the text 562 * @return an <code>AccessibleTextSequence</code> specifying the text if 563 * <code>part</code> and <code>index</code> are valid. Otherwise, 564 * <code>null</code> is returned 565 * 566 * @see javax.accessibility.AccessibleText#CHARACTER 567 * @see javax.accessibility.AccessibleText#WORD 568 * @see javax.accessibility.AccessibleText#SENTENCE 569 * @see javax.accessibility.AccessibleExtendedText#LINE 570 * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN 571 * 572 * @since 1.6 573 */ 574 public AccessibleTextSequence getTextSequenceAt(int part, int index) { 575 if (part == AccessibleText.CHARACTER) { 576 AccessibleTextSequence seq = super.getTextSequenceAt(part, index); 577 if (seq == null) { 578 return null; 579 } 580 return new AccessibleTextSequence(seq.startIndex, seq.endIndex, 581 getEchoString(seq.text)); 582 } else { 583 // Treat the text displayed in the JPasswordField 584 // as one word, sentence, line and attribute run 585 char[] password = getPassword(); 586 if (password == null || 587 index < 0 || index >= password.length) { 588 return null; 589 } 590 String text = new String(password); 591 return new AccessibleTextSequence(0, password.length - 1, 592 getEchoString(text)); 593 } 594 } 595 596 /** 597 * Returns the <code>AccessibleTextSequence</code> after a given 598 * <code>index</code>. 599 * 600 * @param part the <code>CHARACTER</code>, <code>WORD</code>, 601 * <code>SENTENCE</code>, <code>LINE</code> or <code>ATTRIBUTE_RUN</code> to 602 * retrieve 603 * @param index an index within the text 604 * @return an <code>AccessibleTextSequence</code> specifying the text if 605 * <code>part</code> and <code>index</code> are valid. Otherwise, 606 * <code>null</code> is returned 607 * 608 * @see javax.accessibility.AccessibleText#CHARACTER 609 * @see javax.accessibility.AccessibleText#WORD 610 * @see javax.accessibility.AccessibleText#SENTENCE 611 * @see javax.accessibility.AccessibleExtendedText#LINE 612 * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN 613 * 614 * @since 1.6 615 */ 616 public AccessibleTextSequence getTextSequenceAfter(int part, int index) { 617 if (part == AccessibleText.CHARACTER) { 618 AccessibleTextSequence seq = super.getTextSequenceAfter(part, index); 619 if (seq == null) { 620 return null; 621 } 622 return new AccessibleTextSequence(seq.startIndex, seq.endIndex, 623 getEchoString(seq.text)); 624 } else { 625 // There is no word, sentence, line or attribute run 626 // after the text displayed in the JPasswordField. 627 return null; 628 } 629 } 630 631 /** 632 * Returns the <code>AccessibleTextSequence</code> before a given 633 * <code>index</code>. 634 * 635 * @param part the <code>CHARACTER</code>, <code>WORD</code>, 636 * <code>SENTENCE</code>, <code>LINE</code> or <code>ATTRIBUTE_RUN</code> to 637 * retrieve 638 * @param index an index within the text 639 * @return an <code>AccessibleTextSequence</code> specifying the text if 640 * <code>part</code> and <code>index</code> are valid. Otherwise, 641 * <code>null</code> is returned 642 * 643 * @see javax.accessibility.AccessibleText#CHARACTER 644 * @see javax.accessibility.AccessibleText#WORD 645 * @see javax.accessibility.AccessibleText#SENTENCE 646 * @see javax.accessibility.AccessibleExtendedText#LINE 647 * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN 648 * 649 * @since 1.6 650 */ 651 public AccessibleTextSequence getTextSequenceBefore(int part, int index) { 652 if (part == AccessibleText.CHARACTER) { 653 AccessibleTextSequence seq = super.getTextSequenceBefore(part, index); 654 if (seq == null) { 655 return null; 656 } 657 return new AccessibleTextSequence(seq.startIndex, seq.endIndex, 658 getEchoString(seq.text)); 659 } else { 660 // There is no word, sentence, line or attribute run 661 // before the text displayed in the JPasswordField. 662 return null; 663 } 664 } 665 } 666 }