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

(-)openide/nodes/src/org/openide/nodes/AsynchChildren.java (+181 lines)
Added Link Here
1
/*
2
 * The contents of this file are subject to the terms of the Common Development
3
 * and Distribution License (the License). You may not use this file except in
4
 * compliance with the License.
5
 *
6
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7
 * or http://www.netbeans.org/cddl.txt.
8
 *
9
 * When distributing Covered Code, include this CDDL Header Notice in each file
10
 * and include the License file at http://www.netbeans.org/cddl.txt.
11
 * If applicable, add the following below the CDDL Header, with the fields
12
 * enclosed by brackets [] replaced by your own identifying information:
13
 * "Portions Copyrighted [year] [name of copyright owner]"
14
 *
15
 * The Original Software is NetBeans. The Initial Developer of the Original
16
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17
 * Microsystems, Inc. All Rights Reserved.
18
 */
19
package org.openide.nodes;
20
21
import java.awt.Image;
22
import java.lang.Thread;
23
import java.util.ArrayList;
24
import java.util.Collections;
25
import java.util.LinkedList;
26
import java.util.List;
27
import org.openide.util.NbBundle;
28
import org.openide.util.RequestProcessor;
29
import org.openide.util.Utilities;
30
31
/**
32
 * Children object which creates its keys on a background thread.  To use,
33
 * implement AsynchChildren.Provider and pass that to the constructor.
34
 *
35
 * @author Tim Boudreau
36
 */
