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/apichanges.xml (-1 / +32 lines)
Lines 14-20 Link Here
14
"Portions Copyrighted [year] [name of copyright owner]"
14
"Portions Copyrighted [year] [name of copyright owner]"
15
15
16
The Original Software is NetBeans. The Initial Developer of the Original
16
The Original Software is NetBeans. The Initial Developer of the Original
17
Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17
Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
18
Microsystems, Inc. All Rights Reserved.
18
Microsystems, Inc. All Rights Reserved.
19
-->
19
-->
20
<!DOCTYPE apichanges PUBLIC "-//NetBeans//DTD API changes list 1.0//EN" "../../nbbuild/javadoctools/apichanges.dtd">
20
<!DOCTYPE apichanges PUBLIC "-//NetBeans//DTD API changes list 1.0//EN" "../../nbbuild/javadoctools/apichanges.dtd">
Lines 23-28 Link Here
23
<apidef name="nodes">Nodes API</apidef>
23
<apidef name="nodes">Nodes API</apidef>
24
</apidefs>
24
</apidefs>
25
<changes>
25
<changes>
26
    <change id="ChildFactory">
27
        <api name="nodes"/>
28
        <summary>API for Children objects that asynchronously compute keys/child
29
            nodes and simplifies implementation of Children.Keys usages</summary>
30
        <version major="7" minor="1"/>
31
        <date day="20" month="3" year="2007"/>
32
        <author login="tboudreau"/>
33
        <compatibility addition="yes" binary="compatible" source="compatible" semantic="compatible" deprecation="no" deletion="no" modification="no"/>
34
        <description>
35
            Added the class 
36
            <a href="@TOP@/org/openide/nodes/ChildFactory.html">ChildFactory</a>
37
            and the method 
38
            <a href="@TOP@/org/openide/nodes/Children.html#create(org.openide.nodes.ChildFactory, boolean)">Children.create(ChildFactory factory, boolean asynchronous)</a>
39
            to the API.  This simplifies creation of Node children which need
40
            to be computed on a background thread for performance reasons.
41
            Anyone wishing to do this can simply extend ChildFactory and
42
            pass that to Children.create() to automatically get a Node that will
43
            display a Please Wait child node when first expanded.  A ChildFactory
44
            can either compute all child nodes, or batch them in multiple
45
            passes.
46
            <p>
47
            ChildFactory can also be used to implement synchronous children,
48
            by setting the <code>useWaitNode</code> parameter passed to
49
            <code>Children.create()</code> to true.  This could replace most
50
            common usages of Children.Keys, and make it easy to switch to
51
            asynchronous child computation if that is determined to be
52
            necessary for performance reasons.
53
            </p>
54
        </description>
55
        <issue number="91529"/>
56
    </change>
26
    <change id="GenericCookieSet">
57
    <change id="GenericCookieSet">
27
        <api name="nodes"/>
58
        <api name="nodes"/>
28
        <summary>CookieSet can hold any objects and not just cookies</summary>
59
        <summary>CookieSet can hold any objects and not just cookies</summary>
