You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@netbeans.apache.org by GitBox <gi...@apache.org> on 2018/09/07 15:50:51 UTC

[GitHub] matthiasblaesing closed pull request #848: [NETBEANS-406] Fix a potential memory leak involving DocumentUtilitie…

matthiasblaesing closed pull request #848: [NETBEANS-406] Fix a potential memory leak involving DocumentUtilitie…
URL: https://github.com/apache/incubator-netbeans/pull/848
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/ide/editor.lib2/nbproject/project.properties b/ide/editor.lib2/nbproject/project.properties
index 1a5e35fd13..9df291aa4c 100644
--- a/ide/editor.lib2/nbproject/project.properties
+++ b/ide/editor.lib2/nbproject/project.properties
@@ -18,7 +18,7 @@
 is.autoload=true
 javac.source=1.7
 javac.compilerargs=-Xlint:unchecked
-spec.version.base=2.20.0
+spec.version.base=2.21.0
 
 javadoc.arch=${basedir}/arch.xml
 javadoc.apichanges=${basedir}/apichanges.xml
diff --git a/ide/editor.lib2/nbproject/project.xml b/ide/editor.lib2/nbproject/project.xml
index a05fa92b6f..af2f1e267d 100644
--- a/ide/editor.lib2/nbproject/project.xml
+++ b/ide/editor.lib2/nbproject/project.xml
@@ -66,7 +66,7 @@
                     <compile-dependency/>
                     <run-dependency>
                         <release-version>1</release-version>
-                        <specification-version>1.64</specification-version>
+                        <specification-version>1.68</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewOp.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewOp.java
index 9cc825076a..b95c60d218 100644
--- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewOp.java
+++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewOp.java
@@ -909,7 +909,7 @@ public void run() {
             Document doc = docView.getDocument();
             updateTextLimitLine(doc);
             clearStatusBits(AVAILABLE_WIDTH_VALID);
-            DocumentUtilities.addPropertyChangeListener(doc, WeakListeners.propertyChange(this, doc));
+            DocumentUtilities.addWeakPropertyChangeListener(doc, this);
         }
     }
     
diff --git a/ide/editor.util/apichanges.xml b/ide/editor.util/apichanges.xml
index a525a031a4..3592a08fc2 100644
--- a/ide/editor.util/apichanges.xml
+++ b/ide/editor.util/apichanges.xml
@@ -83,6 +83,24 @@ is the proper place.
 <!-- ACTUAL CHANGES BEGIN HERE: -->
 
   <changes>
+      <change id="DocumentUtilities.addWeakPropertyChangeListener">
+          <summary>Added support for adding weak property change listeners to documents</summary>
+          <version major="1" minor="68"/>
+          <date day="6" month="9" year="2018"/>
+          <author login="matthiasblaesing"/>
+          <compatibility addition="yes" binary="compatible" semantic="compatible" />
+          <description>
+              <p>
+                  Added support for adding weak property change listeners to documents
+                  by providing
+                  <code>DocumentUtilities.addWeakPropertyChangeListener(doc, listenerImplementation)</code>.
+                  The supplied <code>listenerImplementation</code> is not held via
+                  a strong reference, but a weak reference and so can be GCed
+                  independendly of the referencing document.
+              </p>
+          </description>
+          <issue number="NETBEANS-406"/>
+      </change>
       <change id="GapListAddAll">
           <summary>Added methods to append elements to a GapList</summary>
           <version major="1" minor="64"/>
diff --git a/ide/editor.util/manifest.mf b/ide/editor.util/manifest.mf
index 9735a30e02..2405358881 100644
--- a/ide/editor.util/manifest.mf
+++ b/ide/editor.util/manifest.mf
@@ -2,4 +2,4 @@ Manifest-Version: 1.0
 OpenIDE-Module: org.netbeans.modules.editor.util/1
 OpenIDE-Module-Localizing-Bundle: org/netbeans/lib/editor/util/Bundle.properties
 AutoUpdate-Show-In-Client: false
-OpenIDE-Module-Specification-Version: 1.67
+OpenIDE-Module-Specification-Version: 1.68
diff --git a/ide/editor.util/nbproject/project.xml b/ide/editor.util/nbproject/project.xml
index f05403b32d..401d78c458 100644
--- a/ide/editor.util/nbproject/project.xml
+++ b/ide/editor.util/nbproject/project.xml
@@ -24,7 +24,16 @@
     <configuration>
         <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
             <code-name-base>org.netbeans.modules.editor.util</code-name-base>
-            <module-dependencies/>
+            <module-dependencies>
+                <dependency>
+                    <code-name-base>org.openide.util</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>9.10</specification-version>
+                    </run-dependency>
+                </dependency>
+            </module-dependencies>
             <test-dependencies>
                 <test-type>
                     <name>unit</name>
diff --git a/ide/editor.util/src/org/netbeans/lib/editor/util/swing/DocumentUtilities.java b/ide/editor.util/src/org/netbeans/lib/editor/util/swing/DocumentUtilities.java
index a070e82de0..b980cc8e89 100644
--- a/ide/editor.util/src/org/netbeans/lib/editor/util/swing/DocumentUtilities.java
+++ b/ide/editor.util/src/org/netbeans/lib/editor/util/swing/DocumentUtilities.java
@@ -46,6 +46,7 @@
 import org.netbeans.lib.editor.util.AbstractCharSequence;
 import org.netbeans.lib.editor.util.CharSequenceUtilities;
 import org.netbeans.lib.editor.util.CompactMap;
