/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/**
* A document that can be marked up with character and paragraph
* styles in a manner similar to the Rich Text Format. The element
* structure for this document represents style crossings for
* style runs. These style runs are mapped into a paragraph element
* structure (which may reside in some other structure). The
* style runs break at paragraph boundaries since logical styles are
* assigned to paragraph boundaries.
* <p>
* <strong>Warning:</strong>
* Serialized objects of this class will not be compatible with
* future Swing releases. The current serialization support is
* appropriate for short term storage or RMI between applications running
* the same version of Swing. As of 1.4, support for long term storage
* of all JavaBeans<sup><font size="-2">TM</font></sup>
* has been added to the <code>java.beans</code> package.
* Please see {@link java.beans.XMLEncoder}.
*
* @author Timothy Prinzing
* @see Document
* @see AbstractDocument
*/
/**
* Constructs a styled document.
*
* @param c the container for the content
* @param styles resources and style definitions which may
* be shared across documents
*/
super(c, styles);
}
/**
* Constructs a styled document with the default content
* storage implementation and a shared set of styles.
*
* @param styles the styles
*/
}
/**
* Constructs a default styled document. This buffers
* input content by a size of <em>BUFFER_SIZE_DEFAULT</em>
* and has a style context that is scoped by the lifetime
* of the document and is not shared with other documents.
*/
public DefaultStyledDocument() {
}
/**
* Gets the default root element.
*
* @return the root
* @see Document#getDefaultRootElement
*/
return buffer.getRootElement();
}
/**
* Initialize the document to reflect the given element
* structure (i.e. the structure reported by the
* <code>getDefaultRootElement</code> method. If the
* document contained any data it will first be removed.
*/
try {
if (getLength() != 0) {
}
writeLock();
// install the content
Content c = getContent();
for (int i = 0; i < n; i++) {
}
}
// build the event and element structure
// update bidi (possibly)
// notify the listeners
} catch (BadLocationException ble) {
throw new StateInvariantError("problem initializing");
} finally {
writeUnlock();
}
}
/**
* Inserts new elements in bulk. This is useful to allow
* parsing with the document in an unlocked state and
* prepare an element structure modification. This method
* takes an array of tokens that describe how to update an
* element structure so the time within a write lock can
* be greatly reduced in an asynchronous update situation.
* <p>
* This method is thread safe, although most Swing methods
* are not. Please see
* to Use Threads</A> for more information.
*
* @param offset the starting offset >= 0
* @param data the element data
* @exception BadLocationException for an invalid starting offset
*/
return;
}
try {
writeLock();
// install the content
Content c = getContent();
for (int i = 0; i < n; i++) {
}
}
// Nothing to insert, bail.
return;
}
// create event and build the element structure
// update bidi (possibly)
// notify the listeners
} finally {
writeUnlock();
}
}
/**
* Removes an element from this document.
*
* <p>The element is removed from its parent element, as well as
* the text in the range identified by the element. If the
* element isn't associated with the document, {@code
* IllegalArgumentException} is thrown.</p>
*
* <p>As empty branch elements are not allowed in the document, if the
* element is the sole child, its parent element is removed as well,
* recursively. This means that when replacing all the children of a
* particular element, new children should be added <em>before</em>
* removing old children.
*
* <p>Element removal results in two events being fired, the
* {@code DocumentEvent} for changes in element structure and {@code
* UndoableEditEvent} for changes in document content.</p>
*
* <p>If the element contains end-of-content mark (the last {@code
* "\n"} character in document), this character is not removed;
* instead, preceding leaf element is extended to cover the
* character. If the last leaf already ends with {@code "\n",} it is
* included in content removal.</p>
*
* <p>If the element is {@code null,} {@code NullPointerException} is
* thrown. If the element structure would become invalid after the removal,
* for example if the element is the document root element, {@code
* IllegalArgumentException} is thrown. If the current element structure is
* invalid, {@code IllegalStateException} is thrown.</p>
*
* @param elem the element to remove
* @throws NullPointerException if the element is {@code null}
* @throws IllegalArgumentException if the element could not be removed
* @throws IllegalStateException if the element structure is invalid
*
* @since 1.7
*/
try {
writeLock();
} finally {
writeUnlock();
}
}
if (elem.getDocument() != this) {
throw new IllegalArgumentException("element doesn't belong to document");
}
throw new IllegalArgumentException("can't remove the root element");
}
int removeFrom = startOffset;
boolean atEnd = false;
if (endOffset >= lastEndOffset) {
// element includes the last "\n" character, needs special handling
if (startOffset <= 0) {
throw new IllegalArgumentException("can't remove the whole content");
}
try {
removeFrom--; // preceding leaf ends with "\n", remove it
}
throw new IllegalStateException(ble);
}
atEnd = true;
}
// do not leave empty branch elements
throw new IllegalStateException("invalid element structure");
}
}
if (length > 0) {
try {
}
} catch (BadLocationException ble) {
// can only happen if the element structure is severely broken
throw new IllegalStateException(ble);
}
lastEndOffset -= length;
}
if (atEnd) {
// preceding leaf element should be extended to cover orphaned "\n"
}
throw new IllegalStateException("invalid element structure");
}
prevRemoved, prevAdded));
}
// do not fire UndoabeEdit event for composed text edit (unsupported)
}
}
/**
* Adds a new style into the logical style hierarchy. Style attributes
* resolve from bottom up so an attribute specified in a child
* will override an attribute specified in the parent.
*
* @param nm the name of the style (must be unique within the
* collection of named styles). The name may be null if the style
* is unnamed, but the caller is responsible
* for managing the reference returned as an unnamed style can't
* be fetched by name. An unnamed style may be useful for things
* like character attribute overrides such as found in a style
* run.
* @param parent the parent style. This may be null if unspecified
* attributes need not be resolved in some other style.
* @return the style
*/
}
/**
* Removes a named style previously added to the document.
*
* @param nm the name of the style to remove
*/
}
/**
* Fetches a named style previously added.
*
* @param nm the name of the style
* @return the style
*/
}
/**
* Fetches the list of of style names.
*
* @return all the style names
*/
}
/**
* Sets the logical style to use for the paragraph at the
* given position. If attributes aren't explicitly set
* for character and paragraph attributes they will resolve
* through the logical style assigned to the paragraph, which
* in turn may resolve through some hierarchy completely
* independent of the element hierarchy in the document.
* <p>
* This method is thread safe, although most Swing methods
* are not. Please see
* to Use Threads</A> for more information.
*
* @param pos the offset from the start of the document >= 0
* @param s the logical style to assign to the paragraph, null if none
*/
try {
writeLock();
e.end();
fireUndoableEditUpdate(new UndoableEditEvent(this, e));
} finally {
writeUnlock();
}
}
}
/**
* Fetches the logical style assigned to the paragraph
* represented by the given position.
*
* @param p the location to translate to a paragraph
* and determine the logical style assigned >= 0. This
* is an offset from the start of the document.
* @return the style, null if none
*/
}
}
return s;
}
/**
* Sets attributes for some part of the document.
* A write lock is held by this operation while changes
* are being made, and a DocumentEvent is sent to the listeners
* after the change has been successfully completed.
* <p>
* This method is thread safe, although most Swing methods
* are not. Please see
* to Use Threads</A> for more information.
*
* @param offset the offset in the document >= 0
* @param length the length >= 0
* @param s the attributes
* @param replace true if the previous attributes should be replaced
* before setting the new attributes
*/
if (length == 0) {
return;
}
try {
writeLock();
// split elements that need it
// PENDING(prinz) - this isn't a very efficient way to iterate
int lastEnd;
// offset + length beyond length of document, bail.
break;
}
if (replace) {
}
attr.addAttributes(s);
}
} finally {
writeUnlock();
}
}
/**
* Sets attributes for a paragraph.
* <p>
* This method is thread safe, although most Swing methods
* are not. Please see
* to Use Threads</A> for more information.
*
* @param offset the offset into the paragraph >= 0
* @param length the number of characters affected >= 0
* @param s the attributes
* @param replace whether to replace existing attributes, or merge them
*/
boolean replace) {
try {
writeLock();
// PENDING(prinz) - this assumes a particular element structure
boolean hasRuns = false;
if (replace) {
}
attr.addAttributes(s);
}
}
if (hasRuns) {
updateBidi( changes );
}
} finally {
writeUnlock();
}
}
/**
* Gets the paragraph element at the offset <code>pos</code>.
* A paragraph consists of at least one child Element, which is usually
* a leaf.
*
* @param pos the starting offset >= 0
* @return the element
*/
Element e;
for (e = getDefaultRootElement(); ! e.isLeaf(); ) {
e = e.getElement(index);
}
if(e != null)
return e.getParentElement();
return e;
}
/**
* Gets a character element based on a position.
*
* @param pos the position in the document >= 0
* @return the element
*/
Element e;
for (e = getDefaultRootElement(); ! e.isLeaf(); ) {
e = e.getElement(index);
}
return e;
}
// --- local methods -------------------------------------------------
/**
* Updates document structure as a result of text insertion. This
* will happen within a write lock. This implementation simply
* parses the inserted content for line breaks and builds up a set
* of instructions for the element buffer.
*
* @param chng a description of the document change
* @param attr the attributes
*/
}
// Paragraph attributes should come from point after insertion.
// You really only notice this when inserting at a paragraph
// boundary.
// Character attributes should come from actual insertion point.
(offset));
try {
boolean insertingAfterNewline = false;
// Check if the previous character was a newline.
if (offset > 0) {
// Inserting after a newline.
insertingAfterNewline = true;
counter--) {
break;
}
}
}
}
// If not inserting after a new line, pull the attributes for
// new paragraphs from the paragraph under the insertion point.
int lastOffset = s.offset;
for (int i = s.offset; i < n; i++) {
if (txt[i] == '\n') {
int breakOffset = i + 1;
breakOffset - lastOffset));
}
}
if (lastOffset < n) {
n - lastOffset));
}
// Check for join previous of first content.
}
if(lastStartSpec != null) {
if(insertingAfterNewline) {
}
// Join to the fracture if NOT inserting at the end
// (fracture only happens when not inserting at end of
// paragraph).
}
// Join to next if parent of pParagraph has another
// element after pParagraph, and it isn't a leaf.
else {
}
}
}
// Do a JoinNext for last spec if it is content, it doesn't
// already have a direction set, no new paragraphs have been
// inserted or a new paragraph has been inserted and its join
// direction isn't originate, and the element at endOffset
// is a leaf.
// Don't try joining to a branch!
}
}
}
// If not inserting at boundary and there is going to be a
// fracture, then can join next on last content if cattr
// matches the new attributes.
}
}
// Check for the composed text element. If it is, merge the character attributes
// into this element as well.
// Assure that the composed text element is named properly
// and doesn't have the CR attribute defined.
}
}
} catch (BadLocationException bl) {
}
}
/**
* This is called by insertUpdate when inserting after a new line.
* It generates, in <code>parseBuffer</code>, ElementSpecs that will
* position the stack in <code>paragraph</code>.<p>
* It returns the direction the last StartSpec should have (this don't
* necessarily create the last start spec).
*/
// Need to find the common parent of pParagraph and paragraph.
// The simple (and common) case that pParagraph and
// paragraph have the same parent.
return ElementSpec.JoinFractureDirection;
return ElementSpec.JoinNextDirection;
}
else {
// Will only happen for text with more than 2 levels.
// Find the common parent of a paragraph and pParagraph
Element e = pParagraph;
while(e != null) {
e = e.getParentElement();
}
e = paragraph;
int leftIndex = -1;
e = e.getParentElement();
}
if(e != null) {
// e identifies the common parent.
// Build the ends.
counter++) {
}
// And the starts.
if(counter > 0)
}
// If there are right parents, then we generated starts
// down the right subtree and there will be an element to
// join to.
return ElementSpec.JoinNextDirection;
// No right subtree, e.getElement(endOffset) is a
// leaf. There will be a facture.
return ElementSpec.JoinFractureDirection;
}
// else: Could throw an exception here, but should never get here!
}
return ElementSpec.OriginateDirection;
}
/**
* Updates document structure as a result of text removal.
*
* @param chng a description of the document change
*/
super.removeUpdate(chng);
}
/**
* Creates the root element to be used to represent the
* default document structure.
*
* @return the element base
*/
// grabs a write-lock for this initialization and
// abandon it during initialization so in normal
// operation we can detect an illegitimate attempt
// to mutate attributes.
writeLock();
writeUnlock();
return section;
}
/**
* Gets the foreground color from an attribute set.
*
* @param attr the attribute set
* @return the color
*/
}
/**
* Gets the background color from an attribute set.
*
* @param attr the attribute set
* @return the color
*/
}
/**
* Gets the font from an attribute set.
*
* @param attr the attribute set
* @return the font
*/
}
/**
* Called when any of this document's styles have changed.
* Subclasses may wish to be intelligent about what gets damaged.
*
* @param style The Style that has changed.
*/
// Only propagate change updated if have content
if (getLength() != 0) {
// lazily create a ChangeUpdateRunnable
if (updateRunnable == null) {
updateRunnable = new ChangeUpdateRunnable();
}
// We may get a whole batch of these at once, so only
// queue the runnable if it is not already pending
synchronized(updateRunnable) {
if (!updateRunnable.isPending) {
updateRunnable.isPending = true;
}
}
}
}
/**
* Adds a document listener for notification of any changes.
*
* @param listener the listener
* @see Document#addDocumentListener
*/
synchronized(listeningStyles) {
(DocumentListener.class);
super.addDocumentListener(listener);
if (oldDLCount == 0) {
if (styleContextChangeListener == null) {
}
if (styleContextChangeListener != null) {
for (ChangeListener l: staleListeners) {
}
}
}
}
}
/**
* Removes a document listener.
*
* @param listener the listener
* @see Document#removeDocumentListener
*/
synchronized(listeningStyles) {
super.removeDocumentListener(listener);
counter--) {
}
if (styleContextChangeListener != null) {
}
}
}
}
/**
* Returns a new instance of StyleChangeHandler.
*/
return new StyleChangeHandler(this);
}
/**
* Returns a new instance of StyleContextChangeHandler.
*/
return new StyleContextChangeHandler(this);
}
/**
* Adds a ChangeListener to new styles, and removes ChangeListener from
* old styles.
*/
void updateStylesListeningTo() {
synchronized(listeningStyles) {
if (styleChangeListener == null) {
}
while (styleNames.hasMoreElements()) {
if (index == -1) {
for (ChangeListener l: staleListeners) {
}
}
else {
v.removeElementAt(index);
}
}
}
}
}
}
}
throws ClassNotFoundException, IOException {
s.defaultReadObject();
// Reinstall style listeners.
if (styleContextChangeListener == null &&
if (styleContextChangeListener != null) {
}
}
}
// --- member variables -----------------------------------------------------------
/**
* The default size of the initial content buffer.
*/
/** Styles listening to. */
/** Listens to Styles. */
/** Listens to Styles. */
/** Run to create a change event for the document */
/**
* Default root element for a document... maps out the
* paragraphs/lines contained.
* <p>
* <strong>Warning:</strong>
* Serialized objects of this class will not be compatible with
* future Swing releases. The current serialization support is
* appropriate for short term storage or RMI between applications running
* the same version of Swing. As of 1.4, support for long term storage
* of all JavaBeans<sup><font size="-2">TM</font></sup>
* has been added to the <code>java.beans</code> package.
* Please see {@link java.beans.XMLEncoder}.
*/
/**
* Creates a new SectionElement.
*/
public SectionElement() {
}
/**
* Gets the name of the element.
*
* @return the name
*/
return SectionElementName;
}
}
/**
* Specification for building elements.
* <p>
* <strong>Warning:</strong>
* Serialized objects of this class will not be compatible with
* future Swing releases. The current serialization support is
* appropriate for short term storage or RMI between applications running
* the same version of Swing. As of 1.4, support for long term storage
* of all JavaBeans<sup><font size="-2">TM</font></sup>
* has been added to the <code>java.beans</code> package.
* Please see {@link java.beans.XMLEncoder}.
*/
public static class ElementSpec {
/**
* A possible value for getType. This specifies
* that this record type is a start tag and
* represents markup that specifies the start
* of an element.
*/
/**
* A possible value for getType. This specifies
* that this record type is a end tag and
* represents markup that specifies the end
* of an element.
*/
/**
* A possible value for getType. This specifies
* that this record type represents content.
*/
/**
* A possible value for getDirection. This specifies
* that the data associated with this record should
* be joined to what precedes it.
*/
/**
* A possible value for getDirection. This specifies
* that the data associated with this record should
* be joined to what follows it.
*/
/**
* A possible value for getDirection. This specifies
* that the data associated with this record should
* be used to originate a new element. This would be
* the normal value.
*/
/**
* A possible value for getDirection. This specifies
* that the data associated with this record should
* be joined to the fractured element.
*/
/**
* Constructor useful for markup when the markup will not
* be stored in the document.
*
* @param a the attributes for the element
* @param type the type of the element (StartTagType, EndTagType,
* ContentType)
*/
}
/**
* Constructor for parsing inside the document when
* the data has already been added, but len information
* is needed.
*
* @param a the attributes for the element
* @param type the type of the element (StartTagType, EndTagType,
* ContentType)
* @param len the length >= 0
*/
}
/**
* Constructor for creating a spec externally for batch
* input of content and markup into the document.
*
* @param a the attributes for the element
* @param type the type of the element (StartTagType, EndTagType,
* ContentType)
* @param txt the text for the element
* @param offs the offset into the text >= 0
* @param len the length of the text >= 0
*/
attr = a;
this.direction = OriginateDirection;
}
/**
* Sets the element type.
*
* @param type the type of the element (StartTagType, EndTagType,
* ContentType)
*/
}
/**
* Gets the element type.
*
* @return the type of the element (StartTagType, EndTagType,
* ContentType)
*/
public short getType() {
return type;
}
/**
* Sets the direction.
*
* @param direction the direction (JoinPreviousDirection,
* JoinNextDirection)
*/
}
/**
* Gets the direction.
*
* @return the direction (JoinPreviousDirection, JoinNextDirection)
*/
public short getDirection() {
return direction;
}
/**
* Gets the element attributes.
*
* @return the attribute set
*/
return attr;
}
/**
* Gets the array of characters.
*
* @return the array
*/
public char[] getArray() {
return data;
}
/**
* Gets the starting offset.
*
* @return the offset >= 0
*/
public int getOffset() {
return offs;
}
/**
* Gets the length.
*
* @return the length >= 0
*/
public int getLength() {
return len;
}
/**
* Converts the element to a string.
*
* @return the string
*/
switch(type) {
case StartTagType:
tlbl = "StartTag";
break;
case ContentType:
tlbl = "Content";
break;
case EndTagType:
tlbl = "EndTag";
break;
}
switch(direction) {
case JoinPreviousDirection:
plbl = "JoinPrevious";
break;
case JoinNextDirection:
plbl = "JoinNext";
break;
case OriginateDirection:
plbl = "Originate";
break;
case JoinFractureDirection:
plbl = "Fracture";
break;
}
}
private int len;
private short type;
private short direction;
private int offs;
private char[] data;
}
/**
* Class to manage changes to the element
* hierarchy.
* <p>
* <strong>Warning:</strong>
* Serialized objects of this class will not be compatible with
* future Swing releases. The current serialization support is
* appropriate for short term storage or RMI between applications running
* the same version of Swing. As of 1.4, support for long term storage
* of all JavaBeans<sup><font size="-2">TM</font></sup>
* has been added to the <code>java.beans</code> package.
* Please see {@link java.beans.XMLEncoder}.
*/
/**
* Creates a new ElementBuffer.
*
* @param root the root element
* @since 1.4
*/
}
/**
* Gets the root element.
*
* @return the root element
*/
return root;
}
/**
* Inserts new content.
*
* @param offset the starting offset >= 0
* @param length the length >= 0
* @param data the data to insert
* @param de the event capturing this edit
*/
if (length == 0) {
// Nothing was inserted, no structure change.
return;
}
insertOp = true;
insertOp = false;
}
insertOp = true;
// PENDING(prinz) this needs to be fixed to create a new
// root element as well, but requires changes to the
// DocumentEvent to inform the views that there is a new
// root element.
// Recreate the ending fake element to have the correct offsets.
}
child.getEndOffset()));
pop();
}
// Reset the root elements attributes.
}
}
// fold in the specified subtree
for (int i = 1; i < n; i++) {
insertElement(data[i]);
}
// pop the remaining path
pop();
}
insertOp = false;
}
/**
* Removes content.
*
* @param offset the starting offset >= 0
* @param length the length >= 0
* @param de the event capturing this edit
*/
removeUpdate();
}
/**
* Changes content.
*
* @param offset the starting offset >= 0
* @param length the length >= 0
* @param de the event capturing this edit
*/
changeUpdate();
}
/**
* Inserts an update into the document.
*
* @param data the elements to insert
*/
// push the path
}
// Build a copy of the original path.
// Haven't created the fracture yet.
createdFracture = false;
// Insert the first content.
int i;
recreateLeafs = false;
i = 1;
}
else {
i = 0;
}
// fold in the specified subtree
for (; i < n; i++) {
insertElement(data[i]);
}
// Fracture, if we haven't yet.
if(!createdFracture)
fracture(-1);
// pop the remaining path
pop();
}
// Offset the last index if necessary.
if(offsetLastIndex && offsetLastIndexOnReplace) {
}
// Make sure an edit is going to be created for each of the
// original path items that have a change.
counter--) {
// PENDING(sky): Do I need to worry about order here?
}
}
// An insert at 0 with an initial end implies some elements
// will have no children (the bottomost leaf would have length 0)
// this will find what element need to be removed and remove it.
int counter = 0;
counter++;
}
counter - 1];
}
}
/**
* Updates the element structure in response to a removal from the
* associated sequence in the document. Any elements consumed by the
* span of the removal are removed.
*/
protected void removeUpdate() {
}
/**
* Updates the element structure in response to a change in the
* document.
*/
protected void changeUpdate() {
if (! didEnd) {
// need to do the other end
pop();
}
}
pop();
}
}
boolean splitEnd = false;
// push the path
while (! e.isLeaf()) {
e = e.getElement(index);
}
// make sure there is something to do... if the
// offset is already at a boundary then there is
// nothing to do.
// we need to split, now see if the other end is within
// the same parent.
// it's a range split in the same parent
// it's a three-way split
return true;
} else {
// end is already on a boundary
}
}
splitEnd = true;
}
// split the first location
// pick up things in the middle
}
}
}
return splitEnd;
}
/**
* Creates the UndoableEdit record for the edits made
* in the buffer.
*/
for (int i = 0; i < n; i++) {
}
/*
for (int i = 0; i < n; i++) {
ElemChanges ec = (ElemChanges) changes.elementAt(i);
System.err.print("edited: " + ec.parent + " at: " + ec.index +
" removed " + ec.removed.size());
if (ec.removed.size() > 0) {
int r0 = ((Element) ec.removed.firstElement()).getStartOffset();
int r1 = ((Element) ec.removed.lastElement()).getEndOffset();
System.err.print("[" + r0 + "," + r1 + "]");
}
System.err.print(" added " + ec.added.size());
if (ec.added.size() > 0) {
int p0 = ((Element) ec.added.firstElement()).getStartOffset();
int p1 = ((Element) ec.added.lastElement()).getEndOffset();
System.err.print("[" + p0 + "," + p1 + "]");
}
System.err.println("");
}
*/
}
/**
* Initialize the buffer
*/
} else {
}
} else {
}
offsetLastIndex = offsetLastIndexOnReplace = false;
}
/**
* Pushes a new element onto the stack that represents
* the current path.
* @param record Whether or not the push should be
* recorded as an element change or not.
* @param isFracture true if pushing on an element that was created
* as the result of a fracture.
*/
}
}
void pop() {
if(e.getElementCount() == 0) {
// if we pushed a branch element that didn't get
// used, make sure its not marked as having been added.
}
}
}
/**
* move the current offset forward by n.
*/
void advance(int n) {
pos += n;
}
case ElementSpec.StartTagType:
switch(es.getDirection()) {
case ElementSpec.JoinNextDirection:
// Don't create a new element, use the existing one
// at the specified location.
// This happens if inserting into a leaf, followed
// by a join next where next sibling is not a leaf.
else
throw new StateInvariantError("Join next to leaf");
}
// Not really a fracture, but need to treat it like
// one so that content join next will work correctly.
// We can do this because there will never be a join
// next followed by a join fracture.
break;
if(!createdFracture) {
// Should always be something on the stack!
}
// If parent isn't a fracture, fracture will be
// fracturedChild.
if(!ec.isFracture) {
}
else
// Parent is a fracture, use 1st element.
break;
default:
es.getAttributes());
break;
}
break;
case ElementSpec.EndTagType:
pop();
break;
case ElementSpec.ContentType:
}
else {
// JoinNext on tail is only applicable if last element
// and attributes come from that of first element.
// With a little extra testing it would be possible
// to NOT due this again, as more than likely fracture()
// created this element.
if(!ec.isFracture) {
if(insertPath != null) {
break;
}
}
}
}
else {
// Parent was fractured element.
}
}
break;
}
}
/**
* Remove the elements from <code>elem</code> in range
* <code>rmOffs0</code>, <code>rmOffs1</code>. This uses
* <code>canJoin</code> and <code>join</code> to handle joining
* the endpoints of the insertion.
*
* @return true if elem will no longer have any elements.
*/
// update path for changes
// if the range is contained by one element,
// we just forward the request
// Element totally removed.
}
}
} else {
// the removal range spans elements. If we can join
// the two endpoints, do it. Otherwise we remove the
// interior and forward to the endpoints.
// remove and join
}
} else {
// remove interior and forward
(index0 == 0 &&
// start element completely consumed
}
if (!containsOffs1) {
rmIndex1++;
}
// end element not touched
}
}
}
}
}
}
}
}
}
// publish changes
pop();
// Return true if we no longer have any children.
return true;
}
}
return false;
}
/**
* Can the two given elements be coelesced together
* into one element?
*/
return false;
}
// Don't join a leaf to a branch.
return false;
}
if (leaf0) {
// Only join leaves if the attributes match, otherwise
// style information will be lost.
}
// Only join non-leafs if the names are equal. This may result
// in loss of style information, but this is typically acceptable
// for non-leafs.
}
}
// Both names null, treat as equal.
return true;
}
/**
* Joins the two elements carving out a hole for the
* given removed range.
*/
right.getEndOffset());
// join two branch elements. This copies the children before
// the removal range on the left element, and after the removal
// range on the right element. The two elements on the edge
// are joined if possible and needed.
}
}
// transfer the left
for (int i = 0; i < ljIndex; i++) {
}
children.addElement(e);
} else {
}
}
}
// transfer the right
int n = right.getElementCount();
}
// install the children
return to;
} else {
throw new StateInvariantError(
"No support to join leaf element with non-leaf element");
}
}
/**
* Creates a copy of this element, with a different
* parent.
*
* @param parent the parent element
* @param clonee the element to be cloned
* @return the copy
*/
clonee.getEndOffset());
}
int n = clonee.getElementCount();
for (int i = 0; i < n; i++) {
}
return e;
}
/**
* Creates a copy of this element, with a different
* parent. Children of this element included in the
* removal range will be discarded.
*/
clonee.getEndOffset());
}
int n = clonee.getElementCount();
for (int i = 0; i < n; i++) {
}
}
return e;
}
/**
* Determines if a fracture needs to be performed. A fracture
* can be thought of as moving the right part of a tree to a
* new location, where the right part is determined by what has
* been inserted. <code>depth</code> is used to indicate a
* JoinToFracture is needed to an element at a depth
* of <code>depth</code>. Where the root is 0, 1 is the children
* of the root...
* <p>This will invoke <code>fractureFrom</code> if it is determined
* a fracture needs to happen.
*/
int lastIndex = -1;
boolean needRecreate = recreateLeafs;
// Use childAltered to determine when a child has been altered,
// that is the point of insertion is less than the element count.
createdFracture = true;
// Determine where to start recreating from.
// Start at - 2, as first one is indicated by recreateLeafs and
// childAltered.
if(!needRecreate && childAltered) {
needRecreate = true;
if(deepestAlteredIndex == -1)
}
}
childAltered = true;
}
}
if(needRecreate) {
// Recreate all children to right of parent starting
// at lastIndex.
if(lastIndex == -1)
}
}
/**
* Recreates the elements to the right of the insertion point.
* This starts at <code>startIndex</code> in <code>changed</code>,
* and calls duplicate to duplicate existing elements.
* This will also duplicate the elements along the insertion
* point, until a depth of <code>endFractureIndex</code> is
* reached, at which point only the elements to the right of
* the insertion point are duplicated.
*/
int endFractureIndex) {
// Recreate the element representing the inserted index.
else
}
else {
child.getAttributes());
}
// Recreate all the elements to the right of the
// insertion point.
while(++startIndex < endFractureIndex) {
// Create the newChild, a duplicate of the elment at
// index. This isn't done if isEnd and offsetLastIndex are true
// indicating a join previous was done.
// Determine the child to duplicate, won't have to duplicate
// if at end of fracture, or offseting index.
if(isEnd) {
if(offsetLastIndex || !isEndLeaf)
else
}
else {
}
// Duplicate it.
}
else {
child.getAttributes());
}
}
else
// Recreate the remaining children (there may be none).
int moveStartIndex;
int kidStartIndex = 1;
// Last part of fracture.
if(isEndLeaf) {
kidsToMove--;
}
else {
}
kidStartIndex = 0;
}
else {
if(!isEnd) {
// Branch.
kidsToMove++;
}
else {
// Last leaf, need to recreate part of it.
}
}
counter++) {
}
}
}
/**
* Recreates <code>toDuplicate</code>. This is called when an
* element needs to be created as the result of an insertion. This
* will recurse and create all the children. This is similiar to
* <code>clone</code>, but deteremines the offsets differently.
*/
if(toDuplicate.isLeaf()) {
}
// Not a leaf
getAttributes());
}
return newParent;
}
/**
* Splits the bottommost leaf in <code>path</code>.
* This is called from insert when the first element is NOT content.
*/
// Split the bottommost leaf. It will be recreated elsewhere.
// Inserts at offset 0 do not need to recreate child (it would
// have a length of 0!).
if (offset != 0) {
offset);
}
recreateLeafs = true;
else
offsetLastIndex = true;
}
/**
* Inserts the first content. This needs to be separate to handle
* joining.
*/
switch(firstSpec.getDirection()) {
!isOnlyContent) {
// Create the left split part containing new content.
// Remainder will be created later.
recreateLeafs = true;
else
offsetLastIndex = true;
}
else {
offsetLastIndex = true;
offsetLastIndexOnReplace = true;
}
// else Inserted at end, and is total length.
break;
case ElementSpec.JoinNextDirection:
if(offset != 0) {
// Recreate the first element, its offset will have
// changed.
offset);
// Recreate the second, merge part. We do no checking
// to see if JoinNextDirection is valid here!
if(isOnlyContent)
else
}
// else nothin to do.
// PENDING: if !isOnlyContent could raise here!
break;
default:
// Inserted into middle, need to recreate split left
// new content, and split right.
offset);
}
// new content
// Signals need to recreate right split later.
recreateLeafs = true;
}
else {
offsetLastIndex = true;
}
break;
}
}
transient int offset;
transient int length;
transient int endOffset;
transient boolean insertOp;
/** For insert, path to inserted elements. */
/** Only for insert, set to true when the fracture has been created. */
transient boolean createdFracture;
/** Parent that contains the fractured child. */
/** Fractured child. */
/** Used to indicate when fracturing that the last leaf should be
* skipped. */
transient boolean offsetLastIndex;
/** Used to indicate that the parent of the deepest leaf should
* insert. */
transient boolean offsetLastIndexOnReplace;
/*
* Internal record used to hold element change specifications
*/
class ElemChanges {
this.isFracture = isFracture;
}
}
int index;
boolean isFracture;
}
}
/**
* An UndoableEdit used to remember AttributeSet changes to an
* Element.
*/
boolean isReplacing) {
super();
this.newAttributes = newAttributes;
this.isReplacing = isReplacing;
// If not replacing, it may be more efficient to only copy the
// changed values...
}
/**
* Redoes a change.
*
* @exception CannotRedoException if the change cannot be redone
*/
super.redo();
.getAttributes();
if(isReplacing)
}
/**
* Undoes a change.
*
* @exception CannotUndoException if the change cannot be undone
*/
super.undo();
}
// AttributeSet containing additional entries, must be non-mutable!
// Copy of the AttributeSet the Element contained.
// true if all the attributes in the element were removed first.
protected boolean isReplacing;
// Efected Element.
}
/**
* UndoableEdit for changing the resolve parent of an Element.
*/
super();
}
/**
* Redoes a change.
*
* @exception CannotRedoException if the change cannot be redone
*/
super.redo();
}
/**
* Undoes a change.
*
* @exception CannotUndoException if the change cannot be undone
*/
super.undo();
}
/** Element to change resolve parent of. */
/** New style. */
/** Old style, before setting newStyle. */
}
/**
* Base class for style change handlers with support for stale objects detection.
*/
/* This has an implicit reference to the handler object. */
super(d, q);
}
/**
* Return a reference to the style change handler object.
*/
return AbstractChangeHandler.this;
}
}
/** Class-specific reference queues. */
/** A weak reference to the document object. */
synchronized (queueMap) {
if (q == null) {
q = new ReferenceQueue<DefaultStyledDocument>();
}
}
doc = new DocReference(d, q);
}
/**
* Return a list of stale change listeners.
*
* A change listener becomes "stale" when its document is cleaned by GC.
*/
if (q != null) {
DocReference r;
synchronized (q) {
}
}
}
return staleListeners;
}
/**
* The ChangeListener wrapper which guards against dead documents.
*/
if (d != null) {
fireStateChanged(d, e);
}
}
/** Run the actual class-specific stateChanged() method. */
}
/**
* Added to all the Styles. When instances of this receive a
* stateChanged method, styleChanged is invoked.
*/
super(d);
}
} else {
d.styleChanged(null);
}
}
}
/**
* Added to the StyleContext. When the StyleContext changes, this invokes
* <code>updateStylesListeningTo</code>.
*/
super(d);
}
}
}
/**
* When run this creates a change event for the complete document
* and fires it.
*/
boolean isPending = false;
public void run() {
synchronized(this) {
isPending = false;
}
try {
writeLock();
getLength(),
} finally {
writeUnlock();
}
}
}
}