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

(-)a/openide.nodes/apichanges.xml (+25 lines)
Lines 46-51 made subject to such option by the copyr Link Here
46
<apidef name="nodes">Nodes API</apidef>
46
<apidef name="nodes">Nodes API</apidef>
47
</apidefs>
47
</apidefs>
48
<changes>
48
<changes>
49
    <change id="ChildFactory.Detachable">
50
        <api name="nodes"/>
51
        <summary>Adding ChildFactory.Detachable to allow ChildFactory implementations to
52
        attach and detach listeners more easily
53
        </summary>
54
        <version major="7" minor="7"/>
55
        <date day="25" month="11" year="2008"/>
56
        <author login="tboudreau"/>
57
        <compatibility addition="yes" binary="compatible" source="compatible" semantic="compatible"/>
58
        <description>
59
            <a href="@TOP@/org/openide/nodes/ChildFactory.html">ChildFactory</a>
60
            is useful for creating node children lazily on a background thread,
61
            and for simplifying working with Children.Keys.  One oversight in
62
            the original API was providing for notification that the ChildFactory
63
            is no longer in use and should clean up any resources and detach
64
            any listeners it can.
65
            <p>
66
            ChildFactory.Detachable is an abstract class which adds
67
            addNotify() and removeNotify() methods to ChildFactory.  addNotify()
68
            is called immediately before the first call to createKeys() after
69
            creation or a call to removeNotify().
70
        </description>
71
        <class package="org.openide.nodes" name="ChildFactory"/>
72
        <issue number="153347"/>
73
    </change>
49
    <change id="Children.Keys.lazy">
74
    <change id="Children.Keys.lazy">
50
        <api name="nodes"/>
75
        <api name="nodes"/>
51
        <summary>Adding public int Children.getNodesCount(boolean optimalResult), protected Children.Keys(boolean lazy),
76
        <summary>Adding public int Children.getNodesCount(boolean optimalResult), protected Children.Keys(boolean lazy),
(-)a/openide.nodes/src/org/openide/nodes/AsynchChildren.java (-6 / +17 lines)
Lines 41-47 package org.openide.nodes; Link Here
41
package org.openide.nodes;
41
package org.openide.nodes;
42
42
43
import java.awt.EventQueue;
43
import java.awt.EventQueue;
44
import java.lang.Thread;
45
import java.util.Collections;
44
import java.util.Collections;
46
import java.util.LinkedList;
45
import java.util.LinkedList;
47
import java.util.List;
46
import java.util.List;
Lines 52-57 import org.openide.util.RequestProcessor Link Here
52
 * implement AsynchChildren.Provider and pass that to the constructor.
51
 * implement AsynchChildren.Provider and pass that to the constructor.
53
 *
52
 *
54
 * @author Tim Boudreau
53
 * @author Tim Boudreau
54
 * @param T the type of key object used to create the child nodes
55
 */
55
 */
56
final class AsynchChildren <T> extends Children.Keys <Object> implements 
56
final class AsynchChildren <T> extends Children.Keys <Object> implements 
57
                                                          ChildFactory.Observer, 
57
                                                          ChildFactory.Observer, 
Lines 85-94 final class AsynchChildren <T> extends C Link Here
85
    }
85
    }
86
    
86
    
87
    protected @Override void removeNotify() {
87
    protected @Override void removeNotify() {
88
        cancelled = true;
88
        try {
89
        task.cancel();
89
            cancelled = true;
90
        initialized = false;
90
            task.cancel();
91
        setKeys (Collections.<Object>emptyList());
91
            initialized = false;
92
            setKeys (Collections.<Object>emptyList());
93
        } finally {
94
            if (notified) {
95
                factory.removeNotify();
96
            }
97
        }
92
    }
98
    }
93
    
99
    