+import org.openide.util.WeakListeners;
 
 /**
  * Various utility methods related to swing text documents.
@@ -966,6 +967,9 @@ public static long getDocumentTimestamp(Document doc) {
      * <p>Additionally, the list of document properties that clients can listen on
      * is not part of this contract.
      *
+     * <p>Note that this method does <em>not</em> work with {@code WeakListeners}.
+     * Use {@link #addWeakPropertyChangeListener} for that purpose.
+     *
      * @param doc The document to add the listener to.
      * @param l The listener to add to the document.
      *
@@ -992,4 +996,34 @@ public static void removePropertyChangeListener(Document doc, PropertyChangeList
             pcs.removePropertyChangeListener(l);
         }
     }
+
+    /**
+     * Adds a weak <code>PropertyChangeListener</code> to a document.
+     *
+     * <p>In general, document properties are key-value pairs where both the key
+     * and the value can be any <code>Object</code>. Contrary to that <code>PropertyChangeListener</code>s
+     * can only handle named properties that can have an arbitrary value, but have <code>String</code> names.
+     * Therefore the listenera attached to a document will only ever recieve document
+     * properties, which keys are of <code>java.lang.String</code> type.
+     *
+     * <p>Additionally, the list of document properties that clients can listen on
+     * is not part of this contract.
+     *
+     * @param doc The document to add the listener to.
+     * @param listenerImplementation The listener to be added weakly to the document.
+     * @return the created weak listener - only the returned listener can be
+     * used with {@link #removePropertyChangeListener}. If the document does not
+     * support {@code PropertyChangeLister} {@code null} is returned.
+     *
+     * @since 1.68
+     */
+    public static PropertyChangeListener addWeakPropertyChangeListener(Document doc, PropertyChangeListener listenerImplementation) {
+        PropertyChangeSupport pcs = (PropertyChangeSupport) doc.getProperty(PropertyChangeSupport.class);
+        PropertyChangeListener weakListener = null;
+        if (pcs != null) {
+            weakListener = WeakListeners.propertyChange(listenerImplementation, pcs);
+            pcs.addPropertyChangeListener(weakListener);
+        }
+        return weakListener;
+    }
 }
diff --git a/ide/editor.util/test/unit/src/org/netbeans/lib/editor/util/swing/DocumentUtilitiesTest.java b/ide/editor.util/test/unit/src/org/netbeans/lib/editor/util/swing/DocumentUtilitiesTest.java
index c8ac0c268c..847dff5163 100644
--- a/ide/editor.util/test/unit/src/org/netbeans/lib/editor/util/swing/DocumentUtilitiesTest.java
+++ b/ide/editor.util/test/unit/src/org/netbeans/lib/editor/util/swing/DocumentUtilitiesTest.java
@@ -19,6 +19,11 @@
 
 package org.netbeans.lib.editor.util.swing;
 
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import javax.swing.event.DocumentEvent;
@@ -26,7 +31,6 @@
 import javax.swing.text.BadLocationException;
 import javax.swing.text.PlainDocument;
 import org.netbeans.junit.NbTestCase;
-import org.openide.util.Exceptions;
 
 public class DocumentUtilitiesTest extends NbTestCase {
 
@@ -129,4 +133,56 @@ public void run() {
         
     }
 
+    private static class SampleListener implements PropertyChangeListener {
+        private Map<String,Integer> invokations = new HashMap<>();
+
+        @Override
+        public void propertyChange(PropertyChangeEvent evt) {
+            Integer oldCallCount = invokations.get(evt.getPropertyName());
+            invokations.put(evt.getPropertyName(), oldCallCount == null ? 1 : (oldCallCount + 1));
+        }
+
+        public void reset() {
+            invokations.clear();
+        }
+
+        public int getCallCount(String propertyName) {
+            Integer count = invokations.get(propertyName);
+            return count == null ? 0 : count;
+        }
+    }
+
+    public void testAddWeakPropertyChangeListenerUnsupported() {
+        // It is expected, that if the backing document does not support
+        // PropertyChangeListener (PlainDocument), null is returned
+        SampleListener sl = new SampleListener();
+        PlainDocument pd = new PlainDocument();
+        PropertyChangeListener weakListener = DocumentUtilities.addWeakPropertyChangeListener(pd, sl);
+        assertNull(weakListener);
+    }
+
+    public void testAddWeakPropertyChangeListenerSupported() {
+        SampleListener sl = new SampleListener();
+        // This simulates the construction of the BaseDocument
+        PlainDocument pd = new PlainDocument();
+        PropertyChangeSupport pcs = new PropertyChangeSupport(pd);
+        pd.putProperty(PropertyChangeSupport.class, pcs);
+
+        PropertyChangeListener weakListener = DocumentUtilities.addWeakPropertyChangeListener(pd, sl);
+        // A PropertyChangeListener added through addWeakPropertyChangeListener
+        // to a PropertyChangeListener supporting document, must be reflected
+        // in a non-null return value
+        assertNotNull(weakListener);
+
+        // the backing listner needs to be invoked when a property is changed
+        pcs.firePropertyChange("demoProperty", null, "demoValue");
+        assertEquals(1, sl.getCallCount("demoProperty"));
+
+        // The returned Listner must be usable for listener removal
+        DocumentUtilities.removePropertyChangeListener(pd, weakListener);
+        sl.reset();
+        pcs.firePropertyChange("demoProperty", null, "demoValue");
+        assertEquals(0, sl.getCallCount("demoProperty"));
+
+    }
 }


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists