1 /* 2 * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing; 26 27 import 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<sup><font size="-2">TM</font></sup> 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 public class JPasswordField extends JTextField { 78 79 /** 80 * Constructs a new <code>JPasswordField</code>, 81 * with a default document, <code>null</code> starting 82 * text string, and 0 column width. 83 */ 84 public JPasswordField() { 85 this(null,null,0); 86 } 87 88 /** 89 * Constructs a new <code>JPasswordField</code> initialized 90 * with the specified text. The document model is set to the 91 * default, and the number of columns to 0. 92 * 93 * @param text the text to be displayed, <code>null</code> if none 94 */ 95 public JPasswordField(String text) { 96 this(null, text, 0); 97 } 98 99 /** 100 * Constructs a new empty <code>JPasswordField</code> with the specified 101 * number of columns. A default model is created, and the initial string 102 * is set to <code>null</code>. 103 * 104 * @param columns the number of columns >= 0 105 */ 106 public JPasswordField(int columns) { 107 this(null, null, columns); 108 } 109 110 /** 111 * Constructs a new <code>JPasswordField</code> initialized with 112 * the specified text and columns. The document model is set to 113 * the default. 114 * 115 * @param text the text to be displayed, <code>null</code> if none 116 * @param columns the number of columns >= 0 117 */ 118 public JPasswordField(String text, int columns) { 119 this(null, text, columns); 120 } 121 122 /** 123 * Constructs a new <code>JPasswordField</code> that uses the 124 * given text storage model and the given number of columns. 125 * This is the constructor through which the other constructors feed. 126 * The echo character is set to '*', but may be changed by the current 127 * Look and Feel. If the document model is 128 * <code>null</code>, a default one will be created. 129 * 130 * @param doc the text storage to use 131 * @param txt the text to be displayed, <code>null</code> if none 132 * @param columns the number of columns to use to calculate 133 * the preferred width >= 0; if columns is set to zero, the 134 * preferred width will be whatever naturally results from 135 * the component implementation 136 */ 137 public JPasswordField(Document doc, String txt, int columns) { 138 super(doc, txt, columns); 139 // We could either leave this on, which wouldn't be secure, 140 // or obscure the composted text, which essentially makes displaying 141 // it useless. Therefore, we turn off input methods. 142 enableInputMethods(false); 143 } 144 145 /** 146 * Returns the name of the L&F class that renders this component. 147 * 148 * @return the string "PasswordFieldUI" 149 * @see JComponent#getUIClassID 150 * @see UIDefaults#getUI 151 */ 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 * @beaninfo 193 * description: character to display in place of the real characters 194 * attribute: visualUpdate true 195 */ 196 public void setEchoChar(char c) { 197 echoChar = c; 198 echoCharSet = true; 199 repaint(); 200 revalidate(); 201 } 202 203 /** 204 * Returns true if this <code>JPasswordField</code> has a character 205 * set for echoing. A character is considered to be set if the echo 206 * character is not 0. 207 * 208 * @return true if a character is set for echoing 209 * @see #setEchoChar 210 * @see #getEchoChar 211 */ 212 public boolean echoCharIsSet() { 213 return echoChar != 0; 214 } 215 216 // --- JTextComponent methods ---------------------------------- 217 218 /** 219 * Invokes <code>provideErrorFeedback</code> on the current 220 * look and feel, which typically initiates an error beep. 221 * The normal behavior of transferring the 222 * currently selected range in the associated text model 223 * to the system clipboard, and removing the contents from 224 * the model, is not acceptable for a password field. 225 */ 226 public void cut() { 227 if (getClientProperty("JPasswordField.cutCopyAllowed") != Boolean.TRUE) { 228 UIManager.getLookAndFeel().provideErrorFeedback(this); 229 } else { 230 super.cut(); 231 } 232 } 233 234 /** 235 * Invokes <code>provideErrorFeedback</code> on the current 236 * look and feel, which typically initiates an error beep. 237 * The normal behavior of transferring the 238 * currently selected range in the associated text model 239 * to the system clipboard, and leaving the contents from 240 * the model, is not acceptable for a password field. 241 */ 242 public void copy() { 243 if (getClientProperty("JPasswordField.cutCopyAllowed") != Boolean.TRUE) { 244 UIManager.getLookAndFeel().provideErrorFeedback(this); 245 } else { 246 super.copy(); 247 } 248 } 249 250 /** 251 * Returns the text contained in this <code>TextComponent</code>. 252 * If the underlying document is <code>null</code>, will give a 253 * <code>NullPointerException</code>. 254 * <p> 255 * For security reasons, this method is deprecated. Use the 256 <code>* getPassword</code> method instead. 257 * @deprecated As of Java 2 platform v1.2, 258 * replaced by <code>getPassword</code>. 259 * @return the text 260 */ 261 @Deprecated 262 public String getText() { 263 return super.getText(); 264 } 265 266 /** 267 * Fetches a portion of the text represented by the 268 * component. Returns an empty string if length is 0. 269 * <p> 270 * For security reasons, this method is deprecated. Use the 271 * <code>getPassword</code> method instead. 272 * @deprecated As of Java 2 platform v1.2, 273 * replaced by <code>getPassword</code>. 274 * @param offs the offset >= 0 275 * @param len the length >= 0 276 * @return the text 277 * @exception BadLocationException if the offset or length are invalid 278 */ 279 @Deprecated 280 public String getText(int offs, int len) throws BadLocationException { 281 return super.getText(offs, len); 282 } 283 284 /** 285 * Returns the text contained in this <code>TextComponent</code>. 286 * If the underlying document is <code>null</code>, will give a 287 * <code>NullPointerException</code>. For stronger 288 * security, it is recommended that the returned character array be 289 * cleared after use by setting each character to zero. 290 * 291 * @return the text 292 */ 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 heirarchy 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 public AccessibleContext getAccessibleContext() { 385 if (accessibleContext == null) { 386 accessibleContext = new AccessibleJPasswordField(); 387 } 388 return accessibleContext; 389 } 390 391 /** 392 * This class implements accessibility support for the 393 * <code>JPasswordField</code> class. It provides an implementation of the 394 * Java Accessibility API appropriate to password field user-interface 395 * elements. 396 * <p> 397 * <strong>Warning:</strong> 398 * Serialized objects of this class will not be compatible with 399 * future Swing releases. The current serialization support is 400 * appropriate for short term storage or RMI between applications running 401 * the same version of Swing. As of 1.4, support for long term storage 402 * of all JavaBeans<sup><font size="-2">TM</font></sup> 403 * has been added to the <code>java.beans</code> package. 404 * Please see {@link java.beans.XMLEncoder}. 405 */ 406 protected class AccessibleJPasswordField extends AccessibleJTextField { 407 408 /** 409 * Gets the role of this object. 410 * 411 * @return an instance of AccessibleRole describing the role of the 412 * object (AccessibleRole.PASSWORD_TEXT) 413 * @see AccessibleRole 414 */ 415 public AccessibleRole getAccessibleRole() { 416 return AccessibleRole.PASSWORD_TEXT; 417 } 418 419 /** 420 * Gets the <code>AccessibleText</code> for the <code>JPasswordField</code>. 421 * The returned object also implements the 422 * <code>AccessibleExtendedText</code> interface. 423 * 424 * @return <code>AccessibleText</code> for the JPasswordField 425 * @see javax.accessibility.AccessibleContext 426 * @see javax.accessibility.AccessibleContext#getAccessibleText 427 * @see javax.accessibility.AccessibleText 428 * @see javax.accessibility.AccessibleExtendedText 429 * 430 * @since 1.6 431 */ 432 public AccessibleText getAccessibleText() { 433 return this; 434 } 435 436 /* 437 * Returns a String filled with password echo characters. The String 438 * contains one echo character for each character (including whitespace) 439 * that the user entered in the JPasswordField. 440 */ 441 private String getEchoString(String str) { 442 if (str == null) { 443 return null; 444 } 445 char[] buffer = new char[str.length()]; 446 Arrays.fill(buffer, getEchoChar()); 447 return new String(buffer); 448 } 449 450 /** 451 * Returns the <code>String</code> at a given <code>index</code>. 452 * 453 * @param part the <code>CHARACTER</code>, <code>WORD</code> or 454 * <code>SENTENCE</code> to retrieve 455 * @param index an index within the text 456 * @return a <code>String</code> if <code>part</code> and 457 * <code>index</code> are valid. 458 * Otherwise, <code>null</code> is returned 459 * 460 * @see javax.accessibility.AccessibleText#CHARACTER 461 * @see javax.accessibility.AccessibleText#WORD 462 * @see javax.accessibility.AccessibleText#SENTENCE 463 * 464 * @since 1.6 465 */ 466 public String getAtIndex(int part, int index) { 467 String str = null; 468 if (part == AccessibleText.CHARACTER) { 469 str = super.getAtIndex(part, index); 470 } else { 471 // Treat the text displayed in the JPasswordField 472 // as one word and sentence. 473 char password[] = getPassword(); 474 if (password == null || 475 index < 0 || index >= password.length) { 476 return null; 477 } 478 str = new String(password); 479 } 480 return getEchoString(str); 481 } 482 483 /** 484 * Returns the <code>String</code> after a given <code>index</code>. 485 * 486 * @param part the <code>CHARACTER</code>, <code>WORD</code> or 487 * <code>SENTENCE</code> to retrieve 488 * @param index an index within the text 489 * @return a <code>String</code> if <code>part</code> and 490 * <code>index</code> are valid. 491 * Otherwise, <code>null</code> is returned 492 * 493 * @see javax.accessibility.AccessibleText#CHARACTER 494 * @see javax.accessibility.AccessibleText#WORD 495 * @see javax.accessibility.AccessibleText#SENTENCE 496 * 497 * @since 1.6 498 */ 499 public String getAfterIndex(int part, int index) { 500 if (part == AccessibleText.CHARACTER) { 501 String str = super.getAfterIndex(part, index); 502 return getEchoString(str); 503 } else { 504 // There is no word or sentence after the text 505 // displayed in the JPasswordField. 506 return null; 507 } 508 } 509 510 /** 511 * Returns the <code>String</code> before a given <code>index</code>. 512 * 513 * @param part the <code>CHARACTER</code>, <code>WORD</code> or 514 * <code>SENTENCE</code> to retrieve 515 * @param index an index within the text 516 * @return a <code>String</code> if <code>part</code> and 517 * <code>index</code> are valid. 518 * Otherwise, <code>null</code> is returned 519 * 520 * @see javax.accessibility.AccessibleText#CHARACTER 521 * @see javax.accessibility.AccessibleText#WORD 522 * @see javax.accessibility.AccessibleText#SENTENCE 523 * 524 * @since 1.6 525 */ 526 public String getBeforeIndex(int part, int index) { 527 if (part == AccessibleText.CHARACTER) { 528 String str = super.getBeforeIndex(part, index); 529 return getEchoString(str); 530 } else { 531 // There is no word or sentence before the text 532 // displayed in the JPasswordField. 533 return null; 534 } 535 } 536 537 /** 538 * Returns the text between two <code>indices</code>. 539 * 540 * @param startIndex the start index in the text 541 * @param endIndex the end index in the text 542 * @return the text string if the indices are valid. 543 * Otherwise, <code>null</code> is returned 544 * 545 * @since 1.6 546 */ 547 public String getTextRange(int startIndex, int endIndex) { 548 String str = super.getTextRange(startIndex, endIndex); 549 return getEchoString(str); 550 } 551 552 553 /** 554 * Returns the <code>AccessibleTextSequence</code> at a given 555 * <code>index</code>. 556 * 557 * @param part the <code>CHARACTER</code>, <code>WORD</code>, 558 * <code>SENTENCE</code>, <code>LINE</code> or <code>ATTRIBUTE_RUN</code> to 559 * retrieve 560 * @param index an index within the text 561 * @return an <code>AccessibleTextSequence</code> specifying the text if 562 * <code>part</code> and <code>index</code> are valid. Otherwise, 563 * <code>null</code> is returned 564 * 565 * @see javax.accessibility.AccessibleText#CHARACTER 566 * @see javax.accessibility.AccessibleText#WORD 567 * @see javax.accessibility.AccessibleText#SENTENCE 568 * @see javax.accessibility.AccessibleExtendedText#LINE 569 * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN 570 * 571 * @since 1.6 572 */ 573 public AccessibleTextSequence getTextSequenceAt(int part, int index) { 574 if (part == AccessibleText.CHARACTER) { 575 AccessibleTextSequence seq = super.getTextSequenceAt(part, index); 576 if (seq == null) { 577 return null; 578 } 579 return new AccessibleTextSequence(seq.startIndex, seq.endIndex, 580 getEchoString(seq.text)); 581 } else { 582 // Treat the text displayed in the JPasswordField 583 // as one word, sentence, line and attribute run 584 char password[] = getPassword(); 585 if (password == null || 586 index < 0 || index >= password.length) { 587 return null; 588 } 589 String text = new String(password); 590 return new AccessibleTextSequence(0, password.length - 1, 591 getEchoString(text)); 592 } 593 } 594 595 /** 596 * Returns the <code>AccessibleTextSequence</code> after a given 597 * <code>index</code>. 598 * 599 * @param part the <code>CHARACTER</code>, <code>WORD</code>, 600 * <code>SENTENCE</code>, <code>LINE</code> or <code>ATTRIBUTE_RUN</code> to 601 * retrieve 602 * @param index an index within the text 603 * @return an <code>AccessibleTextSequence</code> specifying the text if 604 * <code>part</code> and <code>index</code> are valid. Otherwise, 605 * <code>null</code> is returned 606 * 607 * @see javax.accessibility.AccessibleText#CHARACTER 608 * @see javax.accessibility.AccessibleText#WORD 609 * @see javax.accessibility.AccessibleText#SENTENCE 610 * @see javax.accessibility.AccessibleExtendedText#LINE 611 * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN 612 * 613 * @since 1.6 614 */ 615 public AccessibleTextSequence getTextSequenceAfter(int part, int index) { 616 if (part == AccessibleText.CHARACTER) { 617 AccessibleTextSequence seq = super.getTextSequenceAfter(part, index); 618 if (seq == null) { 619 return null; 620 } 621 return new AccessibleTextSequence(seq.startIndex, seq.endIndex, 622 getEchoString(seq.text)); 623 } else { 624 // There is no word, sentence, line or attribute run 625 // after the text displayed in the JPasswordField. 626 return null; 627 } 628 } 629 630 /** 631 * Returns the <code>AccessibleTextSequence</code> before a given 632 * <code>index</code>. 633 * 634 * @param part the <code>CHARACTER</code>, <code>WORD</code>, 635 * <code>SENTENCE</code>, <code>LINE</code> or <code>ATTRIBUTE_RUN</code> to 636 * retrieve 637 * @param index an index within the text 638 * @return an <code>AccessibleTextSequence</code> specifying the text if 639 * <code>part</code> and <code>index</code> are valid. Otherwise, 640 * <code>null</code> is returned 641 * 642 * @see javax.accessibility.AccessibleText#CHARACTER 643 * @see javax.accessibility.AccessibleText#WORD 644 * @see javax.accessibility.AccessibleText#SENTENCE 645 * @see javax.accessibility.AccessibleExtendedText#LINE 646 * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN 647 * 648 * @since 1.6 649 */ 650 public AccessibleTextSequence getTextSequenceBefore(int part, int index) { 651 if (part == AccessibleText.CHARACTER) { 652 AccessibleTextSequence seq = super.getTextSequenceBefore(part, index); 653 if (seq == null) { 654 return null; 655 } 656 return new AccessibleTextSequence(seq.startIndex, seq.endIndex, 657 getEchoString(seq.text)); 658 } else { 659 // There is no word, sentence, line or attribute run 660 // before the text displayed in the JPasswordField. 661 return null; 662 } 663 } 664 } 665 }