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 */ 77 @SuppressWarnings("serial") // Same-version serialization only 78 public class JPasswordField extends JTextField { 79 80 /** 81 * Constructs a new <code>JPasswordField</code>, 82 * with a default document, <code>null</code> starting 83 * text string, and 0 column width. 84 */ 85 public JPasswordField() { 86 this(null,null,0); 87 } 88 89 /** 90 * Constructs a new <code>JPasswordField</code> initialized 91 * with the specified text. The document model is set to the 92 * default, and the number of columns to 0. 93 * 94 * @param text the text to be displayed, <code>null</code> if none 95 */ 96 public JPasswordField(String text) { 97 this(null, text, 0); 98 } 99 100 /** 101 * Constructs a new empty <code>JPasswordField</code> with the specified 102 * number of columns. A default model is created, and the initial string 103 * is set to <code>null</code>. 104 * 105 * @param columns the number of columns >= 0 106 */ 107 public JPasswordField(int columns) { 108 this(null, null, columns); 109 } 110 111 /** 112 * Constructs a new <code>JPasswordField</code> initialized with 113 * the specified text and columns. The document model is set to 114 * the default. 115 * 116 * @param text the text to be displayed, <code>null</code> if none 117 * @param columns the number of columns >= 0 118 */ 119 public JPasswordField(String text, int columns) { 120 this(null, text, columns); 121 } 122 123 /** 124 * Constructs a new <code>JPasswordField</code> that uses the 125 * given text storage model and the given number of columns. 126 * This is the constructor through which the other constructors feed. 127 * The echo character is set to '*', but may be changed by the current 128 * Look and Feel. If the document model is 129 * <code>null</code>, a default one will be created. 130 * 131 * @param doc the text storage to use 132 * @param txt the text to be displayed, <code>null</code> if none 133 * @param columns the number of columns to use to calculate 134 * the preferred width >= 0; if columns is set to zero, the 135 * preferred width will be whatever naturally results from 136 * the component implementation 137 */ 138 public JPasswordField(Document doc, String txt, int columns) { 139 super(doc, txt, columns); 140 // We could either leave this on, which wouldn't be secure, 141 // or obscure the composted text, which essentially makes displaying 142 // it useless. Therefore, we turn off input methods. 143 enableInputMethods(false); 144 } 145 146 /** 147 * Returns the name of the L&F class that renders this component. 148 * 149 * @return the string "PasswordFieldUI" 150 * @see JComponent#getUIClassID 151 * @see UIDefaults#getUI 152 */ 153 public String getUIClassID() { 154 return uiClassID; 155 } 156 157 158 /** 159 * {@inheritDoc} 160 * @since 1.6 161 */ 162 public void updateUI() { 163 if(!echoCharSet) { 164 echoChar = '*'; 165 } 166 super.updateUI(); 167 } 168 169 /** 170 * Returns the character to be used for echoing. The default is '*'. 171 * The default may be different depending on the currently running Look 172 * and Feel. For example, Metal/Ocean's default is a bullet character. 173 * 174 * @return the echo character, 0 if unset 175 * @see #setEchoChar 176 * @see #echoCharIsSet 177 */ 178 public char getEchoChar() { 179 return echoChar; 180 } 181 182 /** 183 * Sets the echo character for this <code>JPasswordField</code>. 184 * Note that this is largely a suggestion, since the 185 * view that gets installed can use whatever graphic techniques 186 * it desires to represent the field. Setting a value of 0 indicates 187 * that you wish to see the text as it is typed, similar to 188 * the behavior of a standard <code>JTextField</code>. 189 * 190 * @param c the echo character to display 191 * @see #echoCharIsSet 192 * @see #getEchoChar 193 * @beaninfo 194 * description: character to display in place of the real characters 195 * attribute: visualUpdate true 196 */ 197 public void setEchoChar(char c) { 198 echoChar = c; 199 echoCharSet = true; 200 repaint(); 201 revalidate(); 202 } 203 204 /** 205 * Returns true if this <code>JPasswordField</code> has a character 206 * set for echoing. A character is considered to be set if the echo 207 * character is not 0. 208 * 209 * @return true if a character is set for echoing 210 * @see #setEchoChar 211 * @see #getEchoChar 212 */ 213 public boolean echoCharIsSet() { 214 return echoChar != 0; 215 } 216 217 // --- JTextComponent methods ---------------------------------- 218 219 /** 220 * Invokes <code>provideErrorFeedback</code> on the current 221 * look and feel, which typically initiates an error beep. 222 * The normal behavior of transferring the 223 * currently selected range in the associated text model 224 * to the system clipboard, and removing the contents from 225 * the model, is not acceptable for a password field. 226 */ 227 public void cut() { 228 if (getClientProperty("JPasswordField.cutCopyAllowed") != Boolean.TRUE) { 229 UIManager.getLookAndFeel().provideErrorFeedback(this); 230 } else { 231 super.cut(); 232 } 233 } 234 235 /** 236 * Invokes <code>provideErrorFeedback</code> on the current 237 * look and feel, which typically initiates an error beep. 238 * The normal behavior of transferring the 239 * currently selected range in the associated text model 240 * to the system clipboard, and leaving the contents from 241 * the model, is not acceptable for a password field. 242 */ 243 public void copy() { 244 if (getClientProperty("JPasswordField.cutCopyAllowed") != Boolean.TRUE) { 245 UIManager.getLookAndFeel().provideErrorFeedback(this); 246 } else { 247 super.copy(); 248 } 249 } 250 251 /** 252 * Returns the text contained in this <code>TextComponent</code>. 253 * If the underlying document is <code>null</code>, will give a 254 * <code>NullPointerException</code>. 255 * <p> 256 * For security reasons, this method is deprecated. Use the 257 <code>* getPassword</code> method instead. 258 * @deprecated As of Java 2 platform v1.2, 259 * replaced by <code>getPassword</code>. 260 * @return the text 261 */ 262 @Deprecated 263 public String getText() { 264 return super.getText(); 265 } 266 267 /** 268 * Fetches a portion of the text represented by the 269 * component. Returns an empty string if length is 0. 270 * <p> 271 * For security reasons, this method is deprecated. Use the 272 * <code>getPassword</code> method instead. 273 * @deprecated As of Java 2 platform v1.2, 274 * replaced by <code>getPassword</code>. 275 * @param offs the offset >= 0 276 * @param len the length >= 0 277 * @return the text 278 * @exception BadLocationException if the offset or length are invalid 279 */ 280 @Deprecated 281 public String getText(int offs, int len) throws BadLocationException { 282 return super.getText(offs, len); 283 } 284 285 /** 286 * Returns the text contained in this <code>TextComponent</code>. 287 * If the underlying document is <code>null</code>, will give a 288 * <code>NullPointerException</code>. For stronger 289 * security, it is recommended that the returned character array be 290 * cleared after use by setting each character to zero. 291 * 292 * @return the text 293 */ 294 public char[] getPassword() { 295 Document doc = getDocument(); 296 Segment txt = new Segment(); 297 try { 298 doc.getText(0, doc.getLength(), txt); // use the non-String API 299 } catch (BadLocationException e) { 300 return null; 301 } 302 char[] retValue = new char[txt.count]; 303 System.arraycopy(txt.array, txt.offset, retValue, 0, txt.count); 304 return retValue; 305 } 306 307 /** 308 * See readObject() and writeObject() in JComponent for more 309 * information about serialization in Swing. 310 */ 311 private void writeObject(ObjectOutputStream s) throws IOException { 312 s.defaultWriteObject(); 313 if (getUIClassID().equals(uiClassID)) { 314 byte count = JComponent.getWriteObjCounter(this); 315 JComponent.setWriteObjCounter(this, --count); 316 if (count == 0 && ui != null) { 317 ui.installUI(this); 318 } 319 } 320 } 321 322 // --- variables ----------------------------------------------- 323 324 /** 325 * @see #getUIClassID 326 * @see #readObject 327 */ 328 private static final String uiClassID = "PasswordFieldUI"; 329 330 private char echoChar; 331 332 private boolean echoCharSet = false; 333 334 335 /** 336 * Returns a string representation of this <code>JPasswordField</code>. 337 * This method is intended to be used only for debugging purposes, and the 338 * content and format of the returned string may vary between 339 * implementations. The returned string may be empty but may not 340 * be <code>null</code>. 341 * 342 * @return a string representation of this <code>JPasswordField</code> 343 */ 344 protected String paramString() { 345 return super.paramString() + 346 ",echoChar=" + echoChar; 347 } 348 349 350 /** 351 * This method is a hack to get around the fact that we cannot 352 * directly override setUIProperty because part of the inheritance hierarchy 353 * goes outside of the javax.swing package, and therefore calling a package 354 * private method isn't allowed. This method should return true if the property 355 * was handled, and false otherwise. 356 */ 357 boolean customSetUIProperty(String propertyName, Object value) { 358 if (propertyName == "echoChar") { 359 if (!echoCharSet) { 360 setEchoChar((Character)value); 361 echoCharSet = false; 362 } 363 return true; 364 } 365 return false; 366 } 367 368 ///////////////// 369 // Accessibility support 370 //////////////// 371 372 373 /** 374 * Returns the <code>AccessibleContext</code> associated with this 375 * <code>JPasswordField</code>. For password fields, the 376 * <code>AccessibleContext</code> takes the form of an 377 * <code>AccessibleJPasswordField</code>. 378 * A new <code>AccessibleJPasswordField</code> instance is created 379 * if necessary. 380 * 381 * @return an <code>AccessibleJPasswordField</code> that serves as the 382 * <code>AccessibleContext</code> of this 383 * <code>JPasswordField</code> 384 */ 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 }