KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > nodes > Node


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.openide.nodes;
21
22 import java.awt.Image JavaDoc;
23 import java.awt.datatransfer.Transferable JavaDoc;
24 import java.beans.FeatureDescriptor JavaDoc;
25 import java.beans.PropertyChangeEvent JavaDoc;
26 import java.beans.PropertyChangeListener JavaDoc;
27 import java.beans.PropertyEditor JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.io.PrintWriter JavaDoc;
30 import java.io.StringWriter JavaDoc;
31 import java.lang.ref.Reference JavaDoc;
32 import java.lang.ref.SoftReference JavaDoc;
33 import java.lang.ref.WeakReference JavaDoc;
34 import java.lang.reflect.InvocationTargetException JavaDoc;
35 import java.util.HashSet JavaDoc;
36 import java.util.Map JavaDoc;
37 import java.util.Set JavaDoc;
38 import java.util.WeakHashMap JavaDoc;
39 import java.util.logging.Level JavaDoc;
40 import java.util.logging.Logger JavaDoc;
41 import javax.swing.Action JavaDoc;
42 import javax.swing.JPopupMenu JavaDoc;
43 import javax.swing.event.EventListenerList JavaDoc;
44 import org.openide.util.HelpCtx;
45 import org.openide.util.Lookup;
46 import org.openide.util.LookupEvent;
47 import org.openide.util.LookupListener;
48 import org.openide.util.NbBundle;
49 import org.openide.util.actions.SystemAction;
50 import org.openide.util.datatransfer.NewType;
51 import org.openide.util.datatransfer.PasteType;
52
53 /** A <em>node</em> represents one element in a hierarchy of objects (beans).
54 * It provides all methods that are needed for communication between
55 * an explorer view and the bean.
56 * <P>
57 * The node has three purposes:
58 * <OL>
59 * <LI>visually represent the object in the tree hierarchy (i.e. Explorer)
60 * <LI>provide sets of properties for that object (i.e. Component Inspector, Property Sheet)
61 * <LI>offer actions to perform on itself
62 * </OL>
63 * <P>
64 * Frequently nodes are created to represent <code>DataObject</code>s.
65 * But they may also represent anything to be displayed to the user or manipulated programmatically,
66 * even if they have no data directly stored behind them; for example, a control panel or debugger
67 * breakpoint.
68 * <P>
69 * There are two listeners in this class: {@link PropertyChangeListener}
70 * and {@link NodeListener} (which extends <code>PropertyChangeListener</code>). The first
71 * is designed to listen on properties that can be returned from
72 * {@link #getPropertySets}, the later for listening on changes in the
73 * node itself (including the name, children, parent, set of properties,
74 * icons, etc.). Be sure to distinguish between these two.
75 * <P>
76 * The node is cloneable. When a node is cloned, it is initialized
77 * with an empty set of listeners and no parent. The display name and short description
78 * are copied to the new node. The set of properties is <em>shared</em>.
79 * <P>
80 * Implements {@link org.openide.util.Lookup.Provider} since 3.11.
81 *
82 * @author Jaroslav Tulach,
83 */

