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

(-)src/org/openide/nodes/ChildrenArray.java (-1 / +7 lines)
Lines 31-36 Link Here
31
    /** Creates new ChildrenArray */
31
    /** Creates new ChildrenArray */
32
    private ChildrenArray () {
32
    private ChildrenArray () {
33
    }
33
    }
34
    
35
    public Children getChildren () {
36
        return children;
37
    }
34
38
35
    /** When finalized notify the children.
39
    /** When finalized notify the children.
36
    */
40
    */
Lines 68-74 Link Here
68
        if (nodes == null) {
72
        if (nodes == null) {
69
            nodes = children.justComputeNodes ();
73
            nodes = children.justComputeNodes ();
70
            for (int i = 0; i < nodes.length; i++) {
74
            for (int i = 0; i < nodes.length; i++) {
71
                nodes[i].addNodeListener (this);
75
                // keeps a hard reference from the children node to this
76
                // so we can be GCed only when child nodes are gone
77
                nodes[i].reassignTo (children, this);
72
            }
78
            }
73
            // if at least one node => be weak
79
            // if at least one node => be weak
74
            children.registerChildrenArray (this, nodes.length > 0);
80
            children.registerChildrenArray (this, nodes.length > 0);
(-)src/org/openide/nodes/FilterNode.java (-13 / +37 lines)
Lines 152-158 Link Here
152
    /** Initializes the node.
152
    /** Initializes the node.
153
    */
153
    */
154
    private void init () {
154
    private void init () {
155
        original.addNodeListener (getNodeListener ());
156
        delegateMask = DELEGATE_ALL;
155
        delegateMask = DELEGATE_ALL;
157
    }
156
    }
158
157
Lines 328-384 Link Here
328
    * @param s the string
327
    * @param s the string
329
    */
328
    */
330
    public void setName (String s) {
329
    public void setName (String s) {
331
        if (delegating (DELEGATE_SET_NAME))
330
        if (delegating (DELEGATE_SET_NAME)) {
331
            getNodeListener ();
332
            original.setName (s);
332
            original.setName (s);
333
        else
333
        } else {
334
            super.setName (s);
334
            super.setName (s);
335
        }
335
    }
336
    }
336
337
337
    /* @return the name of the original node
338
    /* @return the name of the original node
338
    */
339
    */
339
    public String getName () {
340
    public String getName () {
340
        if (delegating (DELEGATE_GET_NAME))
341
        if (delegating (DELEGATE_GET_NAME)) {
342
            getNodeListener ();
341
            return original.getName ();
343
            return original.getName ();
342
        else
344
        } else {
343
            return super.getName ();
345
            return super.getName ();
346
        }
344
    }
347
    }
345
348
346
    /* Setter for display name. Fires info about property change.
349
    /* Setter for display name. Fires info about property change.
347
    * @param s the string
350
    * @param s the string
348
    */
351
    */
349
    public void setDisplayName (String s) {
352
    public void setDisplayName (String s) {
350
        if (delegating (DELEGATE_SET_DISPLAY_NAME))
353
        if (delegating (DELEGATE_SET_DISPLAY_NAME)) {
354
            getNodeListener ();
351
            original.setDisplayName (s);
355
            original.setDisplayName (s);
352
        else
356
        } else {
353
            super.setDisplayName (s);
357
            super.setDisplayName (s);
358
        }
354
    }
359
    }
355
360
356
    /* @return the display name of the original node
361
    /* @return the display name of the original node
357
    */
362
    */
358
    public String getDisplayName () {
363
    public String getDisplayName () {
359
        if (delegating (DELEGATE_GET_DISPLAY_NAME))
364
        if (delegating (DELEGATE_GET_DISPLAY_NAME)) {
365
            getNodeListener ();
360
            return original.getDisplayName ();
366
            return original.getDisplayName ();
361
        else
367
        } else {
362
            return super.getDisplayName ();
368
            return super.getDisplayName ();
369
        }
363
    }
370
    }
364
371
365
    /* Setter for short description. Fires info about property change.
372
    /* Setter for short description. Fires info about property change.
366
    * @param s the string
373
    * @param s the string
367
    */
374
    */
368
    public void setShortDescription (String s) {
375
    public void setShortDescription (String s) {
369
        if (delegating (DELEGATE_SET_SHORT_DESCRIPTION))
376
        if (delegating (DELEGATE_SET_SHORT_DESCRIPTION)) {
377
            getNodeListener ();
370
            original.setShortDescription (s);
378
            original.setShortDescription (s);
371
        else
379
        } else {
372
            super.setShortDescription (s);
380
            super.setShortDescription (s);
381
        }
373
    }
382
    }
374
383
375
    /* @return the description of the original node
384
    /* @return the description of the original node
376
    */
385
    */
377
    public String getShortDescription () {
386
    public String getShortDescription () {
378
        if (delegating (DELEGATE_GET_SHORT_DESCRIPTION))
387
        if (delegating (DELEGATE_GET_SHORT_DESCRIPTION)) {
388
            getNodeListener ();
379
            return original.getShortDescription ();
389
            return original.getShortDescription ();
380
        else
390
        } else {
381
            return super.getShortDescription ();
391
            return super.getShortDescription ();
392
        }
382
    }
393
    }
383
394
384
    /* Finds an icon for this node. Delegates to the original.
395
    /* Finds an icon for this node. Delegates to the original.
Lines 388-393 Link Here
388
    * @return icon to use to represent the bean
399
    * @return icon to use to represent the bean
389
    */
400
    */
390
    public Image getIcon (int type) {
401
    public Image getIcon (int type) {
402
        getNodeListener ();
391
        return original.getIcon (type);
403
        return original.getIcon (type);
392
    }
404
    }
393
405
Lines 399-404 Link Here
399
    * @return icon to use to represent the bean when opened
411
    * @return icon to use to represent the bean when opened
400
    */
412
    */
401
    public Image getOpenedIcon (int type) {
413
    public Image getOpenedIcon (int type) {
414
        getNodeListener ();
402
        return original.getOpenedIcon (type);
415
        return original.getOpenedIcon (type);
403
    }
416
    }
404
417
Lines 446-451 Link Here
446
    * @return the array of property sets.
459
    * @return the array of property sets.
447
    */
460
    */
448
    public PropertySet[] getPropertySets () {
461
    public PropertySet[] getPropertySets () {
462
        getNodeListener ();
449
        return original.getPropertySets ();
463
        return original.getPropertySets ();
450
    }
464
    }
451
465
Lines 566-571 Link Here
566
    *    is not supported
580
    *    is not supported
567
    */
581
    */
568
    public Node.Cookie getCookie (Class type) {
582
    public Node.Cookie getCookie (Class type) {
583
        getNodeListener ();
569
        return original.getCookie (type);
584
        return original.getCookie (type);
570
    }
585
    }
571
586
Lines 689-697 Link Here
689
    synchronized NodeListener getNodeListener () {
704
    synchronized NodeListener getNodeListener () {
690
        if (nodeL == null) {
705
        if (nodeL == null) {
691
            nodeL = createNodeListener ();
706
            nodeL = createNodeListener ();
707
            getOriginal().addNodeListener(nodeL);
692
        }
708
        }
693
        return nodeL;
709
        return nodeL;
694
    }
710
    }
711
    
712
    /** Notified from Node that a listener has been added. 
713
     * Thus we force initialization of listeners.
714
     */
715
    final void listenerAdded () {
716
        getNodeListener ();
717
    }
718
    
695
719
696
    /** Check method whether the node has default behaviour or
720
    /** Check method whether the node has default behaviour or
697
    * if it is either subclass of uses different children.
721
    * if it is either subclass of uses different children.
(-)src/org/openide/nodes/Node.java (-9 / +36 lines)
Lines 106-115 Link Here
106
    /** cache of all created lookups */
106
    /** cache of all created lookups */
107
    private static WeakHashMap lookups = new WeakHashMap (37);
107
    private static WeakHashMap lookups = new WeakHashMap (37);
108
    
108
    
109
    /** children representing parent node, for synchronization reasons must be changed only
109
    /** children representing parent node (Children or ChildrenArray), 
110
    * for synchronization reasons must be changed only
110
    * under the Children.MUTEX lock
111
    * under the Children.MUTEX lock
111
    */
112
    */
112
    private Children parent;
113
    private Object parent;
113
    
114
    
114
    /** children list, for synch. reasons change only under Children.MUTEX
115
    /** children list, for synch. reasons change only under Children.MUTEX
115
    * lock
116
    * lock
Lines 224-229 Link Here
224
    */
225
    */
225
    public abstract Node cloneNode ();
226
    public abstract Node cloneNode ();
226
227
228
    
229
    /** Finds the children we are attached to.
230
     * @return children
231
     */
232
    private Children getParentChildren () {
233
        return this.parent instanceof ChildrenArray ? ((ChildrenArray)this.parent).getChildren () : (Children)this.parent;
234
    }
227
235
228
    /** Method that allows Children to change the parent children of
236
    /** Method that allows Children to change the parent children of
229
    * the node when the node is add to a children.
237
    * the node when the node is add to a children.
Lines 233-250 Link Here
233
    * @exception IllegalStateException if this node already belongs to a children
241
    * @exception IllegalStateException if this node already belongs to a children
234
    */
242
    */
235
    final synchronized void assignTo (Children parent, int index) {
243
    final synchronized void assignTo (Children parent, int index) {
236
        if (this.parent != null && this.parent != parent) {
244
        Children ch = getParentChildren ();
237
            throw new IllegalStateException ("Cannot initialize " + index + "th child of node " + parent.getNode () + "; it already belongs to node " + this.parent.getNode ()); // NOI18N
245
        if (ch != null && ch != parent) {
246
            throw new IllegalStateException ("Cannot initialize " + index + "th child of node " + parent.getNode () + "; it already belongs to node " + ch.getNode ()); // NOI18N
238
        }
247
        }
239
        this.parent = parent;
248
        this.parent = parent;
240
    }
249
    }
250
    
251
    /** Code that reasignes the reference from to parent from its
252
     * Children to its ChildrenArray.
253
     */
254
    final synchronized void reassignTo (Children currentParent, ChildrenArray itsArray) {
255
        if (this.parent != currentParent) {
256
            throw new IllegalStateException ("Unauthorized call to change parent: " + this.parent + " and should be: " + currentParent);
257
        }
258
        this.parent = itsArray;
259
    }
241
260
242
    /** Deassignes the node from a children, when it is removed from
261
    /** Deassignes the node from a children, when it is removed from
243
    * a children.
262
    * a children.
244
    */
263
    */
245
    final synchronized void deassignFrom (Children parent) {
264
    final synchronized void deassignFrom (Children parent) {
246
        if (parent != this.parent) {
265
        Children p = getParentChildren ();
247
            throw new IllegalArgumentException ("Deassign from wrong parent. Old: " + this.parent + " Caller: " + parent); //NOI18N
266
        if (parent != p) {
267
            throw new IllegalArgumentException ("Deassign from wrong parent. Old: " + p + " Caller: " + parent); //NOI18N
248
        }
268
        }
249
        this.parent = null;
269
        this.parent = null;
250
    }
270
    }
Lines 364-370 Link Here
364
    */
384
    */
365
    public final Node getParentNode () {
385
    public final Node getParentNode () {
366
        // if contained in a list return its parent node
386
        // if contained in a list return its parent node
367
        return parent == null ? null : parent.getNode ();
387
        Children ch = getParentChildren ();
388
        return ch == null ? null : ch.getNode ();
368
    }
389
    }
369
390
370
    /** Test whether this node can be renamed.
391
    /** Test whether this node can be renamed.
Lines 395-403 Link Here
395
    public void destroy () throws IOException {
416
    public void destroy () throws IOException {
396
        Children.MUTEX.postWriteRequest (new Runnable () {
417
        Children.MUTEX.postWriteRequest (new Runnable () {
397
                                             public void run () {
418
                                             public void run () {
398
                                                 if (parent != null) {
419
                                                 Children p = getParentChildren ();
420
                                                 if (p != null) {
399
                                                     // remove itself from parent
421
                                                     // remove itself from parent
400
                                                     parent.remove (new Node[] {Node.this} );
422
                                                     p.remove (new Node[] {Node.this} );
401
                                                 }
423
                                                 }
402
                                                 // sets the valid flag to false and fires prop. change
424
                                                 // sets the valid flag to false and fires prop. change
403
                                                 fireNodeDestroyed ();
425
                                                 fireNodeDestroyed ();
Lines 653-658 Link Here
653
            ((LookupEventList)listeners).init ();
675
            ((LookupEventList)listeners).init ();
654
        }
676
        }
655
        listeners.add (NodeListener.class, l);
677
        listeners.add (NodeListener.class, l);
678
        listenerAdded ();
679
    }
680
    
681
    /** A method to notify FilterNode that a listenerAdded has been added */
682
    void listenerAdded () {
656
    }
683
    }
657
684
658
    /** Remove a node listener.
685
    /** Remove a node listener.
(-)test/unit/src/org/openide/nodes/NodeLookupTest.java (-19 / +87 lines)
Lines 36-70 Link Here
36
    public static void main(String[] args) {
36
    public static void main(String[] args) {
37
        TestRunner.run(new NbTestSuite(NodeLookupTest.class));
37
        TestRunner.run(new NbTestSuite(NodeLookupTest.class));
38
    }
38
    }
39
39
    
40
    public void testChangesAreFiredFromLookup () {
40
    public void testChangesAreFiredFromLookup () {
41
        InstanceContent ic = new InstanceContent ();
41
        CountableLookup lkp = new CountableLookup ();
42
        AbstractLookup lookup = new AbstractLookup (ic);
42
        Node node = new AbstractNode (createChildren (), lkp);
43
        Node node = new AbstractNode (Children.LEAF, lookup);
44
43
45
        checkInstanceInGetCookie (new Node.Cookie () {}, ic, node);
44
        checkGetNodesDoesNotInitializeLookup (node, lkp.queries);
46
        checkInstanceInGetLookup (new Node.Cookie () {}, ic, node, true);
45
        checkInstanceInGetCookie (new Node.Cookie () {}, lkp.ic, node);
47
        checkInstanceInGetLookup ("Some string", ic, node, true);
46
        checkInstanceInGetLookup (new Node.Cookie () {}, lkp.ic, node, true);
47
        checkInstanceInGetLookup ("Some string", lkp.ic, node, true);
48
    }
48
    }
49
49
50
    public void testChangesAreFiredFromLookupThruFilterNode () {
50
    public void testChangesAreFiredFromLookupThruFilterNode () {
51
        InstanceContent ic = new InstanceContent ();
51
        CountableLookup lkp = new CountableLookup ();
52
        AbstractLookup lookup = new AbstractLookup (ic);
52
        Node node = new FilterNode (new AbstractNode (createChildren (), lkp));
53
        Node node = new FilterNode (new AbstractNode (Children.LEAF, lookup));
54
53
54
        checkGetNodesDoesNotInitializeLookup (node, lkp.queries);
55
        //checkInstanceInGetCookie (new Node.Cookie () {}, ic, node);
55
        //checkInstanceInGetCookie (new Node.Cookie () {}, ic, node);
56
        checkInstanceInGetLookup (new Node.Cookie () {}, ic, node, true);
56
        checkInstanceInGetLookup (new Node.Cookie () {}, lkp.ic, node, true);
57
        checkInstanceInGetLookup ("Some string", ic, node, true);
57
        checkInstanceInGetLookup ("Some string", lkp.ic, node, true);
58
    }
58
    }
59
59
60
    public void testChangesAreFiredFromLookupThruFilterNodeWithOverWrittenGetCookie () {
60
    public void testChangesAreFiredFromLookupThruFilterNodeWithOverWrittenGetCookie () {
61
        final Node.Cookie myInstance = new Node.Cookie () { };
61
        final Node.Cookie myInstance = new Node.Cookie () { };
62
        
62
        final ArrayList queries = new ArrayList ();
63
        
63
        
64
        InstanceContent ic = new InstanceContent ();
64
        InstanceContent ic = new InstanceContent ();
65
        AbstractLookup lookup = new AbstractLookup (ic);
65
        AbstractLookup lookup = new AbstractLookup (ic);
66
        Node node = new FilterNode (new AbstractNode (Children.LEAF, lookup)) {
66
        Node node = new FilterNode (new AbstractNode (createChildren (), lookup)) {
67
            public Node.Cookie getCookie (Class clazz) {
67
            public Node.Cookie getCookie (Class clazz) {
68
                queries.add (clazz);
69
                
68
                if (clazz == myInstance.getClass ()) {
70
                if (clazz == myInstance.getClass ()) {
69
                    return myInstance;
71
                    return myInstance;
70
                }
72
                }
Lines 72-77 Link Here
72
            }
74
            }
73
        };
75
        };
74
76
77
        checkGetNodesDoesNotInitializeLookup (node, queries);
75
        checkInstanceInGetCookie (new Node.Cookie () {}, ic, node);
78
        checkInstanceInGetCookie (new Node.Cookie () {}, ic, node);
76
        checkInstanceInGetLookup (new Node.Cookie () {}, ic, node, true);
79
        checkInstanceInGetLookup (new Node.Cookie () {}, ic, node, true);
77
        // by overwriting the FilterNode.getCookie we disable enhanced support
80
        // by overwriting the FilterNode.getCookie we disable enhanced support
Lines 133-150 Link Here
133
    
136
    
134
    public void testFilterNodeWithOverridenGetCookieIsInTheLookup () {
137
    public void testFilterNodeWithOverridenGetCookieIsInTheLookup () {
135
        CookieNode n = new CookieNode ();
138
        CookieNode n = new CookieNode ();
136
        
139
140
        final ArrayList queries = new ArrayList ();
137
        class MyFN extends FilterNode {
141
        class MyFN extends FilterNode {
138
            public MyFN (Node n) {
142
            public MyFN (Node n) {
139
                super (n);
143
                super (n);
140
            }
144
            }
141
            
145
            
142
            public Node.Cookie getCookie (Class clazz) {
146
            public Node.Cookie getCookie (Class clazz) {
147
                queries.add (clazz);
143
                return super.getCookie (clazz);
148
                return super.getCookie (clazz);
144
            }
149
            }
145
        }
150
        }
146
        
151
        
147
        FilterNode fn = new MyFN (n);
152
        FilterNode fn = new MyFN (n);
153
        checkGetNodesDoesNotInitializeLookup (fn, queries);
154
        checkGetNodesDoesNotInitializeLookup (fn, n.queries);
155
        
148
        Lookup l = fn.getLookup ();
156
        Lookup l = fn.getLookup ();
149
        
157
        
150
        // == must be used instead of equals for nodes!!!
158
        // == must be used instead of equals for nodes!!!
Lines 158-165 Link Here
158
        CookieNode n = new CookieNode ();
166
        CookieNode n = new CookieNode ();
159
        
167
        
160
        FilterNode fn = new FilterNode (n);
168
        FilterNode fn = new FilterNode (n);
161
        Lookup l = fn.getLookup ();
169
        checkGetNodesDoesNotInitializeLookup (fn, n.queries);
162
        
170
        
171
        Lookup l = fn.getLookup ();
163
        // == must be used instead of equals for nodes!!!
172
        // == must be used instead of equals for nodes!!!
164
        assertTrue ("Node is there", fn == l.lookup (Node.class));
173
        assertTrue ("Node is there", fn == l.lookup (Node.class));
165
        Collection c = l.lookup (new Lookup.Template (Node.class)).allInstances ();
174
        Collection c = l.lookup (new Lookup.Template (Node.class)).allInstances ();
Lines 229-234 Link Here
229
238
230
        ic.remove (obj);
239
        ic.remove (obj);
231
        listener.assertEvents ("One change in lookup", -1, 1);
240
        listener.assertEvents ("One change in lookup", -1, 1);
241
    }
242
    
243
    private static void checkGetNodesDoesNotInitializeLookup (final org.openide.nodes.Node n, java.util.List queried) {
244
        assertEquals ("No queries before", Collections.EMPTY_LIST, queried);
245
        
246
        class MyCh extends Children.Keys {
247
            protected void addNotify () {
248
                setKeys (java.util.Collections.singleton(n));
249
            }
250
            
251
            public void clear () {
252
                setKeys (java.util.Collections.EMPTY_LIST);
253
            }
254
            
255
            protected Node[] createNodes (Object key) {
256
                return new Node[] { n };
257
            }
258
        };
259
        MyCh ch = new MyCh ();     
260
        
261
        // initialize the node N
262
        new AbstractNode (ch).getChildren().getNodes ();
263
        
264
        assertEquals ("No queries after", Collections.EMPTY_LIST, queried);
265
        ch.clear ();
266
        assertEquals ("No queries after clean either", Collections.EMPTY_LIST, queried);
232
        
267
        
233
    }
268
    }
234
    
269
    
Lines 261-267 Link Here
261
    
296
    
262
    
297
    
263
    public void testBackwardCompatibleAbstractNodeLookupCanBeGarbageCollected () {
298
    public void testBackwardCompatibleAbstractNodeLookupCanBeGarbageCollected () {
264
        AbstractNode n = new AbstractNode (Children.LEAF);
299
        AbstractNode n = new AbstractNode (createChildren ());
265
        
300
        
266
        Lookup l = n.getLookup ();
301
        Lookup l = n.getLookup ();
267
        assertEquals ("Two invocations share the same lookup", l, n.getLookup ());
302
        assertEquals ("Two invocations share the same lookup", l, n.getLookup ());
Lines 271-276 Link Here
271
        assertGC ("Lookup can be GCed", ref);
306
        assertGC ("Lookup can be GCed", ref);
272
    }        
307
    }        
273
    
308
    
309
    
274
    /** Assert GC.
310
    /** Assert GC.
275
     */
311
     */
276
    private static void assertGC (String text, java.lang.ref.Reference ref) {
312
    private static void assertGC (String text, java.lang.ref.Reference ref) {
Lines 284-289 Link Here
284
        fail (text + " " + ref.get ());
320
        fail (text + " " + ref.get ());
285
    }
321
    }
286
    
322
    
323
    private static Children createChildren () {
324
        return Children.LEAF;
325
    }
326
    
287
    
327
    
288
    private static class Listener extends Object
328
    private static class Listener extends Object
289
    implements LookupListener, NodeListener {
329
    implements LookupListener, NodeListener {
Lines 326-336 Link Here
326
    } // end of Listener
366
    } // end of Listener
327
    
367
    
328
    private static class CookieNode extends AbstractNode {
368
    private static class CookieNode extends AbstractNode {
369
        public ArrayList queries = new ArrayList ();
370
        
329
        public CookieNode () {
371
        public CookieNode () {
330
            super (Children.LEAF);
372
            super (createChildren ());
331
        }
373
        }
332
        public CookieNode (Lookup l) {
374
        public CookieNode (Lookup l) {
333
            super (Children.LEAF, l);
375
            super (createChildren (), l);
334
        }
376
        }
335
        
377
        
336
        public CookieSet cookieSet () {
378
        public CookieSet cookieSet () {
Lines 339-344 Link Here
339
        public void setSet (CookieSet s) {
381
        public void setSet (CookieSet s) {
340
            super.setCookieSet (s);
382
            super.setCookieSet (s);
341
        }
383
        }
384
        
385
        public Node.Cookie getCookie (Class c) {
386
            queries.add (c);
387
            return super.getCookie (c);
388
        }
342
    } // end of CookieNode
389
    } // end of CookieNode
390
    
391
    
392
    private static class CountableLookup extends AbstractLookup {
393
        public final InstanceContent ic;
394
        public final ArrayList queries;
395
        
396
        public CountableLookup () {
397
            this (new InstanceContent (), new ArrayList ());
398
        }
399
        
400
        private CountableLookup (InstanceContent ic, ArrayList queries) {
401
            super (ic);
402
            this.ic = ic;
403
            this.queries = queries;
404
        }
405
        
406
        protected void beforeLookup (Lookup.Template t) {
407
            super.beforeLookup (t);
408
            queries.add (t.getType ());
409
        }
410
    }
343
}
411
}
344
412

Return to bug 30657