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(PropertyChangeListener JavaDoc l) {
874     }
875
876     /** Fire a property change event.
877     *
878     * @param name name of changed property (from {@link #getPropertySets}); may be null
879     * @param o old value; may be null
880     * @param n new value; may be null
881     * @see PropertyChangeEvent
882     */

883     protected final void firePropertyChange(String JavaDoc name, Object JavaDoc o, Object JavaDoc n) {
884         // First check if this property actually exists - if not warn! See #31413.
885
if (err.isLoggable(Level.WARNING) && (name != null) && propertySetsAreKnown()) {
886             Node.PropertySet[] pss = getPropertySets();
887             boolean exists = false;
888
889             for (int i = 0; i < pss.length; i++) {
890                 Node.Property[] ps = pss[i].getProperties();
891
892                 for (int j = 0; j < ps.length; j++) {
893                     if (ps[j].getName().equals(name)) {
894                         exists = true;
895
896                         break;
897                     }
898                 }
899             }
900
901             if (!exists) {
902                 synchronized (warnedBadProperties) {
903                     String JavaDoc clazz = getClass().getName();
904
905                     if (warnedBadProperties.add(clazz + "." + name)) {
906                         StringWriter JavaDoc w = new StringWriter JavaDoc();
907                         IllegalStateException JavaDoc ise = new IllegalStateException JavaDoc("Warning - the node \"" +
908                             getDisplayName() +
909                             "\" [" +
910                             clazz +
911                             "] is trying to fire the property " +
912                             name +
913                             " which is not included in its property sets. This is illegal. See IZ #31413 for details."
914                         ); // NOI18N
915
ise.printStackTrace(new PrintWriter JavaDoc(w));
916                         Logger.getLogger(Node.class.getName()).warning(w.toString());
917                     }
918                 }
919             }
920         }
921
922         // do not fire if the values are the same
923
if ((o != null) && (n != null) && ((o == n) || o.equals(n))) {
924             return;
925         }
926
927         PropertyChangeEvent JavaDoc ev = null;
928
929         Object JavaDoc[] listeners = this.listeners.getListenerList();
930
931         // Process the listeners last to first, notifying
932
// those that are interested in this event
933
for (int i = listeners.length - 2; i >= 0; i -= 2) {
934             if (listeners[i] == PropertyChangeListener JavaDoc.class) {
935                 // Lazily create the event:
936
if (ev == null) {
937                     ev = new PropertyChangeEvent JavaDoc(this, name, o, n);
938                 }
939
940                 ((PropertyChangeListener JavaDoc) listeners[i + 1]).propertyChange(ev);
941             }
942         }
943     }
944
945     /**
946      * If true, property sets have definitely been computed, and it is fine
947      * to call {@link #getPropertySets} without fear of killing laziness.
948      * Used from {@link #firePropertyChange} to only check for bad properties
949      * if the set of properties has already been computed. Otherwise, don't
950      * bother. Subclasses may override - {@link AbstractNode} does.
951      */

952     boolean propertySetsAreKnown() {
953         return false;
954     }
955
956     /** Allow subclasses that override the getName method to fire
957     * the changes of the name by itself. Please notice that default
958     * implementation of setName will fire the change by itself.
959     */

960     protected final void fireNameChange(String JavaDoc o, String JavaDoc n) {
961         fireOwnPropertyChange(PROP_NAME, o, n);
962     }
963
964     /** Allow subclasses that override the getDisplayName method to fire
965     * the changes of the name by itself. Please notice that default
966     * implementation of setDisplayName will fire the change by itself.
967     */

968     protected final void fireDisplayNameChange(String JavaDoc o, String JavaDoc n) {
969         fireOwnPropertyChange(PROP_DISPLAY_NAME, o, n);
970     }
971
972     /** Allow subclasses that override the getShortDescription method to fire
973     * the changes of the description by itself. Please notice that default
974     * implementation of setShortDescription will fire the change by itself.
975     */

976     protected final void fireShortDescriptionChange(String JavaDoc o, String JavaDoc n) {
977         fireOwnPropertyChange(PROP_SHORT_DESCRIPTION, o, n);
978     }
979
980     /** Fire a change event for {@link #PROP_ICON}.
981     */

982     protected final void fireIconChange() {
983         fireOwnPropertyChange(PROP_ICON, null, null);
984     }
985
986     /** Fire a change event for {@link #PROP_OPENED_ICON}.
987     */

