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