(-)openide/nodes/nbproject/project.properties (-1 / +1 lines)
Lines 22-25 Link Here
22
javadoc.arch=${basedir}/../arch/arch-openide-nodes.xml
22
javadoc.arch=${basedir}/../arch/arch-openide-nodes.xml
23
javadoc.apichanges=${basedir}/apichanges.xml
23
javadoc.apichanges=${basedir}/apichanges.xml
24
24
25
spec.version.base=7.0
25
spec.version.base=7.1
(-)openide/nodes/src/org/openide/nodes/AsynchChildren.java (+129 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-2007 Sun
17
 * Microsystems, Inc. All Rights Reserved.
18
 */
19
package org.openide.nodes;
20
21
import java.awt.EventQueue;
22
import java.lang.Thread;
23
import java.util.Collections;
24
import java.util.LinkedList;
25
import java.util.List;
26
import org.openide.util.RequestProcessor;
27
28
/**
29
 * Children object which creates its keys on a background thread.  To use,
30
 * implement AsynchChildren.Provider and pass that to the constructor.
31
 *
32
 * @author Tim Boudreau
33
 */
34
final class AsynchChildren <T> extends Children.Keys <Object> implements 
35
                                                          ChildFactory.Observer, 
36
                                                          Runnable {
37
    private final ChildFactory<T> factory;
38
    private final RequestProcessor.Task task;
39
    private final RequestProcessor PROC = new RequestProcessor("Asynch " //NOI18N
40
             + "children creator " + this, Thread.NORM_PRIORITY, true); //NOI18N
41
    /**
42
     * Create a new AsyncChildren instance with the passed provider object
43
     * which will manufacture key objects and nodes.
44
     * @param provider An object which can provide a list of keys and make
45
     *        Nodes for them
46
     */ 
47
    AsynchChildren(ChildFactory<T> factory) {
48
        factory.setObserver (this);
49
        this.factory = factory;
50
        task = PROC.create(this, true);
51
    }
52
    
53
    volatile boolean initialized = false;
54
    protected void addNotify() {
55
        if ((!initialized && task.isFinished()) || cancelled) {
56
            cancelled = false;
57
            Node n = factory.getWaitNode();
58
            if (n != null) {
59
                setKeys (new Object[] { n });
60
            }
61
            task.schedule(0);
62
        }
63
    }
64
    
65
    protected void removeNotify() {
66
        cancelled = true;
67
        task.cancel();
68
        initialized = false;
69
        setKeys (Collections.<Object>emptyList());
70
    }
71
    
72
    /**
73
     * Notify this AsynchChildren that it should reconstruct its children,
74
     * calling <code>provider.asynchCreateKeys()</code> and setting the
75
     * keys to that.  Call this method if the list of child objects is known
76
     * or likely to have changed.
77
     * @param immediate If true, the keys are updated synchronously from the
78
     *  calling thread.  Set this to true only if you know that updating
79
     *  the keys will <i>not</i> be an expensive or time-consuming operation.
80
     */ 
81
    public void refresh(boolean immediate) {
82
        immediate &= !EventQueue.isDispatchThread();
83
        if (immediate) {
84
            boolean done;
85
            List <T> keys = new LinkedList <T> ();
86
            do {
87
                done = factory.createKeys(keys);
88
            } while (!done);
89
            setKeys (keys);
90
        } else {
91
            task.schedule (0);
92
        }
93
    }
94
    
95
    public Node[] getNodes(boolean optimalResult) {
96
        if (optimalResult) {
97
            task.waitFinished();
98
        }
99
        return super.getNodes();
100
    }
101
    
102
    @SuppressWarnings("unchecked") //NOI18N
103
    protected Node[] createNodes(Object key) {
104
        if (factory.isWaitNode(key)) {
105
            return new Node[] { (Node) key };
106
        } else {
107
            return factory.createNodesForKey ((T) key);
108
        }
109
    }
110
111
    volatile boolean cancelled = false;
112
    public void run() {
113
        if (Thread.interrupted()) {
114
            setKeys (Collections.<T>emptyList());
115
            return;
116
        }
117
        List <T> keys = new LinkedList <T> ();
118
        boolean done;
119
        do {
120
            done = factory.createKeys (keys);
121
            if (cancelled || Thread.interrupted()) {
122
                setKeys (Collections.<Object>emptyList());
123
                return;
124
            }
125
            setKeys (new LinkedList <Object> (keys));
126
        } while (!done);
127
        initialized = done;
128
    }
129
}
(-)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
#AsynchChildren
75
LBL_WAIT=Please Wait...
(-)openide/nodes/src/org/openide/nodes/ChildFactory.java (+195 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.lang.ref.Reference;
22
import java.lang.ref.WeakReference;
23
import java.util.List;
24
import org.openide.util.NbBundle;
25
26
/**
27
 * Factory used to create <a href="Children.html">Children</a>
28
 * objects.  Children objects supply child Nodes for a 
29
 * Node.  Usage is to write a class that extends ChildFactory and
30
 * pass that to Children.create().  When the Node is expanded or its
31
 * children are programmatically requested, the 
32
 * <a href="#createKeys(java.util.List)">createKeys(List &lt;T&gt;)</a> method
33
 * will be invoked to create the List of objects to be modelled as Nodes.
34
 * Later, on demand, each object from the List will be passed in turn to
35
 * <a href="#createNodesForKey(java.lang.Object)">createNodesForKey(T)</a>, 
36
 * which may return an array of zero or more Nodes for the object. 
37
 * <p>
38
 * A ChildFactory can be used either to create typical Children object, or
39
 * one which will be initialized on a background thread (providing
40
 * a "Please Wait" Node in the meantime).  It can be used most simple cases
41
 * that Children.Keys has been historically used for, and makes it easy to
42
 * change a Children object to compute its keys asynchronously if that is
43
 * needed for performance reasons.
44
 * <p>
45
 * Only one ChildFactory object may be used per Children object;  if you wish
46
 * to have multiple Nodes modelling children produced by a single 
47
 * ChildFactory, use @link FilterNode to wrap the Node that owns the
48
 * Children object for this ChildFactory.
49
 * <p>
50
 * To use, simply override 
51
 * <a href="#createKeys(java.util.List)">createKeys(List &lt;T&gt;) and 
52
 * <a href="#createNodesForKey(java.lang.Object)">createNodesForKey(T)</a> or
53
 * <a href="#createNodeForKey(java.lang.Object)">createNodeForKey(T)</a>.
54
 * 
55
 * @param T The type of objects in the keys collection
56
 * @author Tim Boudreau
57
 * @see Children#create(ChildFactory, boolean)
58
 */
59
public abstract class ChildFactory <T> {
60
    /**
61
     * Create a Node for a given key that was put into the list passed into
62
     * createKeys().  Either override this method if there will always be
63
     * 0 or 1 nodes per key, or createNodesForKey() if there may be more
64
     * than one.
65
     * 
66
     * The default implementation returns null.
67
     * 
68
     * @param key An object that was previously put into the list passed 
69
     *            to createKeys()
70
     * @return A node, or null if no node should be shown for this object.
71
     */
72
    protected Node createNodeForKey (T key) {
73
        return null;
74
    }
75
    
76
    /**
77
     * Create Nodes for a given key object (one from the <code>List</code>
78
     * passed to createKeys(List <T>)).  The default implementation simply
79
     * delegates to <code>createNodeForKey</code> and returns the result of
80
     * that call in an array of nodes.
81
     * <p>
82
     * Most Children objects have a 1:1 mapping between keys and nodes.  For
83
     * convenience in that situation, simply override createNodeForKey(T).
84
     * 
85
     * @param key An object from the list returned by 
86
     *        <code>asynchCreateKeys()</code>
87
     * @return zero or more Nodes to represent this key
88
     */
89
    protected Node[] createNodesForKey (T key) {
90
        Node n = createNodeForKey (key);
91
        return n == null ? new Node[0] : new Node[] { n };
92
    }
93
    /**
94
     * Create a list of keys which can be individually passed to
95
     * createNodes() to create child Nodes.  Implementations of
96
     * this method should regularly check Thread.interrupted(), and
97
     * if it returns true (meaning the parent Node was collapsed or
98
     * destroyed), stop creating keys immediately and return
99
     * true.  This method is guaranteed <i>not</i> to be called on the
100
     * AWT event thread if this ChildFactory was passed to
101
     * Children.create() with the <code>asynchronous</code> parameter
102
     * set to true.  If not, then no guarantees are made as to what
103
     * the calling thread is.
104
     * 
105
     * @param toPopulate A list to add key objects to
106
     * @return true if the list of keys has been completely populated,
107
     *         false if the list has only been partially populated and
108
     *         this method should be called again to batch more keys
109
     */ 
110
    protected abstract boolean createKeys (List <T> toPopulate);
111
    
112
    /**
113
     * Call this method when the list of objects being modelled by the
114
     * has changed and the child Nodes of this Node should be updated.  The
115
     * boolean argument is a <i>hint</i> to the refresh mechanism (which
116
     * will cause createKeys() to be invoked again) that it is safe to
117
     * synchronously recreate.
118
     * 
119
     * @param immediate If true, the refresh should occur in the calling
120
     *   thread (be careful not to be holding any locks that might
121
     *   deadlock with your key/child creation methods if you pass true).
122
     *   Note that this parameter is only meaningful when using an 
123
     *   asynchronous children instance (i.e. true was passed as the
124
     *   second parameter to <code>Children.create()</code>).  If the
125
     *   Children object for this ChildFactory is called with <code>immediate</code>
126
     *   true on the AWT event dispatch thread, and it is an asynchronous
127
     *   Children object, this parameter will be ignored and computation
128
     *   will be scheduled on a background thread.
129
     */ 
130
    protected final void refresh (boolean immediate) {
131
        Observer obs = observer == null ? null : observer.get();
132
        if (obs != null) {
133
            obs.refresh(immediate);
134
        }
135
    }
136
    
137
    Node getWaitNode() {
138
        Node n = createWaitNode();
139
        return n == null ? null : new WaitFilterNode (n);
140
    }
141
    
142
    /**
143
     * Create the Node that should be shown while the keys are being computed
144
     * on a background thread.  
145
     * This method will not be called if this ChildFactory is used for a 
146
     * synchronous children which does not compute its keys on a background
147
     * thread.  Whether an instance is synchronous or not is determined by a
148
     * parameter to 
149
     * <a href="Children.html#create(ChildFactory, boolean)">Children.create()</a>.
150
     * <p>
151
     * To show no node at all when the Children object is initially expanded in
152
     * the UI, simply return null.
153
     * <p>
154
     * The default implementation returns a Node that shows an hourglass cursor
155
     * and the localized text &quot;Please Wait...&quot;.
156
     * 
157
     * @return A Node, or null if no wait node should be shown.
158
     */ 
159
    protected Node createWaitNode() {
160
        AbstractNode n = new AbstractNode (Children.LEAF);
161
        n.setIconBaseWithExtension("org/openide/nodes/wait.gif");
162
        n.setDisplayName (NbBundle.getMessage (ChildFactory.class, "LBL_WAIT")); //NOI18N
163
        return n;
164
    }
165
    
166
    private Reference <Observer> observer = null;
167
    final void setObserver (Observer observer) {
168
        if (this.observer != null) {
169
            throw new IllegalStateException ("Attempting to create two Children" + //NOI18N
170
                    " objects for a single ChildFactory.  Use " + //NOI18N
171
                    "FilterNode.Children over the existing Children object " + //NOI18N
172
                    "instead"); //NOI18N
173
        }
174
        this.observer = new WeakReference <Observer> (observer);
175
    }
176
    
177
    interface Observer {
178
        public void refresh (boolean immediate);
179
    }
180
    
181
    static boolean isWaitNode (Object n) {
182
        return n instanceof WaitFilterNode;
183
    }
184
    
185
    /**
186
     * This class exists to uniquify/mark any Node returned by createWaitNode
187
     * such that AsynchChildren can identify it absolutely as not being an
188
     * object that should be passed to createNodeForKey().
189
     */ 
190
    private static final class WaitFilterNode extends FilterNode {
191
        public WaitFilterNode (Node orig) {
192
            super (orig);
193
        }
194
    }
195
}
(-)openide/nodes/src/org/openide/nodes/Children.java (-6 / +50 lines)
Lines 39-52 Link Here
39
import org.openide.util.Enumerations;
39
import org.openide.util.Enumerations;
40
import org.openide.util.Mutex;
40
import org.openide.util.Mutex;
41
41
42
/** Container for array of nodes.
42
/** 
43
* Can be {@link Node#Node associated} with a node and then
43
* Factory for the child Nodes of a Node.  Every Node has a Children object.
44
* all children in the array have that node set as a parent, and this list
44
* Children are initially un-initialized, and child Nodes are created on
45
* will be returned as the node's children.
45
* demand when, for example, the Node is expanded in an Explorer view.
46
* If you know your Node has no child nodes, pass <code>Children.LEAF</code>.
47
* Typically a Children object will create a Collection of objects from
48
* some data model, and create one or more Nodes for each object on demand.
49
* 
50
* If initializing the list of children of a Node is time-consuming (i.e. it
51
* does I/O, parses a file or some other expensive operation), implement
52
* ChildFactory and pass it to Children.create (theFactory, true) to have
53
* the child nodes be computed asynchronously on a background thread.
46
*
54
*
47
* <p>Probably you want to subclass {@link Children.Keys}.
55
* <p>In almost all cases you want to subclass ChildFactory and pass it to
56
* Children.create(), or subclass {@link Children.Keys}.
48
* Subclassing <code>Children</code> directly is not recommended.
57
* Subclassing <code>Children</code> directly is not recommended.
49
*
58
* 
50
* @author Jaroslav Tulach
59
* @author Jaroslav Tulach
51
*/
60
*/
52
public abstract class Children extends Object {
61
public abstract class Children extends Object {
Lines 219-224 Link Here
219
            PR.exitReadAccess();
228
            PR.exitReadAccess();
220
        }
229
        }
221
    }
230
    }