84 public abstract class Node extends FeatureDescriptor JavaDoc implements Lookup.Provider, HelpCtx.Provider {
85     /** An empty leaf node. */
86     public static final Node EMPTY = new AbstractNode(Children.LEAF);
87
88     /* here is list of property names that can be changed in the Node object.
89     * These properties can be notified to the <CODE>NodeListener</CODE>.
90     */

91
92     /** Property for node display name. */
93     public static final String JavaDoc PROP_DISPLAY_NAME = "displayName"; // NOI18N
94

95     /** Property for internal (not displayable) name of a node. This name is often used to
96     * look up a node in the hierarchy, so it should be chosen to be simple.
97     */

98     public static final String JavaDoc PROP_NAME = "name"; // NOI18N
99

100     /** Property for short description of a node. */
101     public static final String JavaDoc PROP_SHORT_DESCRIPTION = "shortDescription"; // NOI18N
102

103     /** Property for the normal (closed) icon of a node. */
104     public static final String JavaDoc PROP_ICON = "icon"; // NOI18N
105

106     /** Property for the opened icon of a node. */
107     public static final String JavaDoc PROP_OPENED_ICON = "openedIcon"; // NOI18N
108

109     /** Property for a node's parent. */
110     public static final String JavaDoc PROP_PARENT_NODE = "parentNode"; // NOI18N
111

112     /** Property for a node's list of property sets. */
113     public static final String JavaDoc PROP_PROPERTY_SETS = "propertySets"; // NOI18N
114

115     /** Property for a node's cookie set. */
116     public static final String JavaDoc PROP_COOKIE = "cookie"; // NOI18N
117

118     /** Property saying whether the Node is Leaf
119      *@since 3.1
120      */

121     public static final String JavaDoc PROP_LEAF = "leaf"; // NOI18N
122

123     /** Error manager used for logging and its precomputed err.isLoggable(Level.FINE)
124      */

125     private static final Logger JavaDoc err = Logger.getLogger("org.openide.nodes.Node"); //NOI18N;
126

127     /** cache of all created lookups */
128     private static Map JavaDoc<EventListenerList JavaDoc,Reference JavaDoc<Lookup>> lookups = new WeakHashMap JavaDoc<EventListenerList JavaDoc,Reference JavaDoc<Lookup>>(37);
129
130     /** class.property names we have warned about for #31413 */
131     private static final Set JavaDoc<String JavaDoc> warnedBadProperties = new HashSet JavaDoc<String JavaDoc>(100);
132
133     /** template for changes in cookies */
134     private static final Lookup.Template<Node.Cookie> TEMPL_COOKIE = new Lookup.Template<Node.Cookie>(Node.Cookie.class);
135
136     /** Lock for initialization */
137     private static final Object JavaDoc INIT_LOCK = new Object JavaDoc();
138
139     /** children representing parent node (Children or ChildrenArray),
140     * for synchronization reasons must be changed only
141     * under the Children.MUTEX lock
142     */

143     private Object JavaDoc parent;
144
145     /** children list, for synch. reasons change only under Children.MUTEX
146     * lock
147     */

148     Children hierarchy;
149
150     /** listeners for changes in hierarchy.
151     */

152     private transient EventListenerList JavaDoc listeners;
153
154     /** Creates a new node with a given hierarchy of children.
155     * @param h the children to use for this node
156     * @exception IllegalStateException if the children object is already in use by
157     * a different node
158     */

159     protected Node(Children h) throws IllegalStateException JavaDoc {
160         this(h, null);
161     }
162
163     /** Creates a new node with a given hierarchy of children and a lookup
164     * providing content for {@link #getCookie} and {@link #getLookup} methods.
165     * <p>
166     * As the lookup needs to be constructed before Node's constructor is called,
167     * it might not be obvious how to add Node or other objects into it without
168     * type casting. Here is the recommended suggestion that uses public/private
169     * pair of constructors:
170     * <PRE>
171     <span class="keyword">public</span> <span class="function-name">MyNode</span><span class="constant">(</span><span class="variable-name">Children</span> <span class="variable-name">ch</span><span class="constant">,</span> <span class="variable-name">FileObject</span> <span class="variable-name">fil</span><span class="variable-name">e</span><span class="constant">)</span> <span class="constant">{</span>
172         <span class="keyword">this</span><span class="constant">(</span><span class="variable-name">ch</span><span class="constant">, </span><span class="variable-name">file</span><span class="constant">,</span> <span class="keyword">new</span> <span class="function-name">InstanceContent</span><span class="constant">(</span><span class="constant">)</span><span class="constant">)</span><span class="constant">;</span>
173     <span class="constant">}</span>
174
175     <span class="comment">/** A private constructor that takes an InstanceContent and</span>
176     <span class="comment"> * uses it as internals for the Node lookup and also allow us</span>
177     <span class="comment"> * to modify the content, for example by adding a reference </span>
178     <span class="comment"> * to the node itself or any other object we want to represent.</span>
179     <span class="comment"> *</span>
180     <span class="comment"> * @param ch children we wish to use</span>
181     <span class="comment"> * @param file sample object we wish to have in lookup</span>
182     <span class="comment"> * @param content the content created by the first constructor</span>
183     <span class="comment"> *<b></b>/</span>
184     <span class="keyword">private</span> <span class="function-name">MyNode</span><span class="constant">(</span><span class="variable-name">Children</span> <span class="variable-name">ch</span><span class="constant">,</span> <span class="variable-name">FileObject</span> <span class="variable-name">file</span><span class="constant">,</span> <span class="variable-name">InstanceContent</span> <span class="variable-name">content</span><span class="constant">)</span> <span class="constant">{</span>
185         <span class="keyword">super</span><span class="constant">(</span><span class="variable-name">ch</span><span class="constant">,</span> <span class="keyword">new</span> <span class="function-name">AbstractLookup</span><span class="constant">(</span><span class="variable-name">content</span><span class="constant">)</span><span class="constant">)</span><span class="constant">;</span>
186         <span class="comment">// adds the node to our own lookup</span>
187         <span class="variable-name">content</span><span class="constant">.</span><span class="function-name">add</span> <span class="constant">(</span><span class="keyword">this</span><span class="constant">)</span><span class="constant">;</span>
188         <span class="comment">// adds additional items to the lookup</span>
189         <span class="variable-name">content</span><span class="constant">.</span><span class="function-name">add</span> <span class="constant">(</span><span class="variable-name">file</span><span class="constant">)</span><span class="constant">;</span>
190     <span class="constant">}</span>
191     </PRE>
192     *
193     * @since 3.11
194     * @param h the children to use for this node
195     * @param lookup the lookup to provide content of {@link #getLookup}
196     * and also {@link #getCookie}
197     * @exception IllegalStateException if the children object is already in use by
198     * a different node
199     */

200     protected Node(Children h, Lookup lookup) throws IllegalStateException JavaDoc {
201         this.hierarchy = h;
202
203         // allow subclasses (FilterNode) to update the lookup
204
lookup = replaceProvidedLookup(lookup);
205
206         if (lookup != null) {
207             this.listeners = new LookupEventList(lookup);
208         } else {
209             this.listeners = new EventListenerList JavaDoc();
210         }
211
212         // attaches to this node
213
h.attachTo(this);
214     }
215
216     /** Method for subclasses to modify provided lookup before its use.
217      * This implementation does nothing.
218      */

219     Lookup replaceProvidedLookup(Lookup l) {
220         return l;
221     }
222
223     /** Method that gives access to internal lookup.
224      * @param init should it be really initialized (ready for queries) or need not be
225      * @return lookup or null
226      */

227     final Lookup internalLookup(boolean init) {
228         if (listeners instanceof LookupEventList) {
229             return ((LookupEventList) listeners).init(init);
230         } else {
231             return null;
232         }
233     }
234
235     /** Implements {@link Object#clone} to behave correctly if cloning is desired.
236     * But {@link Cloneable} is <em>not</em> declared by default.
237     * <P>
238     * The default implementation checks whether the child list implements
239     * <code>Cloneable</code>, and if so, it clones the children.
240     * If the child list does not support cloning, {@link Children#LEAF} is used
241     * instead for the children. The parent of this node is set to <code>null</code> and an empty set
242     * of listeners is attached to the node.
243     *
244     * @return the cloned version of the node
245     * @exception CloneNotSupportedException if the children cannot be cloned
246     * in spite of implementing <code>Cloneable</code>
247     */

248     protected Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
249         Node n = (Node) super.clone();
250         Children hier2;
251
252         if (hierarchy instanceof Cloneable JavaDoc) {
253             hier2 = (Children) hierarchy.cloneHierarchy();
254         } else {
255             hier2 = Children.LEAF;
256         }
257
258         // attach the hierarchy
259
n.hierarchy = hier2;
260         hier2.attachTo(n);
261
262         // no parent
263
n.parent = null;
264
265         // empty set of listeners
266
if (listeners instanceof LookupEventList) {
267             n.listeners = new LookupEventList(internalLookup(false));
268         } else {
269             n.listeners = new EventListenerList JavaDoc();
270         }
271
272         return n;
273     }
274
275     /** Clone the node. The newly created node should reference the same
276     * object is this node does, but it should not be inserted as a child
277     * to any other node. Also it should have an empty set of listeners.
278     * In all other respects the node should behave exactly as the
279     * original one does.
280     *
281     * @return copy of this node
282     */

