This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 188604
Collapse All | Expand All

(-)a/openide.util/apichanges.xml (+21 lines)
Lines 51-56 Link Here
51
    <apidef name="actions">Actions API</apidef>
51
    <apidef name="actions">Actions API</apidef>
52
</apidefs>
52
</apidefs>
53
<changes>
53
<changes>
54
    <change id="weak-listen-on-specific-property">
55
        <api name="util"/>
56
        <summary>Weak property and vetoable listeners for a specific property name.</summary>
57
        <version major="8" minor="41"/>
58
        <date year="2014" month="10" day="14"/>
59
        <author login="mentlicher"/>
60
        <compatibility addition="yes" binary="compatible" source="compatible"/>
61
        <description>
62
            <p>
63
                <code>WeakListeners</code> class got variants of <code>propertyChange()</code>
64
                and <code>vetoableChange()</code> methods, which take the property name.
65
                They are to be used as an argument to
66
                <code>addPropertyChangeListener(String propertyName, PropertyChangeListener listener)</code>
67
                and <code>addVetoableChangeListener(String propertyName, PropertyChangeListener listener)</code>
68
                methods respectively, and will call the appropriate remove methods with the provided
69
                property name.
70
            </p>
71
        </description>
72
        <class package="org.openide.util" name="WeakListeners"/>
73
        <issue number="188604"/>
74
    </change>
54
    <change id="disabled-action-beep">
75
    <change id="disabled-action-beep">
55
        <api name="util"/>
76
        <api name="util"/>
56
        <summary>Platform dependent sound when invoking a disabled action.</summary>
77
        <summary>Platform dependent sound when invoking a disabled action.</summary>
(-)a/openide.util/manifest.mf (-1 / +1 lines)
Lines 1-5 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.openide.util
2
OpenIDE-Module: org.openide.util
3
OpenIDE-Module-Localizing-Bundle: org/openide/util/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/openide/util/Bundle.properties
4
OpenIDE-Module-Specification-Version: 8.40
4
OpenIDE-Module-Specification-Version: 8.41
5
5
(-)a/openide.util/src/org/openide/util/WeakListenerImpl.java (-9 / +53 lines)
Lines 96-103 Link Here
96
     * listenerClass
96
     * listenerClass
97
     */
97
     */
98
    protected WeakListenerImpl(Class<?> listenerClass, java.util.EventListener l) {
98
    protected WeakListenerImpl(Class<?> listenerClass, java.util.EventListener l) {
99
        this(listenerClass, l, null);
100
    }
101
    
102
    /**
103
     * @param listenerClass class/interface of the listener
104
     * @param l listener to delegate to, <code>l</code> must be an instance of
105
     * listenerClass
106
     */
107
    protected WeakListenerImpl(Class<?> listenerClass, java.util.EventListener l, String name) {
99
        this.listenerClass = listenerClass;
108
        this.listenerClass = listenerClass;
100
        ref = new ListenerReference(l, this);
109
        ref = new ListenerReference(l, name, this);
101
    }
110
    }
102
111
103
    /** Setter for the source field. If a WeakReference to an underlying listener is
112
    /** Setter for the source field. If a WeakReference to an underlying listener is
Lines 185-190 Link Here
185
        PropertyChange(Class<?> clazz, PropertyChangeListener l) {
194
        PropertyChange(Class<?> clazz, PropertyChangeListener l) {
186
            super(clazz, l);
195
            super(clazz, l);
187
        }
196
        }
197
        
198
        /** Constructor.
199
        * @param clazz required class
200
        * @param l listener to delegate to
201
        * @param propertyName the associated property name
202
        */
203
        PropertyChange(PropertyChangeListener l, String propertyName) {
204
            super(PropertyChangeListener.class, l, propertyName);
205
        }
188
206
189
        /** Tests if the object we reference to still exists and
207
        /** Tests if the object we reference to still exists and
190
        * if so, delegate to it. Otherwise remove from the source
208
        * if so, delegate to it. Otherwise remove from the source
Lines 217-222 Link Here
217
            super(VetoableChangeListener.class, l);
235
            super(VetoableChangeListener.class, l);
218
        }
236
        }
219
237
238
        /** Constructor.
239
        * @param l listener to delegate to
240
        * @param propertyName the associated property name
241
        */
242
        VetoableChange(VetoableChangeListener l, String propertyName) {
243
            super(VetoableChangeListener.class, l, propertyName);
244
        }
