This is the first part of the work I'm doing on DefaultStyledDocument.
This fixes a couple issues associated with PR 24744 but the bug is not
completely fixed yet, so the testcase for that PR still crashes.  I will
continue to work on this but these changes are ready to go in and may
help get some text/html test apps up and running so I'm committing this
now.

2005-12-19  Anthony Balkissoon  <[EMAIL PROTECTED]>

        * javax/swing/text/DefaultStyledDocument.java:
        (endOffset): New field.
        (ElementBuffer.insert): If length is 0 return early.  Set the endOffset
        variable here.
        (ElementBuffer.insertContentTag): Added special case for when insertion
        covers entire range of the current Element.  Replaced (offset + length)
        with endOffset everywhere.  Added checks to see if the insertion has 
        the same attributes as the surrounding Element(s), in which case we
        don't split them, we join them.
        (SectionElement.getName): Return the field SectionElementName instead 
        of hardcoding a return value.
        (getLogicalStyle): As the specs require, if the attributes from the 
        resolve parent aren't of type Style, return null.
        (setCharacterAttributes): Exit early if length is 0.  Obtain a 
        write lock for the body of this method.  Changed the way we iterate
        through the paragraphs to be more efficient and more intuitive.  Added
        AttributeUndoableEdits to the DocumentEvent and fired the 
        UndoableEditUpdate.
        (setLogicalStyle): Return early if the Element is null.  Obtain a write
        lock for the body of this method.  Fire the appropriate DocumentEvent.
        (setParagraphAttributes): Changed the way we iterated through the 
        paragraphs because it was incorrect and unintuitive.  Obtained a write
        lock within a try/finally block.  Added AttributeUndoableEdits to the 
        DocumentEvent and fired the event.
        (insertUpdate): Changed the way we iterate because we cannot assume 
        the Segment holds the characters starting from index 0.  Give the 
        startTag the same attributes as the paragraph.  Removed the useless
        offset += len line because len is 0.
        (printElements): New temporary debugging method.
        (attributeSetsAreSame): New method.

--Tony
Index: javax/swing/text/DefaultStyledDocument.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/DefaultStyledDocument.java,v
retrieving revision 1.20
diff -u -r1.20 DefaultStyledDocument.java
--- javax/swing/text/DefaultStyledDocument.java	19 Dec 2005 10:21:39 -0000	1.20
+++ javax/swing/text/DefaultStyledDocument.java	19 Dec 2005 22:36:04 -0000
@@ -48,6 +48,7 @@
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
 import javax.swing.event.DocumentEvent;
+import javax.swing.event.UndoableEditEvent;
 import javax.swing.undo.AbstractUndoableEdit;
 import javax.swing.undo.UndoableEdit;
 
@@ -428,6 +429,9 @@
 
     /** Holds the length of structural changes. */
     private int length;
