This fixes a couple of issues in the Swing text code for better
stability. Also this removes the scrollRectToVisible() that causes the
view to scroll around when activating a link.

2006-12-06  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/JEditorPane.java
        (getStream): Buffer the stream for efficiency.
        (setPage): Don't scroll the view at this point.
        * javax/swing/plaf/basic/BasicTextUI.java
        (RootView.paint): Call RootView's setSize to get synchronization.
        (RootView.setSize): Synchronize to prevent race in layout code.
        * javax/swing/text/AbstractDocument.java
        (notifyListeners): New field.
        (fireChangedUpdate): Track notifyListener field.
        (fireRemoveUpdate): Track notifyListener field.
        (fireIndertUpdate): Track notifyListener field.
        (writeLock): Check notifyListener and throw IllegalStateException.
        * javax/swing/text/View.java
        (preferenceChanged): Create local var for better thread safety and
        more efficiency.

/Roman

Index: javax/swing/JEditorPane.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JEditorPane.java,v
retrieving revision 1.41
diff -u -1 -5 -r1.41 JEditorPane.java
--- javax/swing/JEditorPane.java	3 Dec 2006 16:41:55 -0000	1.41
+++ javax/swing/JEditorPane.java	6 Dec 2006 20:18:15 -0000
@@ -28,31 +28,31 @@
 executable, regardless of the license terms of these independent
 modules, and to copy and distribute the resulting executable under
 terms of your choice, provided that you also meet, for each linked
 independent module, the terms and conditions of the license of that
 module.  An independent module is a module which is not derived from
 or based on this library.  If you modify this library, you may extend
 this exception to your version of the library, but you are not
 obligated to do so.  If you do not wish to do so, delete this
 exception statement from your version. */
 
 
 package javax.swing;
 
 import java.awt.Container;
 import java.awt.Dimension;
-import java.awt.Rectangle;
+import java.io.BufferedInputStream;
 import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.io.StringReader;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLConnection;
 import java.util.HashMap;
 
 import javax.accessibility.AccessibleContext;
 import javax.accessibility.AccessibleHyperlink;
 import javax.accessibility.AccessibleHypertext;
 import javax.accessibility.AccessibleStateSet;