245
220
        /** Tests if the object we reference to still exists and
246
        /** Tests if the object we reference to still exists and
221
        * if so, delegate to it. Otherwise remove from the source
247
        * if so, delegate to it. Otherwise remove from the source
222
        * if it has removePropertyChangeListener method.
248
        * if it has removePropertyChangeListener method.
Lines 513-524 Link Here
513
        private static Class<?> lastClass;
539
        private static Class<?> lastClass;
514
        private static String lastMethodName;
540
        private static String lastMethodName;
515
        private static Method lastRemove;
541
        private static Method lastRemove;
542
        private static Method lastNamedRemove;
516
        private static final Object LOCK = new Object();
543
        private static final Object LOCK = new Object();
517
        WeakListenerImpl weakListener;
544
        WeakListenerImpl weakListener;
545
        private String name;
518
546
519
        ListenerReference(Object ref, WeakListenerImpl weakListener) {
547
        ListenerReference(Object ref, String name, WeakListenerImpl weakListener) {
520
            super(ref, Utilities.activeReferenceQueue());
548
            super(ref, Utilities.activeReferenceQueue());
521
            this.weakListener = weakListener;
549
            this.weakListener = weakListener;
550
            this.name = name;
522
        }
551
        }
523
552
524
        /** Requestes cleanup of the listener with a provided source.
553
        /** Requestes cleanup of the listener with a provided source.
Lines 534-540 Link Here
534
                // plan new cleanup into the activeReferenceQueue with this listener and 
563
                // plan new cleanup into the activeReferenceQueue with this listener and 
535
                // provided source
564
                // provided source
536
                weakListener.source = new WeakReference<Object> (source) {
565
                weakListener.source = new WeakReference<Object> (source) {
537
                    ListenerReference doNotGCRef = new ListenerReference(new Object(), weakListener);
566
                    ListenerReference doNotGCRef = new ListenerReference(new Object(), name, weakListener);
538
                };
567
                };
539
            }
568
            }
540
        }
569
        }
Lines 566-579 Link Here
566
            String methodName = ref.removeMethodName();
595
            String methodName = ref.removeMethodName();
567
596
568
            synchronized (LOCK) {
597
            synchronized (LOCK) {
569
                if (lastClass == methodClass && lastRemove != null && methodName.equals(lastMethodName)) {
598
                if (lastClass == methodClass) {
570
                    remove = lastRemove;
599
                    if (name == null) {
600
                        if (lastRemove != null && methodName.equals(lastMethodName)) {
601
                            remove = lastRemove;
602
                        }
603
                    } else {
604
                        if (lastNamedRemove != null && methodName.equals(lastMethodName)) {
605
                            remove = lastNamedRemove;
606
                        }
607
                    }
571
                }
608
                }
572
            }
609
            }
573
610
574
            // get the remove method or use the last one
611
            // get the remove method or use the last one
575
            if (remove == null) {
612
            if (remove == null) {
576
                remove = getRemoveMethod(methodClass, methodName, ref.listenerClass);
613
                if (name == null) {
614
                    remove = getRemoveMethod(methodClass, methodName, ref.listenerClass);
615
                }
577
                if (remove == null) {
616
                if (remove == null) {
578
                    remove = getRemoveMethod(methodClass, methodName, String.class, ref.listenerClass);
617
                    remove = getRemoveMethod(methodClass, methodName, String.class, ref.listenerClass);
579
                }
618
                }
Lines 585-600 Link Here
585
                    synchronized (LOCK) {
624
                    synchronized (LOCK) {
586
                        lastClass = methodClass;
625
                        lastClass = methodClass;
587
                        lastMethodName = methodName;
626
                        lastMethodName = methodName;
588
                        lastRemove = remove;
627
                        if (name == null) {
628
                            lastRemove = remove;
629
                        } else {
630
                            lastNamedRemove = remove;
631
                        }
589
                    }
632
                    }
590
                }
633
                }
591
            }
634
            }
592
635
            
593
            try {
636
            try {
594
                if (remove.getParameterTypes().length == 1) {
637
                if (remove.getParameterTypes().length == 1) {
595
                    remove.invoke(src, new Object[]{ref.getImplementator()});
638
                    remove.invoke(src, new Object[]{ref.getImplementator()});
596
                } else {
639
                } else {
597
                    remove.invoke(src, new Object[]{"", ref.getImplementator()});
640
                    String nameParam = (name == null) ? "" : name;
641
                    remove.invoke(src, new Object[]{nameParam, ref.getImplementator()});
598
                }
642
                }
599
            } catch (Exception ex) { // from invoke(), should not happen
643
            } catch (Exception ex) { // from invoke(), should not happen
600
                // #151415 - ignore exception from AbstractPreferences if node has been removed
644
                // #151415 - ignore exception from AbstractPreferences if node has been removed
(-)a/openide.util/src/org/openide/util/WeakListeners.java (+46 lines)
Lines 285-290 Link Here
285
        return wl;
285
        return wl;
286
    }
286
    }
287
287
288
    /** Creates a weak implementation of PropertyChangeListener to be attached
289
     * for a specific property name. Use with 
290
     * <code>addPropertyChangeListener(String propertyName, PropertyChangeListener listener)</code>
291
     * method. It calls
292
     * <code>removePropertyChangeListener(String propertyName, PropertyChangeListener listener)</code>
293
     * with the given property name to unregister the listener. Be sure to pass
294
     * the same <code>propertyName</code> to this method and to <code>addPropertyChangeListener()</code>
295
     * method.
296
     *
297
     * @param l the listener to delegate to
298
     * @param propertyName the name of the property to listen on changes
299
     * @param source the source that the listener should detach from when
300
     *     listener <CODE>l</CODE> is freed, can be <CODE>null</CODE>
301
     * @return a PropertyChangeListener delegating to <CODE>l</CODE>.
302
     * @since 8.41
303
     */