37
public final class AsynchChildren extends Children.Keys <Object> {
38
    private final Provider provider;
39
    private final RequestProcessor.Task task;
40
    private final RequestProcessor PROC = new RequestProcessor("Asynch " //NOI18N
41
             + "children creator " + this, Thread.NORM_PRIORITY, true); //NOI18N
42
    private final R r = new R();
43
    /**
44
     * Create a new AsyncChildren instance with the passed provider object
45
     * which will manufacture key objects and nodes.
46
     * @param provider An object which can provide a list of keys and make
47
     *        Nodes for them
48
     */ 
49
    public AsynchChildren(Provider provider) {
50
        this.provider = provider;
51
        task = PROC.create(r, true);
52
    }
53
    
54
    volatile boolean initialized = false;
55
    protected void addNotify() {
56
        if ((!initialized && task.isFinished()) || cancelled) {
57
            cancelled = false;
58
            setKeys (new Object[] { new WaitNode() });
59
            task.schedule(0);
60
            Thread.dumpStack();
61
        }
62
    }
63
    
64
    protected void removeNotify() {
65
        cancelled = true;
66
        task.cancel();
67
        initialized = false;
68
        setKeys (Collections.<Object>emptyList());
69
    }
70
    
71
    /**
72
     * Notify this AsynchChildren that it should reconstruct its children,
73
     * calling <code>provider.asynchCreateKeys()</code> and setting the
74
     * keys to that.  Call this method if the list of child objects is known
75
     * or likely to have changed.
76
     * @param immediate If true, the keys are updated synchronously from the
77
     *  calling thread.  Set this to true only if you know that updating
78
     *  the keys will <i>not</i> be an expensive or time-consuming operation.
79
     */ 
80
    @SuppressWarnings("unchecked") //NOI18N
81
    public void update(boolean immediate) {
82
        if (immediate) {
83
            boolean done;
84
            List <Object> keys = new LinkedList <Object> ();
85
            do {
86
                done = provider.asynchCreateKeys(keys);
87
            } while (!done);
88
            setKeys (keys);
89
        } else {
90
            task.schedule (0);
91
        }
92
    }
93
    
94
    public Node[] getNodes(boolean optimalResult) {
95
        if (optimalResult) {
96
            task.waitFinished();
97
        }
98
        return super.getNodes();
99
    }
100
    
101
    @SuppressWarnings("unchecked")
102
    protected Node[] createNodes(Object key) {
103
        if (key instanceof WaitNode) {
104
            return new Node[] { (Node) key };
105
        } else {
106
            return provider.createNodes (key);
107
        }
108
    }
109
110
    /**
111
     * Factory for keys and child nodes for an instance of AsynchChildren.
112
     */ 
113
    public static interface Provider <T> {
114
        /**
115
         * Create Nodes for a given key object (one from the <code>List</code>
116
         * returned by asynchCreateKeys()).
117
         * @param key An object from the list returned by 
118
         *        <code>asynchCreateKeys()</code>
119
         * @return zero or more Nodes to represent this key
120
         */
121
        public Node[] createNodes (T key);
122
        /**
123
         * Create a list of keys which can be individually passed to
124
         * createNodes() to create child Nodes.  Implementations of
125
         * this method should regularly check Thread.interrupted(), and
126
         * if it returns true (meaning the parent Node was collapsed or
127
         * destroyed), stop creating keys immediately and return
128
         * null.  This method is guaranteed <i>not</i> to be called on the
129
         * AWT event thread.
130
         * 
131
         * @param toPopulate A list to add key objects to
132
         * @return true if the list of keys has been completely populated,
133
         *         false if the list has only been partially populated and
134
         *         this method should be called again to batch more keys
135
         */ 
136
        public boolean asynchCreateKeys (List <T> toPopulate);
137
    }
138
    
139
    volatile boolean cancelled = false;
140
    private final class R implements Runnable {
141
        @SuppressWarnings("unchecked")
142
        public void run() {
143
            if (Thread.interrupted()) {
144
                System.err.println("Cancelled");
145
                doSetKeys (Collections.EMPTY_LIST);
146
                return;
147
            }
148
            List <Object> keys = new LinkedList <Object> ();
149
            boolean done;
150
            int ct = 0;
151
            do {
152
                System.err.println("LOOP " + (ct++));
153
                done = provider.asynchCreateKeys (keys);
154
                if (cancelled || Thread.interrupted()) {
155
                    doSetKeys (Collections.<Object>emptyList());
156
                    System.err.println("Cancelled");
157
                    return;
158
                }
159
                doSetKeys (new ArrayList <Object> (keys));
160
                System.err.println("Batch set " + keys + " DONE " + done);
161
            } while (!done);
162
            initialized = done;
163
        }
164
        
165
        private void doSetKeys (List <Object> keys) {
166
            setKeys (keys);
167
        }
168
    }
169
    
170
    //We need a type that no Children class will use as a key
171
    private static final class WaitNode extends AbstractNode {
172
        public WaitNode () {
173
            super (Children.LEAF);
174
            setDisplayName (NbBundle.getMessage (WaitNode.class, "LBL_WAIT")); //NOI18N
175
        }
176
        
177
        public Image getIcon (int type) {
178
            return Utilities.loadImage ("org/openide/nodes/wait.gif"); //NOI18N
179
        }
180
    }
181
}
(-)openide/nodes/src/org/openide/nodes/Bundle.properties (+2 lines)
Lines 71-73 Link Here
71
# NodeMimeTypeCut=application/x-java-openide-nodecut;class=org.openide.nodes.NodeTransfer$Cut
71
# NodeMimeTypeCut=application/x-java-openide-nodecut;class=org.openide.nodes.NodeTransfer$Cut
72
# NodeMimeTypeCopy=application/x-java-openide-nodecopy;class=org.openide.nodes.NodeTransfer$Copy
72
# NodeMimeTypeCopy=application/x-java-openide-nodecopy;class=org.openide.nodes.NodeTransfer$Copy
73
# NodeMimeTypePaste=application/x-java-openide-nodepaste;class=org.openide.nodes.NodeTransfer$Paste
73
# NodeMimeTypePaste=application/x-java-openide-nodepaste;class=org.openide.nodes.NodeTransfer$Paste
74
75
LBL_WAIT=Please Wait...
(-)openide/nodes/test/unit/src/org/openide/nodes/AsynchChildrenTest.java (+251 lines)
Added Link Here
1
/*
2
 * The contents of this file are subject to the terms of the Common Development
3
 * and Distribution License (the License). You may not use this file except in
4
 * compliance with the License.
5
 *
6
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7
 * or http://www.netbeans.org/cddl.txt.
8
 *
9
 * When distributing Covered Code, include this CDDL Header Notice in each file
10
 * and include the License file at http://www.netbeans.org/cddl.txt.
11
 * If applicable, add the following below the CDDL Header, with the fields
12
 * enclosed by brackets [] replaced by your own identifying information:
13
 * "Portions Copyrighted [year] [name of copyright owner]"
14
 *
15
 * The Original Software is NetBeans. The Initial Developer of the Original
16
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17
 * Microsystems, Inc. All Rights Reserved.
18
 */