988     protected final void fireOpenedIconChange() {
989         fireOwnPropertyChange(PROP_OPENED_ICON, null, null);
990     }
991
992     /** Fires info about some structural change in children. Providing
993     * type of operation and set of children changed generates event describing
994     * the change.
995     *
996     *
997     * @param addAction <CODE>true</CODE> if the set of children has been added,
998     * false if it has been removed
999     * @param delta the array with changed children
1000    * @param from the array of nodes to take indices from.
1001    * Can be null if one should find indices from current set of nodes
1002    */

1003    final void fireSubNodesChange(boolean addAction, Node[] delta, Node[] from) {
1004        NodeMemberEvent ev = null;
1005
1006        Object JavaDoc[] listeners = this.listeners.getListenerList();
1007
1008        // Process the listeners last to first, notifying
1009
// those that are interested in this event
1010
for (int i = listeners.length - 2; i >= 0; i -= 2) {
1011            if (listeners[i] == NodeListener.class) {
1012                // Lazily create the event:
1013
if (ev == null) {
1014                    ev = new NodeMemberEvent(this, addAction, delta, from);
1015                }
1016
1017                if (addAction) {
1018                    ((NodeListener) listeners[i + 1]).childrenAdded(ev);
1019                } else {
1020                    ((NodeListener) listeners[i + 1]).childrenRemoved(ev);
1021                }
1022            }
1023        }
1024    }
1025
1026    /** Fires info about reordering of some children.
1027    *
1028    * @param indices array of integers describing the permutation
1029    */

1030    final void fireReorderChange(int[] indices) {
1031        NodeReorderEvent ev = null;
1032
1033        Object JavaDoc[] listeners = this.listeners.getListenerList();
1034
1035        // Process the listeners last to first, notifying
1036
// those that are interested in this event
1037
for (int i = listeners.length - 2; i >= 0; i -= 2) {
1038            if (listeners[i] == NodeListener.class) {
1039                // Lazily create the event:
1040
if (ev == null) {
1041                    ev = new NodeReorderEvent(this, indices);
1042                }
1043
1044                ((NodeListener) listeners[i + 1]).childrenReordered(ev);
1045            }
1046        }
1047    }
1048
1049    /** To all node listeners fire node destroyed notification.
1050    */

1051    protected final void fireNodeDestroyed() {
1052        NodeEvent ev = null;
1053
1054        Object JavaDoc[] listeners = this.listeners.getListenerList();
1055
1056        // Process the listeners last to first, notifying
1057
// those that are interested in this event
1058
for (int i = listeners.length - 2; i >= 0; i -= 2) {
1059            if (listeners[i] == NodeListener.class) {
1060                // Lazily create the event:
1061
if (ev == null) {
1062                    ev = new NodeEvent(this);
1063                }
1064
1065                ((NodeListener) listeners[i + 1]).nodeDestroyed(ev);
1066            }
1067        }
1068    }
1069
1070    /** Fires info about change of parent node.
1071    * @param o old node
1072    * @param n new parent
1073    */

1074    final void fireParentNodeChange(Node o, Node n) {
1075        fireOwnPropertyChange(PROP_PARENT_NODE, o, n);
1076    }
1077
1078    /** Fires a (Bean) property change event (for {@link #PROP_PROPERTY_SETS}).
1079    * @param o the old set
1080    * @param n the new set
1081    */

1082    protected final void firePropertySetsChange(PropertySet[] o, PropertySet[] n) {
1083        fireOwnPropertyChange(PROP_PROPERTY_SETS, o, n);
1084    }
1085
1086    /** Fires a change event for {@link #PROP_COOKIE}.
1087    * The old and new values are set to null.
1088    */

1089    protected final void fireCookieChange() {
1090        Lookup l = findDelegatingLookup();
1091
1092        if (l instanceof NodeLookup) {
1093            ((NodeLookup) l).updateLookupAsCookiesAreChanged(null);
1094        }
1095
1096        fireOwnPropertyChange(PROP_COOKIE, null, null);
1097    }
1098
1099    /** Fires info about change of own property.
1100    * @param name name of property
1101    * @param o old value
1102    * @param n new value
1103    */