304
    public static PropertyChangeListener propertyChange(PropertyChangeListener l, String propertyName, Object source) {
305
        WeakListenerImpl.PropertyChange wl = new WeakListenerImpl.PropertyChange(l, propertyName);
306
        wl.setSource(source);
307
308
        return wl;
309
    }
310
288
    /** Creates a weak implementation of VetoableChangeListener.
311
    /** Creates a weak implementation of VetoableChangeListener.
289
     *
312
     *
290
     * @param l the listener to delegate to
313
     * @param l the listener to delegate to
Lines 299-304 Link Here
299
        return wl;
322
        return wl;
300
    }
323
    }
301
324
325
    /** Creates a weak implementation of VetoableChangeListener to be attached
326
     * for a specific property name. Use with 
327
     * <code>addVetoableChangeListener(String propertyName, PropertyChangeListener listener)</code>
328
     * method. It calls
329
     * <code>removeVetoableChangeListener(String propertyName, PropertyChangeListener listener)</code>
330
     * with the given property name to unregister the listener. Be sure to pass
331
     * the same <code>propertyName</code> to this method and to <code>addVetoableChangeListener()</code>
332
     * method.
333
     *
334
     * @param l the listener to delegate to
335
     * @param propertyName the name of the property to listen on changes
336
     * @param source the source that the listener should detach from when
337
     *     listener <CODE>l</CODE> is freed, can be <CODE>null</CODE>
338
     * @return a VetoableChangeListener delegating to <CODE>l</CODE>.
339
     * @since 8.41
340
     */
341
    public static VetoableChangeListener vetoableChange(VetoableChangeListener l, String propertyName, Object source) {
342
        WeakListenerImpl.VetoableChange wl = new WeakListenerImpl.VetoableChange(l, propertyName);
343
        wl.setSource(source);
344
345
        return wl;
346
    }