231
    
232
    /**
233
     * Create a <code>Children</code> object using the passed <code>ChildFactory</code>
234
     * object.  The <code>ChildFactory</code> will be asked to create a list
235
     * of model objects that are the children;  then for each object in the list,
236
     * <code>ChildFactory.createNodesFor()</code> will be called to instantiate
237
     * one or more <code>Node</code>s for that object.
238
     * @param factory A ChildFactory which will provide child objects
239
     * @param asynchronous If true, the ChildFactory will always be called to
240
     *   create the list of keys on
241
     *   a background thread, displaying a &quot;Please Wait&quot; child Node until
242
     *   some or all child Nodes have been computed.  If so, 
243
     *   when it is expanded, the Node that owns
244
     *   the returned <code>Children</code> object will display a &quot;please wait&quot;
245
     *   node while the children are computed in the background.  Pass true
246
     *   for any case where computing child nodes is expensive and should
247
     *   not be done in the event thread.
248
     * @return A <code>Children</code> object which 
249
     *   will invoke the ChildFactory instance as needed to supply model
250
     *   objects and child nodes for it
251
     * @throws IllegalStateException if the passed ChildFactory has already
252
     *   been used in a previous call to <code>Children.create()</code>.
253
     * @since 7.1
254
     */ 
255
    public static <T> Children create (ChildFactory <T> factory, boolean asynchronous) {
256
        if (factory == null) throw new NullPointerException ("Null factory");
257
        return asynchronous ? new AsynchChildren <T> (factory) : 
258
            new SynchChildren <T> (factory);
259
    }
