* or an empty array if no undoable edit listeners are * currently registered * * @see #addUndoableEditListener * @see #removeUndoableEditListener * * @since 1.4 */ public UndoableEditListener[] getUndoableEditListeners() { return listenerList.getListeners(UndoableEditListener.class); } /** * A convenience method for looking up a property value. It is * equivalent to: * <pre> * getDocumentProperties().get(key); * </pre> * * @param key the non-<code>null</code> property key * @return the value of this property or <code>null</code> * @see #getDocumentProperties */ public final Object getProperty(Object key) { return getDocumentProperties().get(key); } /** * A convenience method for storing up a property value. It is * equivalent to: * <pre> * getDocumentProperties().put(key, value); * </pre> * If <code>value</code> is <code>null</code> this method will * remove the property. * * @param key the non-<code>null</code> key * @param value the property value * @see #getDocumentProperties */ public final void putProperty(Object key, Object value) { if (value != null) { getDocumentProperties().put(key, value); } else { getDocumentProperties().remove(key); } if( key == TextAttribute.RUN_DIRECTION && Boolean.TRUE.equals(getProperty(I18NProperty)) ) { //REMIND - this needs to flip on the i18n property if run dir //is rtl and the i18n property is not already on. writeLock(); try { DefaultDocumentEvent e = new DefaultDocumentEvent(0, getLength(), DocumentEvent.EventType.INSERT); updateBidi( e ); } finally { writeUnlock(); } } } /** * Removes some content from the document. * Removing content causes a write lock to be held while the * actual changes are taking place. Observers are notified * of the change on the thread that called this method. * <p> * This method is thread safe, although most Swing methods * are not. Please see * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How * to Use Threads</A> for more information. * * @param offs the starting offset >= 0 * @param len the number of characters to remove >= 0 * @exception BadLocationException the given remove position is not a valid * position within the document * @see Document#remove */ public void remove(int offs, int len) throws BadLocationException { DocumentFilter filter = getDocumentFilter(); writeLock(); try { if (filter != null) { filter.remove(getFilterBypass(), offs, len); } else { handleRemove(offs, len); } } finally { writeUnlock(); } } /** * Performs the actual work of the remove. It is assumed the caller * will have obtained a <code>writeLock</code> before invoking this. */ void handleRemove(int offs, int len) throws BadLocationException { if (len > 0) { if (offs < 0 || (offs + len) > getLength()) { throw new BadLocationException("Invalid remove", getLength() + 1); } DefaultDocumentEvent chng = new DefaultDocumentEvent(offs, len, DocumentEvent.EventType.REMOVE); boolean isComposedTextElement; // Check whether the position of interest is the composed text isComposedTextElement = Utilities.isComposedTextElement(this, offs); removeUpdate(chng); UndoableEdit u = data.remove(offs, len); if (u != null) { chng.addEdit(u); } postRemoveUpdate(chng); // Mark the edit as done. chng.end(); fireRemoveUpdate(chng); // only fire undo if Content implementation supports it // undo for the composed text is not supported for now if ((u != null) && !isComposedTextElement) { fireUndoableEditUpdate(new UndoableEditEvent(this, chng)); } } } /** * Deletes the region of text from <code>offset</code> to * <code>offset + length</code>, and replaces it with <code>text</code>. * It is up to the implementation as to how this is implemented, some * implementations may treat this as two distinct operations: a remove * followed by an insert, others may treat the replace as one atomic * operation. * * @param offset index of child element * @param length length of text to delete, may be 0 indicating don't * delete anything * @param text text to insert, <code>null</code> indicates no text to insert * @param attrs AttributeSet indicating attributes of inserted text, * <code>null</code> * is legal, and typically treated as an empty attributeset, * but exact interpretation is left to the subclass * @exception BadLocationException the given position is not a valid * position within the document * @since 1.4 */ public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException { if (length == 0 && (text == null || text.length() == 0)) { return; } DocumentFilter filter = getDocumentFilter(); writeLock(); try { if (filter != null) { filter.replace(getFilterBypass(), offset, length, text, attrs); } else { if (length > 0) { remove(offset, length); } if (text != null && text.length() > 0) { insertString(offset, text, attrs); } } } finally { writeUnlock(); } } /** * Inserts some content into the document. * Inserting content causes a write lock to be held while the * actual changes are taking place, followed by notification * to the observers on the thread that grabbed the write lock. * <p> * This method is thread safe, although most Swing methods * are not. Please see * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How * to Use Threads</A> for more information. * * @param offs the starting offset >= 0 * @param str the string to insert; does nothing with null/empty strings * @param a the attributes for the inserted content * @exception BadLocationException the given insert position is not a valid * position within the document * @see Document#insertString */ public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { if ((str == null) || (str.length() == 0)) { return; } DocumentFilter filter = getDocumentFilter(); writeLock(); try { if (filter != null) { filter.insertString(getFilterBypass(), offs, str, a); } else { handleInsertString(offs, str, a); } } finally { writeUnlock(); } } /** * Performs the actual work of inserting the text; it is assumed the * caller has obtained a write lock before invoking this. */ void handleInsertString(int offs, String str, AttributeSet a) throws BadLocationException { if ((str == null) || (str.length() == 0)) { return; } UndoableEdit u = data.insertString(offs, str); DefaultDocumentEvent e = new DefaultDocumentEvent(offs, str.length(), DocumentEvent.EventType.INSERT); if (u != null) { e.addEdit(u); } // see if complex glyph layout support is needed if( getProperty(I18NProperty).equals( Boolean.FALSE ) ) { // if a default direction of right-to-left has been specified, // we want complex layout even if the text is all left to right. Object d = getProperty(TextAttribute.RUN_DIRECTION); if ((d != null) && (d.equals(TextAttribute.RUN_DIRECTION_RTL))) { putProperty( I18NProperty, Boolean.TRUE); } else { char[] chars = str.toCharArray(); if (SwingUtilities2.isComplexLayout(chars, 0, chars.length)) { putProperty( I18NProperty, Boolean.TRUE); } } } insertUpdate(e, a); // Mark the edit as done. e.end(); fireInsertUpdate(e); // only fire undo if Content implementation supports it // undo for the composed text is not supported for now if (u != null && (a == null || !a.isDefined(StyleConstants.ComposedTextAttribute))) { fireUndoableEditUpdate(new UndoableEditEvent(this, e)); } } /** * Gets a sequence of text from the document. * * @param offset the starting offset >= 0 * @param length the number of characters to retrieve >= 0 * @return the text * @exception BadLocationException the range given includes a position * that is not a valid position within the document * @see Document#getText */ public String getText(int offset, int length) throws BadLocationException { if (length < 0) { throw new BadLocationException("Length must be positive", length); } String str = data.getString(offset, length); return str; } /** * Fetches the text contained within the given portion * of the document. * <p> * If the partialReturn property on the txt parameter is false, the * data returned in the Segment will be the entire length requested and * may or may not be a copy depending upon how the data was stored. * If the partialReturn property is true, only the amount of text that * can be returned without creating a copy is returned. Using partial * returns will give better performance for situations where large * parts of the document are being scanned. The following is an example * of using the partial return to access the entire document: * <p> * <pre> * int nleft = doc.getDocumentLength(); * Segment text = new Segment(); * int offs = 0; * text.setPartialReturn(true); * while (nleft > 0) { * doc.getText(offs, nleft, text); * // do something with text * nleft -= text.count; * offs += text.count; * } * </pre> * * @param offset the starting offset >= 0 * @param length the number of characters to retrieve >= 0 * @param txt the Segment object to retrieve the text into * @exception BadLocationException the range given includes a position * that is not a valid position within the document */ public void getText(int offset, int length, Segment txt) throws BadLocationException { if (length < 0) { throw new BadLocationException("Length must be positive", length); } data.getChars(offset, length, txt); } /** * Returns a position that will track change as the document * is altered. * <p> * This method is thread safe, although most Swing methods * are not. Please see * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How * to Use Threads</A> for more information. * * @param offs the position in the model >= 0 * @return the position * @exception BadLocationException if the given position does not * represent a valid location in the associated document * @see Document#createPosition */ public synchronized Position createPosition(int offs) throws BadLocationException { return data.createPosition(offs); } /** * Returns a position that represents the start of the document. The * position returned can be counted on to track change and stay * located at the beginning of the document. * * @return the position */ public final Position getStartPosition() { Position p; try { p = createPosition(0); } catch (BadLocationException bl) { p = null; } return p; } /** * Returns a position that represents the end of the document. The * position returned can be counted on to track change and stay * located at the end of the document. * * @return the position */ public final Position getEndPosition() { Position p; try { p = createPosition(data.length()); } catch (BadLocationException bl) { p = null; } return p; } /** * Gets all root elements defined. Typically, there * will only be one so the default implementation * is to return the default root element. * * @return the root element */ public Element[] getRootElements() { Element[] elems = new Element[2]; elems[0] = getDefaultRootElement(); elems[1] = getBidiRootElement(); return elems; } /** * Returns the root element that views should be based upon * unless some other mechanism for assigning views to element * structures is provided. * * @return the root element * @see Document#getDefaultRootElement */ public abstract Element getDefaultRootElement(); // ---- local methods ----------------------------------------- /** * Returns the <code>FilterBypass</code>. This will create one if one * does not yet exist. */ private DocumentFilter.FilterBypass getFilterBypass() { if (filterBypass == null) { filterBypass = new DefaultFilterBypass(); } return filterBypass; } /** * Returns the root element of the bidirectional structure for this * document. Its children represent character runs with a given * Unicode bidi level. */ public Element getBidiRootElement() { return bidiRoot; } /** * Returns true if the text in the range <code>p0</code> to * <code>p1</code> is left to right. */ boolean isLeftToRight(int p0, int p1) { if(!getProperty(I18NProperty).equals(Boolean.TRUE)) { return true; } Element bidiRoot = getBidiRootElement(); int index = bidiRoot.getElementIndex(p0); Element bidiElem = bidiRoot.getElement(index); if(bidiElem.getEndOffset() >= p1) { AttributeSet bidiAttrs = bidiElem.getAttributes(); return ((StyleConstants.getBidiLevel(bidiAttrs) % 2) == 0); } return true; } /** * Get the paragraph element containing the given position. Sub-classes * must define for themselves what exactly constitutes a paragraph. They * should keep in mind however that a paragraph should at least be the * unit of text over which to run the Unicode bidirectional algorithm. * * @param pos the starting offset >= 0 * @return the element */ public abstract Element getParagraphElement(int pos); /** * Fetches the context for managing attributes. This * method effectively establishes the strategy used * for compressing AttributeSet information. * * @return the context */ protected final AttributeContext getAttributeContext() { return context; } /** * Updates document structure as a result of text insertion. This * will happen within a write lock. If a subclass of * this class reimplements this method, it should delegate to the * superclass as well. * * @param chng a description of the change * @param attr the attributes for the change */ protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) { if( getProperty(I18NProperty).equals( Boolean.TRUE ) ) updateBidi( chng ); // Check if a multi byte is encountered in the inserted text. if (chng.type == DocumentEvent.EventType.INSERT && chng.getLength() > 0 && !Boolean.TRUE.equals(getProperty(MultiByteProperty))) { Segment segment = SegmentCache.getSharedSegment(); try { getText(chng.getOffset(), chng.getLength(), segment); segment.first(); do { if ((int)segment.current() > 255) { putProperty(MultiByteProperty, Boolean.TRUE); break; } } while (segment.next() != Segment.DONE); } catch (BadLocationException ble) { // Should never happen } SegmentCache.releaseSharedSegment(segment); } } /** * Updates any document structure as a result of text removal. This * method is called before the text is actually removed from the Content. * This will happen within a write lock. If a subclass * of this class reimplements this method, it should delegate to the * superclass as well. * * @param chng a description of the change */ protected void removeUpdate(DefaultDocumentEvent chng) { } /**Lines 501–1000 of 3118. Previous lines Load more
* or an empty array if no undoable edit listeners are * currently registered * * @see #addUndoableEditListener * @see #removeUndoableEditListener * * @since 1.4 */ public UndoableEditListener[] getUndoableEditListeners() { return listenerList.getListeners(UndoableEditListener.class); } /** * A convenience method for looking up a property value. It is * equivalent to: * <pre> * getDocumentProperties().get(key); * </pre> * * @param key the non-<code>null</code> property key * @return the value of this property or <code>null</code> * @see #getDocumentProperties */ public final Object getProperty(Object key) { return getDocumentProperties().get(key); } /** * A convenience method for storing up a property value. It is * equivalent to: * <pre> * getDocumentProperties().put(key, value); * </pre> * If <code>value</code> is <code>null</code> this method will * remove the property. * * @param key the non-<code>null</code> key * @param value the property value * @see #getDocumentProperties */ public final void putProperty(Object key, Object value) { if (value != null) { getDocumentProperties().put(key, value); } else { getDocumentProperties().remove(key); } if( key == TextAttribute.RUN_DIRECTION && Boolean.TRUE.equals(getProperty(I18NProperty)) ) { //REMIND - this needs to flip on the i18n property if run dir //is rtl and the i18n property is not already on. writeLock(); try { DefaultDocumentEvent e = new DefaultDocumentEvent(0, getLength(), DocumentEvent.EventType.INSERT); updateBidi( e ); } finally { writeUnlock(); } } } /** * Removes some content from the document. * Removing content causes a write lock to be held while the * actual changes are taking place. Observers are notified * of the change on the thread that called this method. * <p> * This method is thread safe, although most Swing methods * are not. Please see * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How * to Use Threads</A> for more information. * * @param offs the starting offset >= 0 * @param len the number of characters to remove >= 0 * @exception BadLocationException the given remove position is not a valid * position within the document * @see Document#remove */ public void remove(int offs, int len) throws BadLocationException { DocumentFilter filter = getDocumentFilter(); writeLock(); try { if (filter != null) { filter.remove(getFilterBypass(), offs, len); } else { handleRemove(offs, len); } } finally { writeUnlock(); } } /** * Performs the actual work of the remove. It is assumed the caller * will have obtained a <code>writeLock</code> before invoking this. */ void handleRemove(int offs, int len) throws BadLocationException { if (len > 0) { if (offs < 0 || (offs + len) > getLength()) { throw new BadLocationException("Invalid remove", getLength() + 1); } DefaultDocumentEvent chng = new DefaultDocumentEvent(offs, len, DocumentEvent.EventType.REMOVE); boolean isComposedTextElement; // Check whether the position of interest is the composed text isComposedTextElement = Utilities.isComposedTextElement(this, offs); removeUpdate(chng); UndoableEdit u = data.remove(offs, len); if (u != null) { chng.addEdit(u); } postRemoveUpdate(chng); // Mark the edit as done. chng.end(); fireRemoveUpdate(chng); // only fire undo if Content implementation supports it // undo for the composed text is not supported for now if ((u != null) && !isComposedTextElement) { fireUndoableEditUpdate(new UndoableEditEvent(this, chng)); } } } /** * Deletes the region of text from <code>offset</code> to * <code>offset + length</code>, and replaces it with <code>text</code>. * It is up to the implementation as to how this is implemented, some * implementations may treat this as two distinct operations: a remove * followed by an insert, others may treat the replace as one atomic * operation. * * @param offset index of child element * @param length length of text to delete, may be 0 indicating don't * delete anything * @param text text to insert, <code>null</code> indicates no text to insert * @param attrs AttributeSet indicating attributes of inserted text, * <code>null</code> * is legal, and typically treated as an empty attributeset, * but exact interpretation is left to the subclass * @exception BadLocationException the given position is not a valid * position within the document * @since 1.4 */ public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException { if (length == 0 && (text == null || text.length() == 0)) { return; } DocumentFilter filter = getDocumentFilter(); writeLock(); try { if (filter != null) { filter.replace(getFilterBypass(), offset, length, text, attrs); } else { if (length > 0) { remove(offset, length); } if (text != null && text.length() > 0) { insertString(offset, text, attrs); } } } finally { writeUnlock(); } } /** * Inserts some content into the document. * Inserting content causes a write lock to be held while the * actual changes are taking place, followed by notification * to the observers on the thread that grabbed the write lock. * <p> * This method is thread safe, although most Swing methods * are not. Please see * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How * to Use Threads</A> for more information. * * @param offs the starting offset >= 0 * @param str the string to insert; does nothing with null/empty strings * @param a the attributes for the inserted content * @exception BadLocationException the given insert position is not a valid * position within the document * @see Document#insertString */ public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { if ((str == null) || (str.length() == 0)) { return; } DocumentFilter filter = getDocumentFilter(); writeLock(); try { if (filter != null) { filter.insertString(getFilterBypass(), offs, str, a); } else { handleInsertString(offs, str, a); } } finally { writeUnlock(); } } /** * Performs the actual work of inserting the text; it is assumed the * caller has obtained a write lock before invoking this. */ void handleInsertString(int offs, String str, AttributeSet a) throws BadLocationException { if ((str == null) || (str.length() == 0)) { return; } UndoableEdit u = data.insertString(offs, str); DefaultDocumentEvent e = new DefaultDocumentEvent(offs, str.length(), DocumentEvent.EventType.INSERT); if (u != null) { e.addEdit(u); } // see if complex glyph layout support is needed if( getProperty(I18NProperty).equals( Boolean.FALSE ) ) { // if a default direction of right-to-left has been specified, // we want complex layout even if the text is all left to right. Object d = getProperty(TextAttribute.RUN_DIRECTION); if ((d != null) && (d.equals(TextAttribute.RUN_DIRECTION_RTL))) { putProperty( I18NProperty, Boolean.TRUE); } else { char[] chars = str.toCharArray(); if (SwingUtilities2.isComplexLayout(chars, 0, chars.length)) { putProperty( I18NProperty, Boolean.TRUE); } } } insertUpdate(e, a); // Mark the edit as done. e.end(); fireInsertUpdate(e); // only fire undo if Content implementation supports it // undo for the composed text is not supported for now if (u != null && (a == null || !a.isDefined(StyleConstants.ComposedTextAttribute))) { fireUndoableEditUpdate(new UndoableEditEvent(this, e)); } } /** * Gets a sequence of text from the document. * * @param offset the starting offset >= 0 * @param length the number of characters to retrieve >= 0 * @return the text * @exception BadLocationException the range given includes a position * that is not a valid position within the document * @see Document#getText */ public String getText(int offset, int length) throws BadLocationException { if (length < 0) { throw new BadLocationException("Length must be positive", length); } String str = data.getString(offset, length); return str; } /** * Fetches the text contained within the given portion * of the document. * <p> * If the partialReturn property on the txt parameter is false, the * data returned in the Segment will be the entire length requested and * may or may not be a copy depending upon how the data was stored. * If the partialReturn property is true, only the amount of text that * can be returned without creating a copy is returned. Using partial * returns will give better performance for situations where large * parts of the document are being scanned. The following is an example * of using the partial return to access the entire document: * <p> * <pre> * int nleft = doc.getDocumentLength(); * Segment text = new Segment(); * int offs = 0; * text.setPartialReturn(true); * while (nleft > 0) { * doc.getText(offs, nleft, text); * // do something with text * nleft -= text.count; * offs += text.count; * } * </pre> * * @param offset the starting offset >= 0 * @param length the number of characters to retrieve >= 0 * @param txt the Segment object to retrieve the text into * @exception BadLocationException the range given includes a position * that is not a valid position within the document */ public void getText(int offset, int length, Segment txt) throws BadLocationException { if (length < 0) { throw new BadLocationException("Length must be positive", length); } data.getChars(offset, length, txt); } /** * Returns a position that will track change as the document * is altered. * <p> * This method is thread safe, although most Swing methods * are not. Please see * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How * to Use Threads</A> for more information. * * @param offs the position in the model >= 0 * @return the position * @exception BadLocationException if the given position does not * represent a valid location in the associated document * @see Document#createPosition */ public synchronized Position createPosition(int offs) throws BadLocationException { return data.createPosition(offs); } /** * Returns a position that represents the start of the document. The * position returned can be counted on to track change and stay * located at the beginning of the document. * * @return the position */ public final Position getStartPosition() { Position p; try { p = createPosition(0); } catch (BadLocationException bl) { p = null; } return p; } /** * Returns a position that represents the end of the document. The * position returned can be counted on to track change and stay * located at the end of the document. * * @return the position */ public final Position getEndPosition() { Position p; try { p = createPosition(data.length()); } catch (BadLocationException bl) { p = null; } return p; } /** * Gets all root elements defined. Typically, there * will only be one so the default implementation * is to return the default root element. * * @return the root element */ public Element[] getRootElements() { Element[] elems = new Element[2]; elems[0] = getDefaultRootElement(); elems[1] = getBidiRootElement(); return elems; } /** * Returns the root element that views should be based upon * unless some other mechanism for assigning views to element * structures is provided. * * @return the root element * @see Document#getDefaultRootElement */ public abstract Element getDefaultRootElement(); // ---- local methods ----------------------------------------- /** * Returns the <code>FilterBypass</code>. This will create one if one * does not yet exist. */ private DocumentFilter.FilterBypass getFilterBypass() { if (filterBypass == null) { filterBypass = new DefaultFilterBypass(); } return filterBypass; } /** * Returns the root element of the bidirectional structure for this * document. Its children represent character runs with a given * Unicode bidi level. */ public Element getBidiRootElement() { return bidiRoot; } /** * Returns true if the text in the range <code>p0</code> to * <code>p1</code> is left to right. */ boolean isLeftToRight(int p0, int p1) { if(!getProperty(I18NProperty).equals(Boolean.TRUE)) { return true; } Element bidiRoot = getBidiRootElement(); int index = bidiRoot.getElementIndex(p0); Element bidiElem = bidiRoot.getElement(index); if(bidiElem.getEndOffset() >= p1) { AttributeSet bidiAttrs = bidiElem.getAttributes(); return ((StyleConstants.getBidiLevel(bidiAttrs) % 2) == 0); } return true; } /** * Get the paragraph element containing the given position. Sub-classes * must define for themselves what exactly constitutes a paragraph. They * should keep in mind however that a paragraph should at least be the * unit of text over which to run the Unicode bidirectional algorithm. * * @param pos the starting offset >= 0 * @return the element */ public abstract Element getParagraphElement(int pos); /** * Fetches the context for managing attributes. This * method effectively establishes the strategy used * for compressing AttributeSet information. * * @return the context */ protected final AttributeContext getAttributeContext() { return context; } /** * Updates document structure as a result of text insertion. This * will happen within a write lock. If a subclass of * this class reimplements this method, it should delegate to the * superclass as well. * * @param chng a description of the change * @param attr the attributes for the change */ protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) { if( getProperty(I18NProperty).equals( Boolean.TRUE ) ) updateBidi( chng ); // Check if a multi byte is encountered in the inserted text. if (chng.type == DocumentEvent.EventType.INSERT && chng.getLength() > 0 && !Boolean.TRUE.equals(getProperty(MultiByteProperty))) { Segment segment = SegmentCache.getSharedSegment(); try { getText(chng.getOffset(), chng.getLength(), segment); segment.first(); do { if ((int)segment.current() > 255) { putProperty(MultiByteProperty, Boolean.TRUE); break; } } while (segment.next() != Segment.DONE); } catch (BadLocationException ble) { // Should never happen } SegmentCache.releaseSharedSegment(segment); } } /** * Updates any document structure as a result of text removal. This * method is called before the text is actually removed from the Content. * This will happen within a write lock. If a subclass * of this class reimplements this method, it should delegate to the * superclass as well. * * @param chng a description of the change */ protected void removeUpdate(DefaultDocumentEvent chng) { } /**