+    
+    /** Holds the end offset for structural changes. **/
+    private int endOffset;
 
     /**
      * The number of inserted end tags. This is a counter which always gets
@@ -675,8 +679,11 @@
     public void insert(int offset, int length, ElementSpec[] data,
                        DefaultDocumentEvent ev)
     {
+      if (length == 0)
+        return;
       this.offset = offset;
       this.length = length;
+      this.endOffset = offset + length;
       documentEvent = ev;
       // Push the root and the paragraph at offset onto the element stack.
       elementStack.clear();
@@ -799,7 +806,7 @@
         }
       return ret;
     }
-
+    
     /**
      * Inserts a content element into the document structure.
      *
@@ -810,6 +817,7 @@
       prepareContentInsertion();
       int len = tag.getLength();
       int dir = tag.getDirection();
+      AttributeSet tagAtts = createLeafElement(null, null, 0, 0).getAttributes();
       if (dir == ElementSpec.JoinPreviousDirection)
         {
           // The mauve tests to this class show that a JoinPrevious insertion
@@ -845,45 +853,103 @@
           BranchElement paragraph = (BranchElement) elementStack.peek();
           int index = paragraph.getElementIndex(offset);
           Element current = paragraph.getElement(index);
-
+          
           Element[] added;
-          Element[] removed;
+          Element[] removed = new Element[] {current};
           Element[] splitRes = split(current, offset, length);
-          // Special case for when offset == startOffset or offset == endOffset.
           if (splitRes[0] == null)
             {
               added = new Element[2];
-              added[0] = createLeafElement(paragraph, tag.getAttributes(),
-                                           offset, offset + length);
+              added[0] = createLeafElement(paragraph, tagAtts,
+                                           offset, endOffset);
               added[1] = splitRes[1];
               removed = new Element[0];
               index++;
             }
-          else if (current.getStartOffset() == offset)
+          else if (current.getStartOffset() == offset
+                   && current.getEndOffset() - 1 == endOffset)
             {
-              added = new Element[2];
-              added[0] = createLeafElement(paragraph, tag.getAttributes(),
-                                           offset, offset + length);
-              added[1] = splitRes[1];
-              removed = new Element[] { current };
+              // This is if the new insertion covers the entire range of 
+              // <code>current</code>.  This will generally happen for the 
+              // first insertion into a new paragraph.
+              added = new Element[1];
+              added[0] = createLeafElement(paragraph, tagAtts,
+                                           offset, endOffset + 1);              
             }
-          else if (current.getEndOffset() - length == offset)
+          else if (current.getStartOffset() == offset)
+            {                
+              // This is if the new insertion happens immediately before 
+              // the <code>current</code> Element.  In this case, if there is
+              // a split, there are 2 resulting Elements.
+              
+              AttributeSet splitAtts = splitRes[1].getAttributes();              
+              if (attributeSetsAreSame(tagAtts, splitAtts))
+                {
+                  // If the attributes of the adjacent Elements are the same
+                  // then we don't split them, we join them into one.
+                  added = new Element [1];
+                  added[0] = createLeafElement(paragraph, tagAtts, offset,
+                                               splitRes[1].getEndOffset());
+                }
+              else
+                {
+                  // Otherwise we have 2 resulting Elements.
+                  added = new Element[2];
+                  added[0] = createLeafElement(paragraph, tagAtts,
+                                               offset, endOffset);
+                  added[1] = splitRes[1];
+                }
+            }
+          else if (current.getEndOffset() == endOffset + 1)
             {
-              added = new Element[2];
-              added[0] = splitRes[0];
-              added[1] = createLeafElement(paragraph, tag.getAttributes(),
-                                           offset, offset + length);
-              removed = new Element[] { current };
+              // This is if the new insertion happens right at the end of 
+              // the <code>current</code> Element.  In this case, if there is 
+              // a split, there are 2 resulting Elements.
+              AttributeSet splitAtts = splitRes[0].getAttributes();              
+              if (attributeSetsAreSame(tagAtts, splitAtts))
+                {
+                  // If the attributes are the same, no need to split.
+                  added = new Element [1];
+                  added[0] = createLeafElement(paragraph, tagAtts,
+                                               splitRes[0].getStartOffset(),
+                                               endOffset + 1);
+                }
+              else
+                {
+                  // Otherwise there are 2 resulting Elements.
+                  added = new Element[2];
+                  added[0] = splitRes[0];
+                  added[1] = createLeafElement(paragraph, tagAtts, offset,
+                                               endOffset + 1);
+                }
             }
           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 };
-            }
+              // This is if the new insertion is in the middle of the 
+              // <code>current</code> Element.  In this case, if there is a 
+              // split, there will be 3 resulting Elements.  Note, since 
+              // <code>splitRes[0]</code> and <code>splitRes[1]</code> were 
+              // once the same Element, they have the same attributes.
+              AttributeSet split1Atts = splitRes[0].getAttributes();            
+              
+              if (attributeSetsAreSame(tagAtts, split1Atts))
+                {
+                  // If the attributes are the same, no need to split.
+                  added = new Element [1];
+                  added[0] = createLeafElement(paragraph, tagAtts,
+                                               splitRes[0].getStartOffset(),
+                                               splitRes[1].getEndOffset());
+                }
+              else
+                {
+                  // Otherwise there are 3 resulting Elements.
+                  added = new Element[3];
+                  added[0] = splitRes[0];
+                  added[1] = createLeafElement(paragraph, tagAtts, offset,
+                                               endOffset);
+                  added[2] = splitRes[1];
+                }
+            }          
           paragraph.replace(index, removed.length, added);
           addEdit(paragraph, index, removed, added);
         }
@@ -1014,7 +1080,7 @@
      */
     public String getName()
     {
-      return "section";
+      return SectionElementName;
     }
   }
 