@@ -886,31 +886,31 @@
 
   public URL getPage()
   {
     return loader != null ? loader.page : null;
   }
 
   protected InputStream getStream(URL page)
     throws IOException
   {
     URLConnection conn = page.openConnection();
     // Try to detect the content type of the stream data.
     String type = conn.getContentType();
     if (type != null)
       setContentType(type);
     InputStream stream = conn.getInputStream();
-    return stream;
+    return new BufferedInputStream(stream);
   }
 
   public String getText()
   {
     return super.getText();
   }
 
   public String getUIClassID()
   {
     return "EditorPaneUI";
   }
 
   public boolean isFocusCycleRoot()
   {
     return focus_root;
@@ -1049,34 +1049,30 @@
    */
   public void setPage(String url) throws IOException
   {
     setPage(new URL(url));
   }
 
   /**
    * Sets the current URL being displayed.  
    */
   public void setPage(URL page) throws IOException
   {
     if (page == null)
       throw new IOException("invalid url");
 
     URL old = getPage();
-    // Reset scrollbar when URL actually changes.
-    if (! page.equals(old) && page.getRef() == null)
-      scrollRectToVisible(new Rectangle(0, 0, 1, 1));
-
     // Only reload if the URL doesn't point to the same file.
     // This is not the same as equals because there might be different
     // URLs on the same file with different anchors.
     if (old == null || ! old.sameFile(page))
       {
         InputStream in = getStream(page);
         if (editorKit != null)
           {
             Document doc = editorKit.createDefaultDocument();
             doc.putProperty(Document.StreamDescriptionProperty, page);
 
             if (loader != null)
               loader.cancel();
             loader = new PageLoader(doc, in, old, page);
 
Index: javax/swing/plaf/basic/BasicTextUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicTextUI.java,v
retrieving revision 1.96
diff -u -1 -5 -r1.96 BasicTextUI.java
--- javax/swing/plaf/basic/BasicTextUI.java	20 Nov 2006 10:38:09 -0000	1.96
+++ javax/swing/plaf/basic/BasicTextUI.java	6 Dec 2006 20:18:16 -0000
@@ -350,48 +350,53 @@
         count = 1;
       return count;
     }
 
     /**
      * Returns the <code>Container</code> that contains this view. This
      * normally will be the text component that is managed by this TextUI.
      *
      * @return the <code>Container</code> that contains this view
      */
     public Container getContainer()
     {
       return textComponent;
     }
 
-    public void setSize(float w, float h)
+    /**
+     * Sets the size of the renderer. This is synchronized because that
+     * potentially triggers layout and we don't want more than one thread
+     * playing with the layout information.
+     */
+    public synchronized void setSize(float w, float h)
     {
       if (view != null)
         view.setSize(w, h);
     }
 
     /**
      * Paints the view. This is delegated to the real root view.
      *
      * @param g the <code>Graphics</code> context to paint to
      * @param s the allocation for the View
      */
     public void paint(Graphics g, Shape s)
     {
       if (view != null)
         {
           Rectangle b = s instanceof Rectangle ? (Rectangle) s : s.getBounds();
-          view.setSize(b.width, b.height);
+          setSize(b.width, b.height);
           view.paint(g, s);
         }
     }
 
 
     /**
      * Maps a position in the document into the coordinate space of the View.
      * The output rectangle usually reflects the font height but has a width
      * of zero.
      *
      * This is delegated to the real root view.
      *
      * @param position the position of the character in the model
      * @param a the area that is occupied by the view
      * @param bias either [EMAIL PROTECTED] Position.Bias#Forward} or
Index: javax/swing/text/AbstractDocument.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/AbstractDocument.java,v
retrieving revision 1.63
diff -u -1 -5 -r1.63 AbstractDocument.java
--- javax/swing/text/AbstractDocument.java	20 Nov 2006 10:51:37 -0000	1.63
+++ javax/swing/text/AbstractDocument.java	6 Dec 2006 20:18:17 -0000
@@ -164,30 +164,36 @@
    */
   private int numWriters = 0;  
 
   /** An instance of a DocumentFilter.FilterBypass which allows calling
    * the insert, remove and replace method without checking for an installed
    * document filter.
    */
   private DocumentFilter.FilterBypass bypass;
 
   /**
    * The bidi root element.
    */
   private BidiRootElement bidiRoot;
 
   /**
+   * True when we are currently notifying any listeners. This is used
+   * to detect illegal situations in writeLock().
+   */
+  private transient boolean notifyListeners;
+
+  /**
    * Creates a new <code>AbstractDocument</code> with the specified
    * [EMAIL PROTECTED] Content} model.
    *
    * @param doc the <code>Content</code> model to be used in this
    *        <code>Document<code>
    *
    * @see GapContent
    * @see StringContent
    */
   protected AbstractDocument(Content doc)
   {
     this(doc, StyleContext.getDefaultStyleContext());
   }
 
   /**
@@ -313,62 +319,83 @@
    *         location in the documents content model
    */
   public synchronized Position createPosition(final int offset)
     throws BadLocationException
   {
     return content.createPosition(offset);
   }
 
   /**
    * Notifies all registered listeners when the document model changes.
    *
    * @param event the <code>DocumentEvent</code> to be fired
    */
   protected void fireChangedUpdate(DocumentEvent event)
   {
-    DocumentListener[] listeners = getDocumentListeners();
-
-    for (int index = 0; index < listeners.length; ++index)
-      listeners[index].changedUpdate(event);
+    notifyListeners = true;
+    try
+      {
+        DocumentListener[] listeners = getDocumentListeners();
+        for (int index = 0; index < listeners.length; ++index)
+          listeners[index].changedUpdate(event);
+      }
+    finally
+      {
+        notifyListeners = false;
+      }
   }
 
   /**
    * Notifies all registered listeners when content is inserted in the document
    * model.
    *
    * @param event the <code>DocumentEvent</code> to be fired
    */
   protected void fireInsertUpdate(DocumentEvent event)
   {
-    DocumentListener[] listeners = getDocumentListeners();
-
-    for (int index = 0; index < listeners.length; ++index)
-      listeners[index].insertUpdate(event);
+    notifyListeners = true;
+    try
+      {
+        DocumentListener[] listeners = getDocumentListeners();
+        for (int index = 0; index < listeners.length; ++index)
+          listeners[index].insertUpdate(event);
+      }
+    finally
+      {
+        notifyListeners = false;
+      }
   }
 
   /**
    * Notifies all registered listeners when content is removed from the
    * document model.
    *
    * @param event the <code>DocumentEvent</code> to be fired
    */
   protected void fireRemoveUpdate(DocumentEvent event)
   {
-    DocumentListener[] listeners = getDocumentListeners();
-
-    for (int index = 0; index < listeners.length; ++index)
-      listeners[index].removeUpdate(event);
+    notifyListeners = true;
+    try
+      {
+        DocumentListener[] listeners = getDocumentListeners();
+        for (int index = 0; index < listeners.length; ++index)
+          listeners[index].removeUpdate(event);
+      }
+    finally
+      {
+        notifyListeners = false;
+      }
   }
 
   /**
    * Notifies all registered listeners when an <code>UndoableEdit</code> has
    * been performed on this <code>Document</code>.
    *
    * @param event the <code>UndoableEditEvent</code> to be fired
    */
   protected void fireUndoableEditUpdate(UndoableEditEvent event)
   {
     UndoableEditListener[] listeners = getUndoableEditListeners();
 
     for (int index = 0; index < listeners.length; ++index)
       listeners[index].undoableEditHappened(event);
   }
@@ -1326,30 +1353,32 @@
     properties = p;
   }
 
   /**
    * Blocks until a write lock can be obtained.  Must wait if there are 
    * readers currently reading or another thread is currently writing.
    */
   protected synchronized final void writeLock()
   {
     try
       {
         while (numReaders > 0 || currentWriter != null)
           {
             if (Thread.currentThread() == currentWriter)
               {
+                if (notifyListeners)
+                  throw new IllegalStateException("Mutation during notify");
                 numWriters++;
                 return;
               }
             wait();
           }
         currentWriter = Thread.currentThread();
         numWriters = 1;
       }
     catch (InterruptedException ex)
       {
         throw new Error("Interupted during grab write lock");
       }
   }
 
   /**
Index: javax/swing/text/View.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/View.java,v
retrieving revision 1.38
diff -u -1 -5 -r1.38 View.java
--- javax/swing/text/View.java	10 Nov 2006 20:24:02 -0000	1.38
+++ javax/swing/text/View.java	6 Dec 2006 20:18:17 -0000
@@ -323,32 +323,33 @@
           text = getView(index).getToolTipText(x, y, allocation);
       }
     return text;
   }
 
   /**
    * @since 1.3
    */
   public Graphics getGraphics()
   {
     return getContainer().getGraphics();
   }
 
   public void preferenceChanged(View child, boolean width, boolean height)
   {
-    if (parent != null)
-      parent.preferenceChanged(this, width, height);
+    View p = getParent();
+    if (p != null)
+      p.preferenceChanged(this, width, height);
   }
 
   public int getBreakWeight(int axis, float pos, float len)
   {
     int weight = BadBreakWeight;
     if (len > getPreferredSpan(axis))
       weight = GoodBreakWeight;
     return weight;
   }
 
   public View breakView(int axis, int offset, float pos, float len)
   {
     return this;
   }
 

Reply via email to