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 257941
Collapse All | Expand All

(-)a/openide.nodes/apichanges.xml (+24 lines)
Lines 49-54 Link Here
49
<apidef name="nodes">Nodes API</apidef>
49
<apidef name="nodes">Nodes API</apidef>
50
</apidefs>
50
</apidefs>
51
<changes>
51
<changes>
52
    <change id="ChildFactory.DestroyableNodes">
53
        <api name="nodes"/>
54
        <summary>Adding ChildFactory.Detachable to allow ChildFactory implementations to
55
            attach and detach listeners more easily
56
        </summary>
57
        <version major="7" minor="44"/>
58
        <date day="18" month="2" year="2016"/>
59
        <author login="phejl"/>
60
        <compatibility addition="yes" binary="compatible" source="compatible" semantic="compatible"/>
61
        <description>
62
            <a href="@TOP@/org/openide/nodes/ChildFactory.html">ChildFactory</a>
63
            is useful for creating node children lazily on a background thread,
64
            and for simplifying working with Children.Keys.  One oversight in
65
            the original API was providing for notification that the nodes created
66
            by the ChildFactory are no longer in use and should clean up any
67
            resources.
68
            <p>
69
                ChildFactory.DestroyableNodes is an abstract class which adds
70
                destroyNodes methods to ChildFactory.Detachable.
71
            </p>
72
        </description>
73
        <class package="org.openide.nodes" name="ChildFactory"/>
74
        <issue number="257941"/>
75
    </change>
52
    <change id="BeanInfoSearchPath">
76
    <change id="BeanInfoSearchPath">
53
        <api name="nodes"/>
77
        <api name="nodes"/>
54
        <summary>Adding <code>@BeanInfoSearchPath</code> annotation</summary>
78
        <summary>Adding <code>@BeanInfoSearchPath</code> annotation</summary>
(-)a/openide.nodes/manifest.mf (-1 / +1 lines)
Lines 2-6 Link Here
2
OpenIDE-Module: org.openide.nodes
2
OpenIDE-Module: org.openide.nodes
3
OpenIDE-Module-Localizing-Bundle: org/openide/nodes/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/openide/nodes/Bundle.properties
4
AutoUpdate-Essential-Module: true
4
AutoUpdate-Essential-Module: true
5
OpenIDE-Module-Specification-Version: 7.43
5
OpenIDE-Module-Specification-Version: 7.44
6
6
(-)a/openide.nodes/src/org/openide/nodes/AsynchChildren.java (+6 lines)
Lines 161-166 Link Here
161
        }
161
        }
162
    }
162
    }
163
163
164
    @Override
165
    protected void destroyNodes(Node[] arr) {
166
        super.destroyNodes(arr);
167
        factory.destroyNodes(arr);
168
    }
169
164
    volatile boolean cancelled = false;
170
    volatile boolean cancelled = false;
165
    volatile boolean notified;
171
    volatile boolean notified;
166
    private final Object notifyLock = new Object();
172
    private final Object notifyLock = new Object();
(-)a/openide.nodes/src/org/openide/nodes/ChildFactory.java (+26 lines)
Lines 220-225 Link Here
220
        //do nothing
220
        //do nothing
221
    }
221
    }
222
    
222
    
223
    void destroyNodes(Node[] arr) {
224
        //do nothing
225
    }
226
223
    interface Observer {
227
    interface Observer {
224
        public void refresh(boolean immediate);
228
        public void refresh(boolean immediate);
225
    }
229
    }
Lines 270-273 Link Here
270
        }
274
        }
271
275
272
    }
276
    }
277
278
    /**
279
     * Subclass of {@link Detachable} with lifecycle method invoked when
280
     * nodes created by the factory are no longer needed.
281
     *
282
     * @param <T> The key type for this child factory
283
     * @since org.openide.nodes 7.44
284
     */
285
    public static abstract class DestroyableNodes<T> extends Detachable<T>{
286
287
        /**
288
         * Called when nodes created previously by this factory are no longer
289
         * present in the node hierarchy.
290
         *
291
         * @param arr nodes which are no longer needed
292
         */
293
        @Override
294
        protected void destroyNodes(Node[] arr) {
295
            //do nothing
296
        }
297
298
    }