283     public abstract Node cloneNode();
284
285     /** Finds the children we are attached to.
286      * @return children
287      */

288     private Children getParentChildren() {
289         return (this.parent instanceof ChildrenArray) ? ((ChildrenArray) this.parent).getChildren()
290                                                       : (Children) this.parent;
291     }
292
293     /** Method that allows Children to change the parent children of
294     * the node when the node is add to a children.
295     *
296     * @param parent the children that wants to contain this node
297     * @param index index that will be assigned to this node
298     * @exception IllegalStateException if this node already belongs to a children
299     */

300     final synchronized void assignTo(Children parent, int index) {
301         Children ch = getParentChildren();
302
303         if ((ch != null) && (ch != parent)) {
304             throw new IllegalStateException JavaDoc(
305                 "Cannot initialize " + index + "th child of node " + parent.getNode() +
306                 "; it already belongs to node " + ch.getNode()
307             ); // NOI18N
308
}
309
310         if (!(this.parent instanceof ChildrenArray)) {
311             this.parent = parent;
312         }
313     }
314
315     /** Code that reassigns the reference from to parent from its
316      * Children to its ChildrenArray.
317      */

318     final synchronized void reassignTo(Children currentParent, ChildrenArray itsArray) {
319         if ((this.parent != currentParent) && (this.parent != itsArray)) {
320             throw new IllegalStateException JavaDoc(
321                 "Unauthorized call to change parent: " + this.parent + " and should be: " + currentParent
322             );
323         }
324
325         this.parent = itsArray;
326     }
327
328     /** Deassigns the node from a children, when it is removed from
329     * a children.
330     */

