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

(-)a/openide.nodes/apichanges.xml (+33 lines)
Lines 46-51 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="org.openide.nodes.SimpleNode">
50
        <api name="nodes"/>
51
        <summary>Adding SimpleNode, an AbstractNode subclass which handles
52
        a number of common cases and can eliminate some AbstractNode subclasses
53
        </summary>
54
        <version major="7" minor="12"/>
55
        <date day="17" month="10" year="2009"/>
56
        <author login="tboudreau"/>
57
        <compatibility addition="yes" binary="compatible" source="compatible" semantic="compatible"/>
58
        <description>
59
            <a href="@TOP@/org/openide/nodes/SimpleNode.html">SimpleNode</a> is
60
            an AbstractNode subclass which simplifies some common usage patterns
61
            of Nodes:
62
            <ul>
63
                <li>Children object not necessary - simply override
64
                createKeys() and createNodeForKey() to handle child nodes</li>
65
                <li>No lookup, or fixed lookup contents may be passed into constructor,
66
                or createLookup() can be overridden (but not both)</li>
67
                <li>Icon can be passed as constructor parameter, without a subclass being
68
                required</li>
69
                <li>Display name can be annotated with HTML without subclassing, either
70
                using setValue (SimpleNode.ERROR, Boolean.TRUE) or, e.g.,
71
                setValue(SimpleNode.HTML_PREFIX, "&lt;b&gt;")</li>
72
                <li>Children objects do not need to be dealt with directly - simply
73
                override createKeys() and createNodeForKey()</li>
74
                <li>Actions can be handled more simply, and the default Properties action
75
                is suppressed</li>
76
                <li>Fixed Lookup contents can be passed in directly</li>
77
            </ul>
78
        </description>
79
        <class package="org.openide.nodes" name="SimpleNode"/>
80
        <issue number="174805"/>
81
    </change>
49
    <change id="NodeOp.factory">
82
    <change id="NodeOp.factory">
50
        <api name="nodes"/>
83
        <api name="nodes"/>
51
        <summary>Support for declarative Node registrations</summary>
84
        <summary>Support for declarative Node registrations</summary>
(-)a/openide.nodes/nbproject/project.properties (-1 / +1 lines)
Lines 44-47 Link Here
44
javadoc.arch=${basedir}/arch.xml
44
javadoc.arch=${basedir}/arch.xml
45
javadoc.apichanges=${basedir}/apichanges.xml
45
javadoc.apichanges=${basedir}/apichanges.xml
46
46
47
spec.version.base=7.11.0
47
spec.version.base=7.12.0
(-)a/openide.nodes/src/org/openide/nodes/AbstractNode.java (+4 lines)
Lines 571-576 Link Here
571
     */
571
     */