1104    final void fireOwnPropertyChange(String JavaDoc name, Object JavaDoc o, Object JavaDoc n) {
1105        // do not fire if the values are the same
1106
if ((o != null) && (n != null) && ((o == n) || o.equals(n))) {
1107            return;
1108        }
1109
1110        PropertyChangeEvent JavaDoc ev = null;
1111
1112        Object JavaDoc[] listeners = this.listeners.getListenerList();
1113
1114        // Process the listeners last to first, notifying
1115
// those that are interested in this event
1116
for (int i = listeners.length - 2; i >= 0; i -= 2) {
1117            if (listeners[i] == NodeListener.class) {
1118                // Lazily create the event:
1119
if (ev == null) {
1120                    ev = new PropertyChangeEvent JavaDoc(this, name, o, n);
1121                }
1122
1123                ((NodeListener) listeners[i + 1]).propertyChange(ev);
1124            }
1125        }
1126    }
1127
1128    /** Compares for equality. Does special treatment of
1129     * FilterNodes. If argument is FilterNode then this node can be
1130     * equal with it if it is its original.
1131     *
1132     * @param obj object to compare
1133     * @return true if the obj is <code>==</code> or is filter node of this node
1134     */

1135    public boolean equals(Object JavaDoc obj) {
1136        if (obj instanceof FilterNode) {
1137            return ((FilterNode) obj).equals(this);
1138        }
1139
1140        return this == obj;
1141    }
1142
1143    /** Obtains a resource string from bundle.
1144    * @param resName resource name
1145    * @return the string
1146    */

1147    static String JavaDoc getString(final String JavaDoc resName) {
1148        return NbBundle.getBundle(Node.class).getString(resName);
1149    }
1150
1151    public String JavaDoc toString() {
1152        return super.toString() + "[Name=" + getName() + ", displayName=" + getDisplayName() + "]"; // NOI18N
1153
}
1154
1155    /** Marker interface for all cookies.
1156    * <P>
1157    * Most examples are present in {@link org.openide.cookies}.
1158    */

1159    public static interface Cookie {
1160    }
1161
1162    /** Serializable node reference. The node should not
1163    * be serialized directly but via this handle. One can obtain a handle
1164    * by a call to {@link Node#getHandle}.
1165    * <P>
1166    * If that methods returns a non-<code>null</code> value, one can serialize it,
1167    * and after deserialization
1168    * use {@link #getNode} to obtain the original node.
1169    */

1170    public static interface Handle extends java.io.Serializable JavaDoc {
1171        /** @deprecated Only public by accident. */
1172        @Deprecated JavaDoc
1173        /* public static final */ long serialVersionUID = -4518262478987434353L;
1174
1175        /** Reconstitute the node for this handle.
1176        *
1177        * @return the node for this handle
1178        * @exception IOException if the node cannot be created
1179        */

1180        public Node getNode() throws java.io.IOException JavaDoc;
1181    }
1182
1183    /** Class that represents one set of properties. A usual bean has three
1184    * sets of properties: normal, expert, and events.
1185    * <p>You may associate context help with this object, if desired, by setting
1186    * a {@link FeatureDescriptor#setValue custom property} with the name <code>helpID</code>
1187    * and value of type <code>String</code> giving a help ID.
1188    * Normally this is unnecessary as help for the whole {@link Node} will be used by default.
1189    */

1190    public static abstract class PropertySet extends FeatureDescriptor JavaDoc {
1191        /** Default constructor. */
1192        public PropertySet() {
1193        }
1194
1195        /** Create a property set.
1196         * @param name system name of the property set
1197        * @param displayName human presentable name
1198        * @param shortDescription description for the set
1199        */

1200        public PropertySet(String JavaDoc name, String JavaDoc displayName, String JavaDoc shortDescription) {
1201            super.setName(name);
1202            super.setDisplayName(displayName);
1203            super.setShortDescription(shortDescription);
1204        }
1205
1206        /** Get the list of contained properties.
1207        * This list can contain both {@link Node.Property} and {@link Node.IndexedProperty} elements.
1208        *
1209        * @return the properties
1210        */

1211        public abstract Property<?>[] getProperties();
1212
1213        /* Compares just the names.
1214         * @param propertySet The object to compare to
1215         */

1216        public boolean equals(Object JavaDoc propertySet) {
1217            if (!(propertySet instanceof PropertySet)) {
1218                return false;
1219            }
1220
1221            return ((PropertySet) propertySet).getName().equals(getName());
1222        }
1223
1224        /* Returns a hash code value for the object.
1225         *
1226         * @return int hashcode
1227         */

1228        public int hashCode() {
1229            return getName().hashCode();
1230        }
1231
1232        /** Return a variant of the display name containing HTML markup
1233         * conforming to the limited subset of font-markup HTML supported by
1234         * the lightweight HTML renderer org.openide.awt.HtmlRenderer
1235         * (font color, bold, italic and strikethrough supported; font
1236         * colors can be UIManager color keys if they are prefixed with
1237         * a ! character, i.e. &lt;font color=&amp;'controlShadow'&gt;).
1238         * Enclosing HTML tags are not needed.
1239         * <p><strong>This method should return either an HTML display name
1240         * or null; it should not return the non-HTML display name if no
1241         * markup is needed.</strong>
1242         *
1243         * @see org.openide.awt.HtmlRenderer
1244         * @since 4.30
1245         * @return a String containing conformant, legal HTML markup which
1246         * represents the display name, or null. The default implementation
1247         * returns null. */

1248        public String JavaDoc getHtmlDisplayName() {
1249            return null;
1250        }
1251    }
1252
1253    /** Description of a Bean property on a node, and operations on it.
1254    * <p>You may associate context help with this object, if desired, by setting
1255    * a {@link FeatureDescriptor#setValue custom property} with the name <code>helpID</code>
1256    * and value of type <code>String</code> giving a help ID.
1257    * Normally this is unnecessary as help for the whole {@link Node} will be used by default.
1258     * <p><strong>Important:</strong> the {@link FeatureDescriptor#getName code name} you use for the
1259     * property is relevant not only for making properties of a node unique, but also for
1260     * {@link Node#firePropertyChange firing property changes}.
1261     * @param T the type of bean
1262    */