94
    /**
100
    /**
Lines 128-134 final class AsynchChildren <T> extends C Link Here
128
    
134
    
129
    @SuppressWarnings("unchecked") // Union2<T,Node> undesirable since refresh could not use raw keys list
135
    @SuppressWarnings("unchecked") // Union2<T,Node> undesirable since refresh could not use raw keys list
130
    protected Node[] createNodes(Object key) {
136
    protected Node[] createNodes(Object key) {
131
        if (factory.isWaitNode(key)) {
137
        if (ChildFactory.isWaitNode(key)) {
132
            return new Node[] { (Node) key };
138
            return new Node[] { (Node) key };
133
        } else {
139
        } else {
134
            return factory.createNodesForKey ((T) key);
140
            return factory.createNodesForKey ((T) key);
Lines 136-141 final class AsynchChildren <T> extends C Link Here
136
    }
142
    }
137
143
138
    volatile boolean cancelled = false;
144
    volatile boolean cancelled = false;
145
    volatile boolean notified;
139
    public void run() {
146
    public void run() {
140
        if (Thread.interrupted()) {
147
        if (Thread.interrupted()) {
141
            setKeys (Collections.<T>emptyList());
148
            setKeys (Collections.<T>emptyList());
Lines 144-149 final class AsynchChildren <T> extends C Link Here
144
        List <T> keys = new LinkedList <T> ();
151
        List <T> keys = new LinkedList <T> ();
145
        boolean done;
152
        boolean done;
146
        do {
153
        do {
154
            if (!notified) {
155
                notified = true;
156
                factory.addNotify();
157
            }
147
            done = factory.createKeys (keys);
158
            done = factory.createKeys (keys);
148
            if (cancelled || Thread.interrupted()) {
159
            if (cancelled || Thread.interrupted()) {
149
                setKeys (Collections.<T>emptyList());
160
                setKeys (Collections.<T>emptyList());
(-)a/openide.nodes/src/org/openide/nodes/ChildFactory.java (-2 / +38 lines)
Lines 109-119 public abstract class ChildFactory <T> { Link Here
109
     *
109
     *
110
     * @param key An object from the list returned by
110
     * @param key An object from the list returned by
111
     *        <code>asynchCreateKeys()</code>
111
     *        <code>asynchCreateKeys()</code>
112
     * @return zero or more Nodes to represent this key
112
     * @return null if no nodes, or zero or more Nodes to represent this key
113
     */
113
     */
114
    protected Node[] createNodesForKey(T key) {
114
    protected Node[] createNodesForKey(T key) {
115
        Node n = createNodeForKey(key);
115
        Node n = createNodeForKey(key);
116
        return n == null ? new Node[0] : new Node[] { n };
116
        return n == null ? null : new Node[] { n };
117
    }
117
    }
118
    /**
118
    /**
119
     * Create a list of keys which can be individually passed to
119
     * Create a list of keys which can be individually passed to
Lines 198-203 public abstract class ChildFactory <T> { Link Here
198
        }
198
        }
199
        this.observer = new WeakReference <Observer> (observer);
199
        this.observer = new WeakReference <Observer> (observer);
200
    }
200
    }
201
202
    void removeNotify() {
203
        //do nothing
204
    }
205
206
    void addNotify() {
207
        //do nothing
208
    }
201
    
209
    
202
    interface Observer {
210
    interface Observer {
203
        public void refresh(boolean immediate);
211
        public void refresh(boolean immediate);
Lines 217-220 public abstract class ChildFactory <T> { Link Here
217
            super(orig);
225
            super(orig);
218
        }
226
        }
219
    }
227
    }
228
229
    /**
230
     * Subclass of ChildFactory with lifecycle methods which will be called
231
     * on first use and last use.
232
     *
233
     * @param <T> The key type for this child factory
234
     */
235
    public static abstract class Detachable<T> extends ChildFactory<T>{
236
        /**
237
         * Called immediately before the first call to createKeys().  Override
238
         * to set up listening for changes, allocating expensive-to-create
239
         * resources, etc.
240
         */
241
        @Override
242
        protected void addNotify() {
243
            //do nothing
244
        }
245
        /**
246
         * Called when this child factory is no longer in use, to dispose of
247
         * resources, detach listeners, etc.  Does nothing by default;  override
248
         * if you need notification when not in use anymore.
249
         */
250
        @Override
251
        protected void removeNotify() {
252
            //do nothing
253
        }
254
255
    }
220
}
256
}
(-)a/openide.nodes/src/org/openide/nodes/SynchChildren.java (+2 lines)
Lines 64-75 final class SynchChildren<T> extends Chi Link Here
64
    volatile boolean active = false;
64
    volatile boolean active = false;
65
    protected @Override void addNotify() {
65
    protected @Override void addNotify() {
66
        active = true;
66
        active = true;
67
        factory.addNotify();
67
        refresh(true);
68
        refresh(true);
68
    }
69
    }
69
    
70
    
70
    protected @Override void removeNotify() {
71
    protected @Override void removeNotify() {
71
        active = false;
72
        active = false;
72
        setKeys(Collections.<T>emptyList());
73
        setKeys(Collections.<T>emptyList());
74
        factory.removeNotify();
73
    }
75
    }
74
    
76
    