331     final synchronized void deassignFrom(Children parent) {
332         Children p = getParentChildren();
333
334         if (parent != p) {
335             throw new IllegalArgumentException JavaDoc("Deassign from wrong parent. Old: " + p + " Caller: " + parent); //NOI18N
336
}
337
338         this.parent = null;
339     }
340
341     /** Set the system name. Fires a property change event.
342     * @param s the new name
343     * @exception IllegalArgumentException if the new name cannot represent
344     * a valid node name
345     */

346     public void setName(String JavaDoc s) {
347         String JavaDoc name = super.getName();
348
349         if ((name == null) || !name.equals(s)) {
350             super.setName(s);
351             fireNameChange(name, s);
352         }
353     }
354
355     /** Set the display name. Fires a property change event.
356     * @param s the new name
357     */

358     public void setDisplayName(String JavaDoc s) {
359         String JavaDoc displayName = super.getDisplayName();
360
361         if ((displayName == null) || !displayName.equals(s)) {
362             super.setDisplayName(s);
363             fireDisplayNameChange(displayName, s);
364         }
365     }
366
367     /** Set the short description of the node. Fires a property change event.
368     * <p>This description may be used for tool tips, etc.
369     * @param s the new description
370     */

371     public void setShortDescription(String JavaDoc s) {
372         String JavaDoc descr = super.getShortDescription();
373
374         if ((descr == null) || !descr.equals(s)) {
375             super.setShortDescription(s);
376             fireShortDescriptionChange(descr, s);
377         }
378     }
379
380     /** Find an icon for this node (in the closed state).
381     * @param type constant from {@link java.beans.BeanInfo}
382     * @return icon to use to represent the node
383     */

384     public abstract Image JavaDoc getIcon(int type);
385
386     /** Find an icon for this node (in the open state).
387     * This icon is used when the node may have children and is expanded.
388     *
389     * @param type constant from {@link java.beans.BeanInfo}
390     * @return icon to use to represent the node when open
391     */

392     public abstract Image JavaDoc getOpenedIcon(int type);
393
394     /** Get context help associated with this node.
395     * @return the context help object (could be <code>null</code> or {@link HelpCtx#DEFAULT_HELP})
396     */