@@ -1233,7 +1299,11 @@
   {
     Element paragraph = getParagraphElement(position);
     AttributeSet attributes = paragraph.getAttributes();
-    return (Style) attributes.getResolveParent();
+    AttributeSet a = attributes.getResolveParent();
+    // If the resolve parent is not of type Style, we return null.
+    if (a instanceof Style)
+      return (Style) a;
+    return null;
   }
 
   /**
@@ -1302,50 +1372,54 @@
 				     AttributeSet attributes,
 				     boolean replace)
   {
-    DefaultDocumentEvent ev =
-      new DefaultDocumentEvent(offset, length,
-			       DocumentEvent.EventType.CHANGE);
-
-    // Modify the element structure so that the interval begins at an element
-    // start and ends at an element end.
-    buffer.change(offset, length, ev);
-
-    Element root = getDefaultRootElement();
-    // Visit all paragraph elements within the specified interval
-    int paragraphCount =  root.getElementCount();
-    for (int pindex = 0; pindex < paragraphCount; pindex++)
+    // Exit early if length is 0, so no DocumentEvent is created or fired.
+    if (length == 0)
+      return;
+    try
       {
-        Element paragraph = root.getElement(pindex);
-        // Skip paragraphs that lie outside the interval.
-        if ((paragraph.getStartOffset() > offset + length)
-            || (paragraph.getEndOffset() < offset))
-          continue;
-
-        // Visit content elements within this paragraph
-        int contentCount = paragraph.getElementCount();
-        for (int cindex = 0; cindex < contentCount; cindex++)
+        // Must obtain a write lock for this method.  writeLock() and
+        // writeUnlock() should always be in try/finally block to make
+        // sure that locking happens in a balanced manner.
+        writeLock();
+        DefaultDocumentEvent ev = 
+          new DefaultDocumentEvent(
+                                   offset, 
+                                   length, 
+                                   DocumentEvent.EventType.CHANGE);
+
+        // Modify the element structure so that the interval begins at an
+        // element
+        // start and ends at an element end.
+        buffer.change(offset, length, ev);
+
+        Element root = getDefaultRootElement();
+        // Visit all paragraph elements within the specified interval
+        int end = offset + length;
+        Element curr;
+        for (int pos = offset; pos < end; )
           {
-            Element content = paragraph.getElement(cindex);
-            // Skip content that lies outside the interval.
-            if ((content.getStartOffset() > offset + length)
-                || (content.getEndOffset() < offset))
-              continue;
-
-            if (content instanceof AbstractElement)
-              {
-                AbstractElement el = (AbstractElement) content;
-                if (replace)
-                  el.removeAttributes(el);
-                el.addAttributes(attributes);
-              }
-            else
-              throw new AssertionError("content elements are expected to be"
-                                       + "instances of "
-		       + "javax.swing.text.AbstractDocument.AbstractElement");
+            // Get the CharacterElement at offset pos.
+            curr = getCharacterElement(pos);
+            if (pos == curr.getEndOffset())
+              break;
+            
+            MutableAttributeSet a = (MutableAttributeSet) curr.getAttributes();
+            ev.addEdit(new AttributeUndoableEdit(curr, attributes, replace));
+            // If replace is true, remove all the old attributes.
+            if (replace)
+              a.removeAttributes(a);
+            // Add all the new attributes.
+            a.addAttributes(attributes);
+            // Increment pos so we can check the next CharacterElement.
+            pos = curr.getEndOffset();
           }
+        fireChangedUpdate(ev);
+        fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+      }
+    finally
+      {
+        writeUnlock();
       }
-
-    fireChangedUpdate(ev);
   }
   
   /**
@@ -1357,14 +1431,36 @@
   public void setLogicalStyle(int position, Style style)
   {
     Element el = getParagraphElement(position);
-    if (el instanceof AbstractElement)
-      {
-        AbstractElement ael = (AbstractElement) el;
-        ael.setResolveParent(style);
-      }
-    else
-      throw new AssertionError("paragraph elements are expected to be"
-         + "instances of javax.swing.text.AbstractDocument.AbstractElement");
+    // getParagraphElement doesn't return null but subclasses might so
+    // we check for null here.
+    if (el == null)
+      return;
+    try
+    {
+      writeLock();    
+      if (el instanceof AbstractElement)
+        {
+          AbstractElement ael = (AbstractElement) el;
+          ael.setResolveParent(style);
+          int start = el.getStartOffset();
+          int end = el.getEndOffset();
+          DefaultDocumentEvent ev = 
+            new DefaultDocumentEvent (
+                                      start, 
+                                      end - start, 
+                                      DocumentEvent.EventType.CHANGE);
+          // FIXME: Add an UndoableEdit to this event and fire it.
+          fireChangedUpdate(ev);
+        }
+      else
+        throw new 
+        AssertionError("paragraph elements are expected to be"
+                       + "instances of AbstractDocument.AbstractElement");
+    }
+    finally
+    {
+      writeUnlock();
+    }
   }
 
   /**
@@ -1380,22 +1476,48 @@
                                      AttributeSet attributes,
                                      boolean replace)
   {
-    writeLock();
-    int index = offset;
-    while (index < offset + length)
+    try
       {
-        AbstractElement par = (AbstractElement) getParagraphElement(index);
-        // If we have already seen this paragraph element, then exit the loop.
-        if (par.getEndOffset() + 1 == index)
-          break;
-
-        AttributeContext ctx = getAttributeContext();
-        if (replace)
-          par.removeAttributes(par);
-        par.addAttributes(attributes);
-        index = par.getEndOffset() + 1;
+        // Must obtain a write lock for this method.  writeLock() and
+        // writeUnlock() should always be in try/finally blocks to make
+        // sure that locking occurs in a balanced manner.
+        writeLock();
+        
+        // Create a DocumentEvent to use for changedUpdate().
+        DefaultDocumentEvent ev = 
+          new DefaultDocumentEvent (
+                                    offset, 
+                                    length, 
+                                    DocumentEvent.EventType.CHANGE);
+        
+        // Have to iterate through all the _paragraph_ elements that are
+        // contained or partially contained in the interval
+        // (offset, offset + length).
+        Element rootElement = getDefaultRootElement();
+        int startElement = rootElement.getElementIndex(offset);
+        int endElement = rootElement.getElementIndex(offset + length - 1);
+        if (endElement < startElement)
+          endElement = startElement;
+        
+        for (int i = startElement; i <= endElement; i++)
+          {
+            Element par = rootElement.getElement(i);
+            MutableAttributeSet a = (MutableAttributeSet) par.getAttributes();
+            // Add the change to the DocumentEvent.
+            ev.addEdit(new AttributeUndoableEdit(par, attributes, replace));
+            // If replace is true remove the old attributes.
+            if (replace)
+              a.removeAttributes(a);
+            // Add the new attributes.
+            a.addAttributes(attributes);
+          }
+        fireChangedUpdate(ev);
+        fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+      }
+    finally
+      {
+        writeUnlock();
       }
-    writeUnlock();
   }
 
   /**
@@ -1407,10 +1529,12 @@
    */
   protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr)
   {
-    super.insertUpdate(ev, attr);
+    super.insertUpdate(ev, attr);    
     int offset = ev.getOffset();
     int length = ev.getLength();
     int endOffset = offset + length;
+    AttributeSet paragraphAttributes = 
+      getParagraphElement(endOffset).getAttributes();
     Segment txt = new Segment();
     try
       {
@@ -1429,7 +1553,8 @@
     Element prev = getCharacterElement(offset);
     Element next = getCharacterElement(endOffset);
 
-    for (int i = offset; i < endOffset; ++i)
+    int segmentEnd = txt.offset + txt.count;
+    for (int i = txt.offset; i < segmentEnd; ++i)
       {
         len++;
         if (txt.array[i] == '\n')
@@ -1439,7 +1564,7 @@
 
             // If we are at the last index, then check if we could probably be
             // joined with the next element.
-            if (i == endOffset - 1)
+            if (i == segmentEnd - 1)
               {
                 if (next.getAttributes().isEqual(attr))
                   spec.setDirection(ElementSpec.JoinNextDirection);
@@ -1457,13 +1582,12 @@
             // Add ElementSpecs for the newline.
             ElementSpec endTag = new ElementSpec(null, ElementSpec.EndTagType);
             specs.add(endTag);
-            ElementSpec startTag = new ElementSpec(null,
+            ElementSpec startTag = new ElementSpec(paragraphAttributes,
                                                    ElementSpec.StartTagType);
             startTag.setDirection(ElementSpec.JoinFractureDirection);
             specs.add(startTag);
 
             len = 0;
-            offset += len;
           }
       }
 
@@ -1484,7 +1608,7 @@
 
         specs.add(spec);
       }
-
+    
     ElementSpec[] elSpecs =
       (ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]);
 
@@ -1512,6 +1636,20 @@
     // Nothing to do here. This is intended to be overridden by subclasses.
   }
 