19
20
package org.openide.nodes;
21
22
import java.beans.*;
23
import java.util.*;
24
import junit.framework.TestCase;
25
import org.openide.nodes.*;
26
import org.openide.util.NbBundle;
27
28
/** Test for AsynchChildren
29
 * 
30
 * @author Tim Boudreau
31
 */
32
public class AsynchChildrenTest extends TestCase {
33
34
    public AsynchChildrenTest(String name) {
35
        super(name);
36
    }
37
    
38
    private ProviderImpl provider;
39
    private BatchProviderImpl provider2;
40
    private AsynchChildren kids2;
41
    private Node node2;
42
    private AsynchChildren kids;
43
    private Node node;
44
    public void setUp() throws Exception {
45
        provider = new ProviderImpl();
46
        kids = new AsynchChildren (provider);
47
        node = new AbstractNode (kids);
48
        
49
        provider2 = new BatchProviderImpl();
50
        kids2 = new AsynchChildren (provider2);
51
        node2 = new AbstractNode (kids2);
52
    }
53
    
54
    //A word of caution re adding tests:
55
    //Almost anything (getNodes(), justCreateNodes(), etc. can trigger a
56
    //fresh call to Children.addNotify().  Any test that expects a synchronous
57
    //change in the child nodes as a result of having triggered a call
58
    //to setKeys() is probably testing a race condition, not the behavior
59
    //of the children implementation
60
61
    public void testGetNodesWaits() throws Exception {
62
        System.out.println("testGetNodesWaits");
63
        kids.addNotify();
64
        new NL (node);
65
        Node[] n = kids.getNodes(true);
66
        assertEquals (4, n.length);
67
    }
68
    
69
    public void testInitialNodeIsWaitNode() throws Exception {
70
        System.out.println("testInitialNodeIsWaitNode");
71
        provider.wait = true;
72
        kids.addNotify();
73
        Node[] n = kids.justComputeNodes();
74
        assertEquals (1, n.length);
75
        assertEquals (NbBundle.getMessage(AsynchChildren.class, "LBL_WAIT"),
76
                n[0].getDisplayName());
77
        provider.wait = false;
78
        synchronized (provider) {
79
            provider.wait (2000);
80
        }
81
        for (int i = 0; i < 5 && n.length != 4; i++) {
82
            n = kids.getNodes(true);
83
            java.lang.Thread.currentThread().yield();
84
        }
85
        assertEquals (4, n.length);
86
    }
87
    
88
    public void testCancel() throws Exception {
89
        System.out.println("testCancel");
90
        provider.wait = true;
91
        kids.addNotify();
92
        kids.removeNotify();
93
        provider.wait = false;
94
        synchronized (provider) {
95
            provider.wait (2000);
96
        }
97
        assertTrue (provider.cancelled);
98
    }
99
    
100
    public void testBatch() throws Exception {
101
        System.out.println("testBatch");
102
        provider2.wait = true;
103
        kids2.addNotify();
104
        provider2.wait = false;
105
        new NL (node2);
106
        Node[] n = n = kids2.getNodes(true);
107
        assertEquals (4, n.length);
108
        assertEquals (2, provider2.callCount);
109
    }
110
        
111
     static final class ProviderImpl implements AsynchChildren.Provider <String> {
112
        
113
        boolean wait = false;
114
        
115
        public Node[] createNodes(String key) {
116
            AbstractNode nd = new AbstractNode (Children.LEAF);
117
            nd.setDisplayName (key);
118
            return new Node[] { nd };
119
        }
120
121
        boolean cancelled = false;
122
        public boolean asynchCreateKeys(List <String> result) {
123
            try {
124
                while (wait) {
125
                    try {
126
                        Thread.currentThread().sleep (200);
127
                    } catch (Exception e) {
128
129
                    }
130
                }
131
                if (Thread.interrupted()) {
132
                    cancelled = true;
133
                    return true;
134
                }
135
                result.add ("A");
136
                result.add ("B");
137
                result.add ("C");
138
                result.add ("D");
139
                if (Thread.interrupted()) {
140
                    cancelled = true;
141
                    return true;
142
                }
143
                return true;
144
            } finally {
145
                synchronized (this) {
146
                    notifyAll();
147
                }
148
            }
149
        }
150
    }
151
     
152
     static final class BatchProviderImpl implements AsynchChildren.Provider <String> {
153
        boolean firstCycle = true;
154
        volatile boolean wait = false;
155
        
156
        public Node[] createNodes(String key) {
157
            AbstractNode nd = new AbstractNode (Children.LEAF);
158
            nd.setDisplayName (key);
159
            return new Node[] { nd };
160
        }
161
        
162
        private synchronized void waitFor() {
163
            try         {
164
                wait(5000);
165
            } catch (InterruptedException ex) {
166
                throw new Error (ex);
167
            }
168
        }
169
170
        int callCount = 0;
171
        public boolean asynchCreateKeys(List <String> result) {
172
            callCount++;
173
            int wc = 0;
174
            System.err.println("Enter wait");
175
            while (wait && (wc ++ < 20)) {
176
                try {
177
                    Thread.currentThread().sleep (200);
178
                } catch (Exception e) {
179
                    
180
                }
181
            }
182
            System.err.println("Exit wait");
183
            if (Thread.interrupted()) {
184
                return true;
185
            }
186
            boolean wasFirstCycle = firstCycle;
187
            if (wasFirstCycle) {
188
                result.add ("A");
189
                result.add ("B");
190
                wait = true;
191
                firstCycle = false;
192
                return false;
193
            } else {
194
                result.add ("C");
195
                result.add ("D");
196
            }
197
            if (Thread.interrupted()) {
198
                return true;
199
            }
200
            synchronized (this) {
201
                notifyAll();
202
            }
203
            return true;
204
        }
205
    }
206
     
207
    private static final class NL implements NodeListener {
208
        NL (Node n) {
209
            n.addNodeListener (this);
210
            try {
211
                waitFor();
212
            } catch (Exception e) {
213
                throw new Error (e);
214
            }
215
        }
216
        
217
        NL () {
218
            
219
        }
220
        
221
        public void childrenAdded(NodeMemberEvent ev) {
222
            go();
223
        }
224
225
        public void childrenRemoved(NodeMemberEvent ev) {
226
            go();
227
        }
228
229
        public void childrenReordered(NodeReorderEvent ev) {
230
            go();
231
        }
232
233
        public void nodeDestroyed(NodeEvent ev) {
234
        }
235
236
        public void propertyChange(PropertyChangeEvent arg0) {
237
        }
238
        
239
        private void go() {
240
            synchronized (this) {
241
                notifyAll();
242
            }
243
        }
244
        
245
        void waitFor() throws Exception {
246
            synchronized (this) {
247
                wait (10000);
248
            }
249
        }
250
    }
251
}

Return to bug 91529