397     public abstract HelpCtx getHelpCtx();
398
399     /** Get the list of children.
400     * @return the children
401     */

402     public final Children getChildren() {
403         updateChildren();
404
405         return hierarchy;
406     }
407
408     /** Can be overridden in subclasses (probably in FilterNode) to check
409      * whether children are of the right subclass
410      */

411     void updateChildren() {
412     }
413
414     /** Allows to change Children of the node. Call to this method aquires
415      * write lock on the nodes hierarchy. Take care not to call this method
416      * under read lock.<BR>
417      *
418      * @param ch New children to be set on the node.
419      * @since 3.1
420      */

421     protected final void setChildren(final Children ch) {
422         Children.MUTEX.postWriteRequest(new Runnable JavaDoc() { public void run() {
423             Node[] oldNodes = null;
424
425             if (hierarchy.isInitialized()) {
426                 oldNodes = hierarchy.getNodes();
427             }
428             hierarchy.detachFrom();
429
430             boolean wasLeaf = hierarchy == Children.LEAF;
431
432             hierarchy = ch;
433             hierarchy.attachTo(Node.this);
434
435             if (wasLeaf != (hierarchy == Children.LEAF)) {
436                 fireOwnPropertyChange(PROP_LEAF, wasLeaf, hierarchy == Children.LEAF);
437             }
438
439             if ((oldNodes != null) && !wasLeaf) {
440                 fireSubNodesChange(false, oldNodes, oldNodes);
441                 Node[] arr = hierarchy.getNodes();
442                 if (arr.length > 0) {
443                     fireSubNodesChange(true, arr, null);
444                 }
445             }
446         }});
447     }
448
449     /** Test whether the node is a leaf, or may contain children.
450     * @return <code>true</code> if the children list is actually {@link Children#LEAF}
451     */

452     public final boolean isLeaf() {
453         updateChildren();
454
455         return hierarchy == Children.LEAF;
456     }
457
458     /** Get the parent node.
459     * @return the parent node, or <CODE>null</CODE> if this node is the root of a hierarchy
460     */

461     public final Node getParentNode() {
462         // if contained in a list return its parent node
463
Children ch = getParentChildren();
464
465         return (ch == null) ? null : ch.getNode();
466     }
467
468     /** Test whether this node can be renamed.
469     * If true, one can use {@link #getName} to obtain the current name and
470     * {@link #setName} to change it.
471     *
472     * @return <code>true</code> if the node can be renamed
473     */

474     public abstract boolean canRename();
475
476     /** Test whether this node can be deleted.
477     * @return <CODE>true</CODE> if can
478     */

479     public abstract boolean canDestroy();
480
481     // [PENDING] "valid" property? --jglick // NOI18N
482

483     /** Remove the node from its parent and deletes it.
484     * The default
485     * implementation obtains write access to
486     * the {@link Children#MUTEX children's lock}, and removes
487     * the node from its parent (if any). Also fires a property change.
488     * <P>
489     * This may be overridden by subclasses to do any additional
490     * cleanup.
491     *
492     * @exception IOException if something fails
493     */

494     public void destroy() throws IOException JavaDoc {
495         Children.MUTEX.postWriteRequest(
496             new Runnable JavaDoc() {
497                 public void run() {
498                     Children p = getParentChildren();
499
500                     if (p != null) {
501                         // remove itself from parent
502
p.remove(new Node[] { Node.this });
503                     }
504
505                     // sets the valid flag to false and fires prop. change
506
fireNodeDestroyed();
507                 }
508             }
509         );
510     }
511
512     /** Get the list of property sets for this node.
513     * E.g. typically there may be one for normal Bean properties, one for expert
514     * properties, and one for hidden properties.
515     *
516     * @return the property sets
517     */

518     public abstract PropertySet[] getPropertySets();
519
520     /** Called when a node is to be copied to the clipboard.
521     * @return the transferable object representing the
522     * content of the clipboard
523     * @exception IOException when the
524     * copy cannot be performed
525     */