+  void printElements (Element start, int pad)
+  {
+    for (int i = 0; i < pad; i++)
+      System.out.print(" ");
+    if (pad == 0)
+      System.out.println ("ROOT ELEMENT ("+start.getStartOffset()+", "+start.getEndOffset()+")");
+    else if (start instanceof AbstractDocument.BranchElement)
+      System.out.println ("BranchElement ("+start.getStartOffset()+", "+start.getEndOffset()+")");
+    else
+      System.out.println ("LeafElement ("+start.getStartOffset()+", "+start.getEndOffset()+")");
+    for (int i = 0; i < start.getElementCount(); i ++)
+      printElements (start.getElement(i), pad+3);
+  }
+  
   /**
    * Inserts a bulk of structured content at once.
    *
@@ -1545,7 +1683,7 @@
         // If there was no content inserted then exit early.
         if (length == 0)
           return;
-
+        
         UndoableEdit edit = content.insertString(offset,
                                                  contentBuffer.toString());
 
@@ -1600,4 +1738,9 @@
         throw err;
       }
   }
+  
+  static boolean attributeSetsAreSame (AttributeSet a, AttributeSet b)
+  {
+    return (a == null && b == null) || (a != null && a.isEqual(b));
+  }
 }
_______________________________________________
Classpath-patches mailing list
Classpath-patches@gnu.org
http://lists.gnu.org/mailman/listinfo/classpath-patches

Reply via email to