222
260
223
    /** Get the parent node of these children.
261
    /** Get the parent node of these children.
224
    * @return the node attached to this children object, or <code>null</code> if there is none yet
262
    * @return the node attached to this children object, or <code>null</code> if there is none yet
Lines 1913-1918 Link Here
1913
    * given key changes (but the key stays the same), you can
1951
    * given key changes (but the key stays the same), you can
1914
    * call {@link #refreshKey}. Usually this is not necessary.
1952
    * call {@link #refreshKey}. Usually this is not necessary.
1915
    * </ol>
1953
    * </ol>
1954
    * Note that for simple cases, it may be preferable to subclass
1955
    * <a href="ChildFactory.html">ChildFactory</a> and pass the result to
1956
    * <a href="Children.html#create(org.openide.nodes.ChildFactory, boolean)">
1957
    * create()</a>; doing so makes it easy to switch to using child
1958
    * nodes computed on a background thread if necessary for performance
1959
    * reasons.
1916
     * @param T the type of the key
1960
     * @param T the type of the key
1917
    */
1961
    */
1918
    public static abstract class Keys<T> extends Children.Array {
1962
    public static abstract class Keys<T> extends Children.Array {
(-)openide/nodes/src/org/openide/nodes/SynchChildren.java (+62 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.util.Collections;
22
import java.util.LinkedList;
23
import java.util.List;
24
25
/**
26
 * Synchronous children implementation that takes a ChildFactory.
27
 *
28
 * @author Tim Boudreau
29
 */
30
final class SynchChildren <T> extends Children.Keys <Object> implements ChildFactory.Observer {
31
    private final ChildFactory<T> factory;
32
    
33
    /** Creates a new instance of SynchChildren */
34
    SynchChildren(ChildFactory<T> factory) {
35
        this.factory = factory;
36
        factory.setObserver(this);
37
    }
38
  
39
    volatile boolean active = false;
40
    protected void addNotify() {
41
        active = true;
42
        refresh (true);
43
    }
44
    
45
    protected void removeNotify() {
46
        active = false;
47
        setKeys (Collections.<Object>emptyList());
48
    }
49
    
50
    @SuppressWarnings("unchecked") //NOI18N
51
    protected Node[] createNodes(Object key) {
52
        return factory.createNodesForKey((T) key);
53
    }
54
55
    public void refresh(boolean immediate) {
56
        if (active) {
57
            List <T> toPopulate = new LinkedList<T>();
58
            while (!factory.createKeys(toPopulate)) {}
59
            setKeys (toPopulate);
60
        }
61
    }
62
}
(-)openide/nodes/test/unit/src/org/openide/nodes/ChildFactoryTest.java (+372 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-2007 Sun
17
 * Microsystems, Inc. All Rights Reserved.
18
 */
19
package org.openide.nodes;
20
21
import java.awt.EventQueue;
22
import java.beans.*;
23
import java.util.*;
24
import junit.framework.TestCase;
25
import org.openide.nodes.*;
26
import org.openide.nodes.NodeAdapter;
27
import org.openide.util.NbBundle;
28
29
/** Test for AsynchChildren
30
 * 
31
 * @author Tim Boudreau
32
 */
33
public class ChildFactoryTest extends TestCase {
34
35
    public ChildFactoryTest(String name) {
36
        super(name);
37
    }
38
    
39
    private ProviderImpl factory;
40
    private BatchProviderImpl factory2;
41
    private AsynchChildren kids2;
42
    private Node node2;
43
    private AsynchChildren kids;
44
    private Node node;
45
    public void setUp() throws Exception {
46
        factory = new ProviderImpl();
47
        kids = new AsynchChildren (factory);
48
        node = new AbstractNode (kids);
49
        
50
        factory2 = new BatchProviderImpl();
51
        kids2 = new AsynchChildren (factory2);
52
        node2 = new AbstractNode (kids2);
53
    }
54
    
55
    public void testChildrenCreate() {
56
        System.out.println("testChildrenCreate");
57
        ChildFactory f = new ProviderImpl();
58
        Children kids = Children.create (f, true);
59
        assertTrue (kids instanceof AsynchChildren);
60
        
61
        ChildFactory ff = new ProviderImpl();
62
        Children kids2 = Children.create (ff, false);
63
        assertFalse (kids2 instanceof AsynchChildren);
64
        assertTrue (kids2 instanceof SynchChildren);
65
        
66
        RuntimeException e = null;
67
        Children kids3 = null;
68
        try {
69
            kids3 = Children.create (ff, true);
70
        } catch (RuntimeException ex) {
71
            e = ex;
72
        }
73
        assertNull (kids3);
74
        assertNotNull ("Exception should have been thrown creating two " +
75
                "Children objects over the same ChildFactory", e);
76
    }
77
    
78
    //A word of caution re adding tests:
79
    //Almost anything (getNodes(), justCreateNodes(), etc. can trigger a
80
    //fresh call to Children.addNotify().  Any test that expects a synchronous
81
    //change in the child nodes as a result of having triggered a call
82
    //to setKeys() is probably testing a race condition, not the behavior
83
    //of the children implementation
84
85
    public void testGetNodesWaits() throws Exception {
86
        System.out.println("testGetNodesWaits");
87
        factory.wait = false;
88
        kids.getNodes (false);
89
        synchronized (factory.lock) {
90
            factory.lock.wait (300);
91
        }
92
        Thread.currentThread().yield();
93
        new NL (node);
94
        Node[] n = kids.getNodes(true);
95
        assertEquals (4, n.length);
96
    }
97
    
98
    public void testInitialNodeIsWaitNode() throws Exception {
99
        System.out.println("testInitialNodeIsWaitNode");
100
        factory.wait = true;
101
        kids.addNotify();
102
        Node[] n = kids.getNodes(false);
103
        factory.wait = false;
104
        assertEquals (1, n.length);
105
        assertEquals (NbBundle.getMessage(AsynchChildren.class, "LBL_WAIT"),
106
                n[0].getDisplayName());
107
        factory.wait = false;
108
        synchronized (factory) {
109
            factory.wait (2000);
110
        }
111
        for (int i = 0; i < 5 && n.length != 4; i++) {
112
            n = kids.getNodes(true);
113
            java.lang.Thread.currentThread().yield();
114
        }
115
        assertEquals (4, n.length);
116
    }
117
    
118
    public void testBatch() throws Exception {
119
        System.out.println("testBatch");
120
        kids2.addNotify();
121
        Thread.currentThread().yield();
122
        synchronized (factory2.lock) {
123
            factory2.lock.notifyAll();
124
        }
125
        new NL (node2);
126
        Node[] n = n = kids2.getNodes(true);
127
        assertEquals (4, n.length);
128
        assertEquals (2, factory2.callCount);
129
    } 
130
     
131
    public void testSynchChildren() throws Exception {
132
        System.out.println("testSynchChildren");
133
        final SynchProviderImpl factory = new SynchProviderImpl();
134
        final Children ch = Children.create (factory, false);
135
        assertTrue (ch instanceof SynchChildren);
136
        factory.assertCreateKeysNotCalled();
137
        factory.assertCreateNodesForKeyNotCalled();
138
        final Node nd = new AbstractNode (ch);
139
        NodeAdapter adap = new NodeAdapter() {};
140
        nd.addNodeListener (adap);
141
        
142
        EventQueue.invokeAndWait (new Runnable() {
143
            public void run() {
144
                ch.getNodes(true);
145
            }
146
        });
147
        ((SynchChildren) ch).active = true;
148
        synchronized (factory) {
149
            factory.wait (1000);
150
        }
151
        factory.assertCreateKeysCalled();
152
        factory.assertCreateNodesForKeyCalled();
153
        Node[] nodes = nd.getChildren().getNodes(true);
154
        assertEquals (factory.CONTENTS1.size(), nodes.length);
155
        int ix = 0;
156
        for (String s : factory.CONTENTS1) {
157
            assertEquals (s, nodes[ix].getName());
158
            ix++;
159
        }
160
        factory.switchChildren();
161
        nodes = nd.getChildren().getNodes(true);
162
        assertEquals (factory.CONTENTS2.size(), nodes.length);
163
        ix = 0;
164
        for (String s : factory.CONTENTS2) {
165
            assertEquals (s, nodes[ix].getName());
166
            ix++;
167
        }
168
    }
169
        
170
    public void testCancel() throws Exception {
171
        System.out.println("testCancel");
172
        Thread.interrupted();
173
        factory.wait = true;
174
        kids.addNotify();
175
        Thread.currentThread().yield();
176
        synchronized (factory.lock) {
177
            factory.lock.wait (500);
178
        }
179
        kids.removeNotify();
180
        factory.wait = false;
181
        synchronized (factory) {
182
            factory.wait (2000);
183
        }
184
        assertTrue (kids.cancelled);
185
        assertTrue (factory.cancelled);
186
    }
187
    
188
     static final class ProviderImpl extends ChildFactory <String> {
189
        Object lock = new Object();
190
        volatile boolean wait = false;
191
        
192
        public Node[] createNodesForKey(String key) {
193
            AbstractNode nd = new AbstractNode (Children.LEAF);
194
            nd.setDisplayName (key);
195
            return new Node[] { nd };
196
        }
197
        
198
        boolean cancelled = false;
199
        public boolean createKeys(List <String> result) {
200
            try {
201
                while (wait) {
202
                    Thread.currentThread().yield();
203
                }
204
                synchronized (lock) {
205
                    lock.notifyAll();
206
                }
207
                if (Thread.interrupted()) {
208
                    cancelled = true;
209
                    return true;
210
                }
211
                result.add ("A");
212
                result.add ("B");
213
                result.add ("C");
214
                result.add ("D");
215
                if (Thread.interrupted()) {
216
                    cancelled = true;
217
                }
218
                return true;
219
            } finally {
220
                synchronized (this) {
221
                    notifyAll();
222
                }
223
            }
224
        }
225
    }
226
     
227
     static final class BatchProviderImpl extends ChildFactory <String> {
228
        boolean firstCycle = true;
229
        
230
        public Node[] createNodesForKey(String key) {
231
            AbstractNode nd = new AbstractNode (Children.LEAF);
232
            nd.setDisplayName (key);
233
            return new Node[] { nd };
234
        }
235
        
236
        Object lock = new Object();
237
        int callCount = 0;
238
        public boolean createKeys(List <String> result) {
239
            callCount++;
240
            synchronized (lock) {
241
                try {
242
                    lock.wait(500);
243
                } catch (InterruptedException ex) {
244
                    //re-interrupt
245
                    Thread.currentThread().interrupt();
246
                }
247
            }
248
            if (Thread.interrupted()) {
249
                return true;
250
            }
251
            boolean wasFirstCycle = firstCycle;
252
            if (wasFirstCycle) {
253
                result.add ("A");
254
                result.add ("B");
255
                firstCycle = false;
256
                return false;
257
            } else {
258
                result.add ("C");
259
                result.add ("D");
260
            }
261
            if (Thread.interrupted()) {
262
                return true;
263
            }
264
            synchronized (this) {
265
                notifyAll();
266
            }
267
            return true;
268
        }
269
    }
270
     
271
    private static final class NL implements NodeListener {
272
        NL (Node n) {
273
            n.addNodeListener (this);
274
            try {
275
                waitFor();
276
            } catch (InterruptedException ex) {
277
                Thread.currentThread().interrupt();
278
            } catch (Exception e) {
279
                throw new Error (e);
280
            }
281
        }
282
        
283
        NL () {
284
            
285
        }
286
        
287
        public void childrenAdded(NodeMemberEvent ev) {
288
            go();
289
        }
290
291
        public void childrenRemoved(NodeMemberEvent ev) {
292
            go();
293
        }
294
295
        public void childrenReordered(NodeReorderEvent ev) {
296
            go();
297
        }
298
299
        public void nodeDestroyed(NodeEvent ev) {
300
        }
301
302
        public void propertyChange(PropertyChangeEvent arg0) {
303
        }
304
        
305
        private void go() {
306
            synchronized (this) {
307
                notifyAll();
308
            }
309
        }
310
        
311
        void waitFor() throws Exception {
312
            System.err.println("Enter waitfor");
313
            synchronized (this) {
314
                wait (1000);
315
            }
316
        }
317
    }
318
    
319
    private static final class SynchProviderImpl extends ChildFactory <String> {
320
        static List <String> CONTENTS1 = Arrays.<String>asList (new String[] {
321
            "One", "Two", "Three", "Four"
322
        });
323
        static List <String> CONTENTS2 = Arrays.<String>asList (new String[] {
324
            "Five", "Six", "Seven", "Eight", "Nine"
325
        });
326
        
327
        boolean createNodesForKeyCalled = false;
328
        public Node[] createNodesForKey(String key) {
329
            createNodesForKeyCalled = true;
330
            Node result = new AbstractNode(Children.LEAF);
331
            result.setDisplayName (key);
332
            result.setName (key);
333
            return new Node[] { result };
334
        }
335
        
336
        boolean createKeysCalled = false;
337
        public boolean createKeys(List <String> toPopulate) {
338
            createKeysCalled = true;
339
            List <String> l = switched ? CONTENTS2 : CONTENTS1;
340
            toPopulate.addAll (l);
341
            return true;
342
        }
343
        
344
        void assertCreateNodesForKeyNotCalled() {
345
            assertFalse (createNodesForKeyCalled);
346
        }
347
        
348
        void assertCreateKeysNotCalled() {
349
            assertFalse (createKeysCalled);
350
        }
351
        
352
        boolean assertCreateNodesForKeyCalled() {
353
            boolean result = createNodesForKeyCalled;
354
            createNodesForKeyCalled = false;
355
            assertTrue (result);
356
            return result;
357
        }
358
        
359
        boolean assertCreateKeysCalled() {
360
            boolean result = createKeysCalled;
361
            createKeysCalled = false;
362
            assertTrue (result);
363
            return result;
364
        }
365
        
366
        volatile boolean switched = false;
367
        void switchChildren() {
368
            switched = !switched;
369
            refresh(true);
370
        }
371
    }
372
}

Return to bug 91529