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