alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.nodes;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.awt.Image;
import java.awt.datatransfer.Transferable;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.*;
import javax.swing.Action;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;

import org.openide.util.HelpCtx;
import org.openide.util.Utilities;
import org.openide.util.Lookup;
import org.openide.util.datatransfer.*;
import org.openide.util.actions.SystemAction;

/** A basic implementation of a node.
*
* 

It simplifies creation of the display name, based on a message * format and the system name. It also simplifies working with icons: * one need only specify the base name and all icons will be loaded * when needed. Other common requirements are handled as well. * * @author Jaroslav Tulach */ public class AbstractNode extends Node { /** messages to create a resource identification for each type of * icon from the base name for the icon. */ private static final String[] icons = { // color 16x16 ".gif", // NOI18N // color 32x32 "32.gif", // NOI18N // mono 16x16 ".gif", // NOI18N // mono 32x32 "32.gif", // NOI18N // opened color 16x16 "Open.gif", // NOI18N // opened color 32x32 "Open32.gif", // NOI18N // opened mono 16x16 "Open.gif", // NOI18N // opened mono 32x32 "Open32.gif" // NOI18N }; /** To index normal icon from previous array use * + ICON_BASE. */ private static final int ICON_BASE = -1; /** for indexing opened icons */ private static final int OPENED_ICON_BASE = 3; /** empty array of paste types */ private static final PasteType[] NO_PASTE_TYPES = {}; /** empty array of new types */ private static final NewType[] NO_NEW_TYPES = {}; /** Message format to use for creation of the display name. * It permits conversion of text from * {@link #getName} to the one sent to {@link #setDisplayName}. The format can take * one parameter, {0}, which will be filled by a value from getName(). * *

The default format just uses the simple name; subclasses may * change it, though it will not take effect until the next {@link #setName} call. * *

Can be set to null. Then there is no connection between the * name and display name; they may be independently modified. */ protected MessageFormat displayFormat; /** Preferred action */ private Action preferredAction; /** default icon base for all nodes */ private static final String DEFAULT_ICON_BASE = "org/openide/resources/defaultNode"; // NOI18N private static final String DEFAULT_ICON = DEFAULT_ICON_BASE + ".gif"; // NOI18N /** Resource base for icons (without suffix denoting right icon) */ private String iconBase = DEFAULT_ICON_BASE; /** array of cookies for this node */ private Object lookup; /** set of properties to use */ private Sheet sheet; /** Actions for the node. They are used only for the pop-up menus * of this node. */ protected SystemAction[] systemActions; /** Listener for changes in the sheet and the cookie set. */ private final class SheetAndCookieListener implements PropertyChangeListener, ChangeListener { SheetAndCookieListener() {} public void propertyChange (PropertyChangeEvent ev) { AbstractNode.this.firePropertySetsChange (null, null); } public void stateChanged (ChangeEvent ev) { AbstractNode.this.fireCookieChange (); } } private SheetAndCookieListener sheetCookieL = null; /** Create a new abstract node with a given child set. * @param children the children to use for this node */ public AbstractNode(Children children) { this (children, null); } /** Create a new abstract node with a given child set and associated * lookup. If you use this constructor, please do not call methods * getCookieSet and setCookieSet they will * throw an exception. *

* More info on the correct usage of constructor with Lookup can be found * in the {@link Node#Node(org.openide.nodes.Children, org.openide.util.Lookup)} * javadoc. * * @param children the children to use for this node * @param lookup the lookup to provide content of {@link #getLookup} * and also {@link #getCookie} * @since 3.11 */ public AbstractNode (Children children, Lookup lookup) { super (children, lookup); // Setting the name to non-null value for the node // to return "reasonable" name and displayName // not using this.setName since the descendants // can override it and might assume that it is // not called from constructor (see e.g. DataNode) super.setName(""); // NOI18N } /** Clone the node. If the object implements {@link Cloneable}, * that is used; otherwise a {@link FilterNode filter node} * is created. * * @return copy of this node */ public Node cloneNode () { try { if (this instanceof Cloneable) { return (Node)clone (); } } catch (CloneNotSupportedException ex) { } return new FilterNode (this); } /** Set the system name. Fires a property change event. * Also may change the display name according to {@link #displayFormat}. * * @param s the new name */ public void setName (String s) { super.setName (s); MessageFormat mf = displayFormat; if (mf != null) { setDisplayName (mf.format (new Object[] { s })); } else { // additional hack, because if no display name is set, then it // is taken from the getName, that means calling setName can // also change display name // fix of 10665 fireDisplayNameChange (null, null); } } /** Change the icon. * One need only specify the base resource name; * the real name of the icon is obtained by the applying icon message * formats. * *

For example, for the base resource/MyIcon, the * following images may be used according to the icon state and * {@link java.beans.BeanInfo#getIcon presentation type}: * *

  • resource/MyIcon.gif
  • resource/MyIconOpen.gif *
  • resource/MyIcon32.gif
  • resource/MyIconOpen32.gif
* *

* This method may be used to dynamically switch between different sets * of icons for different configurations. If the set is changed, * an icon property change event is fired. * * @param base base resouce name (no initial slash) */ public void setIconBase (String base) { this.iconBase = base; fireIconChange (); fireOpenedIconChange (); } /** Find an icon for this node. Uses an {@link #setIconBase icon set}. * * @param type constants from {@link java.beans.BeanInfo} * * @return icon to use to represent the bean */ public Image getIcon (int type) { return findIcon (type, ICON_BASE); } /** Finds an icon for this node when opened. This icon should represent the node * only when it is opened (when it can have children). * * @param type as in {@link #getIcon} * @return icon to use to represent the bean when opened */ public Image getOpenedIcon (int type) { return findIcon (type, OPENED_ICON_BASE); } public HelpCtx getHelpCtx () { return HelpCtx.DEFAULT_HELP; } /** Tries to find the right icon for the iconbase. * @param type type of icon (from BeanInfo constants) * @param ib base where to scan in the array */ private Image findIcon (int type, int ib) { String res = iconBase + icons[type + ib]; Image im = Utilities.loadImage (res); if (im != null) return im; // try the first icon res = iconBase + icons[java.beans.BeanInfo.ICON_COLOR_16x16 + ib]; im = Utilities.loadImage (res); if (im != null) return im; if (ib == OPENED_ICON_BASE) { // try closed icon also return findIcon (type, ICON_BASE); } // if still not found return default icon return getDefaultIcon (); } Image getDefaultIcon () { Image i = Utilities.loadImage(DEFAULT_ICON); if (i == null) throw new MissingResourceException("No default icon", "", DEFAULT_ICON); // NOI18N return i; } /** Can this node be renamed? * @return false */ public boolean canRename () { return false; } /** Can this node be destroyed? * @return false */ public boolean canDestroy () { return false; } /** Set the set of properties. * A listener is attached to the provided sheet * and any change of the sheet is propagated to the node by * firing a {@link #PROP_PROPERTY_SETS} change event. * * @param s the sheet to use */ protected final synchronized void setSheet (Sheet s) { setSheetImpl(s); firePropertySetsChange (null, null); } private synchronized void setSheetImpl(Sheet s) { if (sheetCookieL == null) { sheetCookieL = new SheetAndCookieListener (); } if (sheet != null) { sheet.removePropertyChangeListener (sheetCookieL); } s.addPropertyChangeListener (sheetCookieL); sheet = s; } /** Initialize a default * property sheet; commonly overridden. If {@link #getSheet} * is called and there is not yet a sheet, * this method is called to allow a subclass * to specify its properties. *

* Warning: Do not call getSheet in this method. *

* The default implementation returns an empty sheet. * * @return the sheet with initialized values (never null) */ protected Sheet createSheet () { return new Sheet (); } /** Get the current property sheet. If the sheet has been * previously set by a call to {@link #setSheet}, that sheet * is returned. Otherwise {@link #createSheet} is called. * * @return the sheet (never null) */ protected final synchronized Sheet getSheet () { if (sheet != null) return sheet; setSheetImpl(createSheet()); return sheet; } /** Get a list of property sets. * * @return the property sets for this node * @see #getSheet */ public PropertySet[] getPropertySets () { Sheet s = getSheet (); return s.toArray (); } boolean propertySetsAreKnown() { return (sheet != null); } /** Copy this node to the clipboard. * * @return {@link org.openide.util.datatransfer.ExTransferable.Single} with one copy flavor * @throws IOException if it could not copy * @see NodeTransfer */ public Transferable clipboardCopy () throws IOException { return NodeTransfer.transferable (this, NodeTransfer.CLIPBOARD_COPY); } /** Cut this node to the clipboard. * * @return {@link org.openide.util.datatransfer.ExTransferable.Single} with one cut flavor * @throws IOException if it could not cut * @see NodeTransfer */ public Transferable clipboardCut () throws IOException { return NodeTransfer.transferable (this, NodeTransfer.CLIPBOARD_CUT); } /** * This implementation only calls clipboardCopy supposing that * copy to clipboard and copy by d'n'd are similar. * * @return transferable to represent this node during a drag * @exception IOException when the * cut cannot be performed */ public Transferable drag () throws IOException { return clipboardCopy (); } /** Can this node be copied? * @return true */ public boolean canCopy () { return true; } /** Can this node be cut? * @return false */ public boolean canCut () { return false; } /** Accumulate the paste types that this node can handle * for a given transferable. *

* The default implementation simply tests whether the transferable supports * intelligent pasting via {@link NodeTransfer#findPaste}, and if so, it obtains the paste types * from the {@link NodeTransfer.Paste transfer data} and inserts them into the set. *

Subclass implementations should typically call super (first or last) so that they * add to, rather than replace, a superclass's available paste types; especially as the * default implementation in AbstractNode is generally desirable to retain. * * @param t a transferable containing clipboard data * @param s a list of {@link PasteType}s that will have added to it all types * valid for this node (ordered as they will be presented to the user) */ protected void createPasteTypes (Transferable t, List s) { NodeTransfer.Paste p = NodeTransfer.findPaste (t); if (p != null) { // adds all its types into the set s.addAll (Arrays.asList (p.types (this))); } } /** Determine which paste operations are allowed when a given transferable is in the clipboard. * Subclasses should override {@link #createPasteTypes}. * * @param t the transferable in the clipboard * @return array of operations that are allowed */ public final PasteType[] getPasteTypes (Transferable t) { List s = new LinkedList (); createPasteTypes (t, s); return (PasteType[])s.toArray (NO_PASTE_TYPES); } /** Default implementation that tries to delegate the implementation * to the createPasteTypes method. Simply calls the method and * tries to take the first provided argument. Ignores the action * argument and index. * * @param t the transferable * @param action the drag'n'drop action to do DnDConstants.ACTION_MOVE, ACTION_COPY, ACTION_LINK * @param index index between children the drop occured at or -1 if not specified * @return null if the transferable cannot be accepted or the paste type * to execute when the drop occures */ public PasteType getDropType (Transferable t, int action, int index) { java.util.List s = new LinkedList (); createPasteTypes (t, s); return s.isEmpty () ? null : (PasteType)s.get (0); } /* List new types that can be created in this node. * @return new types */ public NewType[] getNewTypes () { return NO_NEW_TYPES; } private static final WeakHashMap overridesGetDefaultAction = new WeakHashMap (27); /** Checks whether subclass overrides a method */ private boolean overridesAMethod (String name, Class[] arguments) { // we are subclass of AbstractNode try { java.lang.reflect.Method m = getClass ().getMethod (name, arguments); if (m.getDeclaringClass () != AbstractNode.class) { // ok somebody overriden the method return true; } } catch (NoSuchMethodException ex) { org.openide.ErrorManager.getDefault().notify(ex); } return false; } /** Gets preferred action. * By default, null. * @return preferred action * @see Node#getPreferredAction * @since 3.29 */ public Action getPreferredAction() { boolean delegate = false; Class c = getClass (); if (c != AbstractNode.class) { synchronized (overridesGetDefaultAction) { Object in = overridesGetDefaultAction.get (c); if (in == this) { // catched in a loop of overriding getDefaultAction and // calling super.getDefaultAction // pretend that we do not override overridesGetDefaultAction.put (c, Boolean.FALSE); return preferredAction; } Boolean b; if (in == null) { b = overridesAMethod ("getDefaultAction", new Class[0] ) ? Boolean.TRUE : Boolean.FALSE; // NOI18N if (b.booleanValue()) { // check whether it is safe to call the getDefaultAction overridesGetDefaultAction.put (c, this); getDefaultAction (); if (overridesGetDefaultAction.get (c) == this) { // value unchanged, we have not been cought in a loop overridesGetDefaultAction.put (c, b); } } else { overridesGetDefaultAction.put (c, b); } } else { b = (Boolean)in; } delegate = b.booleanValue(); } } return delegate ? getDefaultAction () : preferredAction; } /** Gets the default action. Overrides superclass method. * @return if there is a default action set, then returns it * @deprecated Use {@link #getPreferredAction} instead. */ public SystemAction getDefaultAction () { Action a = getPreferredAction(); if(a instanceof SystemAction) { return (SystemAction)a; } return null; } /** Set a default action for the node. * @param action the new default action, or null for none * @deprecated Override {@link #getPreferredAction} instead. */ public void setDefaultAction (SystemAction action) { preferredAction = action; } /** Get all actions for the node. * Initialized with {@link #createActions}, or with the superclass's list. * * @return actions for the node * @deprecated Override {@link #getActions(boolean)} instead. */ public SystemAction[] getActions () { if (systemActions == null) { systemActions = createActions (); if (systemActions == null) { systemActions = super.getActions (); } } return systemActions; } /** Lazily initialize set of node's actions (overridable). * The default implementation returns null. *

Warning: do not call {@link #getActions} within this method. * @return array of actions for this node, or null to use the default node actions * @deprecated Override {@link #getActions(boolean)} instead. */ protected SystemAction[] createActions () { return null; } /** Does this node have a customizer? * @return false */ public boolean hasCustomizer () { return false; } /** Get the customizer. * @return null in the default implementation */ public java.awt.Component getCustomizer () { return null; } /** Set the cookie set. * A listener is attached to the provided cookie set, * and any change of the sheet is propagated to the node by * firing {@link #PROP_COOKIE} change events. * * @param s the cookie set to use * @deprecated just use getCookieSet().add(...) instead * @exception IllegalStateException If you pass a Lookup instance into the constructor, this * method cannot be called. */ protected final synchronized void setCookieSet (CookieSet s) { if (internalLookup (false) != null) { throw new IllegalStateException ("CookieSet cannot be used when lookup is associated with the node"); // NOI18N } if (sheetCookieL == null) { sheetCookieL = new SheetAndCookieListener (); } CookieSet cookieSet = (CookieSet)lookup; if (cookieSet != null) { cookieSet.removeChangeListener (sheetCookieL); } s.addChangeListener (sheetCookieL); lookup = s; fireCookieChange (); } /** Get the cookie set. * * @return the cookie set created by {@link #setCookieSet}, or an empty set (never null) * @exception IllegalStateException If you pass a Lookup instance into the constructor, this * method cannot be called. */ protected final CookieSet getCookieSet () { if (internalLookup (false) != null) { throw new IllegalStateException ("CookieSet cannot be used when lookup is associated with the node"); // NOI18N } CookieSet s = (CookieSet)lookup; if (s != null) return s; synchronized (this) { if (lookup != null) return (CookieSet)lookup; // sets empty sheet and adds a listener to it setCookieSet (new CookieSet ()); return (CookieSet)lookup; } } /** Get a cookie from the node. * Uses the cookie set as determined by {@link #getCookieSet}. * * @param type the representation class * @return the cookie or null */ public Node.Cookie getCookie (Class type) { if (lookup instanceof CookieSet) { CookieSet c = (CookieSet)lookup; return c.getCookie (type); } else { return super.getCookie (type); } } /** Get a serializable handle for this node. * @return a {@link DefaultHandle} in the default implementation */ public Handle getHandle () { return DefaultHandle.createHandle (this); } }

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.