1263    public static abstract class Property<T> extends FeatureDescriptor JavaDoc {
1264        /**
1265         * Contains classNames of incorrectly implemented properties which have
1266         * been already logged by an ErrorManager.<br>
1267         * For more information see the
1268         * <a HREF="http://openide.netbeans.org/issues/show_bug.cgi?id=51907">
1269         * discussion in issuezilla</a>
1270         */

1271        private static final Set JavaDoc<String JavaDoc> warnedNames = new HashSet JavaDoc<String JavaDoc>();
1272
1273        /** type that this property works with */
1274        private Class JavaDoc<T> type;
1275
1276        //Soft caching of property editor references to improve JTable
1277
//property sheet performance
1278
private Reference JavaDoc<PropertyEditor JavaDoc> edRef = null;
1279
1280        /** Constructor.
1281        * @param valueType type of the property
1282        */

1283        public Property(Class JavaDoc<T> valueType) {
1284            this.type = valueType;
1285            super.setName(""); // NOI18N
1286
}
1287
1288        /** Get the value type. This is the representation class of the property.
1289        * Remember that e.g. {@link Boolean <code>Boolean.class</code>} means that values are <code>Boolean</code>
1290        * objects; to specify the primitive type, use e.g. {@link Boolean#TYPE}.
1291        * In the latter case, {@link #getValue} and {@link #setValue} will still operate on the wrapper object.
1292        * @return the type
1293        */

1294        public Class JavaDoc<T> getValueType() {
1295            return type;
1296        }
1297
1298        /** Test whether the property is readable.
1299        * @return <CODE>true</CODE> if it is
1300        */

1301        public abstract boolean canRead();
1302
1303        /** Get the value.
1304        * @return the value of the property
1305        * @exception IllegalAccessException cannot access the called method
1306        * @exception InvocationTargetException an exception during invocation
1307        */

1308        public abstract T getValue() throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc;
1309
1310        /** Test whether the property is writable.
1311        * @return <CODE>true</CODE> if the read of the value is supported
1312        */

1313        public abstract boolean canWrite();
1314
1315        /** Set the value.
1316        * @param val the new value of the property
1317        * @exception IllegalAccessException cannot access the called method
1318        * @exception IllegalArgumentException wrong argument
1319        * @exception InvocationTargetException an exception during invocation
1320        */

1321        public abstract void setValue(T val)
1322        throws IllegalAccessException JavaDoc, IllegalArgumentException JavaDoc, InvocationTargetException JavaDoc;
1323
1324        /** Test whether the property had a default value.
1325        * @return <code>true</code> if it does (<code>false</code> by default)
1326        */

1327        public boolean supportsDefaultValue() {
1328            return false;
1329        }
1330
1331        /** Restore this property to its default value, if supported.
1332        * In the default implementation, does nothing.
1333        * Typically you would just call e.g. <code>setValue(default)</code>.
1334        * Note that it is not permitted for this call to throw {@link IllegalArgumentException},
1335        * though the other two exceptions from {@link #setValue} may be passed through.
1336        * @exception IllegalAccessException cannot access the called method
1337        * @exception InvocationTargetException an exception during invocation
1338        */

1339        public void restoreDefaultValue() throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc {
1340        }
1341
1342        /**
1343         * This method indicates whether the current value is the same as
1344         * the value that would otherwise be restored by calling
1345         * <code>restoreDefaultValue()</code> (if <code>supportsDefaultValue()</code>
1346         * returns true). The default implementation returns true and
1347         * it is recommended to also return true when <code>supportsDefaultValue()</code>
1348         * returns false (if we do not support default value any value can
1349         * be considered as the default). If <code>supportsDefaultValue()</code>
1350         * returns false this method will not be called by the default
1351         * implementation of property sheet.
1352         * @since 3.19
1353         */

1354        public boolean isDefaultValue() {
1355            String JavaDoc name = getClass().getName();
1356
1357            // Issue 51907 backward compatibility
1358
if (supportsDefaultValue() && warnedNames.add(name)) {
1359                Logger.getLogger(Node.Property.class.getName()).log(
1360                    Level.WARNING,
1361                    "Class " + name + " must override isDefaultValue() since it " +
1362                    "overrides supportsDefaultValue() to be true"
1363                );
1364            }
1365
1366            return true;
1367        }
1368
1369        /** Get a property editor for this property.
1370         * The default implementation tries to use {@link java.beans.PropertyEditorManager}.
1371         * @return the property editor, or <CODE>null</CODE> if there is no editor */

1372        public PropertyEditor JavaDoc getPropertyEditor() {
1373            if (type == null) {
1374                return null;
1375            }
1376
1377            PropertyEditor JavaDoc result = null;
1378
1379            if (edRef != null) {
1380                result = edRef.get();
1381            }
1382
1383            if (result == null) {
1384                result = java.beans.PropertyEditorManager.findEditor(type);
1385                edRef = new SoftReference JavaDoc<PropertyEditor JavaDoc>(result);
1386            }
1387
1388            return result;
1389        }
1390
1391        /* Standard equals implementation for all property
1392        * classes.
1393        * @param property The object to compare to
1394        */

1395        @Override JavaDoc
1396        public boolean equals(Object JavaDoc property) {
1397            // fix #32845 - check for non-matching types and also for null values
1398
// coming in input parameter 'property'
1399
if (!(property instanceof Property)) {
1400                return false;
1401            }
1402
1403            Class JavaDoc<?> propValueType = ((Property) property).getValueType();
1404            Class JavaDoc<?> valueType = getValueType();
1405
1406            if (((propValueType == null) && (valueType != null)) || ((propValueType != null) && (valueType == null))) {
1407                return false;
1408            }
1409
1410            return ((Property) property).getName().equals(getName()) &&
1411            (((propValueType == null) && (valueType == null)) || propValueType.equals(valueType));
1412        }
1413
1414        /* Returns a hash code value for the object.
1415        *
1416        * @return int hashcode
1417        */

1418        @Override JavaDoc
1419        public int hashCode() {
1420            Class JavaDoc<?> valueType = getValueType();
1421
1422            return getName().hashCode() * ((valueType == null) ? 1 : valueType.hashCode());
1423        }
1424
1425        /** Return a variant of the display name containing HTML markup
1426         * conforming to the limited subset of font-markup HTML supported by
1427         * the lightweight HTML renderer {@link org.openide.awt.HtmlRenderer}
1428         * (font color, bold, italic and strike-through supported; font
1429         * colors can be UIManager color keys if they are prefixed with
1430         * a ! character, i.e. &lt;font color=&amp;'controlShadow'&gt;).
1431         * Enclosing HTML tags are not needed.
1432         * <p><strong>This method should return either an HTML display name
1433         * or null; it should not return the non-HTML display name.
1434         *
1435         * @see org.openide.awt.HtmlRenderer
1436         * @since 4.30
1437         * @return a String containing conformant, legal HTML markup which
1438         * represents the display name, or null. The default implementation
1439         * returns null. */

1440        public String JavaDoc getHtmlDisplayName() {
1441            return null;
1442        }
1443    }
1444
1445    /** Description of an indexed property and operations on it.
1446     * @param T type of the whole property
1447     * @param E type of one element
1448    */