273
}
299
}
(-)a/openide.nodes/src/org/openide/nodes/SynchChildren.java (+7 lines)
Lines 79-84 Link Here
79
    protected Node[] createNodes(T key) {
79
    protected Node[] createNodes(T key) {
80
        return factory.createNodesForKey(key);
80
        return factory.createNodesForKey(key);
81
    }
81
    }
82
83
    @Override
84
    protected void destroyNodes(Node[] arr) {
85
        super.destroyNodes(arr);
86
        factory.destroyNodes(arr);
87
    }
88
    
82
    
89
    
83
    public void refresh(boolean immediate) {
90
    public void refresh(boolean immediate) {
84
        if (active) {
91
        if (active) {
(-)a/openide.nodes/test/unit/src/org/openide/nodes/ChildFactoryTest.java (+83 lines)
Lines 278-283 Link Here
278
        ch = Children.create(b, true);
278
        ch = Children.create(b, true);
279
        assertEquals(4, ch.getNodesCount(true));
279
        assertEquals(4, ch.getNodesCount(true));
280
    }
280
    }
281
    
282
    public void testDestroyNodesSynch() throws Exception {
283
        DestroyableImpl r = new DestroyableImpl();
284
        Children ch = Children.create(r, false);
285
        new AbstractNode (ch);
286
        Node[] n = ch.getNodes(true);
287
        assertEquals (2, n.length);
288
        assertEquals ("foo", n[0].getDisplayName());
289
        assertEquals ("bar", n[1].getDisplayName());
290
        r.refresh(true);
291
        n = ch.getNodes(true);
292
        assertEquals (0, n.length);
293
        Set<Node> destroyed = r.getDestroyed();
294
        Set<String> expected = new HashSet<String>();
295
        Collections.addAll(expected, "foo", "bar");
296
        for (Node node : destroyed) {
297
            assertTrue(node.getDisplayName(), expected.contains(node.getDisplayName()));
298
        }
299
    }
300
301
    public void testDestroyNodesAsynch() throws Exception {
302
        DestroyableImpl r = new DestroyableImpl();
303
        Children ch = Children.create(r, true);
304
        new AbstractNode (ch);
305
        Node[] n = ch.getNodes(true);
306
        assertEquals (2, n.length);
307
        assertEquals ("foo", n[0].getDisplayName());
308
        assertEquals ("bar", n[1].getDisplayName());
309
        r.refresh(false);
310
        synchronized(r) {
311
            r.wait(1000);
312
        }
313
        n = ch.getNodes(true);
314
        assertEquals (0, n.length);
315
        Set<Node> destroyed = r.getDestroyed();
316
        Set<String> expected = new HashSet<String>();
317
        Collections.addAll(expected, r.createWaitNode().getDisplayName(), "foo", "bar");
318
        for (Node node : destroyed) {
319
            assertTrue(node.getDisplayName(), expected.contains(node.getDisplayName()));
320
        }
321
    }
281
322
282
    public void testIncrementalDisplay() throws Exception { // #206556
323
    public void testIncrementalDisplay() throws Exception { // #206556
283
        final Semaphore s1 = new Semaphore(0);
324
        final Semaphore s1 = new Semaphore(0);
Lines 674-677 Link Here
674
            assertTrue (removed);
715
            assertTrue (removed);
675
        }
716
        }
676
    }
717
    }
718
719
    private static final class DestroyableImpl extends ChildFactory.DestroyableNodes<String> {
720
721
        private boolean empty;
722
723
        private final Set<Node> destroyed = Collections.synchronizedSet(new HashSet<Node>());
724
725
        @Override
726
        protected boolean createKeys(List<String> toPopulate) {
727
            if (empty) {
728
                return true;
729
            }
730
731
            toPopulate.add("foo");
732
            toPopulate.add("bar");
733
            synchronized (this) {
734
                notifyAll();
735
            }
736
            empty = true;
737
            return true;
738
        }
739
740
        @Override
741
        protected Node createNodeForKey(String key) {
742
            AbstractNode nd = new AbstractNode(Children.LEAF);
743
            nd.setDisplayName(key);
744
            return nd;
745
        }
746
747
        @Override
748
        protected void destroyNodes(Node[] arr) {
749
            synchronized (destroyed) {
750
                Collections.addAll(destroyed, arr);
751
            }
752
        }
753
754
        public Set<Node> getDestroyed() {
755
            synchronized (destroyed) {
756
                return new HashSet<Node>(destroyed);
757
            }
758
        }
759
    }
677
}
760
}

Return to bug 257941