1 /*
   2  * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing.text;
  26 
  27 import com.sun.beans.util.Cache;
  28 
  29 import java.security.AccessController;
  30 import java.security.PrivilegedAction;
  31 
  32 import java.beans.JavaBean;
  33 import java.beans.BeanProperty;
  34 import java.beans.Transient;
  35 import java.util.HashMap;
  36 import java.util.Hashtable;
  37 import java.util.Enumeration;
  38 import java.util.Vector;
  39 
  40 import java.util.concurrent.*;
  41 
  42 import java.io.*;
  43 
  44 import java.awt.*;
  45 import java.awt.event.*;
  46 import java.awt.print.*;
  47 import java.awt.datatransfer.*;
  48 import java.awt.im.InputContext;
  49 import java.awt.im.InputMethodRequests;
  50 import java.awt.font.TextHitInfo;
  51 import java.awt.font.TextAttribute;
  52 import java.awt.geom.Point2D;
  53 import java.awt.geom.Rectangle2D;
  54 
  55 import java.awt.print.Printable;
  56 import java.awt.print.PrinterException;
  57 
  58 import javax.print.PrintService;
  59 import javax.print.attribute.PrintRequestAttributeSet;
  60 
  61 import java.text.*;
  62 import java.text.AttributedCharacterIterator.Attribute;
  63 
  64 import javax.swing.*;
  65 import javax.swing.event.*;
  66 import javax.swing.plaf.*;
  67 
  68 import javax.accessibility.*;
  69 
  70 import javax.print.attribute.*;
  71 
  72 import sun.awt.AppContext;
  73 
  74 
  75 import sun.swing.PrintingStatus;
  76 import sun.swing.SwingUtilities2;
  77 import sun.swing.text.TextComponentPrintable;
  78 import sun.swing.SwingAccessor;
  79 
  80 /**
  81  * <code>JTextComponent</code> is the base class for swing text
  82  * components.  It tries to be compatible with the
  83  * <code>java.awt.TextComponent</code> class
  84  * where it can reasonably do so.  Also provided are other services
  85  * for additional flexibility (beyond the pluggable UI and bean
  86  * support).
  87  * You can find information on how to use the functionality
  88  * this class provides in
  89  * <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/generaltext.html">General Rules for Using Text Components</a>,
  90  * a section in <em>The Java Tutorial.</em>
  91  *
  92  * <dl>
  93  * <dt><b>Caret Changes</b>
  94  * <dd>
  95  * The caret is a pluggable object in swing text components.
  96  * Notification of changes to the caret position and the selection
  97  * are sent to implementations of the <code>CaretListener</code>
  98  * interface that have been registered with the text component.
  99  * The UI will install a default caret unless a customized caret
 100  * has been set. <br>
 101  * By default the caret tracks all the document changes
 102  * performed on the Event Dispatching Thread and updates it's position
 103  * accordingly if an insertion occurs before or at the caret position
 104  * or a removal occurs before the caret position. <code>DefaultCaret</code>
 105  * tries to make itself visible which may lead to scrolling
 106  * of a text component within <code>JScrollPane</code>. The default caret
 107  * behavior can be changed by the {@link DefaultCaret#setUpdatePolicy} method.
 108  * <br>
 109  * <b>Note</b>: Non-editable text components also have a caret though
 110  * it may not be painted.
 111  *
 112  * <dt><b>Commands</b>
 113  * <dd>
 114  * Text components provide a number of commands that can be used
 115  * to manipulate the component.  This is essentially the way that
 116  * the component expresses its capabilities.  These are expressed
 117  * in terms of the swing <code>Action</code> interface,
 118  * using the <code>TextAction</code> implementation.
 119  * The set of commands supported by the text component can be
 120  * found with the {@link #getActions} method.  These actions
 121  * can be bound to key events, fired from buttons, etc.
 122  *
 123  * <dt><b>Text Input</b>
 124  * <dd>
 125  * The text components support flexible and internationalized text input, using
 126  * keymaps and the input method framework, while maintaining compatibility with
 127  * the AWT listener model.
 128  * <p>
 129  * A {@link javax.swing.text.Keymap} lets an application bind key
 130  * strokes to actions.
 131  * In order to allow keymaps to be shared across multiple text components, they
 132  * can use actions that extend <code>TextAction</code>.
 133  * <code>TextAction</code> can determine which <code>JTextComponent</code>
 134  * most recently has or had focus and therefore is the subject of
 135  * the action (In the case that the <code>ActionEvent</code>
 136  * sent to the action doesn't contain the target text component as its source).
 137  * <p>
 138  * The {@extLink imf_overview Input Method Framework}
 139  * lets text components interact with input methods, separate software
 140  * components that preprocess events to let users enter thousands of
 141  * different characters using keyboards with far fewer keys.
 142  * <code>JTextComponent</code> is an <em>active client</em> of
 143  * the framework, so it implements the preferred user interface for interacting
 144  * with input methods. As a consequence, some key events do not reach the text
 145  * component because they are handled by an input method, and some text input
 146  * reaches the text component as committed text within an {@link
 147  * java.awt.event.InputMethodEvent} instead of as a key event.
 148  * The complete text input is the combination of the characters in
 149  * <code>keyTyped</code> key events and committed text in input method events.
 150  * <p>
 151  * The AWT listener model lets applications attach event listeners to
 152  * components in order to bind events to actions. Swing encourages the
 153  * use of keymaps instead of listeners, but maintains compatibility
 154  * with listeners by giving the listeners a chance to steal an event
 155  * by consuming it.
 156  * <p>
 157  * Keyboard event and input method events are handled in the following stages,
 158  * with each stage capable of consuming the event:
 159  *
 160  * <table class="striped">
 161  * <caption>Stages of keyboard and input method event handling</caption>
 162  * <thead>
 163  * <tr>
 164  * <th id="stage">Stage</th>
 165  * <th id="ke">KeyEvent</th>
 166  * <th id="ime">InputMethodEvent</th></tr>
 167  * </thead>
 168  * <tbody>
 169  * <tr><td headers="stage">1.   </td>
 170  *     <td headers="ke">input methods </td>
 171  *     <td headers="ime">(generated here)</td></tr>
 172  * <tr><td headers="stage">2.   </td>
 173  *     <td headers="ke">focus manager </td>
 174  *     <td headers="ime"></td>
 175  * </tr>
 176  * <tr>
 177  *     <td headers="stage">3.   </td>
 178  *     <td headers="ke">registered key listeners</td>
 179  *     <td headers="ime">registered input method listeners</tr>
 180  * <tr>
 181  *     <td headers="stage">4.   </td>
 182  *     <td headers="ke"></td>
 183  *     <td headers="ime">input method handling in JTextComponent</tr>
 184  * <tr>
 185  *     <td headers="stage">5.   </td><td headers="ke ime" colspan=2>keymap handling using the current keymap</td></tr>
 186  * <tr><td headers="stage">6.   </td><td headers="ke">keyboard handling in JComponent (e.g. accelerators, component navigation, etc.)</td>
 187  *     <td headers="ime"></td></tr>
 188  * </tbody>
 189  * </table>
 190  *
 191  * <p>
 192  * To maintain compatibility with applications that listen to key
 193  * events but are not aware of input method events, the input
 194  * method handling in stage 4 provides a compatibility mode for
 195  * components that do not process input method events. For these
 196  * components, the committed text is converted to keyTyped key events
 197  * and processed in the key event pipeline starting at stage 3
 198  * instead of in the input method event pipeline.
 199  * <p>
 200  * By default the component will create a keymap (named <b>DEFAULT_KEYMAP</b>)
 201  * that is shared by all JTextComponent instances as the default keymap.
 202  * Typically a look-and-feel implementation will install a different keymap
 203  * that resolves to the default keymap for those bindings not found in the
 204  * different keymap. The minimal bindings include:
 205  * <ul>
 206  * <li>inserting content into the editor for the
 207  *  printable keys.
 208  * <li>removing content with the backspace and del
 209  *  keys.
 210  * <li>caret movement forward and backward
 211  * </ul>
 212  *
 213  * <dt><b>Model/View Split</b>
 214  * <dd>
 215  * The text components have a model-view split.  A text component pulls
 216  * together the objects used to represent the model, view, and controller.
 217  * The text document model may be shared by other views which act as observers
 218  * of the model (e.g. a document may be shared by multiple components).
 219  *
 220  * <p style="text-align:center"><img src="doc-files/editor.gif" alt="Diagram showing interaction between Controller, Document, events, and ViewFactory"
 221  *                  HEIGHT=358 WIDTH=587></p>
 222  *
 223  * <p>
 224  * The model is defined by the {@link Document} interface.
 225  * This is intended to provide a flexible text storage mechanism
 226  * that tracks change during edits and can be extended to more sophisticated
 227  * models.  The model interfaces are meant to capture the capabilities of
 228  * expression given by SGML, a system used to express a wide variety of
 229  * content.
 230  * Each modification to the document causes notification of the
 231  * details of the change to be sent to all observers in the form of a
 232  * {@link DocumentEvent} which allows the views to stay up to date with the model.
 233  * This event is sent to observers that have implemented the
 234  * {@link DocumentListener}
 235  * interface and registered interest with the model being observed.
 236  *
 237  * <dt><b>Location Information</b>
 238  * <dd>
 239  * The capability of determining the location of text in
 240  * the view is provided.  There are two methods, {@link #modelToView}
 241  * and {@link #viewToModel} for determining this information.
 242  *
 243  * <dt><b>Undo/Redo support</b>
 244  * <dd>
 245  * Support for an edit history mechanism is provided to allow
 246  * undo/redo operations.  The text component does not itself
 247  * provide the history buffer by default, but does provide
 248  * the <code>UndoableEdit</code> records that can be used in conjunction
 249  * with a history buffer to provide the undo/redo support.
 250  * The support is provided by the Document model, which allows
 251  * one to attach UndoableEditListener implementations.
 252  *
 253  * <dt><b>Thread Safety</b>
 254  * <dd>
 255  * The swing text components provide some support of thread
 256  * safe operations.  Because of the high level of configurability
 257  * of the text components, it is possible to circumvent the
 258  * protection provided.  The protection primarily comes from
 259  * the model, so the documentation of <code>AbstractDocument</code>
 260  * describes the assumptions of the protection provided.
 261  * The methods that are safe to call asynchronously are marked
 262  * with comments.
 263  *
 264  * <dt><b>Newlines</b>
 265  * <dd>
 266  * For a discussion on how newlines are handled, see
 267  * <a href="DefaultEditorKit.html">DefaultEditorKit</a>.
 268  *
 269  *
 270  * <dt><b>Printing support</b>
 271  * <dd>
 272  * Several {@link #print print} methods are provided for basic
 273  * document printing.  If more advanced printing is needed, use the
 274  * {@link #getPrintable} method.
 275  * </dl>
 276  *
 277  * <p>
 278  * <strong>Warning:</strong>
 279  * Serialized objects of this class will not be compatible with
 280  * future Swing releases. The current serialization support is
 281  * appropriate for short term storage or RMI between applications running
 282  * the same version of Swing.  As of 1.4, support for long term storage
 283  * of all JavaBeans&trade;
 284  * has been added to the <code>java.beans</code> package.
 285  * Please see {@link java.beans.XMLEncoder}.
 286  *
 287  * @author  Timothy Prinzing
 288  * @author Igor Kushnirskiy (printing support)
 289  * @see Document
 290  * @see DocumentEvent
 291  * @see DocumentListener
 292  * @see Caret
 293  * @see CaretEvent
 294  * @see CaretListener
 295  * @see TextUI
 296  * @see View
 297  * @see ViewFactory
 298  */
 299 @JavaBean(defaultProperty = "UI")
 300 @SwingContainer(false)
 301 @SuppressWarnings("serial") // Same-version serialization only
 302 public abstract class JTextComponent extends JComponent implements Scrollable, Accessible
 303 {
 304     /**
 305      * Creates a new <code>JTextComponent</code>.
 306      * Listeners for caret events are established, and the pluggable
 307      * UI installed.  The component is marked as editable.  No layout manager
 308      * is used, because layout is managed by the view subsystem of text.
 309      * The document model is set to <code>null</code>.
 310      */
 311     public JTextComponent() {
 312         super();
 313         // enable InputMethodEvent for on-the-spot pre-editing
 314         enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.INPUT_METHOD_EVENT_MASK);
 315         caretEvent = new MutableCaretEvent(this);
 316         addMouseListener(caretEvent);
 317         addFocusListener(caretEvent);
 318         setEditable(true);
 319         setDragEnabled(false);
 320         setLayout(null); // layout is managed by View hierarchy
 321         updateUI();
 322     }
 323 
 324     /**
 325      * Fetches the user-interface factory for this text-oriented editor.
 326      *
 327      * @return the factory
 328      */
 329     public TextUI getUI() { return (TextUI)ui; }
 330 
 331     /**
 332      * Sets the user-interface factory for this text-oriented editor.
 333      *
 334      * @param ui the factory
 335      */
 336     public void setUI(TextUI ui) {
 337         super.setUI(ui);
 338     }
 339 
 340     /**
 341      * Reloads the pluggable UI.  The key used to fetch the
 342      * new interface is <code>getUIClassID()</code>.  The type of
 343      * the UI is <code>TextUI</code>.  <code>invalidate</code>
 344      * is called after setting the UI.
 345      */
 346     public void updateUI() {
 347         setUI((TextUI)UIManager.getUI(this));
 348         invalidate();
 349     }
 350 
 351     /**
 352      * Adds a caret listener for notification of any changes
 353      * to the caret.
 354      *
 355      * @param listener the listener to be added
 356      * @see javax.swing.event.CaretEvent
 357      */
 358     public void addCaretListener(CaretListener listener) {
 359         listenerList.add(CaretListener.class, listener);
 360     }
 361 
 362     /**
 363      * Removes a caret listener.
 364      *
 365      * @param listener the listener to be removed
 366      * @see javax.swing.event.CaretEvent
 367      */
 368     public void removeCaretListener(CaretListener listener) {
 369         listenerList.remove(CaretListener.class, listener);
 370     }
 371 
 372     /**
 373      * Returns an array of all the caret listeners
 374      * registered on this text component.
 375      *
 376      * @return all of this component's <code>CaretListener</code>s
 377      *         or an empty
 378      *         array if no caret listeners are currently registered
 379      *
 380      * @see #addCaretListener
 381      * @see #removeCaretListener
 382      *
 383      * @since 1.4
 384      */
 385     @BeanProperty(bound = false)
 386     public CaretListener[] getCaretListeners() {
 387         return listenerList.getListeners(CaretListener.class);
 388     }
 389 
 390     /**
 391      * Notifies all listeners that have registered interest for
 392      * notification on this event type.  The event instance
 393      * is lazily created using the parameters passed into
 394      * the fire method.  The listener list is processed in a
 395      * last-to-first manner.
 396      *
 397      * @param e the event
 398      * @see EventListenerList
 399      */
 400     protected void fireCaretUpdate(CaretEvent e) {
 401         // Guaranteed to return a non-null array
 402         Object[] listeners = listenerList.getListenerList();
 403         // Process the listeners last to first, notifying
 404         // those that are interested in this event
 405         for (int i = listeners.length-2; i>=0; i-=2) {
 406             if (listeners[i]==CaretListener.class) {
 407                 ((CaretListener)listeners[i+1]).caretUpdate(e);
 408             }
 409         }
 410     }
 411 
 412     /**
 413      * Associates the editor with a text document.
 414      * The currently registered factory is used to build a view for
 415      * the document, which gets displayed by the editor after revalidation.
 416      * A PropertyChange event ("document") is propagated to each listener.
 417      *
 418      * @param doc  the document to display/edit
 419      * @see #getDocument
 420      */
 421     @BeanProperty(expert = true, description
 422             = "the text document model")
 423     public void setDocument(Document doc) {
 424         Document old = model;
 425 
 426         /*
 427          * acquire a read lock on the old model to prevent notification of
 428          * mutations while we disconnecting the old model.
 429          */
 430         try {
 431             if (old instanceof AbstractDocument) {
 432                 ((AbstractDocument)old).readLock();
 433             }
 434             if (accessibleContext != null) {
 435                 model.removeDocumentListener(
 436                     ((AccessibleJTextComponent)accessibleContext));
 437             }
 438             if (inputMethodRequestsHandler != null) {
 439                 model.removeDocumentListener((DocumentListener)inputMethodRequestsHandler);
 440             }
 441             model = doc;
 442 
 443             // Set the document's run direction property to match the
 444             // component's ComponentOrientation property.
 445             Boolean runDir = getComponentOrientation().isLeftToRight()
 446                              ? TextAttribute.RUN_DIRECTION_LTR
 447                              : TextAttribute.RUN_DIRECTION_RTL;
 448             if (runDir != doc.getProperty(TextAttribute.RUN_DIRECTION)) {
 449                 doc.putProperty(TextAttribute.RUN_DIRECTION, runDir );
 450             }
 451             firePropertyChange("document", old, doc);
 452         } finally {
 453             if (old instanceof AbstractDocument) {
 454                 ((AbstractDocument)old).readUnlock();
 455             }
 456         }
 457 
 458         revalidate();
 459         repaint();
 460         if (accessibleContext != null) {
 461             model.addDocumentListener(
 462                 ((AccessibleJTextComponent)accessibleContext));
 463         }
 464         if (inputMethodRequestsHandler != null) {
 465             model.addDocumentListener((DocumentListener)inputMethodRequestsHandler);
 466         }
 467     }
 468 
 469     /**
 470      * Fetches the model associated with the editor.  This is
 471      * primarily for the UI to get at the minimal amount of
 472      * state required to be a text editor.  Subclasses will
 473      * return the actual type of the model which will typically
 474      * be something that extends Document.
 475      *
 476      * @return the model
 477      */
 478     public Document getDocument() {
 479         return model;
 480     }
 481 
 482     // Override of Component.setComponentOrientation
 483     public void setComponentOrientation( ComponentOrientation o ) {
 484         // Set the document's run direction property to match the
 485         // ComponentOrientation property.
 486         Document doc = getDocument();
 487         if( doc !=  null ) {
 488             Boolean runDir = o.isLeftToRight()
 489                              ? TextAttribute.RUN_DIRECTION_LTR
 490                              : TextAttribute.RUN_DIRECTION_RTL;
 491             doc.putProperty( TextAttribute.RUN_DIRECTION, runDir );
 492         }
 493         super.setComponentOrientation( o );
 494     }
 495 
 496     /**
 497      * Fetches the command list for the editor.  This is
 498      * the list of commands supported by the plugged-in UI
 499      * augmented by the collection of commands that the
 500      * editor itself supports.  These are useful for binding
 501      * to events, such as in a keymap.
 502      *
 503      * @return the command list
 504      */
 505     @BeanProperty(bound = false)
 506     public Action[] getActions() {
 507         return getUI().getEditorKit(this).getActions();
 508     }
 509 
 510     /**
 511      * Sets margin space between the text component's border
 512      * and its text.  The text component's default <code>Border</code>
 513      * object will use this value to create the proper margin.
 514      * However, if a non-default border is set on the text component,
 515      * it is that <code>Border</code> object's responsibility to create the
 516      * appropriate margin space (else this property will effectively
 517      * be ignored).  This causes a redraw of the component.
 518      * A PropertyChange event ("margin") is sent to all listeners.
 519      *
 520      * @param m the space between the border and the text
 521      */
 522     @BeanProperty(description
 523             = "desired space between the border and text area")
 524     public void setMargin(Insets m) {
 525         Insets old = margin;
 526         margin = m;
 527         firePropertyChange("margin", old, m);
 528         invalidate();
 529     }
 530 
 531     /**
 532      * Returns the margin between the text component's border and
 533      * its text.
 534      *
 535      * @return the margin
 536      */
 537     public Insets getMargin() {
 538         return margin;
 539     }
 540 
 541     /**
 542      * Sets the <code>NavigationFilter</code>. <code>NavigationFilter</code>
 543      * is used by <code>DefaultCaret</code> and the default cursor movement
 544      * actions as a way to restrict the cursor movement.
 545      * @param filter the filter
 546      *
 547      * @since 1.4
 548      */
 549     public void setNavigationFilter(NavigationFilter filter) {
 550         navigationFilter = filter;
 551     }
 552 
 553     /**
 554      * Returns the <code>NavigationFilter</code>. <code>NavigationFilter</code>
 555      * is used by <code>DefaultCaret</code> and the default cursor movement
 556      * actions as a way to restrict the cursor movement. A null return value
 557      * implies the cursor movement and selection should not be restricted.
 558      *
 559      * @since 1.4
 560      * @return the NavigationFilter
 561      */
 562     public NavigationFilter getNavigationFilter() {
 563         return navigationFilter;
 564     }
 565 
 566     /**
 567      * Fetches the caret that allows text-oriented navigation over
 568      * the view.
 569      *
 570      * @return the caret
 571      */
 572     @Transient
 573     public Caret getCaret() {
 574         return caret;
 575     }
 576 
 577     /**
 578      * Sets the caret to be used.  By default this will be set
 579      * by the UI that gets installed.  This can be changed to
 580      * a custom caret if desired.  Setting the caret results in a
 581      * PropertyChange event ("caret") being fired.
 582      *
 583      * @param c the caret
 584      * @see #getCaret
 585      */
 586     @BeanProperty(expert = true, description
 587             = "the caret used to select/navigate")
 588     public void setCaret(Caret c) {
 589         if (caret != null) {
 590             caret.removeChangeListener(caretEvent);
 591             caret.deinstall(this);
 592         }
 593         Caret old = caret;
 594         caret = c;
 595         if (caret != null) {
 596             caret.install(this);
 597             caret.addChangeListener(caretEvent);
 598         }
 599         firePropertyChange("caret", old, caret);
 600     }
 601 
 602     /**
 603      * Fetches the object responsible for making highlights.
 604      *
 605      * @return the highlighter
 606      */
 607     public Highlighter getHighlighter() {
 608         return highlighter;
 609     }
 610 
 611     /**
 612      * Sets the highlighter to be used.  By default this will be set
 613      * by the UI that gets installed.  This can be changed to
 614      * a custom highlighter if desired.  The highlighter can be set to
 615      * <code>null</code> to disable it.
 616      * A PropertyChange event ("highlighter") is fired
 617      * when a new highlighter is installed.
 618      *
 619      * @param h the highlighter
 620      * @see #getHighlighter
 621      */
 622     @BeanProperty(expert = true, description
 623             = "object responsible for background highlights")
 624     public void setHighlighter(Highlighter h) {
 625         if (highlighter != null) {
 626             highlighter.deinstall(this);
 627         }
 628         Highlighter old = highlighter;
 629         highlighter = h;
 630         if (highlighter != null) {
 631             highlighter.install(this);
 632         }
 633         firePropertyChange("highlighter", old, h);
 634     }
 635 
 636     /**
 637      * Sets the keymap to use for binding events to
 638      * actions.  Setting to <code>null</code> effectively disables
 639      * keyboard input.
 640      * A PropertyChange event ("keymap") is fired when a new keymap
 641      * is installed.
 642      *
 643      * @param map the keymap
 644      * @see #getKeymap
 645      */
 646     @BeanProperty(description
 647             = "set of key event to action bindings to use")
 648     public void setKeymap(Keymap map) {
 649         Keymap old = keymap;
 650         keymap = map;
 651         firePropertyChange("keymap", old, keymap);
 652         updateInputMap(old, map);
 653     }
 654 
 655     /**
 656      * Turns on or off automatic drag handling. In order to enable automatic
 657      * drag handling, this property should be set to {@code true}, and the
 658      * component's {@code TransferHandler} needs to be {@code non-null}.
 659      * The default value of the {@code dragEnabled} property is {@code false}.
 660      * <p>
 661      * The job of honoring this property, and recognizing a user drag gesture,
 662      * lies with the look and feel implementation, and in particular, the component's
 663      * {@code TextUI}. When automatic drag handling is enabled, most look and
 664      * feels (including those that subclass {@code BasicLookAndFeel}) begin a
 665      * drag and drop operation whenever the user presses the mouse button over
 666      * a selection and then moves the mouse a few pixels. Setting this property to
 667      * {@code true} can therefore have a subtle effect on how selections behave.
 668      * <p>
 669      * If a look and feel is used that ignores this property, you can still
 670      * begin a drag and drop operation by calling {@code exportAsDrag} on the
 671      * component's {@code TransferHandler}.
 672      *
 673      * @param b whether or not to enable automatic drag handling
 674      * @exception HeadlessException if
 675      *            <code>b</code> is <code>true</code> and
 676      *            <code>GraphicsEnvironment.isHeadless()</code>
 677      *            returns <code>true</code>
 678      * @see java.awt.GraphicsEnvironment#isHeadless
 679      * @see #getDragEnabled
 680      * @see #setTransferHandler
 681      * @see TransferHandler
 682      * @since 1.4
 683      */
 684     @BeanProperty(bound = false, description
 685             = "determines whether automatic drag handling is enabled")
 686     public void setDragEnabled(boolean b) {
 687         checkDragEnabled(b);
 688         dragEnabled = b;
 689     }
 690 
 691     private static void checkDragEnabled(boolean b) {
 692         if (b && GraphicsEnvironment.isHeadless()) {
 693             throw new HeadlessException();
 694         }
 695     }
 696 
 697     /**
 698      * Returns whether or not automatic drag handling is enabled.
 699      *
 700      * @return the value of the {@code dragEnabled} property
 701      * @see #setDragEnabled
 702      * @since 1.4
 703      */
 704     public boolean getDragEnabled() {
 705         return dragEnabled;
 706     }
 707 
 708     /**
 709      * Sets the drop mode for this component. For backward compatibility,
 710      * the default for this property is <code>DropMode.USE_SELECTION</code>.
 711      * Usage of <code>DropMode.INSERT</code> is recommended, however,
 712      * for an improved user experience. It offers similar behavior of dropping
 713      * between text locations, but does so without affecting the actual text
 714      * selection and caret location.
 715      * <p>
 716      * <code>JTextComponents</code> support the following drop modes:
 717      * <ul>
 718      *    <li><code>DropMode.USE_SELECTION</code></li>
 719      *    <li><code>DropMode.INSERT</code></li>
 720      * </ul>
 721      * <p>
 722      * The drop mode is only meaningful if this component has a
 723      * <code>TransferHandler</code> that accepts drops.
 724      *
 725      * @param dropMode the drop mode to use
 726      * @throws IllegalArgumentException if the drop mode is unsupported
 727      *         or <code>null</code>
 728      * @see #getDropMode
 729      * @see #getDropLocation
 730      * @see #setTransferHandler
 731      * @see javax.swing.TransferHandler
 732      * @since 1.6
 733      */
 734     public final void setDropMode(DropMode dropMode) {
 735         checkDropMode(dropMode);
 736         this.dropMode = dropMode;
 737     }
 738 
 739     private static void checkDropMode(DropMode dropMode) {
 740         if (dropMode != null) {
 741             switch (dropMode) {
 742                 case USE_SELECTION:
 743                 case INSERT:
 744                     return;
 745             }
 746         }
 747 
 748         throw new IllegalArgumentException(dropMode + ": Unsupported drop mode for text");
 749     }
 750 
 751     /**
 752      * Returns the drop mode for this component.
 753      *
 754      * @return the drop mode for this component
 755      * @see #setDropMode
 756      * @since 1.6
 757      */
 758     public final DropMode getDropMode() {
 759         return dropMode;
 760     }
 761 
 762     static {
 763         SwingAccessor.setJTextComponentAccessor(
 764             new SwingAccessor.JTextComponentAccessor() {
 765                 public TransferHandler.DropLocation dropLocationForPoint(JTextComponent textComp,
 766                                                                          Point p)
 767                 {
 768                     return textComp.dropLocationForPoint(p);
 769                 }
 770                 public Object setDropLocation(JTextComponent textComp,
 771                                               TransferHandler.DropLocation location,
 772                                               Object state, boolean forDrop)
 773                 {
 774                     return textComp.setDropLocation(location, state, forDrop);
 775                 }
 776             });
 777     }
 778 
 779 
 780     /**
 781      * Calculates a drop location in this component, representing where a
 782      * drop at the given point should insert data.
 783      * <p>
 784      * Note: This method is meant to override
 785      * <code>JComponent.dropLocationForPoint()</code>, which is package-private
 786      * in javax.swing. <code>TransferHandler</code> will detect text components
 787      * and call this method instead via reflection. It's name should therefore
 788      * not be changed.
 789      *
 790      * @param p the point to calculate a drop location for
 791      * @return the drop location, or <code>null</code>
 792      */
 793     @SuppressWarnings("deprecation")
 794     DropLocation dropLocationForPoint(Point p) {
 795         Position.Bias[] bias = new Position.Bias[1];
 796         int index = getUI().viewToModel(this, p, bias);
 797 
 798         // viewToModel currently returns null for some HTML content
 799         // when the point is within the component's top inset
 800         if (bias[0] == null) {
 801             bias[0] = Position.Bias.Forward;
 802         }
 803 
 804         return new DropLocation(p, index, bias[0]);
 805     }
 806 
 807     /**
 808      * Called to set or clear the drop location during a DnD operation.
 809      * In some cases, the component may need to use it's internal selection
 810      * temporarily to indicate the drop location. To help facilitate this,
 811      * this method returns and accepts as a parameter a state object.
 812      * This state object can be used to store, and later restore, the selection
 813      * state. Whatever this method returns will be passed back to it in
 814      * future calls, as the state parameter. If it wants the DnD system to
 815      * continue storing the same state, it must pass it back every time.
 816      * Here's how this is used:
 817      * <p>
 818      * Let's say that on the first call to this method the component decides
 819      * to save some state (because it is about to use the selection to show
 820      * a drop index). It can return a state object to the caller encapsulating
 821      * any saved selection state. On a second call, let's say the drop location
 822      * is being changed to something else. The component doesn't need to
 823      * restore anything yet, so it simply passes back the same state object
 824      * to have the DnD system continue storing it. Finally, let's say this
 825      * method is messaged with <code>null</code>. This means DnD
 826      * is finished with this component for now, meaning it should restore
 827      * state. At this point, it can use the state parameter to restore
 828      * said state, and of course return <code>null</code> since there's
 829      * no longer anything to store.
 830      * <p>
 831      * Note: This method is meant to override
 832      * <code>JComponent.setDropLocation()</code>, which is package-private
 833      * in javax.swing. <code>TransferHandler</code> will detect text components
 834      * and call this method instead via reflection. It's name should therefore
 835      * not be changed.
 836      *
 837      * @param location the drop location (as calculated by
 838      *        <code>dropLocationForPoint</code>) or <code>null</code>
 839      *        if there's no longer a valid drop location
 840      * @param state the state object saved earlier for this component,
 841      *        or <code>null</code>
 842      * @param forDrop whether or not the method is being called because an
 843      *        actual drop occurred
 844      * @return any saved state for this component, or <code>null</code> if none
 845      */
 846     Object setDropLocation(TransferHandler.DropLocation location,
 847                            Object state,
 848                            boolean forDrop) {
 849 
 850         Object retVal = null;
 851         DropLocation textLocation = (DropLocation)location;
 852 
 853         if (dropMode == DropMode.USE_SELECTION) {
 854             if (textLocation == null) {
 855                 if (state != null) {
 856                     /*
 857                      * This object represents the state saved earlier.
 858                      *     If the caret is a DefaultCaret it will be
 859                      *     an Object array containing, in order:
 860                      *         - the saved caret mark (Integer)
 861                      *         - the saved caret dot (Integer)
 862                      *         - the saved caret visibility (Boolean)
 863                      *         - the saved mark bias (Position.Bias)
 864                      *         - the saved dot bias (Position.Bias)
 865                      *     If the caret is not a DefaultCaret it will
 866                      *     be similar, but will not contain the dot
 867                      *     or mark bias.
 868                      */
 869                     Object[] vals = (Object[])state;
 870 
 871                     if (!forDrop) {
 872                         if (caret instanceof DefaultCaret) {
 873                             ((DefaultCaret)caret).setDot(((Integer)vals[0]).intValue(),
 874                                                          (Position.Bias)vals[3]);
 875                             ((DefaultCaret)caret).moveDot(((Integer)vals[1]).intValue(),
 876                                                          (Position.Bias)vals[4]);
 877                         } else {
 878                             caret.setDot(((Integer)vals[0]).intValue());
 879                             caret.moveDot(((Integer)vals[1]).intValue());
 880                         }
 881                     }
 882 
 883                     caret.setVisible(((Boolean)vals[2]).booleanValue());
 884                 }
 885             } else {
 886                 if (dropLocation == null) {
 887                     boolean visible;
 888 
 889                     if (caret instanceof DefaultCaret) {
 890                         DefaultCaret dc = (DefaultCaret)caret;
 891                         visible = dc.isActive();
 892                         retVal = new Object[] {Integer.valueOf(dc.getMark()),
 893                                                Integer.valueOf(dc.getDot()),
 894                                                Boolean.valueOf(visible),
 895                                                dc.getMarkBias(),
 896                                                dc.getDotBias()};
 897                     } else {
 898                         visible = caret.isVisible();
 899                         retVal = new Object[] {Integer.valueOf(caret.getMark()),
 900                                                Integer.valueOf(caret.getDot()),
 901                                                Boolean.valueOf(visible)};
 902                     }
 903 
 904                     caret.setVisible(true);
 905                 } else {
 906                     retVal = state;
 907                 }
 908 
 909                 if (caret instanceof DefaultCaret) {
 910                     ((DefaultCaret)caret).setDot(textLocation.getIndex(), textLocation.getBias());
 911                 } else {
 912                     caret.setDot(textLocation.getIndex());
 913                 }
 914             }
 915         } else {
 916             if (textLocation == null) {
 917                 if (state != null) {
 918                     caret.setVisible(((Boolean)state).booleanValue());
 919                 }
 920             } else {
 921                 if (dropLocation == null) {
 922                     boolean visible = caret instanceof DefaultCaret
 923                                       ? ((DefaultCaret)caret).isActive()
 924                                       : caret.isVisible();
 925                     retVal = Boolean.valueOf(visible);
 926                     caret.setVisible(false);
 927                 } else {
 928                     retVal = state;
 929                 }
 930             }
 931         }
 932 
 933         DropLocation old = dropLocation;
 934         dropLocation = textLocation;
 935         firePropertyChange("dropLocation", old, dropLocation);
 936 
 937         return retVal;
 938     }
 939 
 940     /**
 941      * Returns the location that this component should visually indicate
 942      * as the drop location during a DnD operation over the component,
 943      * or {@code null} if no location is to currently be shown.
 944      * <p>
 945      * This method is not meant for querying the drop location
 946      * from a {@code TransferHandler}, as the drop location is only
 947      * set after the {@code TransferHandler}'s <code>canImport</code>
 948      * has returned and has allowed for the location to be shown.
 949      * <p>
 950      * When this property changes, a property change event with
 951      * name "dropLocation" is fired by the component.
 952      *
 953      * @return the drop location
 954      * @see #setDropMode
 955      * @see TransferHandler#canImport(TransferHandler.TransferSupport)
 956      * @since 1.6
 957      */
 958     @BeanProperty(bound = false)
 959     public final DropLocation getDropLocation() {
 960         return dropLocation;
 961     }
 962 
 963 
 964     /**
 965      * Updates the <code>InputMap</code>s in response to a
 966      * <code>Keymap</code> change.
 967      * @param oldKm  the old <code>Keymap</code>
 968      * @param newKm  the new <code>Keymap</code>
 969      */
 970     void updateInputMap(Keymap oldKm, Keymap newKm) {
 971         // Locate the current KeymapWrapper.
 972         InputMap km = getInputMap(JComponent.WHEN_FOCUSED);
 973         InputMap last = km;
 974         while (km != null && !(km instanceof KeymapWrapper)) {
 975             last = km;
 976             km = km.getParent();
 977         }
 978         if (km != null) {
 979             // Found it, tweak the InputMap that points to it, as well
 980             // as anything it points to.
 981             if (newKm == null) {
 982                 if (last != km) {
 983                     last.setParent(km.getParent());
 984                 }
 985                 else {
 986                     last.setParent(null);
 987                 }
 988             }
 989             else {
 990                 InputMap newKM = new KeymapWrapper(newKm);
 991                 last.setParent(newKM);
 992                 if (last != km) {
 993                     newKM.setParent(km.getParent());
 994                 }
 995             }
 996         }
 997         else if (newKm != null) {
 998             km = getInputMap(JComponent.WHEN_FOCUSED);
 999             if (km != null) {
1000                 // Couldn't find it.
1001                 // Set the parent of WHEN_FOCUSED InputMap to be the new one.
1002                 InputMap newKM = new KeymapWrapper(newKm);
1003                 newKM.setParent(km.getParent());
1004                 km.setParent(newKM);
1005             }
1006         }
1007 
1008         // Do the same thing with the ActionMap
1009         ActionMap am = getActionMap();
1010         ActionMap lastAM = am;
1011         while (am != null && !(am instanceof KeymapActionMap)) {
1012             lastAM = am;
1013             am = am.getParent();
1014         }
1015         if (am != null) {
1016             // Found it, tweak the Actionap that points to it, as well
1017             // as anything it points to.
1018             if (newKm == null) {
1019                 if (lastAM != am) {
1020                     lastAM.setParent(am.getParent());
1021                 }
1022                 else {
1023                     lastAM.setParent(null);
1024                 }
1025             }
1026             else {
1027                 ActionMap newAM = new KeymapActionMap(newKm);
1028                 lastAM.setParent(newAM);
1029                 if (lastAM != am) {
1030                     newAM.setParent(am.getParent());
1031                 }
1032             }
1033         }
1034         else if (newKm != null) {
1035             am = getActionMap();
1036             if (am != null) {
1037                 // Couldn't find it.
1038                 // Set the parent of ActionMap to be the new one.
1039                 ActionMap newAM = new KeymapActionMap(newKm);
1040                 newAM.setParent(am.getParent());
1041                 am.setParent(newAM);
1042             }
1043         }
1044     }
1045 
1046     /**
1047      * Fetches the keymap currently active in this text
1048      * component.
1049      *
1050      * @return the keymap
1051      */
1052     public Keymap getKeymap() {
1053         return keymap;
1054     }
1055 
1056     /**
1057      * Adds a new keymap into the keymap hierarchy.  Keymap bindings
1058      * resolve from bottom up so an attribute specified in a child
1059      * will override an attribute specified in the parent.
1060      *
1061      * @param nm   the name of the keymap (must be unique within the
1062      *   collection of named keymaps in the document); the name may
1063      *   be <code>null</code> if the keymap is unnamed,
1064      *   but the caller is responsible for managing the reference
1065      *   returned as an unnamed keymap can't
1066      *   be fetched by name
1067      * @param parent the parent keymap; this may be <code>null</code> if
1068      *   unspecified bindings need not be resolved in some other keymap
1069      * @return the keymap
1070      */
1071     public static Keymap addKeymap(String nm, Keymap parent) {
1072         Keymap map = new DefaultKeymap(nm, parent);
1073         if (nm != null) {
1074             // add a named keymap, a class of bindings
1075             getKeymapTable().put(nm, map);
1076         }
1077         return map;
1078     }
1079 
1080     /**
1081      * Removes a named keymap previously added to the document.  Keymaps
1082      * with <code>null</code> names may not be removed in this way.
1083      *
1084      * @param nm  the name of the keymap to remove
1085      * @return the keymap that was removed
1086      */
1087     public static Keymap removeKeymap(String nm) {
1088         return getKeymapTable().remove(nm);
1089     }
1090 
1091     /**
1092      * Fetches a named keymap previously added to the document.
1093      * This does not work with <code>null</code>-named keymaps.
1094      *
1095      * @param nm  the name of the keymap
1096      * @return the keymap
1097      */
1098     public static Keymap getKeymap(String nm) {
1099         return getKeymapTable().get(nm);
1100     }
1101 
1102     private static HashMap<String,Keymap> getKeymapTable() {
1103         synchronized (KEYMAP_TABLE) {
1104             AppContext appContext = AppContext.getAppContext();
1105             @SuppressWarnings("unchecked")
1106             HashMap<String,Keymap> keymapTable =
1107                 (HashMap<String,Keymap>)appContext.get(KEYMAP_TABLE);
1108             if (keymapTable == null) {
1109                 keymapTable = new HashMap<String,Keymap>(17);
1110                 appContext.put(KEYMAP_TABLE, keymapTable);
1111                 //initialize default keymap
1112                 Keymap binding = addKeymap(DEFAULT_KEYMAP, null);
1113                 binding.setDefaultAction(new
1114                                          DefaultEditorKit.DefaultKeyTypedAction());
1115             }
1116             return keymapTable;
1117         }
1118     }
1119 
1120     /**
1121      * Binding record for creating key bindings.
1122      * <p>
1123      * <strong>Warning:</strong>
1124      * Serialized objects of this class will not be compatible with
1125      * future Swing releases. The current serialization support is
1126      * appropriate for short term storage or RMI between applications running
1127      * the same version of Swing.  As of 1.4, support for long term storage
1128      * of all JavaBeans&trade;
1129      * has been added to the <code>java.beans</code> package.
1130      * Please see {@link java.beans.XMLEncoder}.
1131      */
1132     @SuppressWarnings("serial") // Same-version serialization only
1133     public static class KeyBinding {
1134 
1135         /**
1136          * The key.
1137          */
1138         public KeyStroke key;
1139 
1140         /**
1141          * The name of the action for the key.
1142          */
1143         public String actionName;
1144 
1145         /**
1146          * Creates a new key binding.
1147          *
1148          * @param key the key
1149          * @param actionName the name of the action for the key
1150          */
1151         public KeyBinding(KeyStroke key, String actionName) {
1152             this.key = key;
1153             this.actionName = actionName;
1154         }
1155     }
1156 
1157     /**
1158      * <p>
1159      * Loads a keymap with a bunch of
1160      * bindings.  This can be used to take a static table of
1161      * definitions and load them into some keymap.  The following
1162      * example illustrates an example of binding some keys to
1163      * the cut, copy, and paste actions associated with a
1164      * JTextComponent.  A code fragment to accomplish
1165      * this might look as follows:
1166      * <pre><code>
1167      *
1168      *   static final JTextComponent.KeyBinding[] defaultBindings = {
1169      *     new JTextComponent.KeyBinding(
1170      *       KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK),
1171      *       DefaultEditorKit.copyAction),
1172      *     new JTextComponent.KeyBinding(
1173      *       KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK),
1174      *       DefaultEditorKit.pasteAction),
1175      *     new JTextComponent.KeyBinding(
1176      *       KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK),
1177      *       DefaultEditorKit.cutAction),
1178      *   };
1179      *
1180      *   JTextComponent c = new JTextPane();
1181      *   Keymap k = c.getKeymap();
1182      *   JTextComponent.loadKeymap(k, defaultBindings, c.getActions());
1183      *
1184      * </code></pre>
1185      * The sets of bindings and actions may be empty but must be
1186      * non-<code>null</code>.
1187      *
1188      * @param map the keymap
1189      * @param bindings the bindings
1190      * @param actions the set of actions
1191      */
1192     public static void loadKeymap(Keymap map, KeyBinding[] bindings, Action[] actions) {
1193         Hashtable<String, Action> h = new Hashtable<String, Action>();
1194         for (Action a : actions) {
1195             String value = (String)a.getValue(Action.NAME);
1196             h.put((value!=null ? value:""), a);
1197         }
1198         for (KeyBinding binding : bindings) {
1199             Action a = h.get(binding.actionName);
1200             if (a != null) {
1201                 map.addActionForKeyStroke(binding.key, a);
1202             }
1203         }
1204     }
1205 
1206     /**
1207      * Fetches the current color used to render the
1208      * caret.
1209      *
1210      * @return the color
1211      */
1212     public Color getCaretColor() {
1213         return caretColor;
1214     }
1215 
1216     /**
1217      * Sets the current color used to render the caret.
1218      * Setting to <code>null</code> effectively restores the default color.
1219      * Setting the color results in a PropertyChange event ("caretColor")
1220      * being fired.
1221      *
1222      * @param c the color
1223      * @see #getCaretColor
1224      */
1225     @BeanProperty(preferred = true, description
1226             = "the color used to render the caret")
1227     public void setCaretColor(Color c) {
1228         Color old = caretColor;
1229         caretColor = c;
1230         firePropertyChange("caretColor", old, caretColor);
1231     }
1232 
1233     /**
1234      * Fetches the current color used to render the
1235      * selection.
1236      *
1237      * @return the color
1238      */
1239     public Color getSelectionColor() {
1240         return selectionColor;
1241     }
1242 
1243     /**
1244      * Sets the current color used to render the selection.
1245      * Setting the color to <code>null</code> is the same as setting
1246      * <code>Color.white</code>.  Setting the color results in a
1247      * PropertyChange event ("selectionColor").
1248      *
1249      * @param c the color
1250      * @see #getSelectionColor
1251      */
1252     @BeanProperty(preferred = true, description
1253             = "color used to render selection background")
1254     public void setSelectionColor(Color c) {
1255         Color old = selectionColor;
1256         selectionColor = c;
1257         firePropertyChange("selectionColor", old, selectionColor);
1258     }
1259 
1260     /**
1261      * Fetches the current color used to render the
1262      * selected text.
1263      *
1264      * @return the color
1265      */
1266     public Color getSelectedTextColor() {
1267         return selectedTextColor;
1268     }
1269 
1270     /**
1271      * Sets the current color used to render the selected text.
1272      * Setting the color to <code>null</code> is the same as
1273      * <code>Color.black</code>. Setting the color results in a
1274      * PropertyChange event ("selectedTextColor") being fired.
1275      *
1276      * @param c the color
1277      * @see #getSelectedTextColor
1278      */
1279     @BeanProperty(preferred = true, description
1280             = "color used to render selected text")
1281     public void setSelectedTextColor(Color c) {
1282         Color old = selectedTextColor;
1283         selectedTextColor = c;
1284         firePropertyChange("selectedTextColor", old, selectedTextColor);
1285     }
1286 
1287     /**
1288      * Fetches the current color used to render the
1289      * disabled text.
1290      *
1291      * @return the color
1292      */
1293     public Color getDisabledTextColor() {
1294         return disabledTextColor;
1295     }
1296 
1297     /**
1298      * Sets the current color used to render the
1299      * disabled text.  Setting the color fires off a
1300      * PropertyChange event ("disabledTextColor").
1301      *
1302      * @param c the color
1303      * @see #getDisabledTextColor
1304      */
1305     @BeanProperty(preferred = true, description
1306             = "color used to render disabled text")
1307     public void setDisabledTextColor(Color c) {
1308         Color old = disabledTextColor;
1309         disabledTextColor = c;
1310         firePropertyChange("disabledTextColor", old, disabledTextColor);
1311     }
1312 
1313     /**
1314      * Replaces the currently selected content with new content
1315      * represented by the given string.  If there is no selection
1316      * this amounts to an insert of the given text.  If there
1317      * is no replacement text this amounts to a removal of the
1318      * current selection.
1319      * <p>
1320      * This is the method that is used by the default implementation
1321      * of the action for inserting content that gets bound to the
1322      * keymap actions.
1323      *
1324      * @param content  the content to replace the selection with
1325      */
1326     public void replaceSelection(String content) {
1327         Document doc = getDocument();
1328         if (doc != null) {
1329             try {
1330                 boolean composedTextSaved = saveComposedText(caret.getDot());
1331                 int p0 = Math.min(caret.getDot(), caret.getMark());
1332                 int p1 = Math.max(caret.getDot(), caret.getMark());
1333                 if (doc instanceof AbstractDocument) {
1334                     ((AbstractDocument)doc).replace(p0, p1 - p0, content,null);
1335                 }
1336                 else {
1337                     if (p0 != p1) {
1338                         doc.remove(p0, p1 - p0);
1339                     }
1340                     if (content != null && content.length() > 0) {
1341                         doc.insertString(p0, content, null);
1342                     }
1343                 }
1344                 if (composedTextSaved) {
1345                     restoreComposedText();
1346                 }
1347             } catch (BadLocationException e) {
1348                 UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
1349             }
1350         }
1351     }
1352 
1353     /**
1354      * Fetches a portion of the text represented by the
1355      * component.  Returns an empty string if length is 0.
1356      *
1357      * @param offs the offset &ge; 0
1358      * @param len the length &ge; 0
1359      * @return the text
1360      * @exception BadLocationException if the offset or length are invalid
1361      */
1362     public String getText(int offs, int len) throws BadLocationException {
1363         return getDocument().getText(offs, len);
1364     }
1365 
1366     /**
1367      * Converts the given location in the model to a place in
1368      * the view coordinate system.
1369      * The component must have a positive size for
1370      * this translation to be computed (i.e. layout cannot
1371      * be computed until the component has been sized).  The
1372      * component does not have to be visible or painted.
1373      *
1374      * @param pos the position &ge; 0
1375      * @return the coordinates as a rectangle, with (r.x, r.y) as the location
1376      *   in the coordinate system, or null if the component does
1377      *   not yet have a positive size.
1378      * @exception BadLocationException if the given position does not
1379      *   represent a valid location in the associated document
1380      * @see TextUI#modelToView
1381      *
1382      * @deprecated replaced by
1383      *     {@link #modelToView2D(int)}
1384      */
1385     @Deprecated(since = "9")
1386     public Rectangle modelToView(int pos) throws BadLocationException {
1387         return getUI().modelToView(this, pos);
1388     }
1389 
1390     /**
1391      * Converts the given location in the model to a place in
1392      * the view coordinate system.
1393      * The component must have a positive size for
1394      * this translation to be computed (i.e. layout cannot
1395      * be computed until the component has been sized).  The
1396      * component does not have to be visible or painted.
1397      *
1398      * @param pos the position {@code >= 0}
1399      * @return the coordinates as a rectangle, with (r.x, r.y) as the location
1400      *   in the coordinate system, or null if the component does
1401      *   not yet have a positive size.
1402      * @exception BadLocationException if the given position does not
1403      *   represent a valid location in the associated document
1404      * @see TextUI#modelToView2D
1405      *
1406      * @since 9
1407      */
1408     public Rectangle2D modelToView2D(int pos) throws BadLocationException {
1409         return getUI().modelToView2D(this, pos, Position.Bias.Forward);
1410     }
1411 
1412     /**
1413      * Converts the given place in the view coordinate system
1414      * to the nearest representative location in the model.
1415      * The component must have a positive size for
1416      * this translation to be computed (i.e. layout cannot
1417      * be computed until the component has been sized).  The
1418      * component does not have to be visible or painted.
1419      *
1420      * @param pt the location in the view to translate
1421      * @return the offset &ge; 0 from the start of the document,
1422      *   or -1 if the component does not yet have a positive
1423      *   size.
1424      * @see TextUI#viewToModel
1425      *
1426      * @deprecated replaced by
1427      *     {@link #viewToModel2D(Point2D)}
1428      */
1429     @Deprecated(since = "9")
1430     public int viewToModel(Point pt) {
1431         return getUI().viewToModel(this, pt);
1432     }
1433 
1434     /**
1435      * Converts the given place in the view coordinate system
1436      * to the nearest representative location in the model.
1437      * The component must have a positive size for
1438      * this translation to be computed (i.e. layout cannot
1439      * be computed until the component has been sized).  The
1440      * component does not have to be visible or painted.
1441      *
1442      * @param pt the location in the view to translate
1443      * @return the offset {@code >= 0} from the start of the document,
1444      *   or {@code -1} if the component does not yet have a positive
1445      *   size.
1446      * @see TextUI#viewToModel2D
1447      *
1448      * @since 9
1449      */
1450     public int viewToModel2D(Point2D pt) {
1451         return getUI().viewToModel2D(this, pt, new Position.Bias[1]);
1452     }
1453 
1454     /**
1455      * Transfers the currently selected range in the associated
1456      * text model to the system clipboard, removing the contents
1457      * from the model.  The current selection is reset.  Does nothing
1458      * for <code>null</code> selections.
1459      *
1460      * @see java.awt.Toolkit#getSystemClipboard
1461      * @see java.awt.datatransfer.Clipboard
1462      */
1463     public void cut() {
1464         if (isEditable() && isEnabled()) {
1465             invokeAction("cut", TransferHandler.getCutAction());
1466         }
1467     }
1468 
1469     /**
1470      * Transfers the currently selected range in the associated
1471      * text model to the system clipboard, leaving the contents
1472      * in the text model.  The current selection remains intact.
1473      * Does nothing for <code>null</code> selections.
1474      *
1475      * @see java.awt.Toolkit#getSystemClipboard
1476      * @see java.awt.datatransfer.Clipboard
1477      */
1478     public void copy() {
1479         invokeAction("copy", TransferHandler.getCopyAction());
1480     }
1481 
1482     /**
1483      * Transfers the contents of the system clipboard into the
1484      * associated text model.  If there is a selection in the
1485      * associated view, it is replaced with the contents of the
1486      * clipboard.  If there is no selection, the clipboard contents
1487      * are inserted in front of the current insert position in
1488      * the associated view.  If the clipboard is empty, does nothing.
1489      *
1490      * @see #replaceSelection
1491      * @see java.awt.Toolkit#getSystemClipboard
1492      * @see java.awt.datatransfer.Clipboard
1493      */
1494     public void paste() {
1495         if (isEditable() && isEnabled()) {
1496             invokeAction("paste", TransferHandler.getPasteAction());
1497         }
1498     }
1499 
1500     /**
1501      * This is a convenience method that is only useful for
1502      * <code>cut</code>, <code>copy</code> and <code>paste</code>.  If
1503      * an <code>Action</code> with the name <code>name</code> does not
1504      * exist in the <code>ActionMap</code>, this will attempt to install a
1505      * <code>TransferHandler</code> and then use <code>altAction</code>.
1506      */
1507     private void invokeAction(String name, Action altAction) {
1508         ActionMap map = getActionMap();
1509         Action action = null;
1510 
1511         if (map != null) {
1512             action = map.get(name);
1513         }
1514         if (action == null) {
1515             installDefaultTransferHandlerIfNecessary();
1516             action = altAction;
1517         }
1518         action.actionPerformed(new ActionEvent(this,
1519                                ActionEvent.ACTION_PERFORMED, (String)action.
1520                                getValue(Action.NAME),
1521                                EventQueue.getMostRecentEventTime(),
1522                                getCurrentEventModifiers()));
1523     }
1524 
1525     /**
1526      * If the current <code>TransferHandler</code> is null, this will
1527      * install a new one.
1528      */
1529     private void installDefaultTransferHandlerIfNecessary() {
1530         if (getTransferHandler() == null) {
1531             if (defaultTransferHandler == null) {
1532                 defaultTransferHandler = new DefaultTransferHandler();
1533             }
1534             setTransferHandler(defaultTransferHandler);
1535         }
1536     }
1537 
1538     /**
1539      * Moves the caret to a new position, leaving behind a mark
1540      * defined by the last time <code>setCaretPosition</code> was
1541      * called.  This forms a selection.
1542      * If the document is <code>null</code>, does nothing. The position
1543      * must be between 0 and the length of the component's text or else
1544      * an exception is thrown.
1545      *
1546      * @param pos the position
1547      * @exception    IllegalArgumentException if the value supplied
1548      *               for <code>position</code> is less than zero or greater
1549      *               than the component's text length
1550      * @see #setCaretPosition
1551      */
1552     public void moveCaretPosition(int pos) {
1553         Document doc = getDocument();
1554         if (doc != null) {
1555             if (pos > doc.getLength() || pos < 0) {
1556                 throw new IllegalArgumentException("bad position: " + pos);
1557             }
1558             caret.moveDot(pos);
1559         }
1560     }
1561 
1562     /**
1563      * The bound property name for the focus accelerator.
1564      */
1565     public static final String FOCUS_ACCELERATOR_KEY = "focusAcceleratorKey";
1566 
1567     /**
1568      * Sets the key accelerator that will cause the receiving text
1569      * component to get the focus.  The accelerator will be the
1570      * key combination of the platform-specific modifier key and
1571      * the character given (converted to upper case).  For example,
1572      * the ALT key is used as a modifier on Windows and the CTRL+ALT
1573      * combination is used on Mac.  By default, there is no focus
1574      * accelerator key.  Any previous key accelerator setting will be
1575      * superseded.  A '\0' key setting will be registered, and has the
1576      * effect of turning off the focus accelerator.  When the new key
1577      * is set, a PropertyChange event (FOCUS_ACCELERATOR_KEY) will be fired.
1578      *
1579      * @param aKey the key
1580      * @see #getFocusAccelerator
1581      */
1582     @BeanProperty(description
1583             = "accelerator character used to grab focus")
1584     public void setFocusAccelerator(char aKey) {
1585         aKey = Character.toUpperCase(aKey);
1586         char old = focusAccelerator;
1587         focusAccelerator = aKey;
1588         // Fix for 4341002: value of FOCUS_ACCELERATOR_KEY is wrong.
1589         // So we fire both FOCUS_ACCELERATOR_KEY, for compatibility,
1590         // and the correct event here.
1591         firePropertyChange(FOCUS_ACCELERATOR_KEY, old, focusAccelerator);
1592         firePropertyChange("focusAccelerator", old, focusAccelerator);
1593     }
1594 
1595     /**
1596      * Returns the key accelerator that will cause the receiving
1597      * text component to get the focus.  Return '\0' if no focus
1598      * accelerator has been set.
1599      *
1600      * @return the key
1601      */
1602     public char getFocusAccelerator() {
1603         return focusAccelerator;
1604     }
1605 
1606     /**
1607      * Initializes from a stream.  This creates a
1608      * model of the type appropriate for the component
1609      * and initializes the model from the stream.
1610      * By default this will load the model as plain
1611      * text.  Previous contents of the model are discarded.
1612      *
1613      * @param in the stream to read from
1614      * @param desc an object describing the stream; this
1615      *   might be a string, a File, a URL, etc.  Some kinds
1616      *   of documents (such as html for example) might be
1617      *   able to make use of this information; if non-<code>null</code>,
1618      *   it is added as a property of the document
1619      * @exception IOException as thrown by the stream being
1620      *  used to initialize
1621      * @see EditorKit#createDefaultDocument
1622      * @see #setDocument
1623      * @see PlainDocument
1624      */
1625     public void read(Reader in, Object desc) throws IOException {
1626         EditorKit kit = getUI().getEditorKit(this);
1627         Document doc = kit.createDefaultDocument();
1628         if (desc != null) {
1629             doc.putProperty(Document.StreamDescriptionProperty, desc);
1630         }
1631         try {
1632             kit.read(in, doc, 0);
1633             setDocument(doc);
1634         } catch (BadLocationException e) {
1635             throw new IOException(e.getMessage());
1636         }
1637     }
1638 
1639     /**
1640      * Stores the contents of the model into the given
1641      * stream.  By default this will store the model as plain
1642      * text.
1643      *
1644      * @param out the output stream
1645      * @exception IOException on any I/O error
1646      */
1647     public void write(Writer out) throws IOException {
1648         Document doc = getDocument();
1649         try {
1650             getUI().getEditorKit(this).write(out, doc, 0, doc.getLength());
1651         } catch (BadLocationException e) {
1652             throw new IOException(e.getMessage());
1653         }
1654     }
1655 
1656     public void removeNotify() {
1657         super.removeNotify();
1658         if (getFocusedComponent() == this) {
1659             AppContext.getAppContext().remove(FOCUSED_COMPONENT);
1660         }
1661     }
1662 
1663     // --- java.awt.TextComponent methods ------------------------
1664 
1665     /**
1666      * Sets the position of the text insertion caret for the
1667      * <code>TextComponent</code>.  Note that the caret tracks change,
1668      * so this may move if the underlying text of the component is changed.
1669      * If the document is <code>null</code>, does nothing. The position
1670      * must be between 0 and the length of the component's text or else
1671      * an exception is thrown.
1672      *
1673      * @param position the position
1674      * @exception    IllegalArgumentException if the value supplied
1675      *               for <code>position</code> is less than zero or greater
1676      *               than the component's text length
1677      */
1678     @BeanProperty(bound = false, description
1679             = "the caret position")
1680     public void setCaretPosition(int position) {
1681         Document doc = getDocument();
1682         if (doc != null) {
1683             if (position > doc.getLength() || position < 0) {
1684                 throw new IllegalArgumentException("bad position: " + position);
1685             }
1686             caret.setDot(position);
1687         }
1688     }
1689 
1690     /**
1691      * Returns the position of the text insertion caret for the
1692      * text component.
1693      *
1694      * @return the position of the text insertion caret for the
1695      *  text component &ge; 0
1696      */
1697     @Transient
1698     public int getCaretPosition() {
1699         return caret.getDot();
1700     }
1701 
1702     /**
1703      * Sets the text of this <code>TextComponent</code>
1704      * to the specified text.  If the text is <code>null</code>
1705      * or empty, has the effect of simply deleting the old text.
1706      * When text has been inserted, the resulting caret location
1707      * is determined by the implementation of the caret class.
1708      *
1709      * <p>
1710      * Note that text is not a bound property, so no <code>PropertyChangeEvent
1711      * </code> is fired when it changes. To listen for changes to the text,
1712      * use <code>DocumentListener</code>.
1713      *
1714      * @param t the new text to be set
1715      * @see #getText
1716      * @see DefaultCaret
1717      */
1718     @BeanProperty(bound = false, description
1719             = "the text of this component")
1720     public void setText(String t) {
1721         try {
1722             Document doc = getDocument();
1723             if (doc instanceof AbstractDocument) {
1724                 ((AbstractDocument)doc).replace(0, doc.getLength(), t,null);
1725             }
1726             else {
1727                 doc.remove(0, doc.getLength());
1728                 doc.insertString(0, t, null);
1729             }
1730         } catch (BadLocationException e) {
1731             UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
1732         }
1733     }
1734 
1735     /**
1736      * Returns the text contained in this <code>TextComponent</code>.
1737      * If the underlying document is <code>null</code>,
1738      * will give a <code>NullPointerException</code>.
1739      *
1740      * Note that text is not a bound property, so no <code>PropertyChangeEvent
1741      * </code> is fired when it changes. To listen for changes to the text,
1742      * use <code>DocumentListener</code>.
1743      *
1744      * @return the text
1745      * @exception NullPointerException if the document is <code>null</code>
1746      * @see #setText
1747      */
1748     public String getText() {
1749         Document doc = getDocument();
1750         String txt;
1751         try {
1752             txt = doc.getText(0, doc.getLength());
1753         } catch (BadLocationException e) {
1754             txt = null;
1755         }
1756         return txt;
1757     }
1758 
1759     /**
1760      * Returns the selected text contained in this
1761      * <code>TextComponent</code>.  If the selection is
1762      * <code>null</code> or the document empty, returns <code>null</code>.
1763      *
1764      * @return the text
1765      * @exception IllegalArgumentException if the selection doesn't
1766      *  have a valid mapping into the document for some reason
1767      * @see #setText
1768      */
1769     @BeanProperty(bound = false)
1770     public String getSelectedText() {
1771         String txt = null;
1772         int p0 = Math.min(caret.getDot(), caret.getMark());
1773         int p1 = Math.max(caret.getDot(), caret.getMark());
1774         if (p0 != p1) {
1775             try {
1776                 Document doc = getDocument();
1777                 txt = doc.getText(p0, p1 - p0);
1778             } catch (BadLocationException e) {
1779                 throw new IllegalArgumentException(e.getMessage());
1780             }
1781         }
1782         return txt;
1783     }
1784 
1785     /**
1786      * Returns the boolean indicating whether this
1787      * <code>TextComponent</code> is editable or not.
1788      *
1789      * @return the boolean value
1790      * @see #setEditable
1791      */
1792     public boolean isEditable() {
1793         return editable;
1794     }
1795 
1796     /**
1797      * Sets the specified boolean to indicate whether or not this
1798      * <code>TextComponent</code> should be editable.
1799      * A PropertyChange event ("editable") is fired when the
1800      * state is changed.
1801      *
1802      * @param b the boolean to be set
1803      * @see #isEditable
1804      */
1805     @BeanProperty(description
1806             = "specifies if the text can be edited")
1807     public void setEditable(boolean b) {
1808         if (b != editable) {
1809             boolean oldVal = editable;
1810             editable = b;
1811             enableInputMethods(editable);
1812             firePropertyChange("editable", Boolean.valueOf(oldVal), Boolean.valueOf(editable));
1813             repaint();
1814         }
1815     }
1816 
1817     /**
1818      * Returns the selected text's start position.  Return 0 for an
1819      * empty document, or the value of dot if no selection.
1820      *
1821      * @return the start position &ge; 0
1822      */
1823     @Transient
1824     public int getSelectionStart() {
1825         int start = Math.min(caret.getDot(), caret.getMark());
1826         return start;
1827     }
1828 
1829     /**
1830      * Sets the selection start to the specified position.  The new
1831      * starting point is constrained to be before or at the current
1832      * selection end.
1833      * <p>
1834      * This is available for backward compatibility to code
1835      * that called this method on <code>java.awt.TextComponent</code>.
1836      * This is implemented to forward to the <code>Caret</code>
1837      * implementation which is where the actual selection is maintained.
1838      *
1839      * @param selectionStart the start position of the text &ge; 0
1840      */
1841     @BeanProperty(bound = false, description
1842             = "starting location of the selection.")
1843     public void setSelectionStart(int selectionStart) {
1844         /* Route through select method to enforce consistent policy
1845          * between selectionStart and selectionEnd.
1846          */
1847         select(selectionStart, getSelectionEnd());
1848     }
1849 
1850     /**
1851      * Returns the selected text's end position.  Return 0 if the document
1852      * is empty, or the value of dot if there is no selection.
1853      *
1854      * @return the end position &ge; 0
1855      */
1856     @Transient
1857     public int getSelectionEnd() {
1858         int end = Math.max(caret.getDot(), caret.getMark());
1859         return end;
1860     }
1861 
1862     /**
1863      * Sets the selection end to the specified position.  The new
1864      * end point is constrained to be at or after the current
1865      * selection start.
1866      * <p>
1867      * This is available for backward compatibility to code
1868      * that called this method on <code>java.awt.TextComponent</code>.
1869      * This is implemented to forward to the <code>Caret</code>
1870      * implementation which is where the actual selection is maintained.
1871      *
1872      * @param selectionEnd the end position of the text &ge; 0
1873      */
1874     @BeanProperty(bound = false, description
1875             = "ending location of the selection.")
1876     public void setSelectionEnd(int selectionEnd) {
1877         /* Route through select method to enforce consistent policy
1878          * between selectionStart and selectionEnd.
1879          */
1880         select(getSelectionStart(), selectionEnd);
1881     }
1882 
1883     /**
1884      * Selects the text between the specified start and end positions.
1885      * <p>
1886      * This method sets the start and end positions of the
1887      * selected text, enforcing the restriction that the start position
1888      * must be greater than or equal to zero.  The end position must be
1889      * greater than or equal to the start position, and less than or
1890      * equal to the length of the text component's text.
1891      * <p>
1892      * If the caller supplies values that are inconsistent or out of
1893      * bounds, the method enforces these constraints silently, and
1894      * without failure. Specifically, if the start position or end
1895      * position is greater than the length of the text, it is reset to
1896      * equal the text length. If the start position is less than zero,
1897      * it is reset to zero, and if the end position is less than the
1898      * start position, it is reset to the start position.
1899      * <p>
1900      * This call is provided for backward compatibility.
1901      * It is routed to a call to <code>setCaretPosition</code>
1902      * followed by a call to <code>moveCaretPosition</code>.
1903      * The preferred way to manage selection is by calling
1904      * those methods directly.
1905      *
1906      * @param selectionStart the start position of the text
1907      * @param selectionEnd the end position of the text
1908      * @see #setCaretPosition
1909      * @see #moveCaretPosition
1910      */
1911     public void select(int selectionStart, int selectionEnd) {
1912         // argument adjustment done by java.awt.TextComponent
1913         int docLength = getDocument().getLength();
1914 
1915         if (selectionStart < 0) {
1916             selectionStart = 0;
1917         }
1918         if (selectionStart > docLength) {
1919             selectionStart = docLength;
1920         }
1921         if (selectionEnd > docLength) {
1922             selectionEnd = docLength;
1923         }
1924         if (selectionEnd < selectionStart) {
1925             selectionEnd = selectionStart;
1926         }
1927 
1928         setCaretPosition(selectionStart);
1929         moveCaretPosition(selectionEnd);
1930     }
1931 
1932     /**
1933      * Selects all the text in the <code>TextComponent</code>.
1934      * Does nothing on a <code>null</code> or empty document.
1935      */
1936     public void selectAll() {
1937         Document doc = getDocument();
1938         if (doc != null) {
1939             setCaretPosition(0);
1940             moveCaretPosition(doc.getLength());
1941         }
1942     }
1943 
1944     // --- Tooltip Methods ---------------------------------------------
1945 
1946     /**
1947      * Returns the string to be used as the tooltip for <code>event</code>.
1948      * This will return one of:
1949      * <ol>
1950      *  <li>If <code>setToolTipText</code> has been invoked with a
1951      *      non-<code>null</code>
1952      *      value, it will be returned, otherwise
1953      *  <li>The value from invoking <code>getToolTipText</code> on
1954      *      the UI will be returned.
1955      * </ol>
1956      * By default <code>JTextComponent</code> does not register
1957      * itself with the <code>ToolTipManager</code>.
1958      * This means that tooltips will NOT be shown from the
1959      * <code>TextUI</code> unless <code>registerComponent</code> has
1960      * been invoked on the <code>ToolTipManager</code>.
1961      *
1962      * @param event the event in question
1963      * @return the string to be used as the tooltip for <code>event</code>
1964      * @see javax.swing.JComponent#setToolTipText
1965      * @see javax.swing.plaf.TextUI#getToolTipText
1966      * @see javax.swing.ToolTipManager#registerComponent
1967      */
1968     @SuppressWarnings("deprecation")
1969     public String getToolTipText(MouseEvent event) {
1970         String retValue = super.getToolTipText(event);
1971 
1972         if (retValue == null) {
1973             TextUI ui = getUI();
1974             if (ui != null) {
1975                 retValue = ui.getToolTipText(this, new Point(event.getX(),
1976                                                              event.getY()));
1977             }
1978         }
1979         return retValue;
1980     }
1981 
1982     // --- Scrollable methods ---------------------------------------------
1983 
1984     /**
1985      * Returns the preferred size of the viewport for a view component.
1986      * This is implemented to do the default behavior of returning
1987      * the preferred size of the component.
1988      *
1989      * @return the <code>preferredSize</code> of a <code>JViewport</code>
1990      * whose view is this <code>Scrollable</code>
1991      */
1992     @BeanProperty(bound = false)
1993     public Dimension getPreferredScrollableViewportSize() {
1994         return getPreferredSize();
1995     }
1996 
1997 
1998     /**
1999      * Components that display logical rows or columns should compute
2000      * the scroll increment that will completely expose one new row
2001      * or column, depending on the value of orientation.  Ideally,
2002      * components should handle a partially exposed row or column by
2003      * returning the distance required to completely expose the item.
2004      * <p>
2005      * The default implementation of this is to simply return 10% of
2006      * the visible area.  Subclasses are likely to be able to provide
2007      * a much more reasonable value.
2008      *
2009      * @param visibleRect the view area visible within the viewport
2010      * @param orientation either <code>SwingConstants.VERTICAL</code> or
2011      *   <code>SwingConstants.HORIZONTAL</code>
2012      * @param direction less than zero to scroll up/left, greater than
2013      *   zero for down/right
2014      * @return the "unit" increment for scrolling in the specified direction
2015      * @exception IllegalArgumentException for an invalid orientation
2016      * @see JScrollBar#setUnitIncrement
2017      */
2018     public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
2019         switch(orientation) {
2020         case SwingConstants.VERTICAL:
2021             return visibleRect.height / 10;
2022         case SwingConstants.HORIZONTAL:
2023             return visibleRect.width / 10;
2024         default:
2025             throw new IllegalArgumentException("Invalid orientation: " + orientation);
2026         }
2027     }
2028 
2029 
2030     /**
2031      * Components that display logical rows or columns should compute
2032      * the scroll increment that will completely expose one block
2033      * of rows or columns, depending on the value of orientation.
2034      * <p>
2035      * The default implementation of this is to simply return the visible
2036      * area.  Subclasses will likely be able to provide a much more
2037      * reasonable value.
2038      *
2039      * @param visibleRect the view area visible within the viewport
2040      * @param orientation either <code>SwingConstants.VERTICAL</code> or
2041      *   <code>SwingConstants.HORIZONTAL</code>
2042      * @param direction less than zero to scroll up/left, greater than zero
2043      *  for down/right
2044      * @return the "block" increment for scrolling in the specified direction
2045      * @exception IllegalArgumentException for an invalid orientation
2046      * @see JScrollBar#setBlockIncrement
2047      */
2048     public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
2049         switch(orientation) {
2050         case SwingConstants.VERTICAL:
2051             return visibleRect.height;
2052         case SwingConstants.HORIZONTAL:
2053             return visibleRect.width;
2054         default:
2055             throw new IllegalArgumentException("Invalid orientation: " + orientation);
2056         }
2057     }
2058 
2059 
2060     /**
2061      * Returns true if a viewport should always force the width of this
2062      * <code>Scrollable</code> to match the width of the viewport.
2063      * For example a normal text view that supported line wrapping
2064      * would return true here, since it would be undesirable for
2065      * wrapped lines to disappear beyond the right
2066      * edge of the viewport.  Note that returning true for a
2067      * <code>Scrollable</code> whose ancestor is a <code>JScrollPane</code>
2068      * effectively disables horizontal scrolling.
2069      * <p>
2070      * Scrolling containers, like <code>JViewport</code>,
2071      * will use this method each time they are validated.
2072      *
2073      * @return true if a viewport should force the <code>Scrollable</code>s
2074      *   width to match its own
2075      */
2076     @BeanProperty(bound = false)
2077     public boolean getScrollableTracksViewportWidth() {
2078         Container parent = SwingUtilities.getUnwrappedParent(this);
2079         if (parent instanceof JViewport) {
2080             return parent.getWidth() > getPreferredSize().width;
2081         }
2082         return false;
2083     }
2084 
2085     /**
2086      * Returns true if a viewport should always force the height of this
2087      * <code>Scrollable</code> to match the height of the viewport.
2088      * For example a columnar text view that flowed text in left to
2089      * right columns could effectively disable vertical scrolling by
2090      * returning true here.
2091      * <p>
2092      * Scrolling containers, like <code>JViewport</code>,
2093      * will use this method each time they are validated.
2094      *
2095      * @return true if a viewport should force the Scrollables height
2096      *   to match its own
2097      */
2098     @BeanProperty(bound = false)
2099     public boolean getScrollableTracksViewportHeight() {
2100         Container parent = SwingUtilities.getUnwrappedParent(this);
2101         if (parent instanceof JViewport) {
2102             return parent.getHeight() > getPreferredSize().height;
2103         }
2104         return false;
2105     }
2106 
2107 
2108 //////////////////
2109 // Printing Support
2110 //////////////////
2111 
2112     /**
2113      * A convenience print method that displays a print dialog, and then
2114      * prints this {@code JTextComponent} in <i>interactive</i> mode with no
2115      * header or footer text. Note: this method
2116      * blocks until printing is done.
2117      * <p>
2118      * Note: In <i>headless</i> mode, no dialogs will be shown.
2119      *
2120      * <p> This method calls the full featured
2121      * {@link #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2122      * print} method to perform printing.
2123      * @return {@code true}, unless printing is canceled by the user
2124      * @throws PrinterException if an error in the print system causes the job
2125      *         to be aborted
2126      * @throws SecurityException if this thread is not allowed to
2127      *                           initiate a print job request
2128      *
2129      * @see #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2130      *
2131      * @since 1.6
2132      */
2133 
2134     public boolean print() throws PrinterException {
2135         return print(null, null, true, null, null, true);
2136     }
2137 
2138     /**
2139      * A convenience print method that displays a print dialog, and then
2140      * prints this {@code JTextComponent} in <i>interactive</i> mode with
2141      * the specified header and footer text. Note: this method
2142      * blocks until printing is done.
2143      * <p>
2144      * Note: In <i>headless</i> mode, no dialogs will be shown.
2145      *
2146      * <p> This method calls the full featured
2147      * {@link #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2148      * print} method to perform printing.
2149      * @param headerFormat the text, in {@code MessageFormat}, to be
2150      *        used as the header, or {@code null} for no header
2151      * @param footerFormat the text, in {@code MessageFormat}, to be
2152      *        used as the footer, or {@code null} for no footer
2153      * @return {@code true}, unless printing is canceled by the user
2154      * @throws PrinterException if an error in the print system causes the job
2155      *         to be aborted
2156      * @throws SecurityException if this thread is not allowed to
2157      *                           initiate a print job request
2158      *
2159      * @see #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2160      * @see java.text.MessageFormat
2161      * @since 1.6
2162      */
2163     public boolean print(final MessageFormat headerFormat,
2164             final MessageFormat footerFormat) throws PrinterException {
2165         return print(headerFormat, footerFormat, true, null, null, true);
2166     }
2167 
2168     /**
2169      * Prints the content of this {@code JTextComponent}. Note: this method
2170      * blocks until printing is done.
2171      *
2172      * <p>
2173      * Page header and footer text can be added to the output by providing
2174      * {@code MessageFormat} arguments. The printing code requests
2175      * {@code Strings} from the formats, providing a single item which may be
2176      * included in the formatted string: an {@code Integer} representing the
2177      * current page number.
2178      *
2179      * <p>
2180      * {@code showPrintDialog boolean} parameter allows you to specify whether
2181      * a print dialog is displayed to the user. When it is, the user
2182      * may use the dialog to change printing attributes or even cancel the
2183      * print.
2184      *
2185      * <p>
2186      * {@code service} allows you to provide the initial
2187      * {@code PrintService} for the print dialog, or to specify
2188      * {@code PrintService} to print to when the dialog is not shown.
2189      *
2190      * <p>
2191      * {@code attributes} can be used to provide the
2192      * initial values for the print dialog, or to supply any needed
2193      * attributes when the dialog is not shown. {@code attributes} can
2194      * be used to control how the job will print, for example
2195      * <i>duplex</i> or <i>single-sided</i>.
2196      *
2197      * <p>
2198      * {@code interactive boolean} parameter allows you to specify
2199      * whether to perform printing in <i>interactive</i>
2200      * mode. If {@code true}, a progress dialog, with an abort option,
2201      * is displayed for the duration of printing.  This dialog is
2202      * <i>modal</i> when {@code print} is invoked on the <i>Event Dispatch
2203      * Thread</i> and <i>non-modal</i> otherwise. <b>Warning</b>:
2204      * calling this method on the <i>Event Dispatch Thread</i> with {@code
2205      * interactive false} blocks <i>all</i> events, including repaints, from
2206      * being processed until printing is complete. It is only
2207      * recommended when printing from an application with no
2208      * visible GUI.
2209      *
2210      * <p>
2211      * Note: In <i>headless</i> mode, {@code showPrintDialog} and
2212      * {@code interactive} parameters are ignored and no dialogs are
2213      * shown.
2214      *
2215      * <p>
2216      * This method ensures the {@code document} is not mutated during printing.
2217      * To indicate it visually, {@code setEnabled(false)} is set for the
2218      * duration of printing.
2219      *
2220      * <p>
2221      * This method uses {@link #getPrintable} to render document content.
2222      *
2223      * <p>
2224      * This method is thread-safe, although most Swing methods are not. Please
2225      * see <A
2226      * HREF="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">
2227      * Concurrency in Swing</A> for more information.
2228      *
2229      * <p>
2230      * <b>Sample Usage</b>. This code snippet shows a cross-platform print
2231      * dialog and then prints the {@code JTextComponent} in <i>interactive</i> mode
2232      * unless the user cancels the dialog:
2233      *
2234      * <pre>
2235      * textComponent.print(new MessageFormat(&quot;My text component header&quot;),
2236      *     new MessageFormat(&quot;Footer. Page - {0}&quot;), true, null, null, true);
2237      * </pre>
2238      * <p>
2239      * Executing this code off the <i>Event Dispatch Thread</i>
2240      * performs printing on the <i>background</i>.
2241      * The following pattern might be used for <i>background</i>
2242      * printing:
2243      * <pre>
2244      *     FutureTask&lt;Boolean&gt; future =
2245      *         new FutureTask&lt;Boolean&gt;(
2246      *             new Callable&lt;Boolean&gt;() {
2247      *                 public Boolean call() {
2248      *                     return textComponent.print(.....);
2249      *                 }
2250      *             });
2251      *     executor.execute(future);
2252      * </pre>
2253      *
2254      * @param headerFormat the text, in {@code MessageFormat}, to be
2255      *        used as the header, or {@code null} for no header
2256      * @param footerFormat the text, in {@code MessageFormat}, to be
2257      *        used as the footer, or {@code null} for no footer
2258      * @param showPrintDialog {@code true} to display a print dialog,
2259      *        {@code false} otherwise
2260      * @param service initial {@code PrintService}, or {@code null} for the
2261      *        default
2262      * @param attributes the job attributes to be applied to the print job, or
2263      *        {@code null} for none
2264      * @param interactive whether to print in an interactive mode
2265      * @return {@code true}, unless printing is canceled by the user
2266      * @throws PrinterException if an error in the print system causes the job
2267      *         to be aborted
2268      * @throws SecurityException if this thread is not allowed to
2269      *                           initiate a print job request
2270      *
2271      * @see #getPrintable
2272      * @see java.text.MessageFormat
2273      * @see java.awt.GraphicsEnvironment#isHeadless
2274      * @see java.util.concurrent.FutureTask
2275      *
2276      * @since 1.6
2277      */
2278     public boolean print(final MessageFormat headerFormat,
2279             final MessageFormat footerFormat,
2280             final boolean showPrintDialog,
2281             final PrintService service,
2282             final PrintRequestAttributeSet attributes,
2283             final boolean interactive)
2284             throws PrinterException {
2285 
2286         final PrinterJob job = PrinterJob.getPrinterJob();
2287         final Printable printable;
2288         final PrintingStatus printingStatus;
2289         final boolean isHeadless = GraphicsEnvironment.isHeadless();
2290         final boolean isEventDispatchThread =
2291             SwingUtilities.isEventDispatchThread();
2292         final Printable textPrintable = getPrintable(headerFormat, footerFormat);
2293         if (interactive && ! isHeadless) {
2294             printingStatus =
2295                 PrintingStatus.createPrintingStatus(this, job);
2296             printable =
2297                 printingStatus.createNotificationPrintable(textPrintable);
2298         } else {
2299             printingStatus = null;
2300             printable = textPrintable;
2301         }
2302 
2303         if (service != null) {
2304             job.setPrintService(service);
2305         }
2306 
2307         job.setPrintable(printable);
2308 
2309         final PrintRequestAttributeSet attr = (attributes == null)
2310             ? new HashPrintRequestAttributeSet()
2311             : attributes;
2312 
2313         if (showPrintDialog && ! isHeadless && ! job.printDialog(attr)) {
2314             return false;
2315         }
2316 
2317         /*
2318          * there are three cases for printing:
2319          * 1. print non interactively (! interactive || isHeadless)
2320          * 2. print interactively off EDT
2321          * 3. print interactively on EDT
2322          *
2323          * 1 and 2 prints on the current thread (3 prints on another thread)
2324          * 2 and 3 deal with PrintingStatusDialog
2325          */
2326         final Callable<Object> doPrint =
2327             new Callable<Object>() {
2328                 public Object call() throws Exception {
2329                     try {
2330                         job.print(attr);
2331                     } finally {
2332                         if (printingStatus != null) {
2333                             printingStatus.dispose();
2334                         }
2335                     }
2336                     return null;
2337                 }
2338             };
2339 
2340         final FutureTask<Object> futurePrinting =
2341             new FutureTask<Object>(doPrint);
2342 
2343         final Runnable runnablePrinting =
2344             new Runnable() {
2345                 public void run() {
2346                     //disable component
2347                     boolean wasEnabled = false;
2348                     if (isEventDispatchThread) {
2349                         if (isEnabled()) {
2350                             wasEnabled = true;
2351                             setEnabled(false);
2352                         }
2353                     } else {
2354                         try {
2355                             wasEnabled = SwingUtilities2.submit(
2356                                 new Callable<Boolean>() {
2357                                     public Boolean call() throws Exception {
2358                                         boolean rv = isEnabled();
2359                                         if (rv) {
2360                                             setEnabled(false);
2361                                         }
2362                                         return rv;
2363                                     }
2364                                 }).get();
2365                         } catch (InterruptedException e) {
2366                             throw new RuntimeException(e);
2367                         } catch (ExecutionException e) {
2368                             Throwable cause = e.getCause();
2369                             if (cause instanceof Error) {
2370                                 throw (Error) cause;
2371                             }
2372                             if (cause instanceof RuntimeException) {
2373                                 throw (RuntimeException) cause;
2374                             }
2375                             throw new AssertionError(cause);
2376                         }
2377                     }
2378 
2379                     getDocument().render(futurePrinting);
2380 
2381                     //enable component
2382                     if (wasEnabled) {
2383                         if (isEventDispatchThread) {
2384                             setEnabled(true);
2385                         } else {
2386                             try {
2387                                 SwingUtilities2.submit(
2388                                     new Runnable() {
2389                                         public void run() {
2390                                             setEnabled(true);
2391                                         }
2392                                     }, null).get();
2393                             } catch (InterruptedException e) {
2394                                 throw new RuntimeException(e);
2395                             } catch (ExecutionException e) {
2396                                 Throwable cause = e.getCause();
2397                                 if (cause instanceof Error) {
2398                                     throw (Error) cause;
2399                                 }
2400                                 if (cause instanceof RuntimeException) {
2401                                     throw (RuntimeException) cause;
2402                                 }
2403                                 throw new AssertionError(cause);
2404                             }
2405                         }
2406                     }
2407                 }
2408             };
2409 
2410         if (! interactive || isHeadless) {
2411             runnablePrinting.run();
2412         } else {
2413             if (isEventDispatchThread) {
2414                 new Thread(null, runnablePrinting,
2415                            "JTextComponentPrint", 0, false ).start();
2416                 printingStatus.showModal(true);
2417             } else {
2418                 printingStatus.showModal(false);
2419                 runnablePrinting.run();
2420             }
2421         }
2422 
2423         //the printing is done successfully or otherwise.
2424         //dialog is hidden if needed.
2425         try {
2426             futurePrinting.get();
2427         } catch (InterruptedException e) {
2428             throw new RuntimeException(e);
2429         } catch (ExecutionException e) {
2430             Throwable cause = e.getCause();
2431             if (cause instanceof PrinterAbortException) {
2432                 if (printingStatus != null
2433                     && printingStatus.isAborted()) {
2434                     return false;
2435                 } else {
2436                     throw (PrinterAbortException) cause;
2437                 }
2438             } else if (cause instanceof PrinterException) {
2439                 throw (PrinterException) cause;
2440             } else if (cause instanceof RuntimeException) {
2441                 throw (RuntimeException) cause;
2442             } else if (cause instanceof Error) {
2443                 throw (Error) cause;
2444             } else {
2445                 throw new AssertionError(cause);
2446             }
2447         }
2448         return true;
2449     }
2450 
2451 
2452     /**
2453      * Returns a {@code Printable} to use for printing the content of this
2454      * {@code JTextComponent}. The returned {@code Printable} prints
2455      * the document as it looks on the screen except being reformatted
2456      * to fit the paper.
2457      * The returned {@code Printable} can be wrapped inside another
2458      * {@code Printable} in order to create complex reports and
2459      * documents.
2460      *
2461      *
2462      * <p>
2463      * The returned {@code Printable} shares the {@code document} with this
2464      * {@code JTextComponent}. It is the responsibility of the developer to
2465      * ensure that the {@code document} is not mutated while this {@code Printable}
2466      * is used. Printing behavior is undefined when the {@code document} is
2467      * mutated during printing.
2468      *
2469      * <p>
2470      * Page header and footer text can be added to the output by providing
2471      * {@code MessageFormat} arguments. The printing code requests
2472      * {@code Strings} from the formats, providing a single item which may be
2473      * included in the formatted string: an {@code Integer} representing the
2474      * current page number.
2475      *
2476      * <p>
2477      * The returned {@code Printable} when printed, formats the
2478      * document content appropriately for the page size. For correct
2479      * line wrapping the {@code imageable width} of all pages must be the
2480      * same. See {@link java.awt.print.PageFormat#getImageableWidth}.
2481      *
2482      * <p>
2483      * This method is thread-safe, although most Swing methods are not. Please
2484      * see <A
2485      * HREF="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">
2486      * Concurrency in Swing</A> for more information.
2487      *
2488      * <p>
2489      * The returned {@code Printable} can be printed on any thread.
2490      *
2491      * <p>
2492      * This implementation returned {@code Printable} performs all painting on
2493      * the <i>Event Dispatch Thread</i>, regardless of what thread it is
2494      * used on.
2495      *
2496      * @param headerFormat the text, in {@code MessageFormat}, to be
2497      *        used as the header, or {@code null} for no header
2498      * @param footerFormat the text, in {@code MessageFormat}, to be
2499      *        used as the footer, or {@code null} for no footer
2500      * @return a {@code Printable} for use in printing content of this
2501      *         {@code JTextComponent}
2502      *
2503      *
2504      * @see java.awt.print.Printable
2505      * @see java.awt.print.PageFormat
2506      * @see javax.swing.text.Document#render(java.lang.Runnable)
2507      *
2508      * @since 1.6
2509      */
2510     public Printable getPrintable(final MessageFormat headerFormat,
2511                                   final MessageFormat footerFormat) {
2512         return TextComponentPrintable.getPrintable(
2513                    this, headerFormat, footerFormat);
2514     }
2515 
2516 
2517 /////////////////
2518 // Accessibility support
2519 ////////////////
2520 
2521 
2522     /**
2523      * Gets the <code>AccessibleContext</code> associated with this
2524      * <code>JTextComponent</code>. For text components,
2525      * the <code>AccessibleContext</code> takes the form of an
2526      * <code>AccessibleJTextComponent</code>.
2527      * A new <code>AccessibleJTextComponent</code> instance
2528      * is created if necessary.
2529      *
2530      * @return an <code>AccessibleJTextComponent</code> that serves as the
2531      *         <code>AccessibleContext</code> of this
2532      *         <code>JTextComponent</code>
2533      */
2534     @BeanProperty(bound = false)
2535     public AccessibleContext getAccessibleContext() {
2536         if (accessibleContext == null) {
2537             accessibleContext = new AccessibleJTextComponent();
2538         }
2539         return accessibleContext;
2540     }
2541 
2542     /**
2543      * This class implements accessibility support for the
2544      * <code>JTextComponent</code> class.  It provides an implementation of
2545      * the Java Accessibility API appropriate to menu user-interface elements.
2546      * <p>
2547      * <strong>Warning:</strong>
2548      * Serialized objects of this class will not be compatible with
2549      * future Swing releases. The current serialization support is
2550      * appropriate for short term storage or RMI between applications running
2551      * the same version of Swing.  As of 1.4, support for long term storage
2552      * of all JavaBeans&trade;
2553      * has been added to the <code>java.beans</code> package.
2554      * Please see {@link java.beans.XMLEncoder}.
2555      */
2556     @SuppressWarnings("serial") // Same-version serialization only
2557     public class AccessibleJTextComponent extends AccessibleJComponent
2558     implements AccessibleText, CaretListener, DocumentListener,
2559                AccessibleAction, AccessibleEditableText,
2560                AccessibleExtendedText {
2561 
2562         int caretPos;
2563         Point oldLocationOnScreen;
2564 
2565         /**
2566          * Constructs an AccessibleJTextComponent.  Adds a listener to track
2567          * caret change.
2568          */
2569         public AccessibleJTextComponent() {
2570             Document doc = JTextComponent.this.getDocument();
2571             if (doc != null) {
2572                 doc.addDocumentListener(this);
2573             }
2574             JTextComponent.this.addCaretListener(this);
2575             caretPos = getCaretPosition();
2576 
2577             try {
2578                 oldLocationOnScreen = getLocationOnScreen();
2579             } catch (IllegalComponentStateException iae) {
2580             }
2581 
2582             // Fire a ACCESSIBLE_VISIBLE_DATA_PROPERTY PropertyChangeEvent
2583             // when the text component moves (e.g., when scrolling).
2584             // Using an anonymous class since making AccessibleJTextComponent
2585             // implement ComponentListener would be an API change.
2586             JTextComponent.this.addComponentListener(new ComponentAdapter() {
2587 
2588                 public void componentMoved(ComponentEvent e) {
2589                     try {
2590                         Point newLocationOnScreen = getLocationOnScreen();
2591                         firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY,
2592                                            oldLocationOnScreen,
2593                                            newLocationOnScreen);
2594 
2595                         oldLocationOnScreen = newLocationOnScreen;
2596                     } catch (IllegalComponentStateException iae) {
2597                     }
2598                 }
2599             });
2600         }
2601 
2602         /**
2603          * Handles caret updates (fire appropriate property change event,
2604          * which are AccessibleContext.ACCESSIBLE_CARET_PROPERTY and
2605          * AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY).
2606          * This keeps track of the dot position internally.  When the caret
2607          * moves, the internal position is updated after firing the event.
2608          *
2609          * @param e the CaretEvent
2610          */
2611         public void caretUpdate(CaretEvent e) {
2612             int dot = e.getDot();
2613             int mark = e.getMark();
2614             if (caretPos != dot) {
2615                 // the caret moved
2616                 firePropertyChange(ACCESSIBLE_CARET_PROPERTY,
2617                     caretPos, dot);
2618                 caretPos = dot;
2619 
2620                 try {
2621                     oldLocationOnScreen = getLocationOnScreen();
2622                 } catch (IllegalComponentStateException iae) {
2623                 }
2624             }
2625             if (mark != dot) {
2626                 // there is a selection
2627                 firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null,
2628                     getSelectedText());
2629             }
2630         }
2631 
2632         // DocumentListener methods
2633 
2634         /**
2635          * Handles document insert (fire appropriate property change event
2636          * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
2637          * This tracks the changed offset via the event.
2638          *
2639          * @param e the DocumentEvent
2640          */
2641         public void insertUpdate(DocumentEvent e) {
2642             final Integer pos = e.getOffset();
2643             if (SwingUtilities.isEventDispatchThread()) {
2644                 firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
2645             } else {
2646                 Runnable doFire = new Runnable() {
2647                     public void run() {
2648                         firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
2649                                            null, pos);
2650                     }
2651                 };
2652                 SwingUtilities.invokeLater(doFire);
2653             }
2654         }
2655 
2656         /**
2657          * Handles document remove (fire appropriate property change event,
2658          * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
2659          * This tracks the changed offset via the event.
2660          *
2661          * @param e the DocumentEvent
2662          */
2663         public void removeUpdate(DocumentEvent e) {
2664             final Integer pos = e.getOffset();
2665             if (SwingUtilities.isEventDispatchThread()) {
2666                 firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
2667             } else {
2668                 Runnable doFire = new Runnable() {
2669                     public void run() {
2670                         firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
2671                                            null, pos);
2672                     }
2673                 };
2674                 SwingUtilities.invokeLater(doFire);
2675             }
2676         }
2677 
2678         /**
2679          * Handles document remove (fire appropriate property change event,
2680          * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
2681          * This tracks the changed offset via the event.
2682          *
2683          * @param e the DocumentEvent
2684          */
2685         public void changedUpdate(DocumentEvent e) {
2686             final Integer pos = e.getOffset();
2687             if (SwingUtilities.isEventDispatchThread()) {
2688                 firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
2689             } else {
2690                 Runnable doFire = new Runnable() {
2691                     public void run() {
2692                         firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
2693                                            null, pos);
2694                     }
2695                 };
2696                 SwingUtilities.invokeLater(doFire);
2697             }
2698         }
2699 
2700         /**
2701          * Gets the state set of the JTextComponent.
2702          * The AccessibleStateSet of an object is composed of a set of
2703          * unique AccessibleState's.  A change in the AccessibleStateSet
2704          * of an object will cause a PropertyChangeEvent to be fired
2705          * for the AccessibleContext.ACCESSIBLE_STATE_PROPERTY property.
2706          *
2707          * @return an instance of AccessibleStateSet containing the
2708          * current state set of the object
2709          * @see AccessibleStateSet
2710          * @see AccessibleState
2711          * @see #addPropertyChangeListener
2712          */
2713         public AccessibleStateSet getAccessibleStateSet() {
2714             AccessibleStateSet states = super.getAccessibleStateSet();
2715             if (JTextComponent.this.isEditable()) {
2716                 states.add(AccessibleState.EDITABLE);
2717             }
2718             return states;
2719         }
2720 
2721 
2722         /**
2723          * Gets the role of this object.
2724          *
2725          * @return an instance of AccessibleRole describing the role of the
2726          * object (AccessibleRole.TEXT)
2727          * @see AccessibleRole
2728          */
2729         public AccessibleRole getAccessibleRole() {
2730             return AccessibleRole.TEXT;
2731         }
2732 
2733         /**
2734          * Get the AccessibleText associated with this object.  In the
2735          * implementation of the Java Accessibility API for this class,
2736          * return this object, which is responsible for implementing the
2737          * AccessibleText interface on behalf of itself.
2738          *
2739          * @return this object
2740          */
2741         public AccessibleText getAccessibleText() {
2742             return this;
2743         }
2744 
2745 
2746         // --- interface AccessibleText methods ------------------------
2747 
2748         /**
2749          * Many of these methods are just convenience methods; they
2750          * just call the equivalent on the parent
2751          */
2752 
2753         /**
2754          * Given a point in local coordinates, return the zero-based index
2755          * of the character under that Point.  If the point is invalid,
2756          * this method returns -1.
2757          *
2758          * @param p the Point in local coordinates
2759          * @return the zero-based index of the character under Point p.
2760          */
2761         public int getIndexAtPoint(Point p) {
2762             if (p == null) {
2763                 return -1;
2764             }
2765             return JTextComponent.this.viewToModel(p);
2766         }
2767 
2768             /**
2769              * Gets the editor's drawing rectangle.  Stolen
2770              * from the unfortunately named
2771              * BasicTextUI.getVisibleEditorRect()
2772              *
2773              * @return the bounding box for the root view
2774              */
2775             Rectangle getRootEditorRect() {
2776                 Rectangle alloc = JTextComponent.this.getBounds();
2777                 if ((alloc.width > 0) && (alloc.height > 0)) {
2778                         alloc.x = alloc.y = 0;
2779                         Insets insets = JTextComponent.this.getInsets();
2780                         alloc.x += insets.left;
2781                         alloc.y += insets.top;
2782                         alloc.width -= insets.left + insets.right;
2783                         alloc.height -= insets.top + insets.bottom;
2784                         return alloc;
2785                 }
2786                 return null;
2787             }
2788 
2789         /**
2790          * Determines the bounding box of the character at the given
2791          * index into the string.  The bounds are returned in local
2792          * coordinates.  If the index is invalid a null rectangle
2793          * is returned.
2794          *
2795          * The screen coordinates returned are "unscrolled coordinates"
2796          * if the JTextComponent is contained in a JScrollPane in which
2797          * case the resulting rectangle should be composed with the parent
2798          * coordinates.  A good algorithm to use is:
2799          * <pre>
2800          * Accessible a:
2801          * AccessibleText at = a.getAccessibleText();
2802          * AccessibleComponent ac = a.getAccessibleComponent();
2803          * Rectangle r = at.getCharacterBounds();
2804          * Point p = ac.getLocation();
2805          * r.x += p.x;
2806          * r.y += p.y;
2807          * </pre>
2808          *
2809          * Note: the JTextComponent must have a valid size (e.g. have
2810          * been added to a parent container whose ancestor container
2811          * is a valid top-level window) for this method to be able
2812          * to return a meaningful (non-null) value.
2813          *
2814          * @param i the index into the String &ge; 0
2815          * @return the screen coordinates of the character's bounding box
2816          */
2817         public Rectangle getCharacterBounds(int i) {
2818             if (i < 0 || i > model.getLength()-1) {
2819                 return null;
2820             }
2821             TextUI ui = getUI();
2822             if (ui == null) {
2823                 return null;
2824             }
2825             Rectangle rect = null;
2826             Rectangle alloc = getRootEditorRect();
2827             if (alloc == null) {
2828                 return null;
2829             }
2830             if (model instanceof AbstractDocument) {
2831                 ((AbstractDocument)model).readLock();
2832             }
2833             try {
2834                 View rootView = ui.getRootView(JTextComponent.this);
2835                 if (rootView != null) {
2836                     rootView.setSize(alloc.width, alloc.height);
2837 
2838                     Shape bounds = rootView.modelToView(i,
2839                                     Position.Bias.Forward, i+1,
2840                                     Position.Bias.Backward, alloc);
2841 
2842                     rect = (bounds instanceof Rectangle) ?
2843                      (Rectangle)bounds : bounds.getBounds();
2844 
2845                 }
2846             } catch (BadLocationException e) {
2847             } finally {
2848                 if (model instanceof AbstractDocument) {
2849                     ((AbstractDocument)model).readUnlock();
2850                 }
2851             }
2852             return rect;
2853         }
2854 
2855         /**
2856          * Returns the number of characters (valid indices)
2857          *
2858          * @return the number of characters &ge; 0
2859          */
2860         public int getCharCount() {
2861             return model.getLength();
2862         }
2863 
2864         /**
2865          * Returns the zero-based offset of the caret.
2866          *
2867          * Note: The character to the right of the caret will have the
2868          * same index value as the offset (the caret is between
2869          * two characters).
2870          *
2871          * @return the zero-based offset of the caret.
2872          */
2873         public int getCaretPosition() {
2874             return JTextComponent.this.getCaretPosition();
2875         }
2876 
2877         /**
2878          * Returns the AttributeSet for a given character (at a given index).
2879          *
2880          * @param i the zero-based index into the text
2881          * @return the AttributeSet of the character
2882          */
2883         public AttributeSet getCharacterAttribute(int i) {
2884             Element e = null;
2885             if (model instanceof AbstractDocument) {
2886                 ((AbstractDocument)model).readLock();
2887             }
2888             try {
2889                 for (e = model.getDefaultRootElement(); ! e.isLeaf(); ) {
2890                     int index = e.getElementIndex(i);
2891                     e = e.getElement(index);
2892                 }
2893             } finally {
2894                 if (model instanceof AbstractDocument) {
2895                     ((AbstractDocument)model).readUnlock();
2896                 }
2897             }
2898             return e.getAttributes();
2899         }
2900 
2901 
2902         /**
2903          * Returns the start offset within the selected text.
2904          * If there is no selection, but there is
2905          * a caret, the start and end offsets will be the same.
2906          * Return 0 if the text is empty, or the caret position
2907          * if no selection.
2908          *
2909          * @return the index into the text of the start of the selection &ge; 0
2910          */
2911         public int getSelectionStart() {
2912             return JTextComponent.this.getSelectionStart();
2913         }
2914 
2915         /**
2916          * Returns the end offset within the selected text.
2917          * If there is no selection, but there is
2918          * a caret, the start and end offsets will be the same.
2919          * Return 0 if the text is empty, or the caret position
2920          * if no selection.
2921          *
2922          * @return the index into the text of the end of the selection &ge; 0
2923          */
2924         public int getSelectionEnd() {
2925             return JTextComponent.this.getSelectionEnd();
2926         }
2927 
2928         /**
2929          * Returns the portion of the text that is selected.
2930          *
2931          * @return the text, null if no selection
2932          */
2933         public String getSelectedText() {
2934             return JTextComponent.this.getSelectedText();
2935         }
2936 
2937        /**
2938          * IndexedSegment extends Segment adding the offset into the
2939          * the model the <code>Segment</code> was asked for.
2940          */
2941         private class IndexedSegment extends Segment {
2942             /**
2943              * Offset into the model that the position represents.
2944              */
2945             public int modelOffset;
2946         }
2947 
2948 
2949         // TIGER - 4170173
2950         /**
2951          * Returns the String at a given index. Whitespace
2952          * between words is treated as a word.
2953          *
2954          * @param part the CHARACTER, WORD, or SENTENCE to retrieve
2955          * @param index an index within the text
2956          * @return the letter, word, or sentence.
2957          *
2958          */
2959         public String getAtIndex(int part, int index) {
2960             return getAtIndex(part, index, 0);
2961         }
2962 
2963 
2964         /**
2965          * Returns the String after a given index. Whitespace
2966          * between words is treated as a word.
2967          *
2968          * @param part the CHARACTER, WORD, or SENTENCE to retrieve
2969          * @param index an index within the text
2970          * @return the letter, word, or sentence.
2971          */
2972         public String getAfterIndex(int part, int index) {
2973             return getAtIndex(part, index, 1);
2974         }
2975 
2976 
2977         /**
2978          * Returns the String before a given index. Whitespace
2979          * between words is treated a word.
2980          *
2981          * @param part the CHARACTER, WORD, or SENTENCE to retrieve
2982          * @param index an index within the text
2983          * @return the letter, word, or sentence.
2984          */
2985         public String getBeforeIndex(int part, int index) {
2986             return getAtIndex(part, index, -1);
2987         }
2988 
2989 
2990         /**
2991          * Gets the word, sentence, or character at <code>index</code>.
2992          * If <code>direction</code> is non-null this will find the
2993          * next/previous word/sentence/character.
2994          */
2995         private String getAtIndex(int part, int index, int direction) {
2996             if (model instanceof AbstractDocument) {
2997                 ((AbstractDocument)model).readLock();
2998             }
2999             try {
3000                 if (index < 0 || index >= model.getLength()) {
3001                     return null;
3002                 }
3003                 switch (part) {
3004                 case AccessibleText.CHARACTER:
3005                     if (index + direction < model.getLength() &&
3006                         index + direction >= 0) {
3007                         return model.getText(index + direction, 1);
3008                     }
3009                     break;
3010 
3011 
3012                 case AccessibleText.WORD:
3013                 case AccessibleText.SENTENCE:
3014                     IndexedSegment seg = getSegmentAt(part, index);
3015                     if (seg != null) {
3016                         if (direction != 0) {
3017                             int next;
3018 
3019 
3020                             if (direction < 0) {
3021                                 next = seg.modelOffset - 1;
3022                             }
3023                             else {
3024                                 next = seg.modelOffset + direction * seg.count;
3025                             }
3026                             if (next >= 0 && next <= model.getLength()) {
3027                                 seg = getSegmentAt(part, next);
3028                             }
3029                             else {
3030                                 seg = null;
3031                             }
3032                         }
3033                         if (seg != null) {
3034                             return new String(seg.array, seg.offset,
3035                                                   seg.count);
3036                         }
3037                     }
3038                     break;
3039 
3040 
3041                 default:
3042                     break;
3043                 }
3044             } catch (BadLocationException e) {
3045             } finally {
3046                 if (model instanceof AbstractDocument) {
3047                     ((AbstractDocument)model).readUnlock();
3048                 }
3049             }
3050             return null;
3051         }
3052 
3053 
3054         /*
3055          * Returns the paragraph element for the specified index.
3056          */
3057         private Element getParagraphElement(int index) {
3058             if (model instanceof PlainDocument ) {
3059                 PlainDocument sdoc = (PlainDocument)model;
3060                 return sdoc.getParagraphElement(index);
3061             } else if (model instanceof StyledDocument) {
3062                 StyledDocument sdoc = (StyledDocument)model;
3063                 return sdoc.getParagraphElement(index);
3064             } else {
3065                 Element para;
3066                 for (para = model.getDefaultRootElement(); ! para.isLeaf(); ) {
3067                     int pos = para.getElementIndex(index);
3068                     para = para.getElement(pos);
3069                 }
3070                 if (para == null) {
3071                     return null;
3072                 }
3073                 return para.getParentElement();
3074             }
3075         }
3076 
3077         /*
3078          * Returns a <code>Segment</code> containing the paragraph text
3079          * at <code>index</code>, or null if <code>index</code> isn't
3080          * valid.
3081          */
3082         private IndexedSegment getParagraphElementText(int index)
3083                                   throws BadLocationException {
3084             Element para = getParagraphElement(index);
3085 
3086 
3087             if (para != null) {
3088                 IndexedSegment segment = new IndexedSegment();
3089                 try {
3090                     int length = para.getEndOffset() - para.getStartOffset();
3091                     model.getText(para.getStartOffset(), length, segment);
3092                 } catch (BadLocationException e) {
3093                     return null;
3094                 }
3095                 segment.modelOffset = para.getStartOffset();
3096                 return segment;
3097             }
3098             return null;
3099         }
3100 
3101 
3102         /**
3103          * Returns the Segment at <code>index</code> representing either
3104          * the paragraph or sentence as identified by <code>part</code>, or
3105          * null if a valid paragraph/sentence can't be found. The offset
3106          * will point to the start of the word/sentence in the array, and
3107          * the modelOffset will point to the location of the word/sentence
3108          * in the model.
3109          */
3110         private IndexedSegment getSegmentAt(int part, int index) throws
3111                                   BadLocationException {
3112             IndexedSegment seg = getParagraphElementText(index);
3113             if (seg == null) {
3114                 return null;
3115             }
3116             BreakIterator iterator;
3117             switch (part) {
3118             case AccessibleText.WORD:
3119                 iterator = BreakIterator.getWordInstance(getLocale());
3120                 break;
3121             case AccessibleText.SENTENCE:
3122                 iterator = BreakIterator.getSentenceInstance(getLocale());
3123                 break;
3124             default:
3125                 return null;
3126             }
3127             seg.first();
3128             iterator.setText(seg);
3129             int end = iterator.following(index - seg.modelOffset + seg.offset);
3130             if (end == BreakIterator.DONE) {
3131                 return null;
3132             }
3133             if (end > seg.offset + seg.count) {
3134                 return null;
3135             }
3136             int begin = iterator.previous();
3137             if (begin == BreakIterator.DONE ||
3138                          begin >= seg.offset + seg.count) {
3139                 return null;
3140             }
3141             seg.modelOffset = seg.modelOffset + begin - seg.offset;
3142             seg.offset = begin;
3143             seg.count = end - begin;
3144             return seg;
3145         }
3146 
3147         // begin AccessibleEditableText methods -----
3148 
3149         /**
3150          * Returns the AccessibleEditableText interface for
3151          * this text component.
3152          *
3153          * @return the AccessibleEditableText interface
3154          * @since 1.4
3155          */
3156         public AccessibleEditableText getAccessibleEditableText() {
3157             return this;
3158         }
3159 
3160         /**
3161          * Sets the text contents to the specified string.
3162          *
3163          * @param s the string to set the text contents
3164          * @since 1.4
3165          */
3166         public void setTextContents(String s) {
3167             JTextComponent.this.setText(s);
3168         }
3169 
3170         /**
3171          * Inserts the specified string at the given index
3172          *
3173          * @param index the index in the text where the string will
3174          * be inserted
3175          * @param s the string to insert in the text
3176          * @since 1.4
3177          */
3178         public void insertTextAtIndex(int index, String s) {
3179             Document doc = JTextComponent.this.getDocument();
3180             if (doc != null) {
3181                 try {
3182                     if (s != null && s.length() > 0) {
3183                         boolean composedTextSaved = saveComposedText(index);
3184                         doc.insertString(index, s, null);
3185                         if (composedTextSaved) {
3186                             restoreComposedText();
3187                         }
3188                     }
3189                 } catch (BadLocationException e) {
3190                     UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
3191                 }
3192             }
3193         }
3194 
3195         /**
3196          * Returns the text string between two indices.
3197          *
3198          * @param startIndex the starting index in the text
3199          * @param endIndex the ending index in the text
3200          * @return the text string between the indices
3201          * @since 1.4
3202          */
3203         public String getTextRange(int startIndex, int endIndex) {
3204             String txt = null;
3205             int p0 = Math.min(startIndex, endIndex);
3206             int p1 = Math.max(startIndex, endIndex);
3207             if (p0 != p1) {
3208                 try {
3209                     Document doc = JTextComponent.this.getDocument();
3210                     txt = doc.getText(p0, p1 - p0);
3211                 } catch (BadLocationException e) {
3212                     throw new IllegalArgumentException(e.getMessage());
3213                 }
3214             }
3215             return txt;
3216         }
3217 
3218         /**
3219          * Deletes the text between two indices
3220          *
3221          * @param startIndex the starting index in the text
3222          * @param endIndex the ending index in the text
3223          * @since 1.4
3224          */
3225         public void delete(int startIndex, int endIndex) {
3226             if (isEditable() && isEnabled()) {
3227                 try {
3228                     int p0 = Math.min(startIndex, endIndex);
3229                     int p1 = Math.max(startIndex, endIndex);
3230                     if (p0 != p1) {
3231                         Document doc = getDocument();
3232                         doc.remove(p0, p1 - p0);
3233                     }
3234                 } catch (BadLocationException e) {
3235                 }
3236             } else {
3237                 UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
3238             }
3239         }
3240 
3241         /**
3242          * Cuts the text between two indices into the system clipboard.
3243          *
3244          * @param startIndex the starting index in the text
3245          * @param endIndex the ending index in the text
3246          * @since 1.4
3247          */
3248         public void cut(int startIndex, int endIndex) {
3249             selectText(startIndex, endIndex);
3250             JTextComponent.this.cut();
3251         }
3252 
3253         /**
3254          * Pastes the text from the system clipboard into the text
3255          * starting at the specified index.
3256          *
3257          * @param startIndex the starting index in the text
3258          * @since 1.4
3259          */
3260         public void paste(int startIndex) {
3261             setCaretPosition(startIndex);
3262             JTextComponent.this.paste();
3263         }
3264 
3265         /**
3266          * Replaces the text between two indices with the specified
3267          * string.
3268          *
3269          * @param startIndex the starting index in the text
3270          * @param endIndex the ending index in the text
3271          * @param s the string to replace the text between two indices
3272          * @since 1.4
3273          */
3274         public void replaceText(int startIndex, int endIndex, String s) {
3275             selectText(startIndex, endIndex);
3276             JTextComponent.this.replaceSelection(s);
3277         }
3278 
3279         /**
3280          * Selects the text between two indices.
3281          *
3282          * @param startIndex the starting index in the text
3283          * @param endIndex the ending index in the text
3284          * @since 1.4
3285          */
3286         public void selectText(int startIndex, int endIndex) {
3287             JTextComponent.this.select(startIndex, endIndex);
3288         }
3289 
3290         /**
3291          * Sets attributes for the text between two indices.
3292          *
3293          * @param startIndex the starting index in the text
3294          * @param endIndex the ending index in the text
3295          * @param as the attribute set
3296          * @see AttributeSet
3297          * @since 1.4
3298          */
3299         public void setAttributes(int startIndex, int endIndex,
3300             AttributeSet as) {
3301 
3302             // Fixes bug 4487492
3303             Document doc = JTextComponent.this.getDocument();
3304             if (doc != null && doc instanceof StyledDocument) {
3305                 StyledDocument sDoc = (StyledDocument)doc;
3306                 int offset = startIndex;
3307                 int length = endIndex - startIndex;
3308                 sDoc.setCharacterAttributes(offset, length, as, true);
3309             }
3310         }
3311 
3312         // ----- end AccessibleEditableText methods
3313 
3314 
3315         // ----- begin AccessibleExtendedText methods
3316 
3317 // Probably should replace the helper method getAtIndex() to return
3318 // instead an AccessibleTextSequence also for LINE & ATTRIBUTE_RUN
3319 // and then make the AccessibleText methods get[At|After|Before]Point
3320 // call this new method instead and return only the string portion
3321 
3322         /**
3323          * Returns the AccessibleTextSequence at a given <code>index</code>.
3324          * If <code>direction</code> is non-null this will find the
3325          * next/previous word/sentence/character.
3326          *
3327          * @param part the <code>CHARACTER</code>, <code>WORD</code>,
3328          * <code>SENTENCE</code>, <code>LINE</code> or
3329          * <code>ATTRIBUTE_RUN</code> to retrieve
3330          * @param index an index within the text
3331          * @param direction is either -1, 0, or 1
3332          * @return an <code>AccessibleTextSequence</code> specifying the text
3333          * if <code>part</code> and <code>index</code> are valid.  Otherwise,
3334          * <code>null</code> is returned.
3335          *
3336          * @see javax.accessibility.AccessibleText#CHARACTER
3337          * @see javax.accessibility.AccessibleText#WORD
3338          * @see javax.accessibility.AccessibleText#SENTENCE
3339          * @see javax.accessibility.AccessibleExtendedText#LINE
3340          * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN
3341          *
3342          * @since 1.6
3343          */
3344         private AccessibleTextSequence getSequenceAtIndex(int part,
3345             int index, int direction) {
3346             if (index < 0 || index >= model.getLength()) {
3347                 return null;
3348             }
3349             if (direction < -1 || direction > 1) {
3350                 return null;    // direction must be 1, 0, or -1
3351             }
3352 
3353             switch (part) {
3354             case AccessibleText.CHARACTER:
3355                 if (model instanceof AbstractDocument) {
3356                     ((AbstractDocument)model).readLock();
3357                 }
3358                 AccessibleTextSequence charSequence = null;
3359                 try {
3360                     if (index + direction < model.getLength() &&
3361                         index + direction >= 0) {
3362                         charSequence =
3363                             new AccessibleTextSequence(index + direction,
3364                             index + direction + 1,
3365                             model.getText(index + direction, 1));
3366                     }
3367 
3368                 } catch (BadLocationException e) {
3369                     // we are intentionally silent; our contract says we return
3370                     // null if there is any failure in this method
3371                 } finally {
3372                     if (model instanceof AbstractDocument) {
3373                         ((AbstractDocument)model).readUnlock();
3374                     }
3375                 }
3376                 return charSequence;
3377 
3378             case AccessibleText.WORD:
3379             case AccessibleText.SENTENCE:
3380                 if (model instanceof AbstractDocument) {
3381                     ((AbstractDocument)model).readLock();
3382                 }
3383                 AccessibleTextSequence rangeSequence = null;
3384                 try {
3385                     IndexedSegment seg = getSegmentAt(part, index);
3386                     if (seg != null) {
3387                         if (direction != 0) {
3388                             int next;
3389 
3390                             if (direction < 0) {
3391                                 next = seg.modelOffset - 1;
3392                             }
3393                             else {
3394                                 next = seg.modelOffset + seg.count;
3395                             }
3396                             if (next >= 0 && next <= model.getLength()) {
3397                                 seg = getSegmentAt(part, next);
3398                             }
3399                             else {
3400                                 seg = null;
3401                             }
3402                         }
3403                         if (seg != null &&
3404                             (seg.offset + seg.count) <= model.getLength()) {
3405                             rangeSequence =
3406                                 new AccessibleTextSequence (seg.offset,
3407                                 seg.offset + seg.count,
3408                                 new String(seg.array, seg.offset, seg.count));
3409                         } // else we leave rangeSequence set to null
3410                     }
3411                 } catch(BadLocationException e) {
3412                     // we are intentionally silent; our contract says we return
3413                     // null if there is any failure in this method
3414                 } finally {
3415                     if (model instanceof AbstractDocument) {
3416                         ((AbstractDocument)model).readUnlock();
3417                     }
3418                 }
3419                 return rangeSequence;
3420 
3421             case AccessibleExtendedText.LINE:
3422                 AccessibleTextSequence lineSequence = null;
3423                 if (model instanceof AbstractDocument) {
3424                     ((AbstractDocument)model).readLock();
3425                 }
3426                 try {
3427                     int startIndex =
3428                         Utilities.getRowStart(JTextComponent.this, index);
3429                     int endIndex =
3430                         Utilities.getRowEnd(JTextComponent.this, index);
3431                     if (startIndex >= 0 && endIndex >= startIndex) {
3432                         if (direction == 0) {
3433                             lineSequence =
3434                                 new AccessibleTextSequence(startIndex, endIndex,
3435                                     model.getText(startIndex,
3436                                         endIndex - startIndex + 1));
3437                         } else if (direction == -1 && startIndex > 0) {
3438                             endIndex =
3439                                 Utilities.getRowEnd(JTextComponent.this,
3440                                     startIndex - 1);
3441                             startIndex =
3442                                 Utilities.getRowStart(JTextComponent.this,
3443                                     startIndex - 1);
3444                             if (startIndex >= 0 && endIndex >= startIndex) {
3445                                 lineSequence =
3446                                     new AccessibleTextSequence(startIndex,
3447                                         endIndex,
3448                                         model.getText(startIndex,
3449                                             endIndex - startIndex + 1));
3450                             }
3451                         } else if (direction == 1 &&
3452                          endIndex < model.getLength()) {
3453                             startIndex =
3454                                 Utilities.getRowStart(JTextComponent.this,
3455                                     endIndex + 1);
3456                             endIndex =
3457                                 Utilities.getRowEnd(JTextComponent.this,
3458                                     endIndex + 1);
3459                             if (startIndex >= 0 && endIndex >= startIndex) {
3460                                 lineSequence =
3461                                     new AccessibleTextSequence(startIndex,
3462                                         endIndex, model.getText(startIndex,
3463                                             endIndex - startIndex + 1));
3464                             }
3465                         }
3466                         // already validated 'direction' above...
3467                     }
3468                 } catch(BadLocationException e) {
3469                     // we are intentionally silent; our contract says we return
3470                     // null if there is any failure in this method
3471                 } finally {
3472                     if (model instanceof AbstractDocument) {
3473                         ((AbstractDocument)model).readUnlock();
3474                     }
3475                 }
3476                 return lineSequence;
3477 
3478             case AccessibleExtendedText.ATTRIBUTE_RUN:
3479                 // assumptions: (1) that all characters in a single element
3480                 // share the same attribute set; (2) that adjacent elements
3481                 // *may* share the same attribute set
3482 
3483                 int attributeRunStartIndex, attributeRunEndIndex;
3484                 String runText = null;
3485                 if (model instanceof AbstractDocument) {
3486                     ((AbstractDocument)model).readLock();
3487                 }
3488 
3489                 try {
3490                     attributeRunStartIndex = attributeRunEndIndex =
3491                      Integer.MIN_VALUE;
3492                     int tempIndex = index;
3493                     switch (direction) {
3494                     case -1:
3495                         // going backwards, so find left edge of this run -
3496                         // that'll be the end of the previous run
3497                         // (off-by-one counting)
3498                         attributeRunEndIndex = getRunEdge(index, direction);
3499                         // now set ourselves up to find the left edge of the
3500                         // prev. run
3501                         tempIndex = attributeRunEndIndex - 1;
3502                         break;
3503                     case 1:
3504                         // going forward, so find right edge of this run -
3505                         // that'll be the start of the next run
3506                         // (off-by-one counting)
3507                         attributeRunStartIndex = getRunEdge(index, direction);
3508                         // now set ourselves up to find the right edge of the
3509                         // next run
3510                         tempIndex = attributeRunStartIndex;
3511                         break;
3512                     case 0:
3513                         // interested in the current run, so nothing special to
3514                         // set up in advance...
3515                         break;
3516                     default:
3517                         // only those three values of direction allowed...
3518                         throw new AssertionError(direction);
3519                     }
3520 
3521                     // set the unset edge; if neither set then we're getting
3522                     // both edges of the current run around our 'index'
3523                     attributeRunStartIndex =
3524                         (attributeRunStartIndex != Integer.MIN_VALUE) ?
3525                         attributeRunStartIndex : getRunEdge(tempIndex, -1);
3526                     attributeRunEndIndex =
3527                         (attributeRunEndIndex != Integer.MIN_VALUE) ?
3528                         attributeRunEndIndex : getRunEdge(tempIndex, 1);
3529 
3530                     runText = model.getText(attributeRunStartIndex,
3531                                             attributeRunEndIndex -
3532                                             attributeRunStartIndex);
3533                 } catch (BadLocationException e) {
3534                     // we are intentionally silent; our contract says we return
3535                     // null if there is any failure in this method
3536                     return null;
3537                 } finally {
3538                     if (model instanceof AbstractDocument) {
3539                         ((AbstractDocument)model).readUnlock();
3540                     }
3541                 }
3542                 return new AccessibleTextSequence(attributeRunStartIndex,
3543                                                   attributeRunEndIndex,
3544                                                   runText);
3545 
3546             default:
3547                 break;
3548             }
3549             return null;
3550         }
3551 
3552 
3553         /**
3554          * Starting at text position <code>index</code>, and going in
3555          * <code>direction</code>, return the edge of run that shares the
3556          * same <code>AttributeSet</code> and parent element as those at
3557          * <code>index</code>.
3558          *
3559          * Note: we assume the document is already locked...
3560          */
3561         private int getRunEdge(int index, int direction) throws
3562          BadLocationException {
3563             if (index < 0 || index >= model.getLength()) {
3564                 throw new BadLocationException("Location out of bounds", index);
3565             }
3566             // locate the Element at index
3567             Element indexElement;
3568             // locate the Element at our index/offset
3569             int elementIndex = -1;        // test for initialization
3570             for (indexElement = model.getDefaultRootElement();
3571                  ! indexElement.isLeaf(); ) {
3572                 elementIndex = indexElement.getElementIndex(index);
3573                 indexElement = indexElement.getElement(elementIndex);
3574             }
3575             if (elementIndex == -1) {
3576                 throw new AssertionError(index);
3577             }
3578             // cache the AttributeSet and parentElement atindex
3579             AttributeSet indexAS = indexElement.getAttributes();
3580             Element parent = indexElement.getParentElement();
3581 
3582             // find the first Element before/after ours w/the same AttributeSet
3583             // if we are already at edge of the first element in our parent
3584             // then return that edge
3585             Element edgeElement;
3586             switch (direction) {
3587             case -1:
3588             case 1:
3589                 int edgeElementIndex = elementIndex;
3590                 int elementCount = parent.getElementCount();
3591                 while ((edgeElementIndex + direction) > 0 &&
3592                        ((edgeElementIndex + direction) < elementCount) &&
3593                        parent.getElement(edgeElementIndex
3594                        + direction).getAttributes().isEqual(indexAS)) {
3595                     edgeElementIndex += direction;
3596                 }
3597                 edgeElement = parent.getElement(edgeElementIndex);
3598                 break;
3599             default:
3600                 throw new AssertionError(direction);
3601             }
3602             switch (direction) {
3603             case -1:
3604                 return edgeElement.getStartOffset();
3605             case 1:
3606                 return edgeElement.getEndOffset();
3607             default:
3608                 // we already caught this case earlier; this is to satisfy
3609                 // the compiler...
3610                 return Integer.MIN_VALUE;
3611             }
3612         }
3613 
3614         // getTextRange() not needed; defined in AccessibleEditableText
3615 
3616         /**
3617          * Returns the <code>AccessibleTextSequence</code> at a given
3618          * <code>index</code>.
3619          *
3620          * @param part the <code>CHARACTER</code>, <code>WORD</code>,
3621          * <code>SENTENCE</code>, <code>LINE</code> or
3622          * <code>ATTRIBUTE_RUN</code> to retrieve
3623          * @param index an index within the text
3624          * @return an <code>AccessibleTextSequence</code> specifying the text if
3625          * <code>part</code> and <code>index</code> are valid.  Otherwise,
3626          * <code>null</code> is returned
3627          *
3628          * @see javax.accessibility.AccessibleText#CHARACTER
3629          * @see javax.accessibility.AccessibleText#WORD
3630          * @see javax.accessibility.AccessibleText#SENTENCE
3631          * @see javax.accessibility.AccessibleExtendedText#LINE
3632          * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN
3633          *
3634          * @since 1.6
3635          */
3636         public AccessibleTextSequence getTextSequenceAt(int part, int index) {
3637             return getSequenceAtIndex(part, index, 0);
3638         }
3639 
3640         /**
3641          * Returns the <code>AccessibleTextSequence</code> after a given
3642          * <code>index</code>.
3643          *
3644          * @param part the <code>CHARACTER</code>, <code>WORD</code>,
3645          * <code>SENTENCE</code>, <code>LINE</code> or
3646          * <code>ATTRIBUTE_RUN</code> to retrieve
3647          * @param index an index within the text
3648          * @return an <code>AccessibleTextSequence</code> specifying the text
3649          * if <code>part</code> and <code>index</code> are valid.  Otherwise,
3650          * <code>null</code> is returned
3651          *
3652          * @see javax.accessibility.AccessibleText#CHARACTER
3653          * @see javax.accessibility.AccessibleText#WORD
3654          * @see javax.accessibility.AccessibleText#SENTENCE
3655          * @see javax.accessibility.AccessibleExtendedText#LINE
3656          * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN
3657          *
3658          * @since 1.6
3659          */
3660         public AccessibleTextSequence getTextSequenceAfter(int part, int index) {
3661             return getSequenceAtIndex(part, index, 1);
3662         }
3663 
3664         /**
3665          * Returns the <code>AccessibleTextSequence</code> before a given
3666          * <code>index</code>.
3667          *
3668          * @param part the <code>CHARACTER</code>, <code>WORD</code>,
3669          * <code>SENTENCE</code>, <code>LINE</code> or
3670          * <code>ATTRIBUTE_RUN</code> to retrieve
3671          * @param index an index within the text
3672          * @return an <code>AccessibleTextSequence</code> specifying the text
3673          * if <code>part</code> and <code>index</code> are valid.  Otherwise,
3674          * <code>null</code> is returned
3675          *
3676          * @see javax.accessibility.AccessibleText#CHARACTER
3677          * @see javax.accessibility.AccessibleText#WORD
3678          * @see javax.accessibility.AccessibleText#SENTENCE
3679          * @see javax.accessibility.AccessibleExtendedText#LINE
3680          * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN
3681          *
3682          * @since 1.6
3683          */
3684         public AccessibleTextSequence getTextSequenceBefore(int part, int index) {
3685             return getSequenceAtIndex(part, index, -1);
3686         }
3687 
3688         /**
3689          * Returns the <code>Rectangle</code> enclosing the text between
3690          * two indicies.
3691          *
3692          * @param startIndex the start index in the text
3693          * @param endIndex the end index in the text
3694          * @return the bounding rectangle of the text if the indices are valid.
3695          * Otherwise, <code>null</code> is returned
3696          *
3697          * @since 1.6
3698          */
3699         public Rectangle getTextBounds(int startIndex, int endIndex) {
3700             if (startIndex < 0 || startIndex > model.getLength()-1 ||
3701                 endIndex < 0 || endIndex > model.getLength()-1 ||
3702                 startIndex > endIndex) {
3703                 return null;
3704             }
3705             TextUI ui = getUI();
3706             if (ui == null) {
3707                 return null;
3708             }
3709             Rectangle rect = null;
3710             Rectangle alloc = getRootEditorRect();
3711             if (alloc == null) {
3712                 return null;
3713             }
3714             if (model instanceof AbstractDocument) {
3715                 ((AbstractDocument)model).readLock();
3716             }
3717             try {
3718                 View rootView = ui.getRootView(JTextComponent.this);
3719                 if (rootView != null) {
3720                     Shape bounds = rootView.modelToView(startIndex,
3721                                     Position.Bias.Forward, endIndex,
3722                                     Position.Bias.Backward, alloc);
3723 
3724                     rect = (bounds instanceof Rectangle) ?
3725                      (Rectangle)bounds : bounds.getBounds();
3726 
3727                 }
3728             } catch (BadLocationException e) {
3729             } finally {
3730                 if (model instanceof AbstractDocument) {
3731                     ((AbstractDocument)model).readUnlock();
3732                 }
3733             }
3734             return rect;
3735         }
3736 
3737         // ----- end AccessibleExtendedText methods
3738 
3739 
3740         // --- interface AccessibleAction methods ------------------------
3741 
3742         public AccessibleAction getAccessibleAction() {
3743             return this;
3744         }
3745 
3746         /**
3747          * Returns the number of accessible actions available in this object
3748          * If there are more than one, the first one is considered the
3749          * "default" action of the object.
3750          *
3751          * @return the zero-based number of Actions in this object
3752          * @since 1.4
3753          */
3754         public int getAccessibleActionCount() {
3755             Action [] actions = JTextComponent.this.getActions();
3756             return actions.length;
3757         }
3758 
3759         /**
3760          * Returns a description of the specified action of the object.
3761          *
3762          * @param i zero-based index of the actions
3763          * @return a String description of the action
3764          * @see #getAccessibleActionCount
3765          * @since 1.4
3766          */
3767         public String getAccessibleActionDescription(int i) {
3768             Action [] actions = JTextComponent.this.getActions();
3769             if (i < 0 || i >= actions.length) {
3770                 return null;
3771             }
3772             return (String)actions[i].getValue(Action.NAME);
3773         }
3774 
3775         /**
3776          * Performs the specified Action on the object
3777          *
3778          * @param i zero-based index of actions
3779          * @return true if the action was performed; otherwise false.
3780          * @see #getAccessibleActionCount
3781          * @since 1.4
3782          */
3783         public boolean doAccessibleAction(int i) {
3784             Action [] actions = JTextComponent.this.getActions();
3785             if (i < 0 || i >= actions.length) {
3786                 return false;
3787             }
3788             ActionEvent ae =
3789                 new ActionEvent(JTextComponent.this,
3790                                 ActionEvent.ACTION_PERFORMED, null,
3791                                 EventQueue.getMostRecentEventTime(),
3792                                 getCurrentEventModifiers());
3793             actions[i].actionPerformed(ae);
3794             return true;
3795         }
3796 
3797         // ----- end AccessibleAction methods
3798 
3799 
3800     }
3801 
3802 
3803     // --- serialization ---------------------------------------------
3804 
3805     private void readObject(ObjectInputStream s)
3806         throws IOException, ClassNotFoundException
3807     {
3808         ObjectInputStream.GetField f = s.readFields();
3809 
3810         model = (Document) f.get("model", null);
3811         navigationFilter = (NavigationFilter) f.get("navigationFilter", null);
3812         caretColor = (Color) f.get("caretColor", null);
3813         selectionColor = (Color) f.get("selectionColor", null);
3814         selectedTextColor = (Color) f.get("selectedTextColor", null);
3815         disabledTextColor = (Color) f.get("disabledTextColor", null);
3816         editable = f.get("editable", false);
3817         margin = (Insets) f.get("margin", null);
3818         focusAccelerator = f.get("focusAccelerator", '\0');
3819         boolean newDragEnabled = f.get("dragEnabled", false);
3820         checkDragEnabled(newDragEnabled);
3821         dragEnabled = newDragEnabled;
3822         DropMode newDropMode = (DropMode) f.get("dropMode",
3823                 DropMode.USE_SELECTION);
3824         checkDropMode(newDropMode);
3825         dropMode = newDropMode;
3826         composedTextAttribute = (SimpleAttributeSet) f.get("composedTextAttribute", null);
3827         composedTextContent = (String) f.get("composedTextContent", null);
3828         composedTextStart = (Position) f.get("composedTextStart", null);
3829         composedTextEnd = (Position) f.get("composedTextEnd", null);
3830         latestCommittedTextStart = (Position) f.get("latestCommittedTextStart", null);
3831         latestCommittedTextEnd = (Position) f.get("latestCommittedTextEnd", null);
3832         composedTextCaret = (ComposedTextCaret) f.get("composedTextCaret", null);
3833         checkedInputOverride = f.get("checkedInputOverride", false);
3834         needToSendKeyTypedEvent = f.get("needToSendKeyTypedEvent", false);
3835 
3836         caretEvent = new MutableCaretEvent(this);
3837         addMouseListener(caretEvent);
3838         addFocusListener(caretEvent);
3839     }
3840 
3841     // --- member variables ----------------------------------
3842 
3843     /**
3844      * The document model.
3845      */
3846     private Document model;
3847 
3848     /**
3849      * The caret used to display the insert position
3850      * and navigate throughout the document.
3851      *
3852      * PENDING(prinz)
3853      * This should be serializable, default installed
3854      * by UI.
3855      */
3856     private transient Caret caret;
3857 
3858     /**
3859      * Object responsible for restricting the cursor navigation.
3860      */
3861     private NavigationFilter navigationFilter;
3862 
3863     /**
3864      * The object responsible for managing highlights.
3865      *
3866      * PENDING(prinz)
3867      * This should be serializable, default installed
3868      * by UI.
3869      */
3870     private transient Highlighter highlighter;
3871 
3872     /**
3873      * The current key bindings in effect.
3874      *
3875      * PENDING(prinz)
3876      * This should be serializable, default installed
3877      * by UI.
3878      */
3879     private transient Keymap keymap;
3880 
3881     private transient MutableCaretEvent caretEvent;
3882     private Color caretColor;
3883     private Color selectionColor;
3884     private Color selectedTextColor;
3885     private Color disabledTextColor;
3886     private boolean editable;
3887     private Insets margin;
3888     private char focusAccelerator;
3889     private boolean dragEnabled;
3890 
3891     /**
3892      * The drop mode for this component.
3893      */
3894     private DropMode dropMode = DropMode.USE_SELECTION;
3895 
3896     /**
3897      * The drop location.
3898      */
3899     private transient DropLocation dropLocation;
3900 
3901     /**
3902      * Represents a drop location for <code>JTextComponent</code>s.
3903      *
3904      * @see #getDropLocation
3905      * @since 1.6
3906      */
3907     public static final class DropLocation extends TransferHandler.DropLocation {
3908         private final int index;
3909         private final Position.Bias bias;
3910 
3911         private DropLocation(Point p, int index, Position.Bias bias) {
3912             super(p);
3913             this.index = index;
3914             this.bias = bias;
3915         }
3916 
3917         /**
3918          * Returns the index where dropped data should be inserted into the
3919          * associated component. This index represents a position between
3920          * characters, as would be interpreted by a caret.
3921          *
3922          * @return the drop index
3923          */
3924         public int getIndex() {
3925             return index;
3926         }
3927 
3928         /**
3929          * Returns the bias for the drop index.
3930          *
3931          * @return the drop bias
3932          */
3933         public Position.Bias getBias() {
3934             return bias;
3935         }
3936 
3937         /**
3938          * Returns a string representation of this drop location.
3939          * This method is intended to be used for debugging purposes,
3940          * and the content and format of the returned string may vary
3941          * between implementations.
3942          *
3943          * @return a string representation of this drop location
3944          */
3945         public String toString() {
3946             return getClass().getName()
3947                    + "[dropPoint=" + getDropPoint() + ","
3948                    + "index=" + index + ","
3949                    + "bias=" + bias + "]";
3950         }
3951     }
3952 
3953     /**
3954      * TransferHandler used if one hasn't been supplied by the UI.
3955      */
3956     private static DefaultTransferHandler defaultTransferHandler;
3957 
3958     /**
3959      * Maps from class name to Boolean indicating if
3960      * <code>processInputMethodEvent</code> has been overriden.
3961      */
3962     private static Cache<Class<?>,Boolean> METHOD_OVERRIDDEN
3963             = new Cache<Class<?>,Boolean>(Cache.Kind.WEAK, Cache.Kind.STRONG) {
3964         /**
3965          * Returns {@code true} if the specified {@code type} extends {@link JTextComponent}
3966          * and the {@link JTextComponent#processInputMethodEvent} method is overridden.
3967          */
3968         @Override
3969         public Boolean create(final Class<?> type) {
3970             if (JTextComponent.class == type) {
3971                 return Boolean.FALSE;
3972             }
3973             if (get(type.getSuperclass())) {
3974                 return Boolean.TRUE;
3975             }
3976             return AccessController.doPrivileged(
3977                     new PrivilegedAction<Boolean>() {
3978                         public Boolean run() {
3979                             try {
3980                                 type.getDeclaredMethod("processInputMethodEvent", InputMethodEvent.class);
3981                                 return Boolean.TRUE;
3982                             } catch (NoSuchMethodException exception) {
3983                                 return Boolean.FALSE;
3984                             }
3985                         }
3986                     });
3987         }
3988     };
3989 
3990     /**
3991      * Returns a string representation of this <code>JTextComponent</code>.
3992      * This method is intended to be used only for debugging purposes, and the
3993      * content and format of the returned string may vary between
3994      * implementations. The returned string may be empty but may not
3995      * be <code>null</code>.
3996      * <P>
3997      * Overriding <code>paramString</code> to provide information about the
3998      * specific new aspects of the JFC components.
3999      *
4000      * @return  a string representation of this <code>JTextComponent</code>
4001      */
4002     protected String paramString() {
4003         String editableString = (editable ?
4004                                  "true" : "false");
4005         String caretColorString = (caretColor != null ?
4006                                    caretColor.toString() : "");
4007         String selectionColorString = (selectionColor != null ?
4008                                        selectionColor.toString() : "");
4009         String selectedTextColorString = (selectedTextColor != null ?
4010                                           selectedTextColor.toString() : "");
4011         String disabledTextColorString = (disabledTextColor != null ?
4012                                           disabledTextColor.toString() : "");
4013         String marginString = (margin != null ?
4014                                margin.toString() : "");
4015 
4016         return super.paramString() +
4017         ",caretColor=" + caretColorString +
4018         ",disabledTextColor=" + disabledTextColorString +
4019         ",editable=" + editableString +
4020         ",margin=" + marginString +
4021         ",selectedTextColor=" + selectedTextColorString +
4022         ",selectionColor=" + selectionColorString;
4023     }
4024 
4025 
4026     /**
4027      * A Simple TransferHandler that exports the data as a String, and
4028      * imports the data from the String clipboard.  This is only used
4029      * if the UI hasn't supplied one, which would only happen if someone
4030      * hasn't subclassed Basic.
4031      */
4032     static class DefaultTransferHandler extends TransferHandler implements
4033                                         UIResource {
4034         public void exportToClipboard(JComponent comp, Clipboard clipboard,
4035                                       int action) throws IllegalStateException {
4036             if (comp instanceof JTextComponent) {
4037                 JTextComponent text = (JTextComponent)comp;
4038                 int p0 = text.getSelectionStart();
4039                 int p1 = text.getSelectionEnd();
4040                 if (p0 != p1) {
4041                     try {
4042                         Document doc = text.getDocument();
4043                         String srcData = doc.getText(p0, p1 - p0);
4044                         StringSelection contents =new StringSelection(srcData);
4045 
4046                         // this may throw an IllegalStateException,
4047                         // but it will be caught and handled in the
4048                         // action that invoked this method
4049                         clipboard.setContents(contents, null);
4050 
4051                         if (action == TransferHandler.MOVE) {
4052                             doc.remove(p0, p1 - p0);
4053                         }
4054                     } catch (BadLocationException ble) {}
4055                 }
4056             }
4057         }
4058         public boolean importData(JComponent comp, Transferable t) {
4059             if (comp instanceof JTextComponent) {
4060                 DataFlavor flavor = getFlavor(t.getTransferDataFlavors());
4061 
4062                 if (flavor != null) {
4063                     InputContext ic = comp.getInputContext();
4064                     if (ic != null) {
4065                         ic.endComposition();
4066                     }
4067                     try {
4068                         String data = (String)t.getTransferData(flavor);
4069 
4070                         ((JTextComponent)comp).replaceSelection(data);
4071                         return true;
4072                     } catch (UnsupportedFlavorException ufe) {
4073                     } catch (IOException ioe) {
4074                     }
4075                 }
4076             }
4077             return false;
4078         }
4079         public boolean canImport(JComponent comp,
4080                                  DataFlavor[] transferFlavors) {
4081             JTextComponent c = (JTextComponent)comp;
4082             if (!(c.isEditable() && c.isEnabled())) {
4083                 return false;
4084             }
4085             return (getFlavor(transferFlavors) != null);
4086         }
4087         public int getSourceActions(JComponent c) {
4088             return NONE;
4089         }
4090         private DataFlavor getFlavor(DataFlavor[] flavors) {
4091             if (flavors != null) {
4092                 for (DataFlavor flavor : flavors) {
4093                     if (flavor.equals(DataFlavor.stringFlavor)) {
4094                         return flavor;
4095                     }
4096                 }
4097             }
4098             return null;
4099         }
4100     }
4101 
4102     /**
4103      * Returns the JTextComponent that most recently had focus. The returned
4104      * value may currently have focus.
4105      */
4106     static final JTextComponent getFocusedComponent() {
4107         return (JTextComponent)AppContext.getAppContext().
4108             get(FOCUSED_COMPONENT);
4109     }
4110 
4111     @SuppressWarnings("deprecation")
4112     private int getCurrentEventModifiers() {
4113         int modifiers = 0;
4114         AWTEvent currentEvent = EventQueue.getCurrentEvent();
4115         if (currentEvent instanceof InputEvent) {
4116             modifiers = ((InputEvent)currentEvent).getModifiers();
4117         } else if (currentEvent instanceof ActionEvent) {
4118             modifiers = ((ActionEvent)currentEvent).getModifiers();
4119         }
4120         return modifiers;
4121     }
4122 
4123     private static final Object KEYMAP_TABLE =
4124         new StringBuilder("JTextComponent_KeymapTable");
4125 
4126     //
4127     // member variables used for on-the-spot input method
4128     // editing style support
4129     //
4130     private transient InputMethodRequests inputMethodRequestsHandler;
4131     private SimpleAttributeSet composedTextAttribute;
4132     private String composedTextContent;
4133     private Position composedTextStart;
4134     private Position composedTextEnd;
4135     private Position latestCommittedTextStart;
4136     private Position latestCommittedTextEnd;
4137     private ComposedTextCaret composedTextCaret;
4138     private transient Caret originalCaret;
4139     /**
4140      * Set to true after the check for the override of processInputMethodEvent
4141      * has been checked.
4142      */
4143     private boolean checkedInputOverride;
4144     private boolean needToSendKeyTypedEvent;
4145 
4146     static class DefaultKeymap implements Keymap {
4147 
4148         DefaultKeymap(String nm, Keymap parent) {
4149             this.nm = nm;
4150             this.parent = parent;
4151             bindings = new Hashtable<KeyStroke, Action>();
4152         }
4153 
4154         /**
4155          * Fetch the default action to fire if a
4156          * key is typed (ie a KEY_TYPED KeyEvent is received)
4157          * and there is no binding for it.  Typically this
4158          * would be some action that inserts text so that
4159          * the keymap doesn't require an action for each
4160          * possible key.
4161          */
4162         public Action getDefaultAction() {
4163             if (defaultAction != null) {
4164                 return defaultAction;
4165             }
4166             return (parent != null) ? parent.getDefaultAction() : null;
4167         }
4168 
4169         /**
4170          * Set the default action to fire if a key is typed.
4171          */
4172         public void setDefaultAction(Action a) {
4173             defaultAction = a;
4174         }
4175 
4176         public String getName() {
4177             return nm;
4178         }
4179 
4180         public Action getAction(KeyStroke key) {
4181             Action a = bindings.get(key);
4182             if ((a == null) && (parent != null)) {
4183                 a = parent.getAction(key);
4184             }
4185             return a;
4186         }
4187 
4188         public KeyStroke[] getBoundKeyStrokes() {
4189             KeyStroke[] keys = new KeyStroke[bindings.size()];
4190             int i = 0;
4191             for (Enumeration<KeyStroke> e = bindings.keys() ; e.hasMoreElements() ;) {
4192                 keys[i++] = e.nextElement();
4193             }
4194             return keys;
4195         }
4196 
4197         public Action[] getBoundActions() {
4198             Action[] actions = new Action[bindings.size()];
4199             int i = 0;
4200             for (Enumeration<Action> e = bindings.elements() ; e.hasMoreElements() ;) {
4201                 actions[i++] = e.nextElement();
4202             }
4203             return actions;
4204         }
4205 
4206         public KeyStroke[] getKeyStrokesForAction(Action a) {
4207             if (a == null) {
4208                 return null;
4209             }
4210             KeyStroke[] retValue = null;
4211             // Determine local bindings first.
4212             Vector<KeyStroke> keyStrokes = null;
4213             for (Enumeration<KeyStroke> keys = bindings.keys(); keys.hasMoreElements();) {
4214                 KeyStroke key = keys.nextElement();
4215                 if (bindings.get(key) == a) {
4216                     if (keyStrokes == null) {
4217                         keyStrokes = new Vector<KeyStroke>();
4218                     }
4219                     keyStrokes.addElement(key);
4220                 }
4221             }
4222             // See if the parent has any.
4223             if (parent != null) {
4224                 KeyStroke[] pStrokes = parent.getKeyStrokesForAction(a);
4225                 if (pStrokes != null) {
4226                     // Remove any bindings defined in the parent that
4227                     // are locally defined.
4228                     int rCount = 0;
4229                     for (int counter = pStrokes.length - 1; counter >= 0;
4230                          counter--) {
4231                         if (isLocallyDefined(pStrokes[counter])) {
4232                             pStrokes[counter] = null;
4233                             rCount++;
4234                         }
4235                     }
4236                     if (rCount > 0 && rCount < pStrokes.length) {
4237                         if (keyStrokes == null) {
4238                             keyStrokes = new Vector<KeyStroke>();
4239                         }
4240                         for (int counter = pStrokes.length - 1; counter >= 0;
4241                              counter--) {
4242                             if (pStrokes[counter] != null) {
4243                                 keyStrokes.addElement(pStrokes[counter]);
4244                             }
4245                         }
4246                     }
4247                     else if (rCount == 0) {
4248                         if (keyStrokes == null) {
4249                             retValue = pStrokes;
4250                         }
4251                         else {
4252                             retValue = new KeyStroke[keyStrokes.size() +
4253                                                     pStrokes.length];
4254                             keyStrokes.copyInto(retValue);
4255                             System.arraycopy(pStrokes, 0, retValue,
4256                                         keyStrokes.size(), pStrokes.length);
4257                             keyStrokes = null;
4258                         }
4259                     }
4260                 }
4261             }
4262             if (keyStrokes != null) {
4263                 retValue = new KeyStroke[keyStrokes.size()];
4264                 keyStrokes.copyInto(retValue);
4265             }
4266             return retValue;
4267         }
4268 
4269         public boolean isLocallyDefined(KeyStroke key) {
4270             return bindings.containsKey(key);
4271         }
4272 
4273         public void addActionForKeyStroke(KeyStroke key, Action a) {
4274             bindings.put(key, a);
4275         }
4276 
4277         public void removeKeyStrokeBinding(KeyStroke key) {
4278             bindings.remove(key);
4279         }
4280 
4281         public void removeBindings() {
4282             bindings.clear();
4283         }
4284 
4285         public Keymap getResolveParent() {
4286             return parent;
4287         }
4288 
4289         public void setResolveParent(Keymap parent) {
4290             this.parent = parent;
4291         }
4292 
4293         /**
4294          * String representation of the keymap... potentially
4295          * a very long string.
4296          */
4297         public String toString() {
4298             return "Keymap[" + nm + "]" + bindings;
4299         }
4300 
4301         String nm;
4302         Keymap parent;
4303         Hashtable<KeyStroke, Action> bindings;
4304         Action defaultAction;
4305     }
4306 
4307 
4308     /**
4309      * KeymapWrapper wraps a Keymap inside an InputMap. For KeymapWrapper
4310      * to be useful it must be used with a KeymapActionMap.
4311      * KeymapWrapper for the most part, is an InputMap with two parents.
4312      * The first parent visited is ALWAYS the Keymap, with the second
4313      * parent being the parent inherited from InputMap. If
4314      * <code>keymap.getAction</code> returns null, implying the Keymap
4315      * does not have a binding for the KeyStroke,
4316      * the parent is then visited. If the Keymap has a binding, the
4317      * Action is returned, if not and the KeyStroke represents a
4318      * KeyTyped event and the Keymap has a defaultAction,
4319      * <code>DefaultActionKey</code> is returned.
4320      * <p>KeymapActionMap is then able to transate the object passed in
4321      * to either message the Keymap, or message its default implementation.
4322      */
4323     static class KeymapWrapper extends InputMap {
4324         static final Object DefaultActionKey = new Object();
4325 
4326         private Keymap keymap;
4327 
4328         KeymapWrapper(Keymap keymap) {
4329             this.keymap = keymap;
4330         }
4331 
4332         public KeyStroke[] keys() {
4333             KeyStroke[] sKeys = super.keys();
4334             KeyStroke[] keymapKeys = keymap.getBoundKeyStrokes();
4335             int sCount = (sKeys == null) ? 0 : sKeys.length;
4336             int keymapCount = (keymapKeys == null) ? 0 : keymapKeys.length;
4337             if (sCount == 0) {
4338                 return keymapKeys;
4339             }
4340             if (keymapCount == 0) {
4341                 return sKeys;
4342             }
4343             KeyStroke[] retValue = new KeyStroke[sCount + keymapCount];
4344             // There may be some duplication here...
4345             System.arraycopy(sKeys, 0, retValue, 0, sCount);
4346             System.arraycopy(keymapKeys, 0, retValue, sCount, keymapCount);
4347             return retValue;
4348         }
4349 
4350         public int size() {
4351             // There may be some duplication here...
4352             KeyStroke[] keymapStrokes = keymap.getBoundKeyStrokes();
4353             int keymapCount = (keymapStrokes == null) ? 0:
4354                                keymapStrokes.length;
4355             return super.size() + keymapCount;
4356         }
4357 
4358         public Object get(KeyStroke keyStroke) {
4359             Object retValue = keymap.getAction(keyStroke);
4360             if (retValue == null) {
4361                 retValue = super.get(keyStroke);
4362                 if (retValue == null &&
4363                     keyStroke.getKeyChar() != KeyEvent.CHAR_UNDEFINED &&
4364                     keymap.getDefaultAction() != null) {
4365                     // Implies this is a KeyTyped event, use the default
4366                     // action.
4367                     retValue = DefaultActionKey;
4368                 }
4369             }
4370             return retValue;
4371         }
4372     }
4373 
4374 
4375     /**
4376      * Wraps a Keymap inside an ActionMap. This is used with
4377      * a KeymapWrapper. If <code>get</code> is passed in
4378      * <code>KeymapWrapper.DefaultActionKey</code>, the default action is
4379      * returned, otherwise if the key is an Action, it is returned.
4380      */
4381     static class KeymapActionMap extends ActionMap {
4382         private Keymap keymap;
4383 
4384         KeymapActionMap(Keymap keymap) {
4385             this.keymap = keymap;
4386         }
4387 
4388         public Object[] keys() {
4389             Object[] sKeys = super.keys();
4390             Object[] keymapKeys = keymap.getBoundActions();
4391             int sCount = (sKeys == null) ? 0 : sKeys.length;
4392             int keymapCount = (keymapKeys == null) ? 0 : keymapKeys.length;
4393             boolean hasDefault = (keymap.getDefaultAction() != null);
4394             if (hasDefault) {
4395                 keymapCount++;
4396             }
4397             if (sCount == 0) {
4398                 if (hasDefault) {
4399                     Object[] retValue = new Object[keymapCount];
4400                     if (keymapCount > 1) {
4401                         System.arraycopy(keymapKeys, 0, retValue, 0,
4402                                          keymapCount - 1);
4403                     }
4404                     retValue[keymapCount - 1] = KeymapWrapper.DefaultActionKey;
4405                     return retValue;
4406                 }
4407                 return keymapKeys;
4408             }
4409             if (keymapCount == 0) {
4410                 return sKeys;
4411             }
4412             Object[] retValue = new Object[sCount + keymapCount];
4413             // There may be some duplication here...
4414             System.arraycopy(sKeys, 0, retValue, 0, sCount);
4415             if (hasDefault) {
4416                 if (keymapCount > 1) {
4417                     System.arraycopy(keymapKeys, 0, retValue, sCount,
4418                                      keymapCount - 1);
4419                 }
4420                 retValue[sCount + keymapCount - 1] = KeymapWrapper.
4421                                                  DefaultActionKey;
4422             }
4423             else {
4424                 System.arraycopy(keymapKeys, 0, retValue, sCount, keymapCount);
4425             }
4426             return retValue;
4427         }
4428 
4429         public int size() {
4430             // There may be some duplication here...
4431             Object[] actions = keymap.getBoundActions();
4432             int keymapCount = (actions == null) ? 0 : actions.length;
4433             if (keymap.getDefaultAction() != null) {
4434                 keymapCount++;
4435             }
4436             return super.size() + keymapCount;
4437         }
4438 
4439         public Action get(Object key) {
4440             Action retValue = super.get(key);
4441             if (retValue == null) {
4442                 // Try the Keymap.
4443                 if (key == KeymapWrapper.DefaultActionKey) {
4444                     retValue = keymap.getDefaultAction();
4445                 }
4446                 else if (key instanceof Action) {
4447                     // This is a little iffy, technically an Action is
4448                     // a valid Key. We're assuming the Action came from
4449                     // the InputMap though.
4450                     retValue = (Action)key;
4451                 }
4452             }
4453             return retValue;
4454         }
4455     }
4456 
4457     private static final Object FOCUSED_COMPONENT =
4458         new StringBuilder("JTextComponent_FocusedComponent");
4459 
4460     /**
4461      * The default keymap that will be shared by all
4462      * <code>JTextComponent</code> instances unless they
4463      * have had a different keymap set.
4464      */
4465     public static final String DEFAULT_KEYMAP = "default";
4466 
4467     /**
4468      * Event to use when firing a notification of change to caret
4469      * position.  This is mutable so that the event can be reused
4470      * since caret events can be fairly high in bandwidth.
4471      */
4472     static class MutableCaretEvent extends CaretEvent implements ChangeListener, FocusListener, MouseListener {
4473 
4474         MutableCaretEvent(JTextComponent c) {
4475             super(c);
4476         }
4477 
4478         final void fire() {
4479             JTextComponent c = (JTextComponent) getSource();
4480             if (c != null) {
4481                 Caret caret = c.getCaret();
4482                 dot = caret.getDot();
4483                 mark = caret.getMark();
4484                 c.fireCaretUpdate(this);
4485             }
4486         }
4487 
4488         public final String toString() {
4489             return "dot=" + dot + "," + "mark=" + mark;
4490         }
4491 
4492         // --- CaretEvent methods -----------------------
4493 
4494         public final int getDot() {
4495             return dot;
4496         }
4497 
4498         public final int getMark() {
4499             return mark;
4500         }
4501 
4502         // --- ChangeListener methods -------------------
4503 
4504         public final void stateChanged(ChangeEvent e) {
4505             if (! dragActive) {
4506                 fire();
4507             }
4508         }
4509 
4510         // --- FocusListener methods -----------------------------------
4511         public void focusGained(FocusEvent fe) {
4512             AppContext.getAppContext().put(FOCUSED_COMPONENT,
4513                                            fe.getSource());
4514         }
4515 
4516         public void focusLost(FocusEvent fe) {
4517         }
4518 
4519         // --- MouseListener methods -----------------------------------
4520 
4521         /**
4522          * Requests focus on the associated
4523          * text component, and try to set the cursor position.
4524          *
4525          * @param e the mouse event
4526          * @see MouseListener#mousePressed
4527          */
4528         public final void mousePressed(MouseEvent e) {
4529             dragActive = true;
4530         }
4531 
4532         /**
4533          * Called when the mouse is released.
4534          *
4535          * @param e the mouse event
4536          * @see MouseListener#mouseReleased
4537          */
4538         public final void mouseReleased(MouseEvent e) {
4539             dragActive = false;
4540             fire();
4541         }
4542 
4543         public final void mouseClicked(MouseEvent e) {
4544         }
4545 
4546         public final void mouseEntered(MouseEvent e) {
4547         }
4548 
4549         public final void mouseExited(MouseEvent e) {
4550         }
4551 
4552         private boolean dragActive;
4553         private int dot;
4554         private int mark;
4555     }
4556 
4557     //
4558     // Process any input method events that the component itself
4559     // recognizes. The default on-the-spot handling for input method
4560     // composed(uncommitted) text is done here after all input
4561     // method listeners get called for stealing the events.
4562     //
4563     @SuppressWarnings("fallthrough")
4564     protected void processInputMethodEvent(InputMethodEvent e) {
4565         // let listeners handle the events
4566         super.processInputMethodEvent(e);
4567 
4568         if (!e.isConsumed()) {
4569             if (! isEditable()) {
4570                 return;
4571             } else {
4572                 switch (e.getID()) {
4573                 case InputMethodEvent.INPUT_METHOD_TEXT_CHANGED:
4574                     replaceInputMethodText(e);
4575 
4576                     // fall through
4577 
4578                 case InputMethodEvent.CARET_POSITION_CHANGED:
4579                     setInputMethodCaretPosition(e);
4580                     break;
4581                 }
4582             }
4583 
4584             e.consume();
4585         }
4586     }
4587 
4588     //
4589     // Overrides this method to become an active input method client.
4590     //
4591     @BeanProperty(bound = false)
4592     public InputMethodRequests getInputMethodRequests() {
4593         if (inputMethodRequestsHandler == null) {
4594             inputMethodRequestsHandler = new InputMethodRequestsHandler();
4595             Document doc = getDocument();
4596             if (doc != null) {
4597                 doc.addDocumentListener((DocumentListener)inputMethodRequestsHandler);
4598             }
4599         }
4600 
4601         return inputMethodRequestsHandler;
4602     }
4603 
4604     //
4605     // Overrides this method to watch the listener installed.
4606     //
4607     public void addInputMethodListener(InputMethodListener l) {
4608         super.addInputMethodListener(l);
4609         if (l != null) {
4610             needToSendKeyTypedEvent = false;
4611             checkedInputOverride = true;
4612         }
4613     }
4614 
4615 
4616     //
4617     // Default implementation of the InputMethodRequests interface.
4618     //
4619     class InputMethodRequestsHandler implements InputMethodRequests, DocumentListener {
4620 
4621         // --- InputMethodRequests methods ---
4622 
4623         public AttributedCharacterIterator cancelLatestCommittedText(
4624                                                 Attribute[] attributes) {
4625             Document doc = getDocument();
4626             if ((doc != null) && (latestCommittedTextStart != null)
4627                 && (!latestCommittedTextStart.equals(latestCommittedTextEnd))) {
4628                 try {
4629                     int startIndex = latestCommittedTextStart.getOffset();
4630                     int endIndex = latestCommittedTextEnd.getOffset();
4631                     String latestCommittedText =
4632                         doc.getText(startIndex, endIndex - startIndex);
4633                     doc.remove(startIndex, endIndex - startIndex);
4634                     return new AttributedString(latestCommittedText).getIterator();
4635                 } catch (BadLocationException ble) {}
4636             }
4637             return null;
4638         }
4639 
4640         public AttributedCharacterIterator getCommittedText(int beginIndex,
4641                                         int endIndex, Attribute[] attributes) {
4642             int composedStartIndex = 0;
4643             int composedEndIndex = 0;
4644             if (composedTextExists()) {
4645                 composedStartIndex = composedTextStart.getOffset();
4646                 composedEndIndex = composedTextEnd.getOffset();
4647             }
4648 
4649             String committed;
4650             try {
4651                 if (beginIndex < composedStartIndex) {
4652                     if (endIndex <= composedStartIndex) {
4653                         committed = getText(beginIndex, endIndex - beginIndex);
4654                     } else {
4655                         int firstPartLength = composedStartIndex - beginIndex;
4656                         committed = getText(beginIndex, firstPartLength) +
4657                             getText(composedEndIndex, endIndex - beginIndex - firstPartLength);
4658                     }
4659                 } else {
4660                     committed = getText(beginIndex + (composedEndIndex - composedStartIndex),
4661                                         endIndex - beginIndex);
4662                 }
4663             } catch (BadLocationException ble) {
4664                 throw new IllegalArgumentException("Invalid range");
4665             }
4666             return new AttributedString(committed).getIterator();
4667         }
4668 
4669         public int getCommittedTextLength() {
4670             Document doc = getDocument();
4671             int length = 0;
4672             if (doc != null) {
4673                 length = doc.getLength();
4674                 if (composedTextContent != null) {
4675                     if (composedTextEnd == null
4676                           || composedTextStart == null) {
4677                         /*
4678                          * fix for : 6355666
4679                          * this is the case when this method is invoked
4680                          * from DocumentListener. At this point
4681                          * composedTextEnd and composedTextStart are
4682                          * not defined yet.
4683                          */
4684                         length -= composedTextContent.length();
4685                     } else {
4686                         length -= composedTextEnd.getOffset() -
4687                             composedTextStart.getOffset();
4688                     }
4689                 }
4690             }
4691             return length;
4692         }
4693 
4694         public int getInsertPositionOffset() {
4695             int composedStartIndex = 0;
4696             int composedEndIndex = 0;
4697             if (composedTextExists()) {
4698                 composedStartIndex = composedTextStart.getOffset();
4699                 composedEndIndex = composedTextEnd.getOffset();
4700             }
4701             int caretIndex = getCaretPosition();
4702 
4703             if (caretIndex < composedStartIndex) {
4704                 return caretIndex;
4705             } else if (caretIndex < composedEndIndex) {
4706                 return composedStartIndex;
4707             } else {
4708                 return caretIndex - (composedEndIndex - composedStartIndex);
4709             }
4710         }
4711 
4712         public TextHitInfo getLocationOffset(int x, int y) {
4713             if (composedTextAttribute == null) {
4714                 return null;
4715             } else {
4716                 Point p = getLocationOnScreen();
4717                 p.x = x - p.x;
4718                 p.y = y - p.y;
4719                 int pos = viewToModel(p);
4720                 if ((pos >= composedTextStart.getOffset()) &&
4721                     (pos <= composedTextEnd.getOffset())) {
4722                     return TextHitInfo.leading(pos - composedTextStart.getOffset());
4723                 } else {
4724                     return null;
4725                 }
4726             }
4727         }
4728 
4729         public Rectangle getTextLocation(TextHitInfo offset) {
4730             Rectangle r;
4731 
4732             try {
4733                 r = modelToView(getCaretPosition());
4734                 if (r != null) {
4735                     Point p = getLocationOnScreen();
4736                     r.translate(p.x, p.y);
4737                 }
4738             } catch (BadLocationException ble) {
4739                 r = null;
4740             }
4741 
4742             if (r == null)
4743                 r = new Rectangle();
4744 
4745             return r;
4746         }
4747 
4748         public AttributedCharacterIterator getSelectedText(
4749                                                 Attribute[] attributes) {
4750             String selection = JTextComponent.this.getSelectedText();
4751             if (selection != null) {
4752                 return new AttributedString(selection).getIterator();
4753             } else {
4754                 return null;
4755             }
4756         }
4757 
4758         // --- DocumentListener methods ---
4759 
4760         public void changedUpdate(DocumentEvent e) {
4761             latestCommittedTextStart = latestCommittedTextEnd = null;
4762         }
4763 
4764         public void insertUpdate(DocumentEvent e) {
4765             latestCommittedTextStart = latestCommittedTextEnd = null;
4766         }
4767 
4768         public void removeUpdate(DocumentEvent e) {
4769             latestCommittedTextStart = latestCommittedTextEnd = null;
4770         }
4771     }
4772 
4773     //
4774     // Replaces the current input method (composed) text according to
4775     // the passed input method event. This method also inserts the
4776     // committed text into the document.
4777     //
4778     private void replaceInputMethodText(InputMethodEvent e) {
4779         int commitCount = e.getCommittedCharacterCount();
4780         AttributedCharacterIterator text = e.getText();
4781         int composedTextIndex;
4782 
4783         // old composed text deletion
4784         Document doc = getDocument();
4785         if (composedTextExists()) {
4786             try {
4787                 doc.remove(composedTextStart.getOffset(),
4788                            composedTextEnd.getOffset() -
4789                            composedTextStart.getOffset());
4790             } catch (BadLocationException ble) {}
4791             composedTextStart = composedTextEnd = null;
4792             composedTextAttribute = null;
4793             composedTextContent = null;
4794         }
4795 
4796         if (text != null) {
4797             text.first();
4798             int committedTextStartIndex = 0;
4799             int committedTextEndIndex = 0;
4800 
4801             // committed text insertion
4802             if (commitCount > 0) {
4803                 // Remember latest committed text start index
4804                 committedTextStartIndex = caret.getDot();
4805 
4806                 // Need to generate KeyTyped events for the committed text for components
4807                 // that are not aware they are active input method clients.
4808                 if (shouldSynthensizeKeyEvents()) {
4809                     for (char c = text.current(); commitCount > 0;
4810                          c = text.next(), commitCount--) {
4811                         KeyEvent ke = new KeyEvent(this, KeyEvent.KEY_TYPED,
4812                                                    EventQueue.getMostRecentEventTime(),
4813                                                    0, KeyEvent.VK_UNDEFINED, c);
4814                         processKeyEvent(ke);
4815                     }
4816                 } else {
4817                     StringBuilder strBuf = new StringBuilder();
4818                     for (char c = text.current(); commitCount > 0;
4819                          c = text.next(), commitCount--) {
4820                         strBuf.append(c);
4821                     }
4822 
4823                     // map it to an ActionEvent
4824                     mapCommittedTextToAction(strBuf.toString());
4825                 }
4826 
4827                 // Remember latest committed text end index
4828                 committedTextEndIndex = caret.getDot();
4829             }
4830 
4831             // new composed text insertion
4832             composedTextIndex = text.getIndex();
4833             if (composedTextIndex < text.getEndIndex()) {
4834                 createComposedTextAttribute(composedTextIndex, text);
4835                 try {
4836                     replaceSelection(null);
4837                     doc.insertString(caret.getDot(), composedTextContent,
4838                                         composedTextAttribute);
4839                     composedTextStart = doc.createPosition(caret.getDot() -
4840                                                 composedTextContent.length());
4841                     composedTextEnd = doc.createPosition(caret.getDot());
4842                 } catch (BadLocationException ble) {
4843                     composedTextStart = composedTextEnd = null;
4844                     composedTextAttribute = null;
4845                     composedTextContent = null;
4846                 }
4847             }
4848 
4849             // Save the latest committed text information
4850             if (committedTextStartIndex != committedTextEndIndex) {
4851                 try {
4852                     latestCommittedTextStart = doc.
4853                         createPosition(committedTextStartIndex);
4854                     latestCommittedTextEnd = doc.
4855                         createPosition(committedTextEndIndex);
4856                 } catch (BadLocationException ble) {
4857                     latestCommittedTextStart =
4858                         latestCommittedTextEnd = null;
4859                 }
4860             } else {
4861                 latestCommittedTextStart =
4862                     latestCommittedTextEnd = null;
4863             }
4864         }
4865     }
4866 
4867     private void createComposedTextAttribute(int composedIndex,
4868                                         AttributedCharacterIterator text) {
4869         Document doc = getDocument();
4870         StringBuilder strBuf = new StringBuilder();
4871 
4872         // create attributed string with no attributes
4873         for (char c = text.setIndex(composedIndex);
4874              c != CharacterIterator.DONE; c = text.next()) {
4875             strBuf.append(c);
4876         }
4877 
4878         composedTextContent = strBuf.toString();
4879         composedTextAttribute = new SimpleAttributeSet();
4880         composedTextAttribute.addAttribute(StyleConstants.ComposedTextAttribute,
4881                 new AttributedString(text, composedIndex, text.getEndIndex()));
4882     }
4883 
4884     /**
4885      * Saves composed text around the specified position.
4886      *
4887      * The composed text (if any) around the specified position is saved
4888      * in a backing store and removed from the document.
4889      *
4890      * @param pos  document position to identify the composed text location
4891      * @return  {@code true} if the composed text exists and is saved,
4892      *          {@code false} otherwise
4893      * @see #restoreComposedText
4894      * @since 1.7
4895      */
4896     protected boolean saveComposedText(int pos) {
4897         if (composedTextExists()) {
4898             int start = composedTextStart.getOffset();
4899             int len = composedTextEnd.getOffset() -
4900                 composedTextStart.getOffset();
4901             if (pos >= start && pos <= start + len) {
4902                 try {
4903                     getDocument().remove(start, len);
4904                     return true;
4905                 } catch (BadLocationException ble) {}
4906             }
4907         }
4908         return false;
4909     }
4910 
4911     /**
4912      * Restores composed text previously saved by {@code saveComposedText}.
4913      *
4914      * The saved composed text is inserted back into the document. This method
4915      * should be invoked only if {@code saveComposedText} returns {@code true}.
4916      *
4917      * @see #saveComposedText
4918      * @since 1.7
4919      */
4920     protected void restoreComposedText() {
4921         Document doc = getDocument();
4922         try {
4923             doc.insertString(caret.getDot(),
4924                              composedTextContent,
4925                              composedTextAttribute);
4926             composedTextStart = doc.createPosition(caret.getDot() -
4927                                 composedTextContent.length());
4928             composedTextEnd = doc.createPosition(caret.getDot());
4929         } catch (BadLocationException ble) {}
4930     }
4931 
4932     //
4933     // Map committed text to an ActionEvent. If the committed text length is 1,
4934     // treat it as a KeyStroke, otherwise or there is no KeyStroke defined,
4935     // treat it just as a default action.
4936     //
4937     private void mapCommittedTextToAction(String committedText) {
4938         Keymap binding = getKeymap();
4939         if (binding != null) {
4940             Action a = null;
4941             if (committedText.length() == 1) {
4942                 KeyStroke k = KeyStroke.getKeyStroke(committedText.charAt(0));
4943                 a = binding.getAction(k);
4944             }
4945 
4946             if (a == null) {
4947                 a = binding.getDefaultAction();
4948             }
4949 
4950             if (a != null) {
4951                 ActionEvent ae =
4952                     new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
4953                                     committedText,
4954                                     EventQueue.getMostRecentEventTime(),
4955                                     getCurrentEventModifiers());
4956                 a.actionPerformed(ae);
4957             }
4958         }
4959     }
4960 
4961     //
4962     // Sets the caret position according to the passed input method
4963     // event. Also, sets/resets composed text caret appropriately.
4964     //
4965     private void setInputMethodCaretPosition(InputMethodEvent e) {
4966         int dot;
4967 
4968         if (composedTextExists()) {
4969             dot = composedTextStart.getOffset();
4970             if (!(caret instanceof ComposedTextCaret)) {
4971                 if (composedTextCaret == null) {
4972                     composedTextCaret = new ComposedTextCaret();
4973                 }
4974                 originalCaret = caret;
4975                 // Sets composed text caret
4976                 exchangeCaret(originalCaret, composedTextCaret);
4977             }
4978 
4979             TextHitInfo caretPos = e.getCaret();
4980             if (caretPos != null) {
4981                 int index = caretPos.getInsertionIndex();
4982                 dot += index;
4983                 if (index == 0) {
4984                     // Scroll the component if needed so that the composed text
4985                     // becomes visible.
4986                     try {
4987                         Rectangle d = modelToView(dot);
4988                         Rectangle end = modelToView(composedTextEnd.getOffset());
4989                         Rectangle b = getBounds();
4990                         d.x += Math.min(end.x - d.x, b.width);
4991                         scrollRectToVisible(d);
4992                     } catch (BadLocationException ble) {}
4993                 }
4994             }
4995             caret.setDot(dot);
4996         } else if (caret instanceof ComposedTextCaret) {
4997             dot = caret.getDot();
4998             // Restores original caret
4999             exchangeCaret(caret, originalCaret);
5000             caret.setDot(dot);
5001         }
5002     }
5003 
5004     private void exchangeCaret(Caret oldCaret, Caret newCaret) {
5005         int blinkRate = oldCaret.getBlinkRate();
5006         setCaret(newCaret);
5007         caret.setBlinkRate(blinkRate);
5008         caret.setVisible(hasFocus());
5009     }
5010 
5011     /**
5012      * Returns true if KeyEvents should be synthesized from an InputEvent.
5013      */
5014     private boolean shouldSynthensizeKeyEvents() {
5015         if (!checkedInputOverride) {
5016             // Checks whether the client code overrides processInputMethodEvent.
5017             // If it is overridden, need not to generate KeyTyped events for committed text.
5018             // If it's not, behave as an passive input method client.
5019             needToSendKeyTypedEvent = !METHOD_OVERRIDDEN.get(getClass());
5020             checkedInputOverride = true;
5021         }
5022         return needToSendKeyTypedEvent;
5023     }
5024 
5025     //
5026     // Checks whether a composed text in this text component
5027     //
5028     boolean composedTextExists() {
5029         return (composedTextStart != null);
5030     }
5031 
5032     //
5033     // Caret implementation for editing the composed text.
5034     //
5035     class ComposedTextCaret extends DefaultCaret implements Serializable {
5036         Color bg;
5037 
5038         //
5039         // Get the background color of the component
5040         //
5041         public void install(JTextComponent c) {
5042             super.install(c);
5043 
5044             Document doc = c.getDocument();
5045             if (doc instanceof StyledDocument) {
5046                 StyledDocument sDoc = (StyledDocument)doc;
5047                 Element elem = sDoc.getCharacterElement(c.composedTextStart.getOffset());
5048                 AttributeSet attr = elem.getAttributes();
5049                 bg = sDoc.getBackground(attr);
5050             }
5051 
5052             if (bg == null) {
5053                 bg = c.getBackground();
5054             }
5055         }
5056 
5057         //
5058         // Draw caret in XOR mode.
5059         //
5060         public void paint(Graphics g) {
5061             if(isVisible()) {
5062                 try {
5063                     Rectangle r = component.modelToView(getDot());
5064                     g.setXORMode(bg);
5065                     g.drawLine(r.x, r.y, r.x, r.y + r.height - 1);
5066                     g.setPaintMode();
5067                 } catch (BadLocationException e) {
5068                     // can't render I guess
5069                     //System.err.println("Can't render cursor");
5070                 }
5071             }
5072         }
5073 
5074         //
5075         // If some area other than the composed text is clicked by mouse,
5076         // issue endComposition() to force commit the composed text.
5077         //
5078         protected void positionCaret(MouseEvent me) {
5079             JTextComponent host = component;
5080             Point pt = new Point(me.getX(), me.getY());
5081             int offset = host.viewToModel(pt);
5082             int composedStartIndex = host.composedTextStart.getOffset();
5083             if ((offset < composedStartIndex) ||
5084                 (offset > composedTextEnd.getOffset())) {
5085                 try {
5086                     // Issue endComposition
5087                     Position newPos = host.getDocument().createPosition(offset);
5088                     host.getInputContext().endComposition();
5089 
5090                     // Post a caret positioning runnable to assure that the positioning
5091                     // occurs *after* committing the composed text.
5092                     EventQueue.invokeLater(new DoSetCaretPosition(host, newPos));
5093                 } catch (BadLocationException ble) {
5094                     System.err.println(ble);
5095                 }
5096             } else {
5097                 // Normal processing
5098                 super.positionCaret(me);
5099             }
5100         }
5101     }
5102 
5103     //
5104     // Runnable class for invokeLater() to set caret position later.
5105     //
5106     private class DoSetCaretPosition implements Runnable {
5107         JTextComponent host;
5108         Position newPos;
5109 
5110         DoSetCaretPosition(JTextComponent host, Position newPos) {
5111             this.host = host;
5112             this.newPos = newPos;
5113         }
5114 
5115         public void run() {
5116             host.setCaretPosition(newPos.getOffset());
5117         }
5118     }
5119 }