75
    protected Node[] createNodes(T key) {
77
    protected Node[] createNodes(T key) {
(-)a/openide.nodes/test/unit/src/org/openide/nodes/ChildFactoryTest.java (-4 / +88 lines)
Lines 44-51 import java.beans.*; Link Here
44
import java.beans.*;
44
import java.beans.*;
45
import java.util.*;
45
import java.util.*;
46
import junit.framework.TestCase;
46
import junit.framework.TestCase;
47
import org.openide.nodes.*;
47
import org.openide.nodes.ChildFactory.Detachable;
48
import org.openide.nodes.NodeAdapter;
49
import org.openide.util.NbBundle;
48
import org.openide.util.NbBundle;
50
49
51
/** Test for AsynchChildren, ChildFactory and SynchChildren.
50
/** Test for AsynchChildren, ChildFactory and SynchChildren.
Lines 202-208 public class ChildFactoryTest extends Te Link Here
202
        Thread.interrupted();
201
        Thread.interrupted();
203
        factory.wait = true;
202
        factory.wait = true;
204
        kids.addNotify();
203
        kids.addNotify();
205
        Thread.currentThread().yield();
204
        Thread.yield();
206
        synchronized (factory.lock) {
205
        synchronized (factory.lock) {
207
            factory.lock.wait(500);
206
            factory.lock.wait(500);
208
        }
207
        }
Lines 214-220 public class ChildFactoryTest extends Te Link Here
214
        assertTrue(kids.cancelled);
213
        assertTrue(kids.cancelled);
215
        assertTrue(factory.cancelled);
214
        assertTrue(factory.cancelled);
216
    }
215
    }
217
    
216
217
    public void testAddRemoveNotifySynch() throws Exception {
218
        DetachableImpl r = new DetachableImpl();
219
        Children ch = Children.create(r, false);
220
        new AbstractNode (ch);
221
        ch.addNotify();
222
        r.assertAdded();
223
        ch.removeNotify();
224
        r.assertRemoved();
225
        r = new DetachableImpl();
226
        ch = Children.create(r, false);
227
        Node[] n = ch.getNodes(true);
228
        assertEquals (2, n.length);
229
        assertEquals ("foo", n[0].getDisplayName());
230
        assertEquals ("bar", n[1].getDisplayName());
231
        ch.removeNotify();
232
        r.assertRemoved();
233
    }
234
235
    public void testAddRemoveNotifyAsynch() throws Exception {
236
        DetachableImpl r = new DetachableImpl();
237
        Children ch = Children.create(r, true);
238
        new AbstractNode (ch);
239
        ch.addNotify();
240
        synchronized(r) {
241
            r.wait(1000);
242
        }
243
        r.assertAdded();
244
        Node[] n = ch.getNodes(true);
245
        assertEquals (2, n.length);
246
        assertEquals ("foo", n[0].getDisplayName());
247
        assertEquals ("bar", n[1].getDisplayName());
248
        ch.removeNotify();
249
        synchronized(r) {
250
            r.wait(1000);
251
        }
252
        r.assertRemoved();
253
    }
254
255
218
    static final class ProviderImpl extends ChildFactory <String> {
256
    static final class ProviderImpl extends ChildFactory <String> {
219
        Object lock = new Object();
257
        Object lock = new Object();
220
        volatile boolean wait = false;
258
        volatile boolean wait = false;
Lines 421-424 public class ChildFactoryTest extends Te Link Here
421
            refresh(true);
459
            refresh(true);
422
        }
460
        }
423
    }
461
    }
462
463
    private static final class DetachableImpl extends Detachable<String> {
464
        boolean removed;
465
        boolean added;
466
467
        @Override
468
        protected boolean createKeys(List<String> toPopulate) {
469
            toPopulate.add("foo");
470
            toPopulate.add("bar");
471
            synchronized(this) {
472
                notifyAll();
473
            }
474
            return true;
475
        }
476
477
        @Override
478
        protected void removeNotify() {
479
            assertFalse (removed);
480
            synchronized(this) {
481
                notifyAll();
482
            }
483
            removed = true;
484
            added = false;
485
        }
486
487
        @Override
488
        protected Node createNodeForKey(String key) {
489
            AbstractNode nd = new AbstractNode(Children.LEAF);
490
            nd.setDisplayName(key);
491
            return nd;
492
        }
493
494
        @Override
495
        protected void addNotify() {
496
            assertFalse (added);
497
            added = true;
498
        }
499
500
        void assertAdded() {
501
            assertTrue (added);
502
        }
503
504
        void assertRemoved() {
505
            assertTrue (removed);
506
        }
507
    }
424
}
508
}

Return to bug 153347