1449    public static abstract class IndexedProperty<T,E> extends Node.Property<T> {
1450        /** type of element that this property works with */
1451        private Class JavaDoc<E> elementType;
1452
1453        /** Constructor.
1454        * @param valueType type of the property
1455        */

1456        public IndexedProperty(Class JavaDoc<T> valueType, Class JavaDoc<E> elementType) {
1457            super(valueType);
1458            this.elementType = elementType;
1459        }
1460
1461        /** Test whether the property is readable by index.
1462        * @return <CODE>true</CODE> if so
1463        */

1464        public abstract boolean canIndexedRead();
1465
1466        /** Get the element type of the property (not the type of the whole property).
1467        * @return the type
1468        */

1469        public Class JavaDoc<E> getElementType() {
1470            return elementType;
1471        }
1472
1473        /** Get the value of the property at an index.
1474        *
1475        * @param index the index
1476        * @return the value at that index
1477        * @exception IllegalAccessException cannot access the called method
1478        * @exception IllegalArgumentException wrong argument
1479        * @exception InvocationTargetException an exception during invocation
1480        */

1481        public abstract E getIndexedValue(int index)
1482        throws IllegalAccessException JavaDoc, IllegalArgumentException JavaDoc, InvocationTargetException JavaDoc;
1483
1484        /** Test whether the property is writable by index.
1485        * @return <CODE>true</CODE> if so
1486        */

1487        public abstract boolean canIndexedWrite();
1488
1489        /** Set the value of the property at an index.
1490        *
1491        * @param indx the index
1492        * @param val the value to set
1493        * @exception IllegalAccessException cannot access the called method
1494        * @exception IllegalArgumentException wrong argument
1495        * @exception InvocationTargetException an exception during invocation
1496        */

1497        public abstract void setIndexedValue(int indx, E val)
1498        throws IllegalAccessException JavaDoc, IllegalArgumentException JavaDoc, InvocationTargetException JavaDoc;
1499
1500        /** Get a property editor for individual elements in this property.
1501        * @return the property editor for elements
1502        */

1503        public PropertyEditor JavaDoc getIndexedPropertyEditor() {
1504            return java.beans.PropertyEditorManager.findEditor(elementType);
1505        }
1506
1507        /* Standard equals implementation for all property
1508        * classes.
1509        * @param property The object to compare to
1510        */

1511        @Override JavaDoc
1512        public boolean equals(Object JavaDoc property) {
1513            try {
1514                if (!super.equals(property)) {
1515                    return false;
1516                }
1517
1518                Class JavaDoc<?> propElementType = ((IndexedProperty) property).getElementType();
1519                Class JavaDoc<?> elementType = getElementType();
1520
1521                if (
1522                    ((propElementType == null) && (elementType != null)) ||
1523                        ((propElementType != null) && (elementType == null))
1524                ) {
1525                    return false;
1526                }
1527
1528                return (((propElementType == null) && (elementType == null)) || propElementType.equals(elementType));
1529            } catch (ClassCastException JavaDoc e) {
1530                return false;
1531            }
1532        }
1533
1534        /* Returns a hash code value for the object.
1535        *
1536        * @return int hashcode
1537        */

1538        @Override JavaDoc
1539        public int hashCode() {
1540            Class JavaDoc<?> ementType = getElementType();
1541
1542            return super.hashCode() * ((elementType == null) ? 1 : elementType.hashCode());
1543        }
1544    }
1545
1546    /** Special subclass of EventListenerList that can also listen on changes in
1547     * a lookup.
1548     */