347
302
    /** Creates a weak implementation of DocumentListener.
348
    /** Creates a weak implementation of DocumentListener.
303
     *
349
     *
304
     * @param l the listener to delegate to
350
     * @param l the listener to delegate to
(-)a/openide.util/test/unit/src/org/openide/util/WeakListenersTest.java (-5 / +65 lines)
Lines 174-185 Link Here
174
        doTestReleaseOfListener (true);
174
        doTestReleaseOfListener (true);
175
    }
175
    }
176
    
176
    
177
    public void testReleaseOfPropNameListenerWithNullSource () throws Exception {
178
        doTestReleaseOfListener (false, javax.swing.AbstractButton.TEXT_CHANGED_PROPERTY);
179
    }
180
    
181
    public void testReleaseOfPropNameListenerWithSource () throws Exception {
182
        doTestReleaseOfListener (true, javax.swing.AbstractButton.TEXT_CHANGED_PROPERTY);
183
    }
184
    
177
    private void doTestReleaseOfListener (final boolean source) throws Exception {   
185
    private void doTestReleaseOfListener (final boolean source) throws Exception {   
186
        doTestReleaseOfListener(source, null);
187
    }
188
    
189
    private void doTestReleaseOfListener (final boolean source, final String propName) throws Exception {
178
        Listener l = new Listener ();
190
        Listener l = new Listener ();
179
        
191
        
180
        class MyButton extends javax.swing.JButton {
192
        class MyButton extends javax.swing.JButton {
181
            private Thread removedBy;
193
            private Thread removedBy;
182
            private int cnt;
194
            private int cnt;
195
            private int cntNamed;
183
            
196
            
184
            @Override
197
            @Override
185
            public synchronized void removePropertyChangeListener (PropertyChangeListener l) {
198
            public synchronized void removePropertyChangeListener (PropertyChangeListener l) {
Lines 203-208 Link Here
203
                cnt++;
216
                cnt++;
204
                notifyAll ();
217
                notifyAll ();
205
            }
218
            }
219
220
            @Override
221
            public synchronized void removePropertyChangeListener(String propertyName, PropertyChangeListener l) {
222
                // notify prior
223
                LOG.fine("removePropertyChangeListener("+propertyName+"): " + source + " cnt: " + cntNamed);
224
                if (source && cntNamed == 0) {
225
                    notifyAll ();
226
                    try {
227
                        // wait for 1
228
                        LOG.fine("wait for 1");
229
                        wait ();
230
                        LOG.fine("wait for 1 over");
231
                    } catch (InterruptedException ex) {
232
                        fail ("Not happen");
233
                    }
234
                }
235
                assertEquals(propName, propertyName);
236
                LOG.fine("Super removePropertyChangeListener");
237
                super.removePropertyChangeListener (propertyName, l);
238
                LOG.fine("Super over removePropertyChangeListener");
239
                removedBy = Thread.currentThread();
240
                cntNamed++;
241
                notifyAll ();
242
            }
206
            
243
            
207
            public synchronized void waitListener () throws Exception {
244
            public synchronized void waitListener () throws Exception {
208
                int count = 0;
245
                int count = 0;
Lines 224-234 Link Here
224
        
261
        
225
        MyButton button = new MyButton ();
262
        MyButton button = new MyButton ();
226
        LOG.fine("Button is here");
263
        LOG.fine("Button is here");
227
        java.beans.PropertyChangeListener weakL = WeakListeners.propertyChange (l, source ? button : null);
264
        java.beans.PropertyChangeListener weakL;
265
        if (propName == null) {
266
            weakL = WeakListeners.propertyChange (l, source ? button : null);
267
        } else {
268
            weakL = WeakListeners.propertyChange (l, propName, source ? button : null);
269
        }
228
        LOG.fine("WeakListeners created: " + weakL);
270
        LOG.fine("WeakListeners created: " + weakL);
229
        button.addPropertyChangeListener(weakL);
271
        if (propName == null) {
272
            button.addPropertyChangeListener(weakL);
273
        } else {
274
            button.addPropertyChangeListener(propName, weakL);
275
        }
230
        LOG.fine("WeakListeners attached");
276
        LOG.fine("WeakListeners attached");
231
        assertTrue ("Weak listener is there", Arrays.asList (button.getPropertyChangeListeners()).indexOf (weakL) >= 0);
277
        if (propName == null) {
278
            assertTrue ("Weak listener is there", Arrays.asList (button.getPropertyChangeListeners()).indexOf (weakL) >= 0);
279
        } else {
280
            assertTrue ("Weak listener is there", Arrays.asList (button.getPropertyChangeListeners(propName)).indexOf (weakL) >= 0);
281
        }
232
        
282
        
233
        button.setText("Ahoj");
283
        button.setText("Ahoj");
234
        LOG.fine("setText changed to ahoj");
284
        LOG.fine("setText changed to ahoj");
Lines 272-280 Link Here
272
            LOG.fine("Thread.sleep over");
322
            LOG.fine("Thread.sleep over");
273
        }
323
        }
274
324
275
        assertEquals ("Weak listener has been removed", -1, Arrays.asList (button.getPropertyChangeListeners()).indexOf (weakL));
325
        if (propName == null) {
326
            assertEquals ("Weak listener has been removed", -1, Arrays.asList (button.getPropertyChangeListeners()).indexOf (weakL));
327
        } else {
328
            assertEquals ("Weak listener has been removed", -1, Arrays.asList (button.getPropertyChangeListeners(propName)).indexOf (weakL));
329
        }
276
        assertEquals ("Button released from a thread", "Active Reference Queue Daemon", button.removedBy.getName());
330
        assertEquals ("Button released from a thread", "Active Reference Queue Daemon", button.removedBy.getName());
277
        assertEquals ("Unregister called just once", 1, button.cnt);
331
        if (propName == null) {
332
            assertEquals ("Unregister called just once", 1, button.cnt);
333
            assertEquals ("Unregister named not called", 0, button.cntNamed);
334
        } else {
335
            assertEquals ("Unregister called just once", 1, button.cntNamed);
336
            assertEquals ("Unregister unnamed not called", 0, button.cnt);
337
        }
278
        
338
        
279
        // and because it is not here, it can be GCed
339
        // and because it is not here, it can be GCed
280
        Reference<?> weakRef = new WeakReference<Object>(weakL);
340
        Reference<?> weakRef = new WeakReference<Object>(weakL);

Return to bug 188604