View Javadoc
1   /*
2    * Copyright (c) 1995, 2013, 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 java.awt;
26  
27  import java.awt.peer.TextComponentPeer;
28  import java.awt.event.*;
29  import java.util.EventListener;
30  import java.io.ObjectOutputStream;
31  import java.io.ObjectInputStream;
32  import java.io.IOException;
33  import sun.awt.InputMethodSupport;
34  import java.text.BreakIterator;
35  import javax.swing.text.AttributeSet;
36  import javax.accessibility.*;
37  import java.awt.im.InputMethodRequests;
38  import sun.security.util.SecurityConstants;
39  
40  /**
41   * The <code>TextComponent</code> class is the superclass of
42   * any component that allows the editing of some text.
43   * <p>
44   * A text component embodies a string of text.  The
45   * <code>TextComponent</code> class defines a set of methods
46   * that determine whether or not this text is editable. If the
47   * component is editable, it defines another set of methods
48   * that supports a text insertion caret.
49   * <p>
50   * In addition, the class defines methods that are used
51   * to maintain a current <em>selection</em> from the text.
52   * The text selection, a substring of the component's text,
53   * is the target of editing operations. It is also referred
54   * to as the <em>selected text</em>.
55   *
56   * @author      Sami Shaio
57   * @author      Arthur van Hoff
58   * @since       JDK1.0
59   */
60  public class TextComponent extends Component implements Accessible {
61  
62      /**
63       * The value of the text.
64       * A <code>null</code> value is the same as "".
65       *
66       * @serial
67       * @see #setText(String)
68       * @see #getText()
69       */
70      String text;
71  
72      /**
73       * A boolean indicating whether or not this
74       * <code>TextComponent</code> is editable.
75       * It will be <code>true</code> if the text component
76       * is editable and <code>false</code> if not.
77       *
78       * @serial
79       * @see #isEditable()
80       */
81      boolean editable = true;
82  
83      /**
84       * The selection refers to the selected text, and the
85       * <code>selectionStart</code> is the start position
86       * of the selected text.
87       *
88       * @serial
89       * @see #getSelectionStart()
90       * @see #setSelectionStart(int)
91       */
92      int selectionStart;
93  
94      /**
95       * The selection refers to the selected text, and the
96       * <code>selectionEnd</code>
97       * is the end position of the selected text.
98       *
99       * @serial
100      * @see #getSelectionEnd()
101      * @see #setSelectionEnd(int)
102      */
103     int selectionEnd;
104 
105     // A flag used to tell whether the background has been set by
106     // developer code (as opposed to AWT code).  Used to determine
107     // the background color of non-editable TextComponents.
108     boolean backgroundSetByClientCode = false;
109 
110     transient protected TextListener textListener;
111 
112     /*
113      * JDK 1.1 serialVersionUID
114      */
115     private static final long serialVersionUID = -2214773872412987419L;
116 
117     /**
118      * Constructs a new text component initialized with the
119      * specified text. Sets the value of the cursor to
120      * <code>Cursor.TEXT_CURSOR</code>.
121      * @param      text       the text to be displayed; if
122      *             <code>text</code> is <code>null</code>, the empty
123      *             string <code>""</code> will be displayed
124      * @exception  HeadlessException if
125      *             <code>GraphicsEnvironment.isHeadless</code>
126      *             returns true
127      * @see        java.awt.GraphicsEnvironment#isHeadless
128      * @see        java.awt.Cursor
129      */
130     TextComponent(String text) throws HeadlessException {
131         GraphicsEnvironment.checkHeadless();
132         this.text = (text != null) ? text : "";
133         setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
134     }
135 
136     private void enableInputMethodsIfNecessary() {
137         if (checkForEnableIM) {
138             checkForEnableIM = false;
139             try {
140                 Toolkit toolkit = Toolkit.getDefaultToolkit();
141                 boolean shouldEnable = false;
142                 if (toolkit instanceof InputMethodSupport) {
143                     shouldEnable = ((InputMethodSupport)toolkit)
144                       .enableInputMethodsForTextComponent();
145                 }
146                 enableInputMethods(shouldEnable);
147             } catch (Exception e) {
148                 // if something bad happens, just don't enable input methods
149             }
150         }
151     }
152 
153     /**
154      * Enables or disables input method support for this text component. If input
155      * method support is enabled and the text component also processes key events,
156      * incoming events are offered to the current input method and will only be
157      * processed by the component or dispatched to its listeners if the input method
158      * does not consume them. Whether and how input method support for this text
159      * component is enabled or disabled by default is implementation dependent.
160      *
161      * @param enable true to enable, false to disable
162      * @see #processKeyEvent
163      * @since 1.2
164      */
165     public void enableInputMethods(boolean enable) {
166         checkForEnableIM = false;
167         super.enableInputMethods(enable);
168     }
169 
170     boolean areInputMethodsEnabled() {
171         // moved from the constructor above to here and addNotify below,
172         // this call will initialize the toolkit if not already initialized.
173         if (checkForEnableIM) {
174             enableInputMethodsIfNecessary();
175         }
176 
177         // TextComponent handles key events without touching the eventMask or
178         // having a key listener, so just check whether the flag is set
179         return (eventMask & AWTEvent.INPUT_METHODS_ENABLED_MASK) != 0;
180     }
181 
182     public InputMethodRequests getInputMethodRequests() {
183         TextComponentPeer peer = (TextComponentPeer)this.peer;
184         if (peer != null) return peer.getInputMethodRequests();
185         else return null;
186     }
187 
188 
189 
190     /**
191      * Makes this Component displayable by connecting it to a
192      * native screen resource.
193      * This method is called internally by the toolkit and should
194      * not be called directly by programs.
195      * @see       java.awt.TextComponent#removeNotify
196      */
197     public void addNotify() {
198         super.addNotify();
199         enableInputMethodsIfNecessary();
200     }
201 
202     /**
203      * Removes the <code>TextComponent</code>'s peer.
204      * The peer allows us to modify the appearance of the
205      * <code>TextComponent</code> without changing its
206      * functionality.
207      */
208     public void removeNotify() {
209         synchronized (getTreeLock()) {
210             TextComponentPeer peer = (TextComponentPeer)this.peer;
211             if (peer != null) {
212                 text = peer.getText();
213                 selectionStart = peer.getSelectionStart();
214                 selectionEnd = peer.getSelectionEnd();
215             }
216             super.removeNotify();
217         }
218     }
219 
220     /**
221      * Sets the text that is presented by this
222      * text component to be the specified text.
223      * @param       t   the new text;
224      *                  if this parameter is <code>null</code> then
225      *                  the text is set to the empty string ""
226      * @see         java.awt.TextComponent#getText
227      */
228     public synchronized void setText(String t) {
229         boolean skipTextEvent = (text == null || text.isEmpty())
230                 && (t == null || t.isEmpty());
231         text = (t != null) ? t : "";
232         TextComponentPeer peer = (TextComponentPeer)this.peer;
233         // Please note that we do not want to post an event
234         // if TextArea.setText() or TextField.setText() replaces an empty text
235         // by an empty text, that is, if component's text remains unchanged.
236         if (peer != null && !skipTextEvent) {
237             peer.setText(text);
238         }
239     }
240 
241     /**
242      * Returns the text that is presented by this text component.
243      * By default, this is an empty string.
244      *
245      * @return the value of this <code>TextComponent</code>
246      * @see     java.awt.TextComponent#setText
247      */
248     public synchronized String getText() {
249         TextComponentPeer peer = (TextComponentPeer)this.peer;
250         if (peer != null) {
251             text = peer.getText();
252         }
253         return text;
254     }
255 
256     /**
257      * Returns the selected text from the text that is
258      * presented by this text component.
259      * @return      the selected text of this text component
260      * @see         java.awt.TextComponent#select
261      */
262     public synchronized String getSelectedText() {
263         return getText().substring(getSelectionStart(), getSelectionEnd());
264     }
265 
266     /**
267      * Indicates whether or not this text component is editable.
268      * @return     <code>true</code> if this text component is
269      *                  editable; <code>false</code> otherwise.
270      * @see        java.awt.TextComponent#setEditable
271      * @since      JDK1.0
272      */
273     public boolean isEditable() {
274         return editable;
275     }
276 
277     /**
278      * Sets the flag that determines whether or not this
279      * text component is editable.
280      * <p>
281      * If the flag is set to <code>true</code>, this text component
282      * becomes user editable. If the flag is set to <code>false</code>,
283      * the user cannot change the text of this text component.
284      * By default, non-editable text components have a background color
285      * of SystemColor.control.  This default can be overridden by
286      * calling setBackground.
287      *
288      * @param     b   a flag indicating whether this text component
289      *                      is user editable.
290      * @see       java.awt.TextComponent#isEditable
291      * @since     JDK1.0
292      */
293     public synchronized void setEditable(boolean b) {
294         if (editable == b) {
295             return;
296         }
297 
298         editable = b;
299         TextComponentPeer peer = (TextComponentPeer)this.peer;
300         if (peer != null) {
301             peer.setEditable(b);
302         }
303     }
304 
305     /**
306      * Gets the background color of this text component.
307      *
308      * By default, non-editable text components have a background color
309      * of SystemColor.control.  This default can be overridden by
310      * calling setBackground.
311      *
312      * @return This text component's background color.
313      *         If this text component does not have a background color,
314      *         the background color of its parent is returned.
315      * @see #setBackground(Color)
316      * @since JDK1.0
317      */
318     public Color getBackground() {
319         if (!editable && !backgroundSetByClientCode) {
320             return SystemColor.control;
321         }
322 
323         return super.getBackground();
324     }
325 
326     /**
327      * Sets the background color of this text component.
328      *
329      * @param c The color to become this text component's color.
330      *        If this parameter is null then this text component
331      *        will inherit the background color of its parent.
332      * @see #getBackground()
333      * @since JDK1.0
334      */
335     public void setBackground(Color c) {
336         backgroundSetByClientCode = true;
337         super.setBackground(c);
338     }
339 
340     /**
341      * Gets the start position of the selected text in
342      * this text component.
343      * @return      the start position of the selected text
344      * @see         java.awt.TextComponent#setSelectionStart
345      * @see         java.awt.TextComponent#getSelectionEnd
346      */
347     public synchronized int getSelectionStart() {
348         TextComponentPeer peer = (TextComponentPeer)this.peer;
349         if (peer != null) {
350             selectionStart = peer.getSelectionStart();
351         }
352         return selectionStart;
353     }
354 
355     /**
356      * Sets the selection start for this text component to
357      * the specified position. The new start point is constrained
358      * to be at or before the current selection end. It also
359      * cannot be set to less than zero, the beginning of the
360      * component's text.
361      * If the caller supplies a value for <code>selectionStart</code>
362      * that is out of bounds, the method enforces these constraints
363      * silently, and without failure.
364      * @param       selectionStart   the start position of the
365      *                        selected text
366      * @see         java.awt.TextComponent#getSelectionStart
367      * @see         java.awt.TextComponent#setSelectionEnd
368      * @since       JDK1.1
369      */
370     public synchronized void setSelectionStart(int selectionStart) {
371         /* Route through select method to enforce consistent policy
372          * between selectionStart and selectionEnd.
373          */
374         select(selectionStart, getSelectionEnd());
375     }
376 
377     /**
378      * Gets the end position of the selected text in
379      * this text component.
380      * @return      the end position of the selected text
381      * @see         java.awt.TextComponent#setSelectionEnd
382      * @see         java.awt.TextComponent#getSelectionStart
383      */
384     public synchronized int getSelectionEnd() {
385         TextComponentPeer peer = (TextComponentPeer)this.peer;
386         if (peer != null) {
387             selectionEnd = peer.getSelectionEnd();
388         }
389         return selectionEnd;
390     }
391 
392     /**
393      * Sets the selection end for this text component to
394      * the specified position. The new end point is constrained
395      * to be at or after the current selection start. It also
396      * cannot be set beyond the end of the component's text.
397      * If the caller supplies a value for <code>selectionEnd</code>
398      * that is out of bounds, the method enforces these constraints
399      * silently, and without failure.
400      * @param       selectionEnd   the end position of the
401      *                        selected text
402      * @see         java.awt.TextComponent#getSelectionEnd
403      * @see         java.awt.TextComponent#setSelectionStart
404      * @since       JDK1.1
405      */
406     public synchronized void setSelectionEnd(int selectionEnd) {
407         /* Route through select method to enforce consistent policy
408          * between selectionStart and selectionEnd.
409          */
410         select(getSelectionStart(), selectionEnd);
411     }
412 
413     /**
414      * Selects the text between the specified start and end positions.
415      * <p>
416      * This method sets the start and end positions of the
417      * selected text, enforcing the restriction that the start position
418      * must be greater than or equal to zero.  The end position must be
419      * greater than or equal to the start position, and less than or
420      * equal to the length of the text component's text.  The
421      * character positions are indexed starting with zero.
422      * The length of the selection is
423      * <code>endPosition</code> - <code>startPosition</code>, so the
424      * character at <code>endPosition</code> is not selected.
425      * If the start and end positions of the selected text are equal,
426      * all text is deselected.
427      * <p>
428      * If the caller supplies values that are inconsistent or out of
429      * bounds, the method enforces these constraints silently, and
430      * without failure. Specifically, if the start position or end
431      * position is greater than the length of the text, it is reset to
432      * equal the text length. If the start position is less than zero,
433      * it is reset to zero, and if the end position is less than the
434      * start position, it is reset to the start position.
435      *
436      * @param        selectionStart the zero-based index of the first
437                        character (<code>char</code> value) to be selected
438      * @param        selectionEnd the zero-based end position of the
439                        text to be selected; the character (<code>char</code> value) at
440                        <code>selectionEnd</code> is not selected
441      * @see          java.awt.TextComponent#setSelectionStart
442      * @see          java.awt.TextComponent#setSelectionEnd
443      * @see          java.awt.TextComponent#selectAll
444      */
445     public synchronized void select(int selectionStart, int selectionEnd) {
446         String text = getText();
447         if (selectionStart < 0) {
448             selectionStart = 0;
449         }
450         if (selectionStart > text.length()) {
451             selectionStart = text.length();
452         }
453         if (selectionEnd > text.length()) {
454             selectionEnd = text.length();
455         }
456         if (selectionEnd < selectionStart) {
457             selectionEnd = selectionStart;
458         }
459 
460         this.selectionStart = selectionStart;
461         this.selectionEnd = selectionEnd;
462 
463         TextComponentPeer peer = (TextComponentPeer)this.peer;
464         if (peer != null) {
465             peer.select(selectionStart, selectionEnd);
466         }
467     }
468 
469     /**
470      * Selects all the text in this text component.
471      * @see        java.awt.TextComponent#select
472      */
473     public synchronized void selectAll() {
474         this.selectionStart = 0;
475         this.selectionEnd = getText().length();
476 
477         TextComponentPeer peer = (TextComponentPeer)this.peer;
478         if (peer != null) {
479             peer.select(selectionStart, selectionEnd);
480         }
481     }
482 
483     /**
484      * Sets the position of the text insertion caret.
485      * The caret position is constrained to be between 0
486      * and the last character of the text, inclusive.
487      * If the passed-in value is greater than this range,
488      * the value is set to the last character (or 0 if
489      * the <code>TextComponent</code> contains no text)
490      * and no error is returned.  If the passed-in value is
491      * less than 0, an <code>IllegalArgumentException</code>
492      * is thrown.
493      *
494      * @param        position the position of the text insertion caret
495      * @exception    IllegalArgumentException if <code>position</code>
496      *               is less than zero
497      * @since        JDK1.1
498      */
499     public synchronized void setCaretPosition(int position) {
500         if (position < 0) {
501             throw new IllegalArgumentException("position less than zero.");
502         }
503 
504         int maxposition = getText().length();
505         if (position > maxposition) {
506             position = maxposition;
507         }
508 
509         TextComponentPeer peer = (TextComponentPeer)this.peer;
510         if (peer != null) {
511             peer.setCaretPosition(position);
512         } else {
513             select(position, position);
514         }
515     }
516 
517     /**
518      * Returns the position of the text insertion caret.
519      * The caret position is constrained to be between 0
520      * and the last character of the text, inclusive.
521      * If the text or caret have not been set, the default
522      * caret position is 0.
523      *
524      * @return       the position of the text insertion caret
525      * @see #setCaretPosition(int)
526      * @since        JDK1.1
527      */
528     public synchronized int getCaretPosition() {
529         TextComponentPeer peer = (TextComponentPeer)this.peer;
530         int position = 0;
531 
532         if (peer != null) {
533             position = peer.getCaretPosition();
534         } else {
535             position = selectionStart;
536         }
537         int maxposition = getText().length();
538         if (position > maxposition) {
539             position = maxposition;
540         }
541         return position;
542     }
543 
544     /**
545      * Adds the specified text event listener to receive text events
546      * from this text component.
547      * If <code>l</code> is <code>null</code>, no exception is
548      * thrown and no action is performed.
549      * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
550      * >AWT Threading Issues</a> for details on AWT's threading model.
551      *
552      * @param l the text event listener
553      * @see             #removeTextListener
554      * @see             #getTextListeners
555      * @see             java.awt.event.TextListener
556      */
557     public synchronized void addTextListener(TextListener l) {
558         if (l == null) {
559             return;
560         }
561         textListener = AWTEventMulticaster.add(textListener, l);
562         newEventsOnly = true;
563     }
564 
565     /**
566      * Removes the specified text event listener so that it no longer
567      * receives text events from this text component
568      * If <code>l</code> is <code>null</code>, no exception is
569      * thrown and no action is performed.
570      * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
571      * >AWT Threading Issues</a> for details on AWT's threading model.
572      *
573      * @param           l     the text listener
574      * @see             #addTextListener
575      * @see             #getTextListeners
576      * @see             java.awt.event.TextListener
577      * @since           JDK1.1
578      */
579     public synchronized void removeTextListener(TextListener l) {
580         if (l == null) {
581             return;
582         }
583         textListener = AWTEventMulticaster.remove(textListener, l);
584     }
585 
586     /**
587      * Returns an array of all the text listeners
588      * registered on this text component.
589      *
590      * @return all of this text component's <code>TextListener</code>s
591      *         or an empty array if no text
592      *         listeners are currently registered
593      *
594      *
595      * @see #addTextListener
596      * @see #removeTextListener
597      * @since 1.4
598      */
599     public synchronized TextListener[] getTextListeners() {
600         return getListeners(TextListener.class);
601     }
602 
603     /**
604      * Returns an array of all the objects currently registered
605      * as <code><em>Foo</em>Listener</code>s
606      * upon this <code>TextComponent</code>.
607      * <code><em>Foo</em>Listener</code>s are registered using the
608      * <code>add<em>Foo</em>Listener</code> method.
609      *
610      * <p>
611      * You can specify the <code>listenerType</code> argument
612      * with a class literal, such as
613      * <code><em>Foo</em>Listener.class</code>.
614      * For example, you can query a
615      * <code>TextComponent</code> <code>t</code>
616      * for its text listeners with the following code:
617      *
618      * <pre>TextListener[] tls = (TextListener[])(t.getListeners(TextListener.class));</pre>
619      *
620      * If no such listeners exist, this method returns an empty array.
621      *
622      * @param listenerType the type of listeners requested; this parameter
623      *          should specify an interface that descends from
624      *          <code>java.util.EventListener</code>
625      * @return an array of all objects registered as
626      *          <code><em>Foo</em>Listener</code>s on this text component,
627      *          or an empty array if no such
628      *          listeners have been added
629      * @exception ClassCastException if <code>listenerType</code>
630      *          doesn't specify a class or interface that implements
631      *          <code>java.util.EventListener</code>
632      *
633      * @see #getTextListeners
634      * @since 1.3
635      */
636     public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
637         EventListener l = null;
638         if  (listenerType == TextListener.class) {
639             l = textListener;
640         } else {
641             return super.getListeners(listenerType);
642         }
643         return AWTEventMulticaster.getListeners(l, listenerType);
644     }
645 
646     // REMIND: remove when filtering is done at lower level
647     boolean eventEnabled(AWTEvent e) {
648         if (e.id == TextEvent.TEXT_VALUE_CHANGED) {
649             if ((eventMask & AWTEvent.TEXT_EVENT_MASK) != 0 ||
650                 textListener != null) {
651                 return true;
652             }
653             return false;
654         }
655         return super.eventEnabled(e);
656     }
657 
658     /**
659      * Processes events on this text component. If the event is a
660      * <code>TextEvent</code>, it invokes the <code>processTextEvent</code>
661      * method else it invokes its superclass's <code>processEvent</code>.
662      * <p>Note that if the event parameter is <code>null</code>
663      * the behavior is unspecified and may result in an
664      * exception.
665      *
666      * @param e the event
667      */
668     protected void processEvent(AWTEvent e) {
669         if (e instanceof TextEvent) {
670             processTextEvent((TextEvent)e);
671             return;
672         }
673         super.processEvent(e);
674     }
675 
676     /**
677      * Processes text events occurring on this text component by
678      * dispatching them to any registered <code>TextListener</code> objects.
679      * <p>
680      * NOTE: This method will not be called unless text events
681      * are enabled for this component. This happens when one of the
682      * following occurs:
683      * <ul>
684      * <li>A <code>TextListener</code> object is registered
685      * via <code>addTextListener</code>
686      * <li>Text events are enabled via <code>enableEvents</code>
687      * </ul>
688      * <p>Note that if the event parameter is <code>null</code>
689      * the behavior is unspecified and may result in an
690      * exception.
691      *
692      * @param e the text event
693      * @see Component#enableEvents
694      */
695     protected void processTextEvent(TextEvent e) {
696         TextListener listener = textListener;
697         if (listener != null) {
698             int id = e.getID();
699             switch (id) {
700             case TextEvent.TEXT_VALUE_CHANGED:
701                 listener.textValueChanged(e);
702                 break;
703             }
704         }
705     }
706 
707     /**
708      * Returns a string representing the state of this
709      * <code>TextComponent</code>. This
710      * method is intended to be used only for debugging purposes, and the
711      * content and format of the returned string may vary between
712      * implementations. The returned string may be empty but may not be
713      * <code>null</code>.
714      *
715      * @return      the parameter string of this text component
716      */
717     protected String paramString() {
718         String str = super.paramString() + ",text=" + getText();
719         if (editable) {
720             str += ",editable";
721         }
722         return str + ",selection=" + getSelectionStart() + "-" + getSelectionEnd();
723     }
724 
725     /**
726      * Assigns a valid value to the canAccessClipboard instance variable.
727      */
728     private boolean canAccessClipboard() {
729         SecurityManager sm = System.getSecurityManager();
730         if (sm == null) return true;
731         try {
732             sm.checkPermission(SecurityConstants.AWT.ACCESS_CLIPBOARD_PERMISSION);
733             return true;
734         } catch (SecurityException e) {}
735         return false;
736     }
737 
738     /*
739      * Serialization support.
740      */
741     /**
742      * The textComponent SerializedDataVersion.
743      *
744      * @serial
745      */
746     private int textComponentSerializedDataVersion = 1;
747 
748     /**
749      * Writes default serializable fields to stream.  Writes
750      * a list of serializable TextListener(s) as optional data.
751      * The non-serializable TextListener(s) are detected and
752      * no attempt is made to serialize them.
753      *
754      * @serialData Null terminated sequence of zero or more pairs.
755      *             A pair consists of a String and Object.
756      *             The String indicates the type of object and
757      *             is one of the following :
758      *             textListenerK indicating and TextListener object.
759      *
760      * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
761      * @see java.awt.Component#textListenerK
762      */
763     private void writeObject(java.io.ObjectOutputStream s)
764       throws IOException
765     {
766         // Serialization support.  Since the value of the fields
767         // selectionStart, selectionEnd, and text aren't necessarily
768         // up to date, we sync them up with the peer before serializing.
769         TextComponentPeer peer = (TextComponentPeer)this.peer;
770         if (peer != null) {
771             text = peer.getText();
772             selectionStart = peer.getSelectionStart();
773             selectionEnd = peer.getSelectionEnd();
774         }
775 
776         s.defaultWriteObject();
777 
778         AWTEventMulticaster.save(s, textListenerK, textListener);
779         s.writeObject(null);
780     }
781 
782     /**
783      * Read the ObjectInputStream, and if it isn't null,
784      * add a listener to receive text events fired by the
785      * TextComponent.  Unrecognized keys or values will be
786      * ignored.
787      *
788      * @exception HeadlessException if
789      * <code>GraphicsEnvironment.isHeadless()</code> returns
790      * <code>true</code>
791      * @see #removeTextListener
792      * @see #addTextListener
793      * @see java.awt.GraphicsEnvironment#isHeadless
794      */
795     private void readObject(ObjectInputStream s)
796         throws ClassNotFoundException, IOException, HeadlessException
797     {
798         GraphicsEnvironment.checkHeadless();
799         s.defaultReadObject();
800 
801         // Make sure the state we just read in for text,
802         // selectionStart and selectionEnd has legal values
803         this.text = (text != null) ? text : "";
804         select(selectionStart, selectionEnd);
805 
806         Object keyOrNull;
807         while(null != (keyOrNull = s.readObject())) {
808             String key = ((String)keyOrNull).intern();
809 
810             if (textListenerK == key) {
811                 addTextListener((TextListener)(s.readObject()));
812             } else {
813                 // skip value for unrecognized key
814                 s.readObject();
815             }
816         }
817         enableInputMethodsIfNecessary();
818     }
819 
820 
821 /////////////////
822 // Accessibility support
823 ////////////////
824 
825     /**
826      * Gets the AccessibleContext associated with this TextComponent.
827      * For text components, the AccessibleContext takes the form of an
828      * AccessibleAWTTextComponent.
829      * A new AccessibleAWTTextComponent instance is created if necessary.
830      *
831      * @return an AccessibleAWTTextComponent that serves as the
832      *         AccessibleContext of this TextComponent
833      * @since 1.3
834      */
835     public AccessibleContext getAccessibleContext() {
836         if (accessibleContext == null) {
837             accessibleContext = new AccessibleAWTTextComponent();
838         }
839         return accessibleContext;
840     }
841 
842     /**
843      * This class implements accessibility support for the
844      * <code>TextComponent</code> class.  It provides an implementation of the
845      * Java Accessibility API appropriate to text component user-interface
846      * elements.
847      * @since 1.3
848      */
849     protected class AccessibleAWTTextComponent extends AccessibleAWTComponent
850         implements AccessibleText, TextListener
851     {
852         /*
853          * JDK 1.3 serialVersionUID
854          */
855         private static final long serialVersionUID = 3631432373506317811L;
856 
857         /**
858          * Constructs an AccessibleAWTTextComponent.  Adds a listener to track
859          * caret change.
860          */
861         public AccessibleAWTTextComponent() {
862             TextComponent.this.addTextListener(this);
863         }
864 
865         /**
866          * TextListener notification of a text value change.
867          */
868         public void textValueChanged(TextEvent textEvent)  {
869             Integer cpos = Integer.valueOf(TextComponent.this.getCaretPosition());
870             firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, cpos);
871         }
872 
873         /**
874          * Gets the state set of the TextComponent.
875          * The AccessibleStateSet of an object is composed of a set of
876          * unique AccessibleStates.  A change in the AccessibleStateSet
877          * of an object will cause a PropertyChangeEvent to be fired
878          * for the AccessibleContext.ACCESSIBLE_STATE_PROPERTY property.
879          *
880          * @return an instance of AccessibleStateSet containing the
881          * current state set of the object
882          * @see AccessibleStateSet
883          * @see AccessibleState
884          * @see #addPropertyChangeListener
885          */
886         public AccessibleStateSet getAccessibleStateSet() {
887             AccessibleStateSet states = super.getAccessibleStateSet();
888             if (TextComponent.this.isEditable()) {
889                 states.add(AccessibleState.EDITABLE);
890             }
891             return states;
892         }
893 
894 
895         /**
896          * Gets the role of this object.
897          *
898          * @return an instance of AccessibleRole describing the role of the
899          * object (AccessibleRole.TEXT)
900          * @see AccessibleRole
901          */
902         public AccessibleRole getAccessibleRole() {
903             return AccessibleRole.TEXT;
904         }
905 
906         /**
907          * Get the AccessibleText associated with this object.  In the
908          * implementation of the Java Accessibility API for this class,
909          * return this object, which is responsible for implementing the
910          * AccessibleText interface on behalf of itself.
911          *
912          * @return this object
913          */
914         public AccessibleText getAccessibleText() {
915             return this;
916         }
917 
918 
919         // --- interface AccessibleText methods ------------------------
920 
921         /**
922          * Many of these methods are just convenience methods; they
923          * just call the equivalent on the parent
924          */
925 
926         /**
927          * Given a point in local coordinates, return the zero-based index
928          * of the character under that Point.  If the point is invalid,
929          * this method returns -1.
930          *
931          * @param p the Point in local coordinates
932          * @return the zero-based index of the character under Point p.
933          */
934         public int getIndexAtPoint(Point p) {
935             return -1;
936         }
937 
938         /**
939          * Determines the bounding box of the character at the given
940          * index into the string.  The bounds are returned in local
941          * coordinates.  If the index is invalid a null rectangle
942          * is returned.
943          *
944          * @param i the index into the String &gt;= 0
945          * @return the screen coordinates of the character's bounding box
946          */
947         public Rectangle getCharacterBounds(int i) {
948             return null;
949         }
950 
951         /**
952          * Returns the number of characters (valid indicies)
953          *
954          * @return the number of characters &gt;= 0
955          */
956         public int getCharCount() {
957             return TextComponent.this.getText().length();
958         }
959 
960         /**
961          * Returns the zero-based offset of the caret.
962          *
963          * Note: The character to the right of the caret will have the
964          * same index value as the offset (the caret is between
965          * two characters).
966          *
967          * @return the zero-based offset of the caret.
968          */
969         public int getCaretPosition() {
970             return TextComponent.this.getCaretPosition();
971         }
972 
973         /**
974          * Returns the AttributeSet for a given character (at a given index).
975          *
976          * @param i the zero-based index into the text
977          * @return the AttributeSet of the character
978          */
979         public AttributeSet getCharacterAttribute(int i) {
980             return null; // No attributes in TextComponent
981         }
982 
983         /**
984          * Returns the start offset within the selected text.
985          * If there is no selection, but there is
986          * a caret, the start and end offsets will be the same.
987          * Return 0 if the text is empty, or the caret position
988          * if no selection.
989          *
990          * @return the index into the text of the start of the selection &gt;= 0
991          */
992         public int getSelectionStart() {
993             return TextComponent.this.getSelectionStart();
994         }
995 
996         /**
997          * Returns the end offset within the selected text.
998          * If there is no selection, but there is
999          * a caret, the start and end offsets will be the same.
1000          * Return 0 if the text is empty, or the caret position
1001          * if no selection.
1002          *
1003          * @return the index into the text of the end of the selection &gt;= 0
1004          */
1005         public int getSelectionEnd() {
1006             return TextComponent.this.getSelectionEnd();
1007         }
1008 
1009         /**
1010          * Returns the portion of the text that is selected.
1011          *
1012          * @return the text, null if no selection
1013          */
1014         public String getSelectedText() {
1015             String selText = TextComponent.this.getSelectedText();
1016             // Fix for 4256662
1017             if (selText == null || selText.equals("")) {
1018                 return null;
1019             }
1020             return selText;
1021         }
1022 
1023         /**
1024          * Returns the String at a given index.
1025          *
1026          * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
1027          * or AccessibleText.SENTENCE to retrieve
1028          * @param index an index within the text &gt;= 0
1029          * @return the letter, word, or sentence,
1030          *   null for an invalid index or part
1031          */
1032         public String getAtIndex(int part, int index) {
1033             if (index < 0 || index >= TextComponent.this.getText().length()) {
1034                 return null;
1035             }
1036             switch (part) {
1037             case AccessibleText.CHARACTER:
1038                 return TextComponent.this.getText().substring(index, index+1);
1039             case AccessibleText.WORD:  {
1040                     String s = TextComponent.this.getText();
1041                     BreakIterator words = BreakIterator.getWordInstance();
1042                     words.setText(s);
1043                     int end = words.following(index);
1044                     return s.substring(words.previous(), end);
1045                 }
1046             case AccessibleText.SENTENCE:  {
1047                     String s = TextComponent.this.getText();
1048                     BreakIterator sentence = BreakIterator.getSentenceInstance();
1049                     sentence.setText(s);
1050                     int end = sentence.following(index);
1051                     return s.substring(sentence.previous(), end);
1052                 }
1053             default:
1054                 return null;
1055             }
1056         }
1057 
1058         private static final boolean NEXT = true;
1059         private static final boolean PREVIOUS = false;
1060 
1061         /**
1062          * Needed to unify forward and backward searching.
1063          * The method assumes that s is the text assigned to words.
1064          */
1065         private int findWordLimit(int index, BreakIterator words, boolean direction,
1066                                          String s) {
1067             // Fix for 4256660 and 4256661.
1068             // Words iterator is different from character and sentence iterators
1069             // in that end of one word is not necessarily start of another word.
1070             // Please see java.text.BreakIterator JavaDoc. The code below is
1071             // based on nextWordStartAfter example from BreakIterator.java.
1072             int last = (direction == NEXT) ? words.following(index)
1073                                            : words.preceding(index);
1074             int current = (direction == NEXT) ? words.next()
1075                                               : words.previous();
1076             while (current != BreakIterator.DONE) {
1077                 for (int p = Math.min(last, current); p < Math.max(last, current); p++) {
1078                     if (Character.isLetter(s.charAt(p))) {
1079                         return last;
1080                     }
1081                 }
1082                 last = current;
1083                 current = (direction == NEXT) ? words.next()
1084                                               : words.previous();
1085             }
1086             return BreakIterator.DONE;
1087         }
1088 
1089         /**
1090          * Returns the String after a given index.
1091          *
1092          * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
1093          * or AccessibleText.SENTENCE to retrieve
1094          * @param index an index within the text &gt;= 0
1095          * @return the letter, word, or sentence, null for an invalid
1096          *  index or part
1097          */
1098         public String getAfterIndex(int part, int index) {
1099             if (index < 0 || index >= TextComponent.this.getText().length()) {
1100                 return null;
1101             }
1102             switch (part) {
1103             case AccessibleText.CHARACTER:
1104                 if (index+1 >= TextComponent.this.getText().length()) {
1105                    return null;
1106                 }
1107                 return TextComponent.this.getText().substring(index+1, index+2);
1108             case AccessibleText.WORD:  {
1109                     String s = TextComponent.this.getText();
1110                     BreakIterator words = BreakIterator.getWordInstance();
1111                     words.setText(s);
1112                     int start = findWordLimit(index, words, NEXT, s);
1113                     if (start == BreakIterator.DONE || start >= s.length()) {
1114                         return null;
1115                     }
1116                     int end = words.following(start);
1117                     if (end == BreakIterator.DONE || end >= s.length()) {
1118                         return null;
1119                     }
1120                     return s.substring(start, end);
1121                 }
1122             case AccessibleText.SENTENCE:  {
1123                     String s = TextComponent.this.getText();
1124                     BreakIterator sentence = BreakIterator.getSentenceInstance();
1125                     sentence.setText(s);
1126                     int start = sentence.following(index);
1127                     if (start == BreakIterator.DONE || start >= s.length()) {
1128                         return null;
1129                     }
1130                     int end = sentence.following(start);
1131                     if (end == BreakIterator.DONE || end >= s.length()) {
1132                         return null;
1133                     }
1134                     return s.substring(start, end);
1135                 }
1136             default:
1137                 return null;
1138             }
1139         }
1140 
1141 
1142         /**
1143          * Returns the String before a given index.
1144          *
1145          * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
1146          *   or AccessibleText.SENTENCE to retrieve
1147          * @param index an index within the text &gt;= 0
1148          * @return the letter, word, or sentence, null for an invalid index
1149          *  or part
1150          */
1151         public String getBeforeIndex(int part, int index) {
1152             if (index < 0 || index > TextComponent.this.getText().length()-1) {
1153                 return null;
1154             }
1155             switch (part) {
1156             case AccessibleText.CHARACTER:
1157                 if (index == 0) {
1158                     return null;
1159                 }
1160                 return TextComponent.this.getText().substring(index-1, index);
1161             case AccessibleText.WORD:  {
1162                     String s = TextComponent.this.getText();
1163                     BreakIterator words = BreakIterator.getWordInstance();
1164                     words.setText(s);
1165                     int end = findWordLimit(index, words, PREVIOUS, s);
1166                     if (end == BreakIterator.DONE) {
1167                         return null;
1168                     }
1169                     int start = words.preceding(end);
1170                     if (start == BreakIterator.DONE) {
1171                         return null;
1172                     }
1173                     return s.substring(start, end);
1174                 }
1175             case AccessibleText.SENTENCE:  {
1176                     String s = TextComponent.this.getText();
1177                     BreakIterator sentence = BreakIterator.getSentenceInstance();
1178                     sentence.setText(s);
1179                     int end = sentence.following(index);
1180                     end = sentence.previous();
1181                     int start = sentence.previous();
1182                     if (start == BreakIterator.DONE) {
1183                         return null;
1184                     }
1185                     return s.substring(start, end);
1186                 }
1187             default:
1188                 return null;
1189             }
1190         }
1191     }  // end of AccessibleAWTTextComponent
1192 
1193     private boolean checkForEnableIM = true;
1194 }