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