| 1 19 20 package org.openide.nodes; 21 22 import java.awt.Image ; 23 import java.awt.datatransfer.Transferable ; 24 import java.beans.FeatureDescriptor ; 25 import java.beans.PropertyChangeEvent ; 26 import java.beans.PropertyChangeListener ; 27 import java.beans.PropertyEditor ; 28 import java.io.IOException ; 29 import java.io.PrintWriter ; 30 import java.io.StringWriter ; 31 import java.lang.ref.Reference ; 32 import java.lang.ref.SoftReference ; 33 import java.lang.ref.WeakReference ; 34 import java.lang.reflect.InvocationTargetException ; 35 import java.util.HashSet ; 36 import java.util.Map ; 37 import java.util.Set ; 38 import java.util.WeakHashMap ; 39 import java.util.logging.Level ; 40 import java.util.logging.Logger ; 41 import javax.swing.Action ; 42 import javax.swing.JPopupMenu ; 43 import javax.swing.event.EventListenerList ; 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 84 public abstract class Node extends FeatureDescriptor implements Lookup.Provider, HelpCtx.Provider { 85 86 public static final Node EMPTY = new AbstractNode(Children.LEAF); 87 88 91 92 93 public static final String PROP_DISPLAY_NAME = "displayName"; 95 98 public static final String PROP_NAME = "name"; 100 101 public static final String PROP_SHORT_DESCRIPTION = "shortDescription"; 103 104 public static final String PROP_ICON = "icon"; 106 107 public static final String PROP_OPENED_ICON = "openedIcon"; 109 110 public static final String PROP_PARENT_NODE = "parentNode"; 112 113 public static final String PROP_PROPERTY_SETS = "propertySets"; 115 116 public static final String PROP_COOKIE = "cookie"; 118 121 public static final String PROP_LEAF = "leaf"; 123 125 private static final Logger err = Logger.getLogger("org.openide.nodes.Node"); 127 128 private static Map <EventListenerList ,Reference <Lookup>> lookups = new WeakHashMap <EventListenerList ,Reference <Lookup>>(37); 129 130 131 private static final Set <String > warnedBadProperties = new HashSet <String >(100); 132 133 134 private static final Lookup.Template<Node.Cookie> TEMPL_COOKIE = new Lookup.Template<Node.Cookie>(Node.Cookie.class); 135 136 137 private static final Object INIT_LOCK = new Object (); 138 139 143 private Object parent; 144 145 148 Children hierarchy; 149 150 152 private transient EventListenerList listeners; 153 154 159 protected Node(Children h) throws IllegalStateException { 160 this(h, null); 161 } 162 163 200 protected Node(Children h, Lookup lookup) throws IllegalStateException { 201 this.hierarchy = h; 202 203 lookup = replaceProvidedLookup(lookup); 205 206 if (lookup != null) { 207 this.listeners = new LookupEventList(lookup); 208 } else { 209 this.listeners = new EventListenerList (); 210 } 211 212 h.attachTo(this); 214 } 215 216 219 Lookup replaceProvidedLookup(Lookup l) { 220 return l; 221 } 222 223 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 248 protected Object clone() throws CloneNotSupportedException { 249 Node n = (Node) super.clone(); 250 Children hier2; 251 252 if (hierarchy instanceof Cloneable ) { 253 hier2 = (Children) hierarchy.cloneHierarchy(); 254 } else { 255 hier2 = Children.LEAF; 256 } 257 258 n.hierarchy = hier2; 260 hier2.attachTo(n); 261 262 n.parent = null; 264 265 if (listeners instanceof LookupEventList) { 267 n.listeners = new LookupEventList(internalLookup(false)); 268 } else { 269 n.listeners = new EventListenerList (); 270 } 271 272 return n; 273 } 274 275 283 public abstract Node cloneNode(); 284 285 288 private Children getParentChildren() { 289 return (this.parent instanceof ChildrenArray) ? ((ChildrenArray) this.parent).getChildren() 290 : (Children) this.parent; 291 } 292 293 300 final synchronized void assignTo(Children parent, int index) { 301 Children ch = getParentChildren(); 302 303 if ((ch != null) && (ch != parent)) { 304 throw new IllegalStateException ( 305 "Cannot initialize " + index + "th child of node " + parent.getNode() + 306 "; it already belongs to node " + ch.getNode() 307 ); } 309 310 if (!(this.parent instanceof ChildrenArray)) { 311 this.parent = parent; 312 } 313 } 314 315 318 final synchronized void reassignTo(Children currentParent, ChildrenArray itsArray) { 319 if ((this.parent != currentParent) && (this.parent != itsArray)) { 320 throw new IllegalStateException ( 321 "Unauthorized call to change parent: " + this.parent + " and should be: " + currentParent 322 ); 323 } 324 325 this.parent = itsArray; 326 } 327 328 331 final synchronized void deassignFrom(Children parent) { 332 Children p = getParentChildren(); 333 334 if (parent != p) { 335 throw new IllegalArgumentException ("Deassign from wrong parent. Old: " + p + " Caller: " + parent); } 337 338 this.parent = null; 339 } 340 341 346 public void setName(String s) { 347 String name = super.getName(); 348 349 if ((name == null) || !name.equals(s)) { 350 super.setName(s); 351 fireNameChange(name, s); 352 } 353 } 354 355 358 public void setDisplayName(String s) { 359 String displayName = super.getDisplayName(); 360 361 if ((displayName == null) || !displayName.equals(s)) { 362 super.setDisplayName(s); 363 fireDisplayNameChange(displayName, s); 364 } 365 } 366 367 371 public void setShortDescription(String s) { 372 String descr = super.getShortDescription(); 373 374 if ((descr == null) || !descr.equals(s)) { 375 super.setShortDescription(s); 376 fireShortDescriptionChange(descr, s); 377 } 378 } 379 380 384 public abstract Image getIcon(int type); 385 386 392 public abstract Image getOpenedIcon(int type); 393 394 397 public abstract HelpCtx getHelpCtx(); 398 399 402 public final Children getChildren() { 403 updateChildren(); 404 405 return hierarchy; 406 } 407 408 411 void updateChildren() { 412 } 413 414 421 protected final void setChildren(final Children ch) { 422 Children.MUTEX.postWriteRequest(new Runnable () { 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 452 public final boolean isLeaf() { 453 updateChildren(); 454 455 return hierarchy == Children.LEAF; 456 } 457 458 461 public final Node getParentNode() { 462 Children ch = getParentChildren(); 464 465 return (ch == null) ? null : ch.getNode(); 466 } 467 468 474 public abstract boolean canRename(); 475 476 479 public abstract boolean canDestroy(); 480 481 483 494 public void destroy() throws IOException { 495 Children.MUTEX.postWriteRequest( 496 new Runnable () { 497 public void run() { 498 Children p = getParentChildren(); 499 500 if (p != null) { 501 p.remove(new Node[] { Node.this }); 503 } 504 505 fireNodeDestroyed(); 507 } 508 } 509 ); 510 } 511 512 518 public abstract PropertySet[] getPropertySets(); 519 520 526 public abstract Transferable clipboardCopy() throws IOException ; 527 528 534 public abstract Transferable clipboardCut() throws IOException ; 535 536 543 public abstract Transferable drag() throws IOException ; 544 545 548 public abstract boolean canCopy(); 549 550 553 public abstract boolean canCut(); 554 555 560 public abstract PasteType[] getPasteTypes(Transferable t); 561 562 572 public abstract PasteType getDropType(Transferable t, int action, int index); 573 574 578 public abstract NewType[] getNewTypes(); 579 580 594 public Action [] getActions(boolean context) { 595 return context ? getContextActions() : getActions(); 596 } 597 598 607 @Deprecated  608 public SystemAction[] getActions() { 609 return NodeOp.getDefaultActions(); 610 } 611 612 625 @Deprecated  626 public SystemAction[] getContextActions() { 627 return getActions(); 628 } 629 630 634 @Deprecated  635 public SystemAction getDefaultAction() { 636 return null; 637 } 638 639 649 public Action getPreferredAction() { 650 return getDefaultAction(); 651 } 652 653 658 public final JPopupMenu getContextMenu() { 659 return NodeOp.findContextMenuImpl(new Node[] { this }, null); 660 } 661 662 667 public abstract boolean hasCustomizer(); 668 669 672 public abstract java.awt.Component getCustomizer(); 673 674 686 public <T extends Node.Cookie> T getCookie(Class <T> type) { 687 Lookup l = internalLookup(true); 688 689 if (l != null) { 690 Object 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 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 NodeLookup nl = new NodeLookup(this); 724 registerDelegatingLookup(nl); 725 726 return nl; 727 } 728 } 729 730 752 public String getHtmlDisplayName() { 753 return null; 754 } 755 756 758 final void registerDelegatingLookup(NodeLookup l) { 759 synchronized (lookups) { 761 lookups.put(listeners, new WeakReference <Lookup>(l)); 762 } 763 } 764 765 769 final Lookup findDelegatingLookup() { 770 Reference <Lookup> ref = lookups.get(listeners); 771 772 return (ref == null) ? null : ref.get(); 773 } 774 775 781 public abstract Node.Handle getHandle(); 782 783 789 public final void addNodeListener(NodeListener l) { 790 listeners.add(NodeListener.class, l); 791 listenerAdded(); 792 } 793 794 795 void listenerAdded() { 796 } 797 798 801 public final void removeNodeListener(NodeListener l) { 802 listeners.remove(NodeListener.class, l); 803 } 804 805 808 public final void addPropertyChangeListener(PropertyChangeListener l) { 809 int count = -1; 810 811 if (err.isLoggable(Level.FINE)) { 812 count = getPropertyChangeListenersCount(); 813 } 814 815 listeners.add(PropertyChangeListener .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 830 void notifyPropertyChangeListenerAdded(PropertyChangeListener l) { 831 } 832 833 835 int getPropertyChangeListenersCount() { 836 return listeners.getListenerCount(PropertyChangeListener .class); 837 } 838 839 844 protected final boolean hasPropertyChangeListener() { 845 return getPropertyChangeListenersCount() > 0; 846 } 847 848 851 public final void removePropertyChangeListener(PropertyChangeListener l) { 852 int count = -1; 853 854 if (err.isLoggable(Level.FINE)) { 855 count = getPropertyChangeListenersCount(); 856 } 857 858 listeners.remove(PropertyChangeListener .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 873 void notifyPropertyChangeListenerRemoved(PropertyChangeListene
|