572
    private boolean overridesAMethod(String name, Class[] arguments) {
572
    private boolean overridesAMethod(String name, Class[] arguments) {
573
        // we are subclass of AbstractNode
573
        // we are subclass of AbstractNode
574
        if (getClass() == SimpleNode.class) {
575
            return false;
576
        }
577
        
574
        try {
578
        try {
575
            java.lang.reflect.Method m = getClass().getMethod(name, arguments);
579
            java.lang.reflect.Method m = getClass().getMethod(name, arguments);
576
580
(-)a/openide.nodes/src/org/openide/nodes/SimpleNode.java (+603 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * If you wish your version of this file to be governed by only the CDDL
25
 * or only the GPL Version 2, indicate your decision by adding
26
 * "[Contributor] elects to include this software in this distribution
27
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
28
 * single choice of license, a recipient has the option to distribute
29
 * your version of this file under either the CDDL, the GPL Version 2 or
30
 * to extend the choice of license to its licensees as provided above.
31
 * However, if you add GPL Version 2 code and therefore, elected the GPL
32
 * Version 2 license, then the option applies only if the new code is
33
 * made subject to such option by the copyright holder.
34
 *
35
 * Contributor(s):
36
 *
37
 * Portions Copyrighted 2009 Sun Microsystems, Inc.
38
 */
39
40
package org.openide.nodes;
41
42
import java.awt.Graphics;
43
import java.awt.Image;
44
import java.awt.image.ImageObserver;
45
import java.awt.image.ImageProducer;
46
import java.lang.reflect.Method;
47
import java.util.Collections;
48
import java.util.LinkedList;
49
import java.util.List;
50
import java.util.Map;
51
import java.util.WeakHashMap;
52
import javax.swing.Action;
53
import org.openide.util.Exceptions;
54
import org.openide.util.ImageUtilities;
55
import org.openide.util.Lookup;
56
import org.openide.util.lookup.Lookups;
57
import org.openide.util.lookup.ProxyLookup;
58
59
/**
60
 * Node subclass for simple use-cases where the icon and display name are
61
 * known ahead of time, lookup contents may be known ahead of time, which
62
 * takes care of computing its child nodes without exposing a Children
63
 * object.
64
 * <p/>
65
 * This class basically simplifies the following common cases for using
66
 * AbstractNode + Children.Keys:
67
 * <ul>
68
 * <li>Icon can be passed as constructor parameter, without a subclass being
69
 * required</li>
70
 * <li>Display name can be annotated with HTML without subclassing, either
71
 * using <code>setValue (SimpleNode.ERROR, Boolean.TRUE)</code> or, e.g.,
72
 * <code>setValue(SimpleNode.HTML_PREFIX, "&lt;b&gt;")</code></li>
73
 * <li>Children objects do not need to be dealt with directly - simply
74
 * override <code>createKeys(List)</code> and <code>createNodeForKey(KeyType)</code></li>
75
 * <li>Actions can be handled more simply, and the default Properties action
76
 * is suppressed</li>
77
 * <li>Fixed Lookup contents can be passed in directly using one of the
78
 * static factory methods</li>
79
 * <li>No lookup, or fixed lookup contents may be passed into constructor,
80
 * or createLookup() can be overridden (but not both).  The Lookup does not
81
 * need to exist at the time the node is constructed, eliminating some
82
 * awkward code constructs that would otherwise be needed.
83
 * </li>
84
 * </ul>
85
 * Note:  Do not call <code>getCookieSet()</code> to alter the contents of the
86
 * lookup of a SimpleNode - it is not used.  Instead, pass an appropriate
87
 * lookup as a constructor parameter.
88
 * <p/>
89
 * <h2>Managing Child Nodes</h2>
90
 * If your node should have a set of child nodes that can be shown when your
91
 * node is expanded, subclass SimpleNode and override <code>createKeys(List)</code>
92
 * and <code>createNodeForKey(KeyType)</code>.
93
 * <p/>
94
 * <b>How this works:</b> In <code>createKeys(List)</code>, you create (or fetch
95
 * somehow) model objects each of which represents one child node, and add them
96
 * to the passed list.  Later, when the nodes really need to be shown,
97
 * <code>createNodeForKey(KeyType)</code> is called once for every object you
98
 * added to the list in <code>createKeys(List)</code>.
99
 * <p/>
100
 * If you need to update the set of child nodes because something has changed,
101
 * simply call <code>refreshChildren()</code> - this will trigger another call
102
 * to <code>createKeys(List)</code> and so forth.
103
 * <p/>
104
 * If the set of child nodes can change due to external code, and you can
105
 * listen for those changes, override <code>onStartListeningForChildChanges()</code>
106
 * to attach your listeners and <code>onStopListeningForChildChanges()</code>
107
 * <p/>
108
 * If you override <code>createKeys(List)</code>, you must also override
109
 * <code>createNodeForKey(KeyType)</code>.
110
 *
111
 *
112
 * @since 7.12
113
 * @author Tim Boudreau
114
 * @param KeyType the type of object used for the key objects that
115
 * represent child nodes.  If your node has no children, use
116
 * <code>SimpleNode&lt;Void&gt;</code>
117
 */
118
public class SimpleNode<KeyType> extends AbstractNode {
119
    private final Image icon;
120
    /**
121
     * Key which can be used in a call to setValue(ERROR, Boolean), to
122
     * specify that this node should receive error badging in its display
123
     * name and/or icon
124
     */
125
    public static final String ERROR = "_error"; //NOI18N
126
127
    /**
128
     * Key which can be used in a call to setValue(HTML_PREFIX, String), to
129
     * specify an HTML prefix for the display name of this node, to affect
130
     * its appearance somehow
131
     */
132
    public static final String HTML_PREFIX = "_prefix"; //NOI18N
133
    /**
134
     * Flag which is true if setValue() has ever been called - an optimization
135
     * for handling getHtmlDisplayName()
136
     */
137
    private volatile boolean valueSet;
138
    private final CF<KeyType> childFactory;
139
140
    /**
141
     * Create a new SimpleNode with unspecified lookup, display name,
142
     * and lookup contents
143
     */
144
    public SimpleNode() {
145
        this (null, (Image) null, (Lookup) null);
146
    }
147
148
    /**
149
     * Create a new SimpleNode with the passed display name and
150
     * icon resource path
151
     * @param displayName A localized display name
152
     * @param iconResourcePath A resource path to an icon, e.g. com/foo/bar/Icon.png
153
     */
154
    public SimpleNode (String displayName, String iconResourcePath) {
155
        this (displayName, new PathIcon(iconResourcePath));
156
    }
157
158
    /**
159
     * Create a new SimpleNode with the specified display name and icon
160
     * @param displayName The display name, or null
161
     * @param icon The icon, or null
162
     */
163
    public SimpleNode (String displayName, Image icon) {
164
        this (displayName, icon, (Lookup) null);
165
    }
166
167
    /**
168
     * Create a new SimpleNode with the specified display name and lookup
169
     * contents
170
     * @param displayName The display name, or null
171
     * @param lookupContents The lookup contents, or null
172
     */
173
    public static Node create (String displayName, Object... lookupContents) {
174
        return new SimpleNode<Void>(displayName, lookupContents);
175
    }
176
177
    /**
178
     * Create a new SimpleNode with the specified display name, icon and lookup
179
     * contents
180
     * @param displayName The display name, or null
181
     * @param lookupContents The lookup contents, or null
182
     */
183
    public static Node create (String displayName, Image icon, Object... lookupContents) {
184
        return new SimpleNode<Void>(displayName, icon, lookupContents);
185
    }
186
187
    /**
188
     * Create a new SimpleNode with the specified display name, icon and lookup
189
     * contents
190
     * @param displayName The display name, or null
191
     * @param iconResourcePath A resource path to an icon, e.g. com/foo/bar/Icon.png
192
     * @param lookupContents The lookup contents, or null
193
     */
194
    public static Node create (String displayName, String iconResourcePath, Object... lookupContents) {
195
        return new SimpleNode<Void>(displayName, iconResourcePath == null ? null : new PathIcon(iconResourcePath), lookupContents);
196
    }
197
198
199
    /**
200
     * Create a new SimpleNode with the specified display name and lookup
201
     * contents
202
     * @param displayName The display name, or null
203
     * @param lookupContents The lookup contents, or null
204
     */
205
    SimpleNode (String displayName, Object... lookupContents) {
206
        //package private to avoid constructor calls requiring extra casts
207
        this (displayName, null, lookupContents);
208
    }
209
210
    /**
211
     * Create a new SimpleNode with the specified display name, icon and lookup contents
212
     * @param displayName The display name, or null
213
     * @param icon The icon, or null
214
     * @param lookupContents the contents of the lookup (may be empty but not
215
     * null)
216
     */
217
    SimpleNode (String displayName, Image icon, Object... lookupContents) {
218
        //package private to avoid constructor calls requiring extra casts
219
        this (displayName, icon, Lookups.fixed(lookupContents));
220
    }
221
222
    /**
223
     * Create a new SimpleNode with the specified display name, icon and lookup
224
     * @param displayName The display name, or null
225
     * @param iconResourcePath A resource path to an icon, e.g. com/foo/bar/Icon.png
226
     * @param lookup The lookup, or null
227
     */
228
    public SimpleNode (String displayName, String iconResourcePath, Lookup lookup) {
229
        this (displayName, iconResourcePath == null ? null : new PathIcon (iconResourcePath), lookup);
230
    }
231
232
    /**
233
     * Create a new SimpleNode with the specified display name, icon and lookup
234
     * @param displayName The display name, or null
235
     * @param icon The icon, or null
236
     * @param lookup The lookup, or null
237
     */
238
    public SimpleNode (String displayName, Image icon, Lookup lookup) {
239
        super(Children.LEAF, new L());
240
        if (lookup != null) assert !overridesCreateLookup() : "Passing a " + //NOI18N
241
                "non-null lookup to a SimpleNode which overrides createLookup." + //NOI18N
242
                "Passed lookup will not be used"; //NOI18N
243
        if (displayName != null) {
244
            setName (displayName);
245
            setDisplayName(displayName);
246
        }
247
        ((L) getLookup()).set(lookup == null ? createLookup() : lookup);
248
        if (overridesCreateKeys()) {
249
            setChildren (Children.create(childFactory = new CF<KeyType>(this), true));
250
        } else {
251
            childFactory = null;
252
        }
253
        this.icon = icon;
254
    }
255
256
    /**
257
     * Create this node's lookup.  This method is called once, from the
258
     * superclass constructor (be careful not to refer to instance fields
259
     * if you override this method), and only if no Lookup or array of
260
     * Lookup contents was passed to the constructor.
261
     *
262
     * @return A Lookup.  The default implementation returns
263
     * Lookups.singleton(this)
264
     */
265
    protected Lookup createLookup() {
266
        return Lookups.singleton(this);
267
    }
268
269
    /**
270
     * Populate a list of &quot;key&quot; objects which will be passed
271
     * individually to createNodeForKey() to create this node's child nodes.
272
     * Think of this as keys in a map, where the values are Nodes.  Create
273
     * your collection of keys in this method, and add them all to the passed
274
     * list.  Later, when the nodes need to be displayed, the system will
275
     * call createNodeForKey(), passing each key you provided individually,
276
     * at which point you create a child node.
277
     * <p/>
278
     * If you override this method, <i>you must also override createNodeForKey()</i>
279
     * or an exception will be thrown if your node is expanded at runtime.
280
     * <p/>
281
     * Note that this method is not called on the AWT event thread, but on a
282
     * background thread.  Code inside this method should not directly
283
     * call other code that is not thread-safe.
284
     * <p/>
285
     * If your node should not have child nodes, simply do not override this
286
     * method.
287
     * @param toPopulate A list which can be added to
288
     * @return true if the list has been fully populated, false if another call
289
     * to createKeys() should be enqueued to add more key objects to the list
290
     * (do this if computing the keys is very slow, perhaps involving I/O,
291
     * and you want to show a partial result).
292
     */
293
    protected boolean createKeys(List<KeyType> toPopulate) {
294
        return true;
295
    }
296
297
    /**
298
     * Call this method if the list of children of this node changes, to trigger
299
     * an update of its children.
300
     *
301
     * @param immediate If true, the children should be updated immediately
302
     * (resulting in a synchronous call to createKeys() - if creating the keys
303
     * is slow, do not call this method with an argument of true from the
304
     * AWT event thread)
305
     */
306
    protected final void refreshChildren(boolean immediate) {
307
        if (childFactory != null) {
308
            childFactory.refresh(immediate);
309
        }
310
    }
311
312
    /**
313
     * Create a child node for the passed key object.  The key abject is one
314
     * of the objects created and added to the passed list in toPopulate.
315
     *
316
     * @param key The key object
317
     * @return A node
318
     */
319
    protected Node createChildNodeFor(KeyType key) {
320
        // XXX new BeanNode(key) ?
321
        throw new UnsupportedOperationException("Must override createNodeForKey"); //NOI18N
322
    }
323
324
    /**
325
     * Called when the user expands this node in the UI, or something
326
     * programmatically expresses an interest in the children of this node.
327
     * If the set of child nodes can change due to external events, such as
328
     * a file being deleted or created, start listening for changes in whatever
329
     * model object determines the set of child nodes here.
330
     * <p/>
331
     * The default implementation does nothing.  This method will only ever
332
     * be called if you are overriding <code>createKeys()</code> and
333
     * <code>createNodeForKey()</code>
334
     */
335
    protected void onStartListeningForChildChanges() {
336
        //do nothing
337
    }
338
339
    /**
340
     * Called some time after the user has collapsed this node in the UI, or the
341
     * last listener interested in the children of this node is removed or
342
     * garbage collected..
343
     * If the set of child nodes can change due to external events, such as
344
     * a file being deleted or created, detach your listeners from whatever
345
     * model object determines the children here.
346
     * <p/>
347
     * The default implementation does nothing.  This method will only ever
348
     * be called if you are overriding <code>createKeys()</code> and
349
     * <code>createNodeForKey()</code>
350
     */
351
    protected void onStopListeningForChildChanges() {
352
        //do nothing
353
    }
354
355
356
    /**
357
     * Overridden to be final, to prevent overriding in subclasses.  To
358
     * affect the contents of this Node's Lookup (and thus the return values
359
     * of getCookie()), provide your own Lookup as a constructor parameter or
360
     * as the return value from <code>createLookup()</code>
361
     * @param <T> the cookie type
362
     * @param type the cookie type
363
     * @return a object of type T
364
     * @deprecated  use getLookup().lookup(Class) instead
365
     */
366
    @Deprecated
367
    @Override
368
    public final <T extends Cookie> T getCookie(Class<T> type) {
369
        return super.getCookie(type);
370
    }
371
372
    /**
373
     * Overridden to make use of flags that can be set on this node.
374
     * Call setValue(SimpleNode.ERROR, Boolean.TRUE) to have this node's
375
     * title show up in the default error color (usually red).  Call
376
     * setValue (SimpleNode.HTML_PREFIX, "<b>") with an html prefix
377
     * to prepend to the display name.
378
     * <p/>
379
     * Note that if you override this method, calls to setValue(HTML_PREFIX, String)
380
     * and setValue(ERROR, Boolean) will have no effect unless you handle these
381
     * values in your own code or call super.getHtmlDisplayName() and decorate
382
     * but do not replace the result.
383
     *
384
     * @return The node's display name decorated with HTML or null if HTML
385
     * is not needed
386
     */
387
    @Override
388
    public String getHtmlDisplayName() {
389
        if (valueSet) {
390
            String pfix = (String) getValue(HTML_PREFIX);
391
            boolean err = Boolean.TRUE.equals(getValue(ERROR));
392
            if (err) {
393
                String errHead = "<font color='!nb.errorForeground'>"; //NOI18N
394
                pfix = pfix == null ? errHead : pfix + errHead;
395
                return pfix + getDisplayName();
396
            } else if (pfix != null) {
397
                return pfix + getDisplayName();
398
            }
399
        }
400
        return super.getHtmlDisplayName();
401
    }
402
403
    /**
404
     * Allows nodes to contain ad-hoc key/value pairs.  Will trigger a
405
     * display name change event if a key which affects html display name
406
     * is passed
407
     * @param key The key
408
     * @param o The value
409
     */
410
    @Override
411
    public final void setValue(String key, Object o) {
412
        valueSet = true;
413
        boolean isChange = ERROR.equals(key) || HTML_PREFIX.equals(key);
414
        String oldName = isChange ? getDisplayName() : null;
415
        super.setValue(key, o);
416
        if (isChange) {
417
            fireDisplayNameChange(oldName, getDisplayName());
418
            if (ERROR.equals(key)) {
419
                fireIconChange();
420
            }
421
        }
422
    }
423
424
    /**
425
     * Overridden to return the icon passed as a constructor argument, if any.
426
     * @param type The icon type,
427
     * @return the icon passed to the constructor, or the return value of
428
     * a call to super.getIcon(type) if that was null
429
     */
430
    @Override
431
    public Image getIcon(int type) {
432
        Image result = icon == null ? super.getIcon(type) :
433
            icon instanceof PathIcon ? ((PathIcon) icon).actualImage() : icon;
434
        return badge(result);
435
    }
436
437
    /**
438
     * Overridden to return the icon passed as a constructor argument, or
439
     * if null, the return value of super.getOpenedIcon(type).
440
     * @param type The icon type,
441
     * @return the icon passed to the constructor, or the return value of
442
     * a call to super.getOpenedIcon(type) if that was null.  
443
     */
444
    @Override
445
    public Image getOpenedIcon(int type) {
446
        Image result = icon == null ? super.getOpenedIcon(type) :
447
            icon instanceof PathIcon ? ((PathIcon) icon).actualImage() : icon;
448
        return badge(result);
449
    }
450
451
    private Image badge(Image img) {
452
        if (img != null && valueSet && Boolean.TRUE.equals(getValue(ERROR))) {
453
            Image badge = ImageUtilities.loadImage(
454
                    "org/openide/nodes/errorBadge.png"); //NOI18N
455
            img = ImageUtilities.mergeImages(img, badge, 8, 8);
456
        }
457
        return img;
458
    }
459
460
    /**
461
     * Populate the passed list with any actions which should be available
462
     * @param actions A list of actions which can be added to
463
     */
464
    public void createActions(List<Action> actions) {
465
        //do nothing
466
    }
467
468
    /**
469
     * Overridden as final to delegate to createActions(List)
470
     * @param context ignored
471
     * @return an array of actions
472
     */
473
    @Override
474
    public final Action[] getActions(boolean context) {
475
        List<Action> l = new LinkedList<Action>();
476
        createActions(l);
477
        return l.toArray(new Action[0]);
478
    }
479
480
    private static class CF<KeyType> extends ChildFactory.Detachable <KeyType> {
481
        private final SimpleNode<KeyType> nd;
482
        CF (SimpleNode<KeyType> nd) {
483
            this.nd = nd;
484
        }
485
486
        @Override
487
        protected boolean createKeys(List<KeyType> toPopulate) {
488
            return nd.createKeys(toPopulate);
489
        }
490
491
        @Override
492
        protected Node createNodeForKey(KeyType key) {
493
            return nd.createChildNodeFor(key);
494
        }
495
496
        @Override
497
        protected void addNotify() {
498
            nd.onStartListeningForChildChanges();
499
        }
500
501
        @Override
502
        protected void removeNotify() {
503
            nd.onStopListeningForChildChanges();
504
        }
505
    }
506
507
    private static final class L extends ProxyLookup {
508
        void set(Lookup real) {
509
            setLookups(real);
510
        }
511
    }
512
513
    private static final Map<Class<?>, Boolean> CREATE_KEYS_OVERRIDERS =
514
            Collections.synchronizedMap(new WeakHashMap<Class<?>,Boolean>());
515
    /**
516
     * Used to determine if Children.LEAF should be retained as the
517
     * Children object
518
     * @return true if this class overrides the createKeys() method
519
     */
520
    private boolean overridesCreateKeys() {
521
        if (getClass() == SimpleNode.class) return false;
522
        Boolean val = CREATE_KEYS_OVERRIDERS.get(getClass());
523
        if (val == null) {
524
            try {
525
                val = overrides ("createKeys", List.class);
526
                CREATE_KEYS_OVERRIDERS.put (getClass(), val);
527
            } catch (SecurityException ex) {
528
                Exceptions.printStackTrace(ex);
529
                val = false;
530
            }
531
        }
532
        return val;
533
    }
534
535
    private boolean overrides (String name, Class... ptypes) {
536
        Class<?> type = getClass();
537
        boolean result = false;
538
        while (type != SimpleNode.class) {
539
            try {
540
                type.getDeclaredMethod(name, ptypes);
541
                result = true;
542
                break;
543
            } catch (NoSuchMethodException e) {
544
                //do nothing
545
            }
546
            type = type.getSuperclass();
547
        }
548
        return result;
549
    }
550
551
    /**
552
     * Used to trigger an assertion error of createLookup() is overridden
553
     * and a non-null lookup is also passed to the constructor
554
     * @return whether or not this subclass overrides createLookup()
555
     */
556
    private boolean overridesCreateLookup() {
557
        if (getClass() == SimpleNode.class) return false;
558
        try {
559
            return overrides ("createLookup");
560
        } catch (SecurityException ex) {
561
            Exceptions.printStackTrace(ex);
562
        }
563
        return false;
564
    }
565
566
    private static final class PathIcon extends Image {
567
        private final String path;
568
        PathIcon(String path) {
569
            this.path = path;
570
        }
571
572
        Image actualImage() {
573
            return ImageUtilities.loadImage(path);
574
        }
575
576
577
        @Override
578
        public int getWidth(ImageObserver observer) {
579
            throw new AssertionError();
580
        }
581
582
        @Override
583
        public int getHeight(ImageObserver observer) {
584
            throw new AssertionError();
585
        }
586
587
        @Override
588
        public ImageProducer getSource() {
589
            throw new AssertionError();
590
        }
591
592
        @Override
593
        public Graphics getGraphics() {
594
            throw new AssertionError();
595
        }
596
597
        @Override
598
        public Object getProperty(String name, ImageObserver observer) {
599
            throw new AssertionError();
600
        }
601
602
    }
603
}
(-)a/openide.nodes/test/unit/src/org/openide/nodes/SimpleNodeTest.java (+327 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * If you wish your version of this file to be governed by only the CDDL
25
 * or only the GPL Version 2, indicate your decision by adding
26
 * "[Contributor] elects to include this software in this distribution
27
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
28
 * single choice of license, a recipient has the option to distribute
29
 * your version of this file under either the CDDL, the GPL Version 2 or
30
 * to extend the choice of license to its licensees as provided above.
31
 * However, if you add GPL Version 2 code and therefore, elected the GPL
32
 * Version 2 license, then the option applies only if the new code is
33
 * made subject to such option by the copyright holder.
34
 *
35
 * Contributor(s):
36
 *
37
 * Portions Copyrighted 2009 Sun Microsystems, Inc.
38
 */
39
40
package org.openide.nodes;
41
42
import java.awt.Graphics;
43
import java.awt.Image;
44
import java.awt.event.ActionEvent;
45
import java.awt.image.ImageObserver;
46
import java.awt.image.ImageProducer;
47
import java.util.Collection;
48
import java.util.List;
49
import javax.swing.AbstractAction;
50
import javax.swing.Action;
51
import org.junit.After;
52
import org.junit.AfterClass;
53
import org.junit.Before;
54
import org.junit.BeforeClass;
55
import org.junit.Test;
56
import org.openide.util.ImageUtilities;
57
import static org.junit.Assert.*;
58
import org.openide.util.Lookup;
59
import org.openide.util.lookup.Lookups;
60
61
/**
62
 *
63
 * @author tim
64
 */
65
public class SimpleNodeTest {
66
67
    public SimpleNodeTest() {
68
    }
69
70
    @BeforeClass
71
    public static void setUpClass() throws Exception {
72
    }
73
74
    @AfterClass
75
    public static void tearDownClass() throws Exception {
76
    }
77
78
    @Before
79
    public void setUp() {
80
    }
81
82
    @After
83
    public void tearDown() {
84
    }
85
86
    /**
87
     * Test of create method, of class SimpleNode.
88
     */
89
    @Test
90
    public void testCreate_String_ObjectArr() {
91
        System.out.println("create");
92
        String displayName = "Boo";
93
        Object[] lookupContents = new String[] { "A", "B", "C" };
94
        Node n = SimpleNode.create(displayName, lookupContents);
95
        assertEquals ("Boo", n.getDisplayName());
96
        Collection<? extends String> c = n.getLookup().lookupAll(String.class);
97
        assertTrue (c.contains("A"));
98
        assertTrue (c.contains("B"));
99
        assertTrue (c.contains("C"));
100
        assertFalse (c.contains("D"));
101
        assertEquals(3, c.size());
102
    }
103
104
    /**
105
     * Test of create method, of class SimpleNode.
106
     */
107
    @Test
108
    public void testCreate_3args_1() {
109
        System.out.println("create");
110
        String displayName = "X";
111
        Image icon = ImageUtilities.loadImage("org/openide/nodes/beans.gif");
112
        Object[] lookupContents = new Object [] { displayName, icon };
113
        Node n = SimpleNode.create(displayName, icon, lookupContents);
114
        assertSame (icon, n.getLookup().lookup(Image.class));
115
        assertEquals (displayName, n.getLookup().lookup(String.class));
116
        assertEquals (displayName, n.getDisplayName());
117
        assertNull (n.getHtmlDisplayName());
118
        assertSame (icon, n.getOpenedIcon(0));
119
    }
120
121
    /**
122
     * Test of create method, of class SimpleNode.
123
     */
124
    @Test
125
    public void testCreate_3args_2() {
126
        System.out.println("create");
127
        String displayName = "X";
128
        String iconResourcePath = "org/openide/nodes/beans.gif";
129
        Object[] lookupContents = new Object[] { displayName, iconResourcePath };
130
        Node n = SimpleNode.create(displayName, iconResourcePath, lookupContents);
131
        assertNotNull (n.getIcon(0));
132
        assertEquals (displayName, n.getDisplayName());
133
        assertNotNull (n.getDisplayName());
134
        Collection <? extends String> c = n.getLookup().lookupAll(String.class);
135
        assertTrue (c.contains(displayName));
136
        assertTrue (c.contains(iconResourcePath));
137
        assertEquals(2, c.size());
138
    }
139
140
    /**
141
     * Test of createLookup method, of class SimpleNode.
142
     */
143
    @Test
144
    public void testCreateLookup() {
145
        System.out.println("createLookup");
146
        StringBuilder thing1 = new StringBuilder("Thing 1");
147
        StringBuilder thing2 = new StringBuilder("Thing 2");
148
        final Lookup lkp = Lookups.fixed(thing1, thing2);
149
        final boolean[] called = new boolean[1];
150
        SimpleNode<Object> instance = new SimpleNode<Object>() {
151
            @Override
152
            protected Lookup createLookup() {
153
                called[0] = true;
154
                return lkp;
155
            }
156
        };
157
        Lookup lookup = instance.getLookup();
158
        assertTrue(called[0]);
159
        Collection<? extends StringBuilder> c  = lookup.lookupAll(StringBuilder.class);
160
        assertTrue (c.contains(thing1));
161
        assertTrue (c.contains(thing2));
162
        assertSame (Children.LEAF, instance.getChildren());
163
    }
164
165
    /**
166
     * Test of createKeys method, of class SimpleNode.
167
     */
168
    @Test
169
    public void testCreateKeys() {
170
        System.out.println("createKeys");
171
    }
172
173
    /**
174
     * Test of getHtmlDisplayName method, of class SimpleNode.
175
     */
176
    @Test
177
    public void testGetHtmlDisplayName() {
178
        System.out.println("getHtmlDisplayName");
179
        SimpleNode instance = new SimpleNode("Foo");
180
        assertNull (instance.getHtmlDisplayName());
181
        instance.setValue(SimpleNode.ERROR, Boolean.TRUE);
182
        assertNotNull (instance.getHtmlDisplayName());
183
        instance.setValue(SimpleNode.ERROR, Boolean.FALSE);
184
        assertNull (instance.getHtmlDisplayName());
185
        instance.setValue(SimpleNode.HTML_PREFIX, "<b>");
186
        assertNotNull (instance.getHtmlDisplayName());
187
        assertEquals ("<b>Foo", instance.getHtmlDisplayName());
188
    }
189
190
    /**
191
     * Test of getIcon method, of class SimpleNode.
192
     */
193
    @Test
194
    public void testGetIcon() {
195
        System.out.println("getIcon");
196
        Image img = new Image() {
197
198
            @Override
199
            public int getWidth(ImageObserver observer) {
200
                throw new UnsupportedOperationException("Not supported yet.");
201
            }
202
203
            @Override
204
            public int getHeight(ImageObserver observer) {
205
                throw new UnsupportedOperationException("Not supported yet.");
206
            }
207
208
            @Override
209
            public ImageProducer getSource() {
210
                throw new UnsupportedOperationException("Not supported yet.");
211
            }
212
213
            @Override
214
            public Graphics getGraphics() {
215
                throw new UnsupportedOperationException("Not supported yet.");
216
            }
217
218
            @Override
219
            public Object getProperty(String name, ImageObserver observer) {
220
                throw new UnsupportedOperationException("Not supported yet.");
221
            }
222
        };
223
224
        SimpleNode nd = new SimpleNode ("Foo", img);
225
        assertSame (img, nd.getIcon(0));
226
        assertSame (img, nd.getOpenedIcon(0));
227
    }
228
229
    /**
230
     * Test of createActions method, of class SimpleNode.
231
     */
232
    @Test
233
    public void testCreateActions() {
234
        System.out.println("createActions");
235
        class A extends AbstractAction {
236
237
            public void actionPerformed(ActionEvent e) {
238
                throw new UnsupportedOperationException("Not supported yet.");
239
            }
240
241
        }
242
        final A[] as = new A[] { new A() };
243
        SimpleNode instance = new SimpleNode() {
244
245
            @Override
246
            public void createActions(List actions) {
247
                actions.add (as[0]);
248
            }
249
        };
250
        Action[] acts = instance.getActions(true);
251
        assertEquals (1, acts.length);
252
        assertSame (as[0], acts[0]);
253
    }
254
255
    @Test
256
    public void testChildren() throws Exception {
257
        N n = new N();
258
        Children k = n.getChildren();
259
        assertFalse (k == Children.LEAF);
260
        k.addNotify();
261
        Node[] nds = k.getNodes(true);
262
        synchronized(n) {
263
            n.wait(10000);
264
        }
265
        nds = k.getNodes(true);
266
        assertTrue (n.keysCalled);
267
        assertTrue (n.nodeCalled);
268
        assertEquals(3, nds.length);
269
        for (int i = 0; i < nds.length; i++) {
270
            Node nd = nds[i];
271
            switch(i) {
272
                case 0:
273
                    assertEquals("A", nd.getDisplayName());
274
                    break;
275
                case 1:
276
                    assertEquals("B", nd.getDisplayName());
277
                    break;
278
                case 2:
279
                    assertEquals("C", nd.getDisplayName());
280
                    break;
281
            }
282
        }
283
    }
284
285
    private static final class N extends SimpleNode<String> {
286
        boolean keysCalled;
287
        boolean nodeCalled;
288
        N() {
289
            super ("N", (String) null, Lookups.fixed("Foo"));
290
        }
291
292
        public void bibbleRocket() {
293
            
294
        }
295
296
        @Override
297
        protected boolean createKeys(List<String> toPopulate) {
298
            keysCalled = true;
299
            toPopulate.add("A");
300
            toPopulate.add("B");
301
            toPopulate.add("C");
302
            return true;
303
        }
304
305
        int nds;
306
        @Override
307
        protected Node createChildNodeFor(String key) {
308
            nodeCalled = true;
309
            nds++;
310
            Node result = new SimpleNode(key);
311
            if (nds == 3) {
312
                Thread t = new Thread(new Runnable() {
313
                    public void run() {
314
                        synchronized(N.this) {
315
                            N.this.notifyAll();
316
                        }
317
                    }
318
                    
319
                });
320
                t.setDaemon(true);
321
                t.setPriority(Thread.MIN_PRIORITY);
322
                t.start();
323
            }
324
            return result;
325
        }
326
    }
327
}

Return to bug 174805