1549    private final class LookupEventList extends EventListenerList JavaDoc implements LookupListener {
1550        public final Lookup lookup;
1551        private Lookup.Result<Node.Cookie> result;
1552
1553        public LookupEventList(Lookup l) {
1554            this.lookup = l;
1555        }
1556
1557        public Lookup init(boolean init) {
1558            boolean doInit = false;
1559
1560            synchronized (INIT_LOCK) {
1561                if (init && (result == null)) {
1562                    result = lookup.lookup(TEMPL_COOKIE);
1563                    assert result != null : "Null lookup result from " + lookup + " in " + Node.this;
1564                    result.addLookupListener(this);
1565                    doInit = true;
1566                }
1567            }
1568
1569            if (doInit) {
1570                result.allItems();
1571            }
1572
1573            return lookup;
1574        }
1575
1576        public void resultChanged(LookupEvent ev) {
1577            if (Node.this instanceof FilterNode) {
1578                FilterNode f = (FilterNode) Node.this;
1579
1580                // See #40734 and NodeLookupTest and CookieActionIsTooSlowTest.
1581
if (f.getOriginal() == NodeLookup.NO_COOKIE_CHANGE.get()) {
1582                    // this is not real cookie change, do not fire it
1583
// issue 40734
1584
return;
1585                }
1586            }
1587
1588            fireCookieChange();
1589        }
1590    }
1591}
1592
Popular Tags