Index: javax/swing/text/AbstractDocument.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/AbstractDocument.java,v
retrieving revision 1.39
diff -u -r1.39 AbstractDocument.java
--- javax/swing/text/AbstractDocument.java 17 Nov 2005 20:31:51 -0000 1.39
+++ javax/swing/text/AbstractDocument.java 23 Nov 2005 14:11:37 -0000
@@ -544,7 +544,8 @@
insertUpdate(event, attributes);
writeUnlock();
- fireInsertUpdate(event);
+ if (event.modified)
+ fireInsertUpdate(event);
if (undo != null)
fireUndoableEditUpdate(new UndoableEditEvent(this, undo));
}
@@ -1809,6 +1810,12 @@
Hashtable changes;
/**
+ * Indicates if this event has been modified or not. This is used to
+ * determine if this event is thrown.
+ */
+ boolean modified;
+
+ /**
* Creates a new DefaultDocumentEvent
.
*
* @param offset the starting offset of the change
@@ -1822,6 +1829,7 @@
this.length = length;
this.type = type;
changes = new Hashtable();
+ modified = false;
}
/**
@@ -1836,6 +1844,7 @@
// XXX - Fully qualify ElementChange to work around gcj bug #2499.
if (edit instanceof DocumentEvent.ElementChange)
{
+ modified = true;
DocumentEvent.ElementChange elEdit =
(DocumentEvent.ElementChange) edit;
changes.put(elEdit.getElement(), elEdit);
Index: javax/swing/text/DefaultStyledDocument.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/DefaultStyledDocument.java,v
retrieving revision 1.17
diff -u -r1.17 DefaultStyledDocument.java
--- javax/swing/text/DefaultStyledDocument.java 8 Nov 2005 16:43:29 -0000 1.17
+++ javax/swing/text/DefaultStyledDocument.java 23 Nov 2005 14:11:38 -0000
@@ -42,6 +42,7 @@
import java.awt.Font;
import java.io.Serializable;
import java.util.Enumeration;
+import java.util.Stack;
import java.util.Vector;
import javax.swing.event.ChangeEvent;
@@ -429,6 +430,26 @@
private int length;
/**
+ * The number of inserted end tags. This is a counter which always gets
+ * incremented when an end tag is inserted. This is evaluated before
+ * content insertion to go up the element stack.
+ */
+ private int numEndTags;
+
+ /**
+ * The number of inserted start tags. This is a counter which always gets
+ * incremented when an end tag is inserted. This is evaluated before
+ * content insertion to go up the element stack.
+ */
+ private int numStartTags;
+
+ /**
+ * The current position in the element tree. This is used for bulk inserts
+ * using ElementSpecs.
+ */
+ private Stack elementStack;
+
+ /**
* Holds fractured elements during insertion of end and start tags.
* Inserting an end tag may lead to fracturing of the current paragraph
* element. The elements that have been cut off may be added to the
@@ -450,6 +471,7 @@
public ElementBuffer(Element root)
{
this.root = root;
+ elementStack = new Stack();
}
/**
@@ -493,11 +515,50 @@
{
// Split up the element at the start offset if necessary.
Element el = getCharacterElement(offset);
- split(el, offset);
+ Element[] res = split(el, offset, 0);
+ BranchElement par = (BranchElement) el.getParentElement();
+ if (res[1] != null)
+ {
+ int index = par.getElementIndex(offset);
+ Element[] removed;
+ Element[] added;
+ if (res[0] == null)
+ {
+ removed = new Element[0];
+ added = new Element[]{ res[1] };
+ index++;
+ }
+ else
+ {
+ removed = new Element[]{ el };
+ added = new Element[]{ res[0], res[1] };
+ }
+ par.replace(index, removed.length, added);
+ addEdit(par, index, removed, added);
+ }
int endOffset = offset + length;
el = getCharacterElement(endOffset);
- split(el, endOffset);
+ res = split(el, endOffset, 0);
+ par = (BranchElement) el.getParentElement();
+ if (res[1] != null)
+ {
+ int index = par.getElementIndex(offset);
+ Element[] removed;
+ Element[] added;
+ if (res[1] == null)
+ {
+ removed = new Element[0];
+ added = new Element[]{ res[1] };
+ }
+ else
+ {
+ removed = new Element[]{ el };
+ added = new Element[]{ res[0], res[1] };
+ }
+ par.replace(index, removed.length, added);
+ addEdit(par, index, removed, added);
+ }
}
/**
@@ -505,42 +566,96 @@
*
* @param el the Element to possibly split
* @param offset the offset at which to possibly split
- */
- void split(Element el, int offset)
- {
- if (el instanceof AbstractElement)
+ * @param space the amount of space to create between the splitted parts
+ *
+ * @return An array of elements which represent the split result. This
+ * array has two elements, the two parts of the split. The first
+ * element might be null, which means that the element which should
+ * be splitted can remain in place. The second element might also
+ * be null, which means that the offset is already at an element
+ * boundary and the element doesn't need to be splitted.
+ *
+ */
+ private Element[] split(Element el, int offset, int space)
+ {
+ // If we are at an element boundary, then return an empty array.
+ if ((offset == el.getStartOffset() || offset == el.getEndOffset())
+ && space == 0 && el.isLeaf())
+ return new Element[2];
+
+ // If the element is an instance of BranchElement, then we recursivly
+ // call this method to perform the split.
+ Element[] res = new Element[2];
+ if (el instanceof BranchElement)
{
- AbstractElement ael = (AbstractElement) el;
- int startOffset = ael.getStartOffset();
- int endOffset = ael.getEndOffset();
- int len = endOffset - startOffset;
- if (startOffset != offset && endOffset != offset)
+ int index = el.getElementIndex(offset);
+ Element child = el.getElement(index);
+ Element[] result = split(child, offset, space);
+ Element[] removed;
+ Element[] added;
+ Element[] newAdded;
+
+ int count = el.getElementCount();
+ if (!(result[1] == null))
{
- Element paragraph = ael.getParentElement();
- if (paragraph instanceof BranchElement)
+ // This is the case when we can keep the first element.
+ if (result[0] == null)
{
- BranchElement par = (BranchElement) paragraph;
- Element child1 = createLeafElement(par, ael, startOffset,
- offset);
- Element child2 = createLeafElement(par, ael, offset,
- endOffset);
- int index = par.getElementIndex(startOffset);
- Element[] add = new Element[]{ child1, child2 };
- par.replace(index, 1, add);
- documentEvent.addEdit(new ElementEdit(par, index,
- new Element[]{ el },
- add));
+ removed = new Element[count - index - 1];
+ newAdded = new Element[count - index - 1];
+ added = new Element[]{};
}
+ // This is the case when we may not keep the first element.
else
- throw new AssertionError("paragraph elements are expected to "
- + "be instances of "
- + "javax.swing.text.AbstractDocument.BranchElement");
+ {
+ removed = new Element[count - index];
+ newAdded = new Element[count - index];
+ added = new Element[]{result[0]};
+ }
+ newAdded[0] = result[1];
+ for (int i = index; i < count; i++)
+ {
+ Element el2 = el.getElement(i);
+ int ind = i - count + removed.length;
+ removed[ind] = el2;
+ if (ind != 0)
+ newAdded[ind] = el2;
+ }
+
+ ((BranchElement) el).replace(index, removed.length, added);
+ addEdit(el, index, removed, added);
+ BranchElement newPar =
+ (BranchElement) createBranchElement(el.getParentElement(),
+ el.getAttributes());
+ newPar.replace(0, 0, newAdded);
+ res = new Element[]{ null, newPar };
+ }
+ else
+ {
+ removed = new Element[count - index];
+ for (int i = index; i < count; ++i)
+ removed[i - index] = el.getElement(i);
+ added = new Element[0];
+ ((BranchElement) el).replace(index, removed.length,
+ added);
+ addEdit(el, index, removed, added);
+ BranchElement newPar =
+ (BranchElement) createBranchElement(el.getParentElement(),
+ el.getAttributes());
+ newPar.replace(0, 0, removed);
+ res = new Element[]{ null, newPar };
}
}
- else
- throw new AssertionError("content elements are expected to be "
- + "instances of "
- + "javax.swing.text.AbstractDocument.AbstractElement");
+ else if (el instanceof LeafElement)
+ {
+ BranchElement par = (BranchElement) el.getParentElement();
+ Element el1 = createLeafElement(par, el.getAttributes(),
+ el.getStartOffset(), offset);
+ Element el2 = createLeafElement(par, el.getAttributes(),
+ offset + space, el.getEndOffset());
+ res = new Element[]{ el1, el2 };
+ }
+ return res;
}
/**
@@ -563,6 +678,12 @@
this.offset = offset;
this.length = length;
documentEvent = ev;
+ // Push the root and the paragraph at offset onto the element stack.
+ elementStack.clear();
+ elementStack.push(root);
+ elementStack.push(root.getElement(root.getElementIndex(offset)));
+ numEndTags = 0;
+ numStartTags = 0;
insertUpdate(data);
}
@@ -581,93 +702,102 @@
switch (data[i].getType())
{
case ElementSpec.StartTagType:
- insertStartTag(data[i]);
+ numStartTags++;
break;
case ElementSpec.EndTagType:
- insertEndTag(data[i]);
+ numEndTags++;
break;
default:
insertContentTag(data[i]);
break;
}
}
+ endEdit();
}
/**
- * Insert a new paragraph after the paragraph at the current position.
- *
- * @param tag the element spec that describes the element to be inserted
+ * Finishes an insertion by possibly evaluating the outstanding start and
+ * end tags. However, this is only performed if the event has received any
+ * modifications.
*/
- void insertStartTag(ElementSpec tag)
+ private void endEdit()
{
- BranchElement root = (BranchElement) getDefaultRootElement();
- int index = root.getElementIndex(offset);
- if (index == -1)
- index = 0;
-
- BranchElement newParagraph =
- (BranchElement) createBranchElement(root, tag.getAttributes());
- newParagraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE));
-
- // Add new paragraph into document structure.
- Element[] added = new Element[]{newParagraph};
- root.replace(index + 1, 0, added);
- ElementEdit edit = new ElementEdit(root, index + 1, new Element[0],
- added);
- documentEvent.addEdit(edit);
+ if (documentEvent.modified)
+ prepareContentInsertion();
+ }
- // Maybe add fractured elements.
- if (tag.getDirection() == ElementSpec.JoinFractureDirection)
+ /**
+ * Evaluates the number of inserted end tags and performs the corresponding
+ * structural changes.
+ */
+ private void prepareContentInsertion()
+ {
+ while (numEndTags > 0)
{
- Element[] newFracture = new Element[fracture.length];
- for (int i = 0; i < fracture.length; i++)
- {
- Element oldLeaf = fracture[i];
- Element newLeaf = createLeafElement(newParagraph,
- oldLeaf.getAttributes(),
- oldLeaf.getStartOffset(),
- oldLeaf.getEndOffset());
- newFracture[i] = newLeaf;
- }
- newParagraph.replace(0, 0, newFracture);
- edit = new ElementEdit(newParagraph, 0, new Element[0],
- fracture);
- documentEvent.addEdit(edit);
- fracture = new Element[0];
+ elementStack.pop();
+ numEndTags--;
+ }
+
+ while (numStartTags > 0)
+ {
+ Element current = (Element) elementStack.peek();
+ Element newParagraph =
+ insertParagraph((BranchElement) current, offset);
+ elementStack.push(newParagraph);
+ numStartTags--;
}
}
- /**
- * Inserts an end tag into the document structure. This cuts of the
- * current paragraph element, possibly fracturing it's child elements.
- * The fractured elements are saved so that they can be joined later
- * with a new paragraph element.
- */
- void insertEndTag(ElementSpec tag)
- {
- BranchElement root = (BranchElement) getDefaultRootElement();
- int parIndex = root.getElementIndex(offset);
- BranchElement paragraph = (BranchElement) root.getElement(parIndex);
-
- int index = paragraph.getElementIndex(offset);
- LeafElement content = (LeafElement) paragraph.getElement(index);
- // We might have to split the element at offset.
- split(content, offset);
- index = paragraph.getElementIndex(offset);
-
- int count = paragraph.getElementCount();
- // Store fractured elements.
- fracture = new Element[count - index];
- for (int i = index; i < count; ++i)
- fracture[i - index] = paragraph.getElement(i);
-
- // Delete fractured elements.
- paragraph.replace(index, count - index, new Element[0]);
-
- // Add this action to the document event.
- ElementEdit edit = new ElementEdit(paragraph, index, fracture,
- new Element[0]);
- documentEvent.addEdit(edit);
+ private Element insertParagraph(BranchElement par, int offset)
+ {
+ Element current = par.getElement(par.getElementIndex(offset));
+ Element[] res = split(current, offset, 0);
+ int index = par.getElementIndex(offset);
+ Element ret;
+ if (res[1] != null)
+ {
+ Element[] removed;
+ Element[] added;
+ if (res[0] == null)
+ {
+ removed = new Element[0];
+ if (res[1] instanceof BranchElement)
+ {
+ added = new Element[]{ res[1] };
+ ret = res[1];
+ }
+ else
+ {
+ ret = createBranchElement(par, null);
+ added = new Element[]{ ret, res[1] };
+ }
+ index++;
+ }
+ else
+ {
+ removed = new Element[]{ current };
+ if (res[1] instanceof BranchElement)
+ {
+ ret = res[1];
+ added = new Element[]{ res[0], res[1] };
+ }
+ else
+ {
+ ret = createBranchElement(par, null);
+ added = new Element[]{ res[0], ret, res[1] };
+ }
+ }
+ par.replace(index, removed.length, added);
+ addEdit(par, index, removed, added);
+ }
+ else
+ {
+ ret = createBranchElement(par, null);
+ Element[] added = new Element[]{ ret };
+ par.replace(index, 0, added);
+ addEdit(par, index, new Element[0], added);
+ }
+ return ret;
}
/**
@@ -675,100 +805,87 @@
*
* @param tag the element spec
*/
- void insertContentTag(ElementSpec tag)
+ private void insertContentTag(ElementSpec tag)
{
+ prepareContentInsertion();
int len = tag.getLength();
int dir = tag.getDirection();
if (dir == ElementSpec.JoinPreviousDirection)
{
- Element prev = getCharacterElement(offset);
- BranchElement prevParent = (BranchElement) prev.getParentElement();
- Element join = createLeafElement(prevParent, tag.getAttributes(),
- prev.getStartOffset(),
- Math.max(prev.getEndOffset(),
- offset + len));
- int ind = prevParent.getElementIndex(offset);
- if (ind == -1)
- ind = 0;
- Element[] add = new Element[]{join};
- prevParent.replace(ind, 1, add);
-
- // Add this action to the document event.
- ElementEdit edit = new ElementEdit(prevParent, ind,
- new Element[]{prev}, add);
- documentEvent.addEdit(edit);
+ // The mauve tests to this class show that a JoinPrevious insertion
+ // does not add any edits to the document event. To me this means
+ // that nothing is done here. The previous element naturally should
+ // expand so that it covers the new characters.
}
else if (dir == ElementSpec.JoinNextDirection)
{
- Element next = getCharacterElement(offset + len);
- BranchElement nextParent = (BranchElement) next.getParentElement();
- Element join = createLeafElement(nextParent, tag.getAttributes(),
- offset,
- next.getEndOffset());
- int ind = nextParent.getElementIndex(offset + len);
- if (ind == -1)
- ind = 0;
- Element[] add = new Element[]{join};
- nextParent.replace(ind, 1, add);
+ BranchElement paragraph = (BranchElement) elementStack.peek();
+ int currentIndex = paragraph.getElementIndex(offset);
+ Element current = paragraph.getElement(currentIndex);
+ Element next = paragraph.getElement(currentIndex + 1);
+
+ Element newEl1 = createLeafElement(paragraph,
+ current.getAttributes(),
+ current.getStartOffset(),
+ offset);
+ Element newEl2 = createLeafElement(paragraph,
+ current.getAttributes(),
+ offset,
+ next.getEndOffset());
+
+ Element[] add = new Element[] {newEl1, newEl2};
+ Element[] remove = new Element[] {current, next};
+ paragraph.replace(currentIndex, 2, add);
// Add this action to the document event.
- ElementEdit edit = new ElementEdit(nextParent, ind,
- new Element[]{next}, add);
- documentEvent.addEdit(edit);
+ addEdit(paragraph, currentIndex, remove, add);
}
else
{
- BranchElement par = (BranchElement) getParagraphElement(offset);
-
- int ind = par.getElementIndex(offset);
-
- // Make room for the element.
- // Cut previous element.
- Element prev = par.getElement(ind);
- if (prev != null && prev.getStartOffset() < offset)
+ BranchElement paragraph = (BranchElement) elementStack.peek();
+ int index = paragraph.getElementIndex(offset);
+ Element current = paragraph.getElement(index);
+
+ Element[] added;
+ Element[] removed;
+ Element[] splitRes = split(current, offset, length);
+ // Special case for when offset == startOffset or offset == endOffset.
+ if (splitRes[0] == null)
{
- Element cutPrev = createLeafElement(par, prev.getAttributes(),
- prev.getStartOffset(),
- offset);
- Element[] remove = new Element[]{prev};
- Element[] add = new Element[]{cutPrev};
- if (prev.getEndOffset() > offset + len)
- {
- Element rem = createLeafElement(par, prev.getAttributes(),
- offset + len,
- prev.getEndOffset());
- add = new Element[]{cutPrev, rem};
- }
-
- par.replace(ind, 1, add);
- documentEvent.addEdit(new ElementEdit(par, ind, remove, add));
- ind++;
+ added = new Element[2];
+ added[0] = createLeafElement(paragraph, tag.getAttributes(),
+ offset, offset + length);
+ added[1] = splitRes[1];
+ removed = new Element[0];
+ index++;
}
- // ind now points to the next element.
-
- // Cut next element if necessary.
- Element next = par.getElement(ind);
- if (next != null && next.getStartOffset() < offset + len)
+ else if (current.getStartOffset() == offset)
{
- Element cutNext = createLeafElement(par, next.getAttributes(),
- offset + len,
- next.getEndOffset());
- Element[] remove = new Element[]{next};
- Element[] add = new Element[]{cutNext};
- par.replace(ind, 1, add);
- documentEvent.addEdit(new ElementEdit(par, ind, remove,
- add));
+ added = new Element[2];
+ added[0] = createLeafElement(paragraph, tag.getAttributes(),
+ offset, offset + length);
+ added[1] = splitRes[1];
+ removed = new Element[] { current };
}
-
- // Insert new element.
- Element newEl = createLeafElement(par, tag.getAttributes(),
- offset, offset + len);
- Element[] added = new Element[]{newEl};
- par.replace(ind, 0, added);
- // Add this action to the document event.
- ElementEdit edit = new ElementEdit(par, ind, new Element[0],
- added);
- documentEvent.addEdit(edit);
+ else if (current.getEndOffset() - length == offset)
+ {
+ added = new Element[2];
+ added[0] = splitRes[0];
+ added[1] = createLeafElement(paragraph, tag.getAttributes(),
+ offset, offset + length);
+ removed = new Element[] { current };
+ }
+ else
+ {
+ added = new Element[3];
+ added[0] = splitRes[0];
+ added[1] = createLeafElement(paragraph, tag.getAttributes(),
+ offset, offset + length);
+ added[2] = splitRes[1];
+ removed = new Element[] { current };
+ }
+ paragraph.replace(index, removed.length, added);
+ addEdit(paragraph, index, removed, added);
}
offset += len;
}
@@ -800,6 +917,79 @@
result.replace(0, 0, children);
return result;
}
+
+ /**
+ * Adds an ElementChange for a given element modification to the document
+ * event. If there already is an ElementChange registered for this element,
+ * this method tries to merge the ElementChanges together. However, this
+ * is only possible if the indices of the new and old ElementChange are
+ * equal.
+ *
+ * @param e the element
+ * @param i the index of the change
+ * @param removed the removed elements, or null
+ * @param added the added elements, or null
+ */
+ private void addEdit(Element e, int i, Element[] removed, Element[] added)
+ {
+ // Perform sanity check first.
+ DocumentEvent.ElementChange ec = documentEvent.getChange(e);
+
+ // Merge the existing stuff with the new stuff.
+ Element[] oldAdded = ec == null ? null: ec.getChildrenAdded();
+ Element[] newAdded;
+ if (oldAdded != null && added != null)
+ {
+ if (ec.getIndex() <= i)
+ {
+ int index = i - ec.getIndex();
+ // Merge adds together.
+ newAdded = new Element[oldAdded.length + added.length];
+ System.arraycopy(oldAdded, 0, newAdded, 0, index);
+ System.arraycopy(added, 0, newAdded, index, added.length);
+ System.arraycopy(oldAdded, index, newAdded, index + added.length,
+ oldAdded.length - index);
+ i = ec.getIndex();
+ }
+ else
+ throw new AssertionError("Not yet implemented case.");
+ }
+ else if (added != null)
+ newAdded = added;
+ else if (oldAdded != null)
+ newAdded = oldAdded;
+ else
+ newAdded = new Element[0];
+
+ Element[] oldRemoved = ec == null ? null: ec.getChildrenRemoved();
+ Element[] newRemoved;
+ if (oldRemoved != null && removed != null)
+ {
+ if (ec.getIndex() <= i)
+ {
+ int index = i - ec.getIndex();
+ // Merge removes together.
+ newRemoved = new Element[oldRemoved.length + removed.length];
+ System.arraycopy(oldAdded, 0, newRemoved, 0, index);
+ System.arraycopy(removed, 0, newRemoved, index, removed.length);
+ System.arraycopy(oldRemoved, index, newRemoved,
+ index + removed.length,
+ oldRemoved.length - index);
+ i = ec.getIndex();
+ }
+ else
+ throw new AssertionError("Not yet implemented case.");
+ }
+ else if (removed != null)
+ newRemoved = removed;
+ else if (oldRemoved != null)
+ newRemoved = oldRemoved;
+ else
+ newRemoved = new Element[0];
+
+ // Replace the existing edit for the element with the merged.
+ documentEvent.addEdit(new ElementEdit(e, i, newRemoved, newAdded));
+ }
}
/**
@@ -1353,7 +1543,8 @@
// Finally we must update the document structure and fire the insert update
// event.
buffer.insert(offset, index - offset, data, ev);
- fireInsertUpdate(ev);
+ if (ev.modified)
+ fireInsertUpdate(ev);
writeUnlock();
}