/* * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.awt; import java.awt.peer.TextComponentPeer; import java.awt.event.*; import java.util.EventListener; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.IOException; import sun.awt.InputMethodSupport; import java.text.BreakIterator; import javax.swing.text.AttributeSet; import javax.accessibility.*; import java.awt.im.InputMethodRequests; /** * The TextComponent class is the superclass of * any component that allows the editing of some text. *

* A text component embodies a string of text. The * TextComponent class defines a set of methods * that determine whether or not this text is editable. If the * component is editable, it defines another set of methods * that supports a text insertion caret. *

* In addition, the class defines methods that are used * to maintain a current selection from the text. * The text selection, a substring of the component's text, * is the target of editing operations. It is also referred * to as the selected text. * * @author Sami Shaio * @author Arthur van Hoff * @since JDK1.0 */ public class TextComponent extends Component implements Accessible { /** * The value of the text. * A null value is the same as "". * * @serial * @see #setText(String) * @see #getText() */ String text; /** * A boolean indicating whether or not this * TextComponent is editable. * It will be true if the text component * is editable and false if not. * * @serial * @see #isEditable() */ boolean editable = true; /** * The selection refers to the selected text, and the * selectionStart is the start position * of the selected text. * * @serial * @see #getSelectionStart() * @see #setSelectionStart(int) */ int selectionStart; /** * The selection refers to the selected text, and the * selectionEnd * is the end position of the selected text. * * @serial * @see #getSelectionEnd() * @see #setSelectionEnd(int) */ int selectionEnd; // A flag used to tell whether the background has been set by // developer code (as opposed to AWT code). Used to determine // the background color of non-editable TextComponents. boolean backgroundSetByClientCode = false; transient protected TextListener textListener; /* * JDK 1.1 serialVersionUID */ private static final long serialVersionUID = -2214773872412987419L; /** * Constructs a new text component initialized with the * specified text. Sets the value of the cursor to * Cursor.TEXT_CURSOR. * @param text the text to be displayed; if * text is null, the empty * string "" will be displayed * @exception HeadlessException if * GraphicsEnvironment.isHeadless * returns true * @see java.awt.GraphicsEnvironment#isHeadless * @see java.awt.Cursor */ TextComponent(String text) throws HeadlessException { GraphicsEnvironment.checkHeadless(); this.text = (text != null) ? text : ""; setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); } private void enableInputMethodsIfNecessary() { if (checkForEnableIM) { checkForEnableIM = false; try { Toolkit toolkit = Toolkit.getDefaultToolkit(); boolean shouldEnable = false; if (toolkit instanceof InputMethodSupport) { shouldEnable = ((InputMethodSupport)toolkit) .enableInputMethodsForTextComponent(); } enableInputMethods(shouldEnable); } catch (Exception e) { // if something bad happens, just don't enable input methods } } } /** * Enables or disables input method support for this text component. If input * method support is enabled and the text component also processes key events, * incoming events are offered to the current input method and will only be * processed by the component or dispatched to its listeners if the input method * does not consume them. Whether and how input method support for this text * component is enabled or disabled by default is implementation dependent. * * @param enable true to enable, false to disable * @see #processKeyEvent * @since 1.2 */ public void enableInputMethods(boolean enable) { checkForEnableIM = false; super.enableInputMethods(enable); } boolean areInputMethodsEnabled() { // moved from the constructor above to here and addNotify below, // this call will initialize the toolkit if not already initialized. if (checkForEnableIM) { enableInputMethodsIfNecessary(); } // TextComponent handles key events without touching the eventMask or // having a key listener, so just check whether the flag is set return (eventMask & AWTEvent.INPUT_METHODS_ENABLED_MASK) != 0; } public InputMethodRequests getInputMethodRequests() { TextComponentPeer peer = (TextComponentPeer)this.peer; if (peer != null) return peer.getInputMethodRequests(); else return null; } /** * Makes this Component displayable by connecting it to a * native screen resource. * This method is called internally by the toolkit and should * not be called directly by programs. * @see java.awt.TextComponent#removeNotify */ public void addNotify() { super.addNotify(); enableInputMethodsIfNecessary(); } /** * Removes the TextComponent's peer. * The peer allows us to modify the appearance of the * TextComponent without changing its * functionality. */ public void removeNotify() { synchronized (getTreeLock()) { TextComponentPeer peer = (TextComponentPeer)this.peer; if (peer != null) { text = peer.getText(); selectionStart = peer.getSelectionStart(); selectionEnd = peer.getSelectionEnd(); } super.removeNotify(); } } /** * Sets the text that is presented by this * text component to be the specified text. * @param t the new text; * if this parameter is null then * the text is set to the empty string "" * @see java.awt.TextComponent#getText */ public synchronized void setText(String t) { boolean skipTextEvent = (text == null || text.isEmpty()) && (t == null || t.isEmpty()); text = (t != null) ? t : ""; TextComponentPeer peer = (TextComponentPeer)this.peer; // Please note that we do not want to post an event // if TextArea.setText() or TextField.setText() replaces an empty text // by an empty text, that is, if component's text remains unchanged. if (peer != null && !skipTextEvent) { peer.setText(text); } } /** * Returns the text that is presented by this text component. * By default, this is an empty string. * * @return the value of this TextComponent * @see java.awt.TextComponent#setText */ public synchronized String getText() { TextComponentPeer peer = (TextComponentPeer)this.peer; if (peer != null) { text = peer.getText(); } return text; } /** * Returns the selected text from the text that is * presented by this text component. * @return the selected text of this text component * @see java.awt.TextComponent#select */ public synchronized String getSelectedText() { return getText().substring(getSelectionStart(), getSelectionEnd()); } /** * Indicates whether or not this text component is editable. * @return true if this text component is * editable; false otherwise. * @see java.awt.TextComponent#setEditable * @since JDK1.0 */ public boolean isEditable() { return editable; } /** * Sets the flag that determines whether or not this * text component is editable. *

* If the flag is set to true, this text component * becomes user editable. If the flag is set to false, * the user cannot change the text of this text component. * By default, non-editable text components have a background color * of SystemColor.control. This default can be overridden by * calling setBackground. * * @param b a flag indicating whether this text component * is user editable. * @see java.awt.TextComponent#isEditable * @since JDK1.0 */ public synchronized void setEditable(boolean b) { if (editable == b) { return; } editable = b; TextComponentPeer peer = (TextComponentPeer)this.peer; if (peer != null) { peer.setEditable(b); } } /** * Gets the background color of this text component. * * By default, non-editable text components have a background color * of SystemColor.control. This default can be overridden by * calling setBackground. * * @return This text component's background color. * If this text component does not have a background color, * the background color of its parent is returned. * @see #setBackground(Color) * @since JDK1.0 */ public Color getBackground() { if (!editable && !backgroundSetByClientCode) { return SystemColor.control; } return super.getBackground(); } /** * Sets the background color of this text component. * * @param c The color to become this text component's color. * If this parameter is null then this text component * will inherit the background color of its parent. * @see #getBackground() * @since JDK1.0 */ public void setBackground(Color c) { backgroundSetByClientCode = true; super.setBackground(c); } /** * Gets the start position of the selected text in * this text component. * @return the start position of the selected text * @see java.awt.TextComponent#setSelectionStart * @see java.awt.TextComponent#getSelectionEnd */ public synchronized int getSelectionStart() { TextComponentPeer peer = (TextComponentPeer)this.peer; if (peer != null) { selectionStart = peer.getSelectionStart(); } return selectionStart; } /** * Sets the selection start for this text component to * the specified position. The new start point is constrained * to be at or before the current selection end. It also * cannot be set to less than zero, the beginning of the * component's text. * If the caller supplies a value for selectionStart * that is out of bounds, the method enforces these constraints * silently, and without failure. * @param selectionStart the start position of the * selected text * @see java.awt.TextComponent#getSelectionStart * @see java.awt.TextComponent#setSelectionEnd * @since JDK1.1 */ public synchronized void setSelectionStart(int selectionStart) { /* Route through select method to enforce consistent policy * between selectionStart and selectionEnd. */ select(selectionStart, getSelectionEnd()); } /** * Gets the end position of the selected text in * this text component. * @return the end position of the selected text * @see java.awt.TextComponent#setSelectionEnd * @see java.awt.TextComponent#getSelectionStart */ public synchronized int getSelectionEnd() { TextComponentPeer peer = (TextComponentPeer)this.peer; if (peer != null) { selectionEnd = peer.getSelectionEnd(); } return selectionEnd; } /** * Sets the selection end for this text component to * the specified position. The new end point is constrained * to be at or after the current selection start. It also * cannot be set beyond the end of the component's text. * If the caller supplies a value for selectionEnd * that is out of bounds, the method enforces these constraints * silently, and without failure. * @param selectionEnd the end position of the * selected text * @see java.awt.TextComponent#getSelectionEnd * @see java.awt.TextComponent#setSelectionStart * @since JDK1.1 */ public synchronized void setSelectionEnd(int selectionEnd) { /* Route through select method to enforce consistent policy * between selectionStart and selectionEnd. */ select(getSelectionStart(), selectionEnd); } /** * Selects the text between the specified start and end positions. *

* This method sets the start and end positions of the * selected text, enforcing the restriction that the start position * must be greater than or equal to zero. The end position must be * greater than or equal to the start position, and less than or * equal to the length of the text component's text. The * character positions are indexed starting with zero. * The length of the selection is * endPosition - startPosition, so the * character at endPosition is not selected. * If the start and end positions of the selected text are equal, * all text is deselected. *

* If the caller supplies values that are inconsistent or out of * bounds, the method enforces these constraints silently, and * without failure. Specifically, if the start position or end * position is greater than the length of the text, it is reset to * equal the text length. If the start position is less than zero, * it is reset to zero, and if the end position is less than the * start position, it is reset to the start position. * * @param selectionStart the zero-based index of the first character (char value) to be selected * @param selectionEnd the zero-based end position of the text to be selected; the character (char value) at selectionEnd is not selected * @see java.awt.TextComponent#setSelectionStart * @see java.awt.TextComponent#setSelectionEnd * @see java.awt.TextComponent#selectAll */ public synchronized void select(int selectionStart, int selectionEnd) { String text = getText(); if (selectionStart < 0) { selectionStart = 0; } if (selectionStart > text.length()) { selectionStart = text.length(); } if (selectionEnd > text.length()) { selectionEnd = text.length(); } if (selectionEnd < selectionStart) { selectionEnd = selectionStart; } this.selectionStart = selectionStart; this.selectionEnd = selectionEnd; TextComponentPeer peer = (TextComponentPeer)this.peer; if (peer != null) { peer.select(selectionStart, selectionEnd); } } /** * Selects all the text in this text component. * @see java.awt.TextComponent#select */ public synchronized void selectAll() { this.selectionStart = 0; this.selectionEnd = getText().length(); TextComponentPeer peer = (TextComponentPeer)this.peer; if (peer != null) { peer.select(selectionStart, selectionEnd); } } /** * Sets the position of the text insertion caret. * The caret position is constrained to be between 0 * and the last character of the text, inclusive. * If the passed-in value is greater than this range, * the value is set to the last character (or 0 if * the TextComponent contains no text) * and no error is returned. If the passed-in value is * less than 0, an IllegalArgumentException * is thrown. * * @param position the position of the text insertion caret * @exception IllegalArgumentException if position * is less than zero * @since JDK1.1 */ public synchronized void setCaretPosition(int position) { if (position < 0) { throw new IllegalArgumentException("position less than zero."); } int maxposition = getText().length(); if (position > maxposition) { position = maxposition; } TextComponentPeer peer = (TextComponentPeer)this.peer; if (peer != null) { peer.setCaretPosition(position); } else { select(position, position); } } /** * Returns the position of the text insertion caret. * The caret position is constrained to be between 0 * and the last character of the text, inclusive. * If the text or caret have not been set, the default * caret position is 0. * * @return the position of the text insertion caret * @see #setCaretPosition(int) * @since JDK1.1 */ public synchronized int getCaretPosition() { TextComponentPeer peer = (TextComponentPeer)this.peer; int position = 0; if (peer != null) { position = peer.getCaretPosition(); } else { position = selectionStart; } int maxposition = getText().length(); if (position > maxposition) { position = maxposition; } return position; } /** * Adds the specified text event listener to receive text events * from this text component. * If l is null, no exception is * thrown and no action is performed. *

Refer to AWT Threading Issues for details on AWT's threading model. * * @param l the text event listener * @see #removeTextListener * @see #getTextListeners * @see java.awt.event.TextListener */ public synchronized void addTextListener(TextListener l) { if (l == null) { return; } textListener = AWTEventMulticaster.add(textListener, l); newEventsOnly = true; } /** * Removes the specified text event listener so that it no longer * receives text events from this text component * If l is null, no exception is * thrown and no action is performed. *

Refer to AWT Threading Issues for details on AWT's threading model. * * @param l the text listener * @see #addTextListener * @see #getTextListeners * @see java.awt.event.TextListener * @since JDK1.1 */ public synchronized void removeTextListener(TextListener l) { if (l == null) { return; } textListener = AWTEventMulticaster.remove(textListener, l); } /** * Returns an array of all the text listeners * registered on this text component. * * @return all of this text component's TextListeners * or an empty array if no text * listeners are currently registered * * * @see #addTextListener * @see #removeTextListener * @since 1.4 */ public synchronized TextListener[] getTextListeners() { return getListeners(TextListener.class); } /** * Returns an array of all the objects currently registered * as FooListeners * upon this TextComponent. * FooListeners are registered using the * addFooListener method. * *

* You can specify the listenerType argument * with a class literal, such as * FooListener.class. * For example, you can query a * TextComponent t * for its text listeners with the following code: * *

TextListener[] tls = (TextListener[])(t.getListeners(TextListener.class));
* * If no such listeners exist, this method returns an empty array. * * @param listenerType the type of listeners requested; this parameter * should specify an interface that descends from * java.util.EventListener * @return an array of all objects registered as * FooListeners on this text component, * or an empty array if no such * listeners have been added * @exception ClassCastException if listenerType * doesn't specify a class or interface that implements * java.util.EventListener * * @see #getTextListeners * @since 1.3 */ public T[] getListeners(Class listenerType) { EventListener l = null; if (listenerType == TextListener.class) { l = textListener; } else { return super.getListeners(listenerType); } return AWTEventMulticaster.getListeners(l, listenerType); } // REMIND: remove when filtering is done at lower level boolean eventEnabled(AWTEvent e) { if (e.id == TextEvent.TEXT_VALUE_CHANGED) { if ((eventMask & AWTEvent.TEXT_EVENT_MASK) != 0 || textListener != null) { return true; } return false; } return super.eventEnabled(e); } /** * Processes events on this text component. If the event is a * TextEvent, it invokes the processTextEvent * method else it invokes its superclass's processEvent. *

Note that if the event parameter is null * the behavior is unspecified and may result in an * exception. * * @param e the event */ protected void processEvent(AWTEvent e) { if (e instanceof TextEvent) { processTextEvent((TextEvent)e); return; } super.processEvent(e); } /** * Processes text events occurring on this text component by * dispatching them to any registered TextListener objects. *

* NOTE: This method will not be called unless text events * are enabled for this component. This happens when one of the * following occurs: *

*

Note that if the event parameter is null * the behavior is unspecified and may result in an * exception. * * @param e the text event * @see Component#enableEvents */ protected void processTextEvent(TextEvent e) { TextListener listener = textListener; if (listener != null) { int id = e.getID(); switch (id) { case TextEvent.TEXT_VALUE_CHANGED: listener.textValueChanged(e); break; } } } /** * Returns a string representing the state of this * TextComponent. This * method is intended to be used only for debugging purposes, and the * content and format of the returned string may vary between * implementations. The returned string may be empty but may not be * null. * * @return the parameter string of this text component */ protected String paramString() { String str = super.paramString() + ",text=" + getText(); if (editable) { str += ",editable"; } return str + ",selection=" + getSelectionStart() + "-" + getSelectionEnd(); } /** * Assigns a valid value to the canAccessClipboard instance variable. */ private boolean canAccessClipboard() { SecurityManager sm = System.getSecurityManager(); if (sm == null) return true; try { sm.checkSystemClipboardAccess(); return true; } catch (SecurityException e) {} return false; } /* * Serialization support. */ /** * The textComponent SerializedDataVersion. * * @serial */ private int textComponentSerializedDataVersion = 1; /** * Writes default serializable fields to stream. Writes * a list of serializable TextListener(s) as optional data. * The non-serializable TextListener(s) are detected and * no attempt is made to serialize them. * * @serialData Null terminated sequence of zero or more pairs. * A pair consists of a String and Object. * The String indicates the type of object and * is one of the following : * textListenerK indicating and TextListener object. * * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener) * @see java.awt.Component#textListenerK */ private void writeObject(java.io.ObjectOutputStream s) throws IOException { // Serialization support. Since the value of the fields // selectionStart, selectionEnd, and text aren't necessarily // up to date, we sync them up with the peer before serializing. TextComponentPeer peer = (TextComponentPeer)this.peer; if (peer != null) { text = peer.getText(); selectionStart = peer.getSelectionStart(); selectionEnd = peer.getSelectionEnd(); } s.defaultWriteObject(); AWTEventMulticaster.save(s, textListenerK, textListener); s.writeObject(null); } /** * Read the ObjectInputStream, and if it isn't null, * add a listener to receive text events fired by the * TextComponent. Unrecognized keys or values will be * ignored. * * @exception HeadlessException if * GraphicsEnvironment.isHeadless() returns * true * @see #removeTextListener * @see #addTextListener * @see java.awt.GraphicsEnvironment#isHeadless */ private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException, HeadlessException { GraphicsEnvironment.checkHeadless(); s.defaultReadObject(); // Make sure the state we just read in for text, // selectionStart and selectionEnd has legal values this.text = (text != null) ? text : ""; select(selectionStart, selectionEnd); Object keyOrNull; while(null != (keyOrNull = s.readObject())) { String key = ((String)keyOrNull).intern(); if (textListenerK == key) { addTextListener((TextListener)(s.readObject())); } else { // skip value for unrecognized key s.readObject(); } } enableInputMethodsIfNecessary(); } ///////////////// // Accessibility support //////////////// /** * */ int getIndexAtPoint(Point p) { return -1; /* To be fully implemented in a future release if (peer == null) { return -1; } TextComponentPeer peer = (TextComponentPeer)this.peer; return peer.getIndexAtPoint(p.x, p.y); */ } /** * */ Rectangle getCharacterBounds(int i) { return null; /* To be fully implemented in a future release if (peer == null) { return null; } TextComponentPeer peer = (TextComponentPeer)this.peer; return peer.getCharacterBounds(i); */ } /** * Gets the AccessibleContext associated with this TextComponent. * For text components, the AccessibleContext takes the form of an * AccessibleAWTTextComponent. * A new AccessibleAWTTextComponent instance is created if necessary. * * @return an AccessibleAWTTextComponent that serves as the * AccessibleContext of this TextComponent * @since 1.3 */ public AccessibleContext getAccessibleContext() { if (accessibleContext == null) { accessibleContext = new AccessibleAWTTextComponent(); } return accessibleContext; } /** * This class implements accessibility support for the * TextComponent class. It provides an implementation of the * Java Accessibility API appropriate to text component user-interface * elements. * @since 1.3 */ protected class AccessibleAWTTextComponent extends AccessibleAWTComponent implements AccessibleText, TextListener { /* * JDK 1.3 serialVersionUID */ private static final long serialVersionUID = 3631432373506317811L; /** * Constructs an AccessibleAWTTextComponent. Adds a listener to track * caret change. */ public AccessibleAWTTextComponent() { TextComponent.this.addTextListener(this); } /** * TextListener notification of a text value change. */ public void textValueChanged(TextEvent textEvent) { Integer cpos = Integer.valueOf(TextComponent.this.getCaretPosition()); firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, cpos); } /** * Gets the state set of the TextComponent. * The AccessibleStateSet of an object is composed of a set of * unique AccessibleStates. A change in the AccessibleStateSet * of an object will cause a PropertyChangeEvent to be fired * for the AccessibleContext.ACCESSIBLE_STATE_PROPERTY property. * * @return an instance of AccessibleStateSet containing the * current state set of the object * @see AccessibleStateSet * @see AccessibleState * @see #addPropertyChangeListener */ public AccessibleStateSet getAccessibleStateSet() { AccessibleStateSet states = super.getAccessibleStateSet(); if (TextComponent.this.isEditable()) { states.add(AccessibleState.EDITABLE); } return states; } /** * Gets the role of this object. * * @return an instance of AccessibleRole describing the role of the * object (AccessibleRole.TEXT) * @see AccessibleRole */ public AccessibleRole getAccessibleRole() { return AccessibleRole.TEXT; } /** * Get the AccessibleText associated with this object. In the * implementation of the Java Accessibility API for this class, * return this object, which is responsible for implementing the * AccessibleText interface on behalf of itself. * * @return this object */ public AccessibleText getAccessibleText() { return this; } // --- interface AccessibleText methods ------------------------ /** * Many of these methods are just convenience methods; they * just call the equivalent on the parent */ /** * Given a point in local coordinates, return the zero-based index * of the character under that Point. If the point is invalid, * this method returns -1. * * @param p the Point in local coordinates * @return the zero-based index of the character under Point p. */ public int getIndexAtPoint(Point p) { return TextComponent.this.getIndexAtPoint(p); } /** * Determines the bounding box of the character at the given * index into the string. The bounds are returned in local * coordinates. If the index is invalid a null rectangle * is returned. * * @param i the index into the String >= 0 * @return the screen coordinates of the character's bounding box */ public Rectangle getCharacterBounds(int i) { return TextComponent.this.getCharacterBounds(i); } /** * Returns the number of characters (valid indicies) * * @return the number of characters >= 0 */ public int getCharCount() { return TextComponent.this.getText().length(); } /** * Returns the zero-based offset of the caret. * * Note: The character to the right of the caret will have the * same index value as the offset (the caret is between * two characters). * * @return the zero-based offset of the caret. */ public int getCaretPosition() { return TextComponent.this.getCaretPosition(); } /** * Returns the AttributeSet for a given character (at a given index). * * @param i the zero-based index into the text * @return the AttributeSet of the character */ public AttributeSet getCharacterAttribute(int i) { return null; // No attributes in TextComponent } /** * Returns the start offset within the selected text. * If there is no selection, but there is * a caret, the start and end offsets will be the same. * Return 0 if the text is empty, or the caret position * if no selection. * * @return the index into the text of the start of the selection >= 0 */ public int getSelectionStart() { return TextComponent.this.getSelectionStart(); } /** * Returns the end offset within the selected text. * If there is no selection, but there is * a caret, the start and end offsets will be the same. * Return 0 if the text is empty, or the caret position * if no selection. * * @return the index into teh text of the end of the selection >= 0 */ public int getSelectionEnd() { return TextComponent.this.getSelectionEnd(); } /** * Returns the portion of the text that is selected. * * @return the text, null if no selection */ public String getSelectedText() { String selText = TextComponent.this.getSelectedText(); // Fix for 4256662 if (selText == null || selText.equals("")) { return null; } return selText; } /** * Returns the String at a given index. * * @param part the AccessibleText.CHARACTER, AccessibleText.WORD, * or AccessibleText.SENTENCE to retrieve * @param index an index within the text >= 0 * @return the letter, word, or sentence, * null for an invalid index or part */ public String getAtIndex(int part, int index) { if (index < 0 || index >= TextComponent.this.getText().length()) { return null; } switch (part) { case AccessibleText.CHARACTER: return TextComponent.this.getText().substring(index, index+1); case AccessibleText.WORD: { String s = TextComponent.this.getText(); BreakIterator words = BreakIterator.getWordInstance(); words.setText(s); int end = words.following(index); return s.substring(words.previous(), end); } case AccessibleText.SENTENCE: { String s = TextComponent.this.getText(); BreakIterator sentence = BreakIterator.getSentenceInstance(); sentence.setText(s); int end = sentence.following(index); return s.substring(sentence.previous(), end); } default: return null; } } private static final boolean NEXT = true; private static final boolean PREVIOUS = false; /** * Needed to unify forward and backward searching. * The method assumes that s is the text assigned to words. */ private int findWordLimit(int index, BreakIterator words, boolean direction, String s) { // Fix for 4256660 and 4256661. // Words iterator is different from character and sentence iterators // in that end of one word is not necessarily start of another word. // Please see java.text.BreakIterator JavaDoc. The code below is // based on nextWordStartAfter example from BreakIterator.java. int last = (direction == NEXT) ? words.following(index) : words.preceding(index); int current = (direction == NEXT) ? words.next() : words.previous(); while (current != BreakIterator.DONE) { for (int p = Math.min(last, current); p < Math.max(last, current); p++) { if (Character.isLetter(s.charAt(p))) { return last; } } last = current; current = (direction == NEXT) ? words.next() : words.previous(); } return BreakIterator.DONE; } /** * Returns the String after a given index. * * @param part the AccessibleText.CHARACTER, AccessibleText.WORD, * or AccessibleText.SENTENCE to retrieve * @param index an index within the text >= 0 * @return the letter, word, or sentence, null for an invalid * index or part */ public String getAfterIndex(int part, int index) { if (index < 0 || index >= TextComponent.this.getText().length()) { return null; } switch (part) { case AccessibleText.CHARACTER: if (index+1 >= TextComponent.this.getText().length()) { return null; } return TextComponent.this.getText().substring(index+1, index+2); case AccessibleText.WORD: { String s = TextComponent.this.getText(); BreakIterator words = BreakIterator.getWordInstance(); words.setText(s); int start = findWordLimit(index, words, NEXT, s); if (start == BreakIterator.DONE || start >= s.length()) { return null; } int end = words.following(start); if (end == BreakIterator.DONE || end >= s.length()) { return null; } return s.substring(start, end); } case AccessibleText.SENTENCE: { String s = TextComponent.this.getText(); BreakIterator sentence = BreakIterator.getSentenceInstance(); sentence.setText(s); int start = sentence.following(index); if (start == BreakIterator.DONE || start >= s.length()) { return null; } int end = sentence.following(start); if (end == BreakIterator.DONE || end >= s.length()) { return null; } return s.substring(start, end); } default: return null; } } /** * Returns the String before a given index. * * @param part the AccessibleText.CHARACTER, AccessibleText.WORD, * or AccessibleText.SENTENCE to retrieve * @param index an index within the text >= 0 * @return the letter, word, or sentence, null for an invalid index * or part */ public String getBeforeIndex(int part, int index) { if (index < 0 || index > TextComponent.this.getText().length()-1) { return null; } switch (part) { case AccessibleText.CHARACTER: if (index == 0) { return null; } return TextComponent.this.getText().substring(index-1, index); case AccessibleText.WORD: { String s = TextComponent.this.getText(); BreakIterator words = BreakIterator.getWordInstance(); words.setText(s); int end = findWordLimit(index, words, PREVIOUS, s); if (end == BreakIterator.DONE) { return null; } int start = words.preceding(end); if (start == BreakIterator.DONE) { return null; } return s.substring(start, end); } case AccessibleText.SENTENCE: { String s = TextComponent.this.getText(); BreakIterator sentence = BreakIterator.getSentenceInstance(); sentence.setText(s); int end = sentence.following(index); end = sentence.previous(); int start = sentence.previous(); if (start == BreakIterator.DONE) { return null; } return s.substring(start, end); } default: return null; } } } // end of AccessibleAWTTextComponent private boolean checkForEnableIM = true; }