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