001 /*
002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing;
026
027 import javax.swing.text.*;
028 import javax.swing.plaf.*;
029 import javax.accessibility.*;
030
031 import java.io.ObjectOutputStream;
032 import java.io.ObjectInputStream;
033 import java.io.IOException;
034 import java.io.*;
035 import java.util.Arrays;
036
037 /**
038 * <code>JPasswordField</code> is a lightweight component that allows
039 * the editing of a single line of text where the view indicates
040 * something was typed, but does not show the original characters.
041 * You can find further information and examples in
042 * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/textfield.html">How to Use Text Fields</a>,
043 * a section in <em>The Java Tutorial.</em>
044 * <p>
045 * <code>JPasswordField</code> is intended
046 * to be source-compatible with <code>java.awt.TextField</code>
047 * used with <code>echoChar</code> set. It is provided separately
048 * to make it easier to safely change the UI for the
049 * <code>JTextField</code> without affecting password entries.
050 * <p>
051 * <strong>NOTE:</strong>
052 * By default, JPasswordField disables input methods; otherwise, input
053 * characters could be visible while they were composed using input methods.
054 * If an application needs the input methods support, please use the
055 * inherited method, <code>enableInputMethods(true)</code>.
056 * <p>
057 * <strong>Warning:</strong> Swing is not thread safe. For more
058 * information see <a
059 * href="package-summary.html#threading">Swing's Threading
060 * Policy</a>.
061 * <p>
062 * <strong>Warning:</strong>
063 * Serialized objects of this class will not be compatible with
064 * future Swing releases. The current serialization support is
065 * appropriate for short term storage or RMI between applications running
066 * the same version of Swing. As of 1.4, support for long term storage
067 * of all JavaBeans<sup><font size="-2">TM</font></sup>
068 * has been added to the <code>java.beans</code> package.
069 * Please see {@link java.beans.XMLEncoder}.
070 *
071 * @beaninfo
072 * attribute: isContainer false
073 * description: Allows the editing of a line of text but doesn't show the characters.
074 *
075 * @author Timothy Prinzing
076 * @version 1.66 05/05/07
077 */
078 public class JPasswordField extends JTextField {
079
080 /**
081 * Constructs a new <code>JPasswordField</code>,
082 * with a default document, <code>null</code> starting
083 * text string, and 0 column width.
084 */
085 public JPasswordField() {
086 this (null, null, 0);
087 }
088
089 /**
090 * Constructs a new <code>JPasswordField</code> initialized
091 * with the specified text. The document model is set to the
092 * default, and the number of columns to 0.
093 *
094 * @param text the text to be displayed, <code>null</code> if none
095 */
096 public JPasswordField(String text) {
097 this (null, text, 0);
098 }
099
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 * {@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)
281 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 * 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() + ",echoChar=" + echoChar;
345 }
346
347 /**
348 * This method is a hack to get around the fact that we cannot
349 * directly override setUIProperty because part of the inheritance heirarchy
350 * goes outside of the javax.swing package, and therefore calling a package
351 * private method isn't allowed. This method should return true if the property
352 * was handled, and false otherwise.
353 */
354 boolean customSetUIProperty(String propertyName, Object value) {
355 if (propertyName == "echoChar") {
356 if (!echoCharSet) {
357 setEchoChar((Character) value);
358 echoCharSet = false;
359 }
360 return true;
361 }
362 return false;
363 }
364
365 /////////////////
366 // Accessibility support
367 ////////////////
368
369 /**
370 * Returns the <code>AccessibleContext</code> associated with this
371 * <code>JPasswordField</code>. For password fields, the
372 * <code>AccessibleContext</code> takes the form of an
373 * <code>AccessibleJPasswordField</code>.
374 * A new <code>AccessibleJPasswordField</code> instance is created
375 * if necessary.
376 *
377 * @return an <code>AccessibleJPasswordField</code> that serves as the
378 * <code>AccessibleContext</code> of this
379 * <code>JPasswordField</code>
380 */
381 public AccessibleContext getAccessibleContext() {
382 if (accessibleContext == null) {
383 accessibleContext = new AccessibleJPasswordField();
384 }
385 return accessibleContext;
386 }
387
388 /**
389 * This class implements accessibility support for the
390 * <code>JPasswordField</code> class. It provides an implementation of the
391 * Java Accessibility API appropriate to password field user-interface
392 * elements.
393 * <p>
394 * <strong>Warning:</strong>
395 * Serialized objects of this class will not be compatible with
396 * future Swing releases. The current serialization support is
397 * appropriate for short term storage or RMI between applications running
398 * the same version of Swing. As of 1.4, support for long term storage
399 * of all JavaBeans<sup><font size="-2">TM</font></sup>
400 * has been added to the <code>java.beans</code> package.
401 * Please see {@link java.beans.XMLEncoder}.
402 */
403 protected class AccessibleJPasswordField extends
404 AccessibleJTextField {
405
406 /**
407 * Gets the role of this object.
408 *
409 * @return an instance of AccessibleRole describing the role of the
410 * object (AccessibleRole.PASSWORD_TEXT)
411 * @see AccessibleRole
412 */
413 public AccessibleRole getAccessibleRole() {
414 return AccessibleRole.PASSWORD_TEXT;
415 }
416
417 /**
418 * Gets the <code>AccessibleText</code> for the <code>JPasswordField</code>.
419 * The returned object also implements the
420 * <code>AccessibleExtendedText</code> interface.
421 *
422 * @return <code>AccessibleText</code> for the JPasswordField
423 * @see javax.accessibility.AccessibleContext
424 * @see javax.accessibility.AccessibleContext#getAccessibleText
425 * @see javax.accessibility.AccessibleText
426 * @see javax.accessibility.AccessibleExtendedText
427 *
428 * @since 1.6
429 */
430 public AccessibleText getAccessibleText() {
431 return this ;
432 }
433
434 /*
435 * Returns a String filled with password echo characters. The String
436 * contains one echo character for each character (including whitespace)
437 * that the user entered in the JPasswordField.
438 */
439 private String getEchoString(String str) {
440 if (str == null) {
441 return null;
442 }
443 char[] buffer = new char[str.length()];
444 Arrays.fill(buffer, getEchoChar());
445 return new String(buffer);
446 }
447
448 /**
449 * Returns the <code>String</code> at a given <code>index</code>.
450 *
451 * @param part the <code>CHARACTER</code>, <code>WORD</code> or
452 * <code>SENTENCE</code> to retrieve
453 * @param index an index within the text
454 * @return a <code>String</code> if <code>part</code> and
455 * <code>index</code> are valid.
456 * Otherwise, <code>null</code> is returned
457 *
458 * @see javax.accessibility.AccessibleText#CHARACTER
459 * @see javax.accessibility.AccessibleText#WORD
460 * @see javax.accessibility.AccessibleText#SENTENCE
461 *
462 * @since 1.6
463 */
464 public String getAtIndex(int part, int index) {
465 String str = null;
466 if (part == AccessibleText.CHARACTER) {
467 str = super .getAtIndex(part, index);
468 } else {
469 // Treat the text displayed in the JPasswordField
470 // as one word and sentence.
471 char password[] = getPassword();
472 if (password == null || index < 0
473 || index >= password.length) {
474 return null;
475 }
476 str = new String(password);
477 }
478 return getEchoString(str);
479 }
480
481 /**
482 * Returns the <code>String</code> after a given <code>index</code>.
483 *
484 * @param part the <code>CHARACTER</code>, <code>WORD</code> or
485 * <code>SENTENCE</code> to retrieve
486 * @param index an index within the text
487 * @return a <code>String</code> if <code>part</code> and
488 * <code>index</code> are valid.
489 * Otherwise, <code>null</code> is returned
490 *
491 * @see javax.accessibility.AccessibleText#CHARACTER
492 * @see javax.accessibility.AccessibleText#WORD
493 * @see javax.accessibility.AccessibleText#SENTENCE
494 *
495 * @since 1.6
496 */
497 public String getAfterIndex(int part, int index) {
498 if (part == AccessibleText.CHARACTER) {
499 String str = super .getAfterIndex(part, index);
500 return getEchoString(str);
501 } else {
502 // There is no word or sentence after the text
503 // displayed in the JPasswordField.
504 return null;
505 }
506 }
507
508 /**
509 * Returns the <code>String</code> before a given <code>index</code>.
510 *
511 * @param part the <code>CHARACTER</code>, <code>WORD</code> or
512 * <code>SENTENCE</code> to retrieve
513 * @param index an index within the text
514 * @return a <code>String</code> if <code>part</code> and
515 * <code>index</code> are valid.
516 * Otherwise, <code>null</code> is returned
517 *
518 * @see javax.accessibility.AccessibleText#CHARACTER
519 * @see javax.accessibility.AccessibleText#WORD
520 * @see javax.accessibility.AccessibleText#SENTENCE
521 *
522 * @since 1.6
523 */
524 public String getBeforeIndex(int part, int index) {
525 if (part == AccessibleText.CHARACTER) {
526 String str = super .getBeforeIndex(part, index);
527 return getEchoString(str);
528 } else {
529 // There is no word or sentence before the text
530 // displayed in the JPasswordField.
531 return null;
532 }
533 }
534
535 /**
536 * Returns the text between two <code>indices</code>.
537 *
538 * @param startIndex the start index in the text
539 * @param endIndex the end index in the text
540 * @return the text string if the indices are valid.
541 * Otherwise, <code>null</code> is returned
542 *
543 * @since 1.6
544 */
545 public String getTextRange(int startIndex, int endIndex) {
546 String str = super .getTextRange(startIndex, endIndex);
547 return getEchoString(str);
548 }
549
550 /**
551 * Returns the <code>AccessibleTextSequence</code> at a given
552 * <code>index</code>.
553 *
554 * @param part the <code>CHARACTER</code>, <code>WORD</code>,
555 * <code>SENTENCE</code>, <code>LINE</code> or <code>ATTRIBUTE_RUN</code> to
556 * retrieve
557 * @param index an index within the text
558 * @return an <code>AccessibleTextSequence</code> specifying the text if
559 * <code>part</code> and <code>index</code> are valid. Otherwise,
560 * <code>null</code> is returned
561 *
562 * @see javax.accessibility.AccessibleText#CHARACTER
563 * @see javax.accessibility.AccessibleText#WORD
564 * @see javax.accessibility.AccessibleText#SENTENCE
565 * @see javax.accessibility.AccessibleExtendedText#LINE
566 * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN
567 *
568 * @since 1.6
569 */
570 public AccessibleTextSequence getTextSequenceAt(int part,
571 int index) {
572 if (part == AccessibleText.CHARACTER) {
573 AccessibleTextSequence seq = super .getTextSequenceAt(
574 part, index);
575 if (seq == null) {
576 return null;
577 }
578 return new AccessibleTextSequence(seq.startIndex,
579 seq.endIndex, getEchoString(seq.text));
580 } else {
581 // Treat the text displayed in the JPasswordField
582 // as one word, sentence, line and attribute run
583 char password[] = getPassword();
584 if (password == null || index < 0
585 || index >= password.length) {
586 return null;
587 }
588 String text = new String(password);
589 return new AccessibleTextSequence(0,
590 password.length - 1, getEchoString(text));
591 }
592 }
593
594 /**
595 * Returns the <code>AccessibleTextSequence</code> after a given
596 * <code>index</code>.
597 *
598 * @param part the <code>CHARACTER</code>, <code>WORD</code>,
599 * <code>SENTENCE</code>, <code>LINE</code> or <code>ATTRIBUTE_RUN</code> to
600 * retrieve
601 * @param index an index within the text
602 * @return an <code>AccessibleTextSequence</code> specifying the text if
603 * <code>part</code> and <code>index</code> are valid. Otherwise,
604 * <code>null</code> is returned
605 *
606 * @see javax.accessibility.AccessibleText#CHARACTER
607 * @see javax.accessibility.AccessibleText#WORD
608 * @see javax.accessibility.AccessibleText#SENTENCE
609 * @see javax.accessibility.AccessibleExtendedText#LINE
610 * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN
611 *
612 * @since 1.6
613 */
614 public AccessibleTextSequence getTextSequenceAfter(int part,
615 int index) {
616 if (part == AccessibleText.CHARACTER) {
617 AccessibleTextSequence seq = super
618 .getTextSequenceAfter(part, index);
619 if (seq == null) {
620 return null;
621 }
622 return new AccessibleTextSequence(seq.startIndex,
623 seq.endIndex, 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,
652 int index) {
653 if (part == AccessibleText.CHARACTER) {
654 AccessibleTextSequence seq = super
655 .getTextSequenceBefore(part, index);
656 if (seq == null) {
657 return null;
658 }
659 return new AccessibleTextSequence(seq.startIndex,
660 seq.endIndex, getEchoString(seq.text));
661 } else {
662 // There is no word, sentence, line or attribute run
663 // before the text displayed in the JPasswordField.
664 return null;
665 }
666 }
667 }
668 }
|