526     public abstract Transferable JavaDoc clipboardCopy() throws IOException JavaDoc;
527
528     /** Called when a node is to be cut to the clipboard.
529     * @return the transferable object representing the
530     * content of the clipboard
531     * @exception IOException when the
532     * cut cannot be performed
533     */

534     public abstract Transferable JavaDoc clipboardCut() throws IOException JavaDoc;
535
536     /** Called when a drag is started with this node.
537     * The node can attach a transfer listener to ExTransferable and
538     * will be then notified about progress of the drag (accept/reject).
539     *
540     * @return transferable to represent this node during a drag
541     * @exception IOException if a drag cannot be started
542     */

543     public abstract Transferable JavaDoc drag() throws IOException JavaDoc;
544
545     /** Test whether this node permits copying.
546     * @return <code>true</code> if so
547     */

548     public abstract boolean canCopy();
549
550     /** Test whether this node permits cutting.
551     * @return <code>true</code> if so
552     */

553     public abstract boolean canCut();
554
555     /** Determine which paste operations are allowed when a given transferable is in the clipboard.
556     * For example, a node representing a Java package will permit classes to be pasted into it.
557     * @param t the transferable in the clipboard
558     * @return array of operations that are allowed
559     */

560     public abstract PasteType[] getPasteTypes(Transferable JavaDoc t);
561
562     /** Determine if there is a paste operation that can be performed
563     * on provided transferable. Used by drag'n'drop code to check
564     * whether the drop is possible.
565     *
566     * @param t the transferable
567     * @param action the drag'n'drop action to do DnDConstants.ACTION_MOVE, ACTION_COPY, ACTION_LINK
568     * @param index index between children the drop occurred at or -1 if not specified
569     * @return null if the transferable cannot be accepted or the paste type
570     * to execute when the drop occurs
571     */

572     public abstract PasteType getDropType(Transferable JavaDoc t, int action, int index);
573
574     /** Get the new types that can be created in this node.
575     * For example, a node representing a Java package will permit classes to be added.
576     * @return array of new type operations that are allowed
577     */

578     public abstract NewType[] getNewTypes();
579
580     /** Get the set of actions that are associated with this node.
581      * This set is used to construct the context menu for the node.
582      *
583      * <P>
584      * By default this method delegates to the deprecated getActions or getContextActions
585      * method depending on the value of supplied argument.
586      * <P>
587      * It is supposed to be overridden by subclasses accordingly.
588      *
589      * @param context whether to find actions for context meaning or for the
590      * node itself
591      * @return a list of actions (you may include nulls for separators)
592      * @since 3.29
593      */

594     public Action JavaDoc[] getActions(boolean context) {
595         return context ? getContextActions() : getActions();
596     }
597
598     /** Get the set of actions associated with this node.
599     * This may be used e.g. in constructing a {@link #getContextMenu context menu}.
600     *
601     * <P>
602     * By default returns the actions in {@link NodeOp#getDefaultActions}.
603     *
604     * @return system actions appropriate to the node
605     * @deprecated Use getActions (false) instead.
606     */

607     @Deprecated JavaDoc
608     public SystemAction[] getActions() {
609         return NodeOp.getDefaultActions();
610     }
611
612     /** Get a special set of actions
613     * for situations when this node is displayed as a context.
614     * <p>For example, right-clicking on a parent node in a hierarchical view (such as
615     * the normal Explorer) should use <code>getActions</code>. However, if this node
616     * is serving as the parent of a (say) a window tab full of icons (e.g., in
617     * <code>IconView</code>), and the users right-clicks on
618     * the empty space in this pane, then this method should be used to get
619     * the appropriate actions for a context menu.
620     * <p>Note that in the Windows UI system, e.g., these action sets are quite different.
621     *
622     * @return actions for a context. In the default implementation, same as {@link #getActions}.
623     * @deprecated Use getActions (true) instead.
624     */

625     @Deprecated JavaDoc
626     public SystemAction[] getContextActions() {
627         return getActions();
628     }
629
630     /** Gets the default action for this node.
631      * @return <code>null</code> indicating there should be none default action
632      * @deprecated Use {@link #getPreferredAction} instead.
633      */

