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