634     @Deprecated JavaDoc
635     public SystemAction getDefaultAction() {
636         return null;
637     }
638
639     /** Gets the preferred action for this node.
640      * This action can but need not to be one from the action array returned
641      * from {@link #getActions(boolean)}.
642      * In case it is, the context menu created from those actions
643      * is encouraged to highlight the preferred action.
644      * Override in subclasses accordingly.
645      *
646      * @return the preferred action, or <code>null</code> if there is none
647      * @since 3.29
648      */

649     public Action JavaDoc getPreferredAction() {
650         return getDefaultAction();
651     }
652
653     /** Make a context menu for this node.
654     * The menu is constructed from the set of actions returned by {@link #getActions}.
655     *
656     * @return the context menu
657     */

658     public final JPopupMenu JavaDoc getContextMenu() {
659         return NodeOp.findContextMenuImpl(new Node[] { this }, null);
660     }
661
662     /** Test whether there is a customizer for this node. If true,
663     * the customizer can be obtained via {@link #getCustomizer}.
664     *
665     * @return <CODE>true</CODE> if there is a customizer
666     */

667     public abstract boolean hasCustomizer();
668
669     /** Get the customizer component.
670     * @return the component, or <CODE>null</CODE> if there is no customizer
671     */

672     public abstract java.awt.Component JavaDoc getCustomizer();
673
674     /** Get a cookie for this node.
675     * <P>
676     * The set of cookies can change. If a node changes its set of
677     * cookies, it fires a property change event with {@link #PROP_COOKIE}.
678     * <P>
679     * If the Node was constructed with a <code>Lookup</code> in constructor
680     * than this method delegates to the provided lookup object.
681     *
682     * @param type the representation class of the cookie
683     * @return a cookie assignable to that class, or <code>null</code> if this node has no such cookie
684     * @see Lookup
685     */

686     public <T extends Node.Cookie> T getCookie(Class JavaDoc<T> type) {
687         Lookup l = internalLookup(true);
688
689         if (l != null) {
690             Object JavaDoc obj = l.lookup(type);
691             if (Node.Cookie.class.isInstance(obj)) {
692                 return type.cast(obj);
693             }
694             CookieSet.enhancedQueryMode(l, type);
695         }
696
697         return null;
698     }
699
700     /** Obtains a Lookup representing additional content of this Node.
701      * If the lookup was provided in a constructor, it is returned here,
702      * if not, a lookup based on the content of <link>getCookie</link>
703      * method is provided.
704      *
705      * @return lookup for this node
706      * @since 3.11
707      */

708     public final Lookup getLookup() {
709         synchronized (listeners) {
710             Lookup l = internalLookup(true);
711
712             if (l != null) {
713                 return l;
714             }
715
716             l = findDelegatingLookup();
717
718             if (l != null) {
719                 return l;
720             }
721
722             // create new lookup and use it
723
NodeLookup nl = new NodeLookup(this);
724             registerDelegatingLookup(nl);
725
726             return nl;
727         }
728     }
729
730     /** Return a variant of the display name containing HTML markup
731      * conforming to the limited subset of font-markup HTML supported by
732      * the lightweight HTML renderer <code>org.openide.awt.HtmlRenderer</code>
733      * (font color, bold, italic and strike-through supported; font
734      * colors can be UIManager color keys if they are prefixed with
735      * a ! character, i.e. <samp>&lt;font color='!controlShadow'&gt;</samp>).
736      * Enclosing <samp>&lt;html&gt;</samp> tags are not needed. If returning non-null, HTML
737      * markup characters that should be literally rendered must be
738      * escaped (<samp>&gt;</samp> becomes <samp>&amp;gt;</samp> and so forth).
739      * <p><strong>This method should return either an HTML display name
740      * or null; it should not return the non-HTML display name.</strong>
741      * <p>
742      * Note there is no property corresponding to the HTML display name -
743      * if it should change, a change in the display name should be fired; this
744      * should not be a mechanism for returning anything other than a marked
745      * up version of the return value of <code>getDisplayName</code>.
746      *
747      * @see org.openide.awt.HtmlRenderer
748      * @since 4.30
749      * @return a String containing conformant HTML markup which
750      * represents the display name, or null. The default implementation
751      * returns null. */

752     public String JavaDoc getHtmlDisplayName() {
753         return null;
754     }
755
756     /** Register delegating lookup so it can always be found.
757      */

758     final void registerDelegatingLookup(NodeLookup l) {
759         // to have just one thread accessing the static lookups variable
760
synchronized (lookups) {
761             lookups.put(listeners, new WeakReference JavaDoc<Lookup>(l));
762         }
763     }
764
765     /** Finds delegating lookup that was previously registered
766      * @return the lookup or null if nothing was registered or the
767      * lookup was GCed.
768      */

769     final Lookup findDelegatingLookup() {
770         Reference JavaDoc<Lookup> ref = lookups.get(listeners);
771
772         return (ref == null) ? null : ref.get();
773     }
774
775     /** Obtain handle for this node (for serialization).
776     * The handle can be serialized and {@link Handle#getNode} used after
777     * deserialization to obtain the original node.
778     *
779     * @return the handle, or <code>null</code> if this node is not persistable
780     */

781     public abstract Node.Handle getHandle();
782
783     /** Add a listener to changes in the node's intrinsic properties (name, cookies, etc.).
784      * <P>
785      * The listener is not notified about changes in subnodes until the
786      * method <CODE>getChildren().getNodes()</CODE> is called.
787      * @param l the listener to add
788      */

789     public final void addNodeListener(NodeListener l) {
790         listeners.add(NodeListener.class, l);
791         listenerAdded();
792     }
793
794     /** A method to notify FilterNode that a listenerAdded has been added */
795     void listenerAdded() {
796     }
797
798     /** Remove a node listener.
799      * @param l the listener
800     */

801     public final void removeNodeListener(NodeListener l) {
802         listeners.remove(NodeListener.class, l);
803     }
804
805     /** Add a listener to the node's computed Bean properties.
806      * @param l the listener
807     */

808     public final void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
809         int count = -1;
810
811         if (err.isLoggable(Level.FINE)) {
812             count = getPropertyChangeListenersCount();
813         }
814
815         listeners.add(PropertyChangeListener JavaDoc.class, l);
816
817         if (err.isLoggable(Level.FINE)) {
818             err.log(
819                 Level.FINE,
820                 "ADD - " + getName() + " [" + count + "]->[" + getPropertyChangeListenersCount() + "] " + l
821             );
822         }
823
824         notifyPropertyChangeListenerAdded(l);
825     }
826
827     /** Called to notify subclasses (FilterNode) about addition of
828      * PropertyChangeListener.
829      */

830     void notifyPropertyChangeListenerAdded(PropertyChangeListener JavaDoc l) {
831     }
832
833     /** Returns the number of property change listeners attached to this node
834      */

835     int getPropertyChangeListenersCount() {
836         return listeners.getListenerCount(PropertyChangeListener JavaDoc.class);
837     }
838
839     /** Allows to figure out, whether the node has any
840      * PropertyChangeListeners attached.
841      * @return True if node has one or more PropertyChangeListeners attached.
842      * @since 1.36
843      */

844     protected final boolean hasPropertyChangeListener() {
845         return getPropertyChangeListenersCount() > 0;
846     }
847
848     /** Remove a Bean property change listener.
849      * @param l the listener
850     */

851     public final void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
852         int count = -1;
853
854         if (err.isLoggable(Level.FINE)) {
855             count = getPropertyChangeListenersCount();
856         }
857
858         listeners.remove(PropertyChangeListener JavaDoc.class, l);
859
860         if (err.isLoggable(Level.FINE)) {
861             err.log(
862                 Level.FINE,
863                 "RMV - " + getName() + " [" + count + "]->[" + getPropertyChangeListenersCount() + "] " + l
864             );
865         }
866
867         notifyPropertyChangeListenerRemoved(l);
868     }
869
870     /** Called to notify subclasses (FilterNode) about removal of
871      * PropertyChangeListener.
872      */

873     void notifyPropertyChangeListenerRemoved(PropertyChangeListene