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.explorer.view;

import java.awt.Image;
import java.beans.BeanInfo;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.*;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.SwingUtilities;
import javax.swing.event.EventListenerList;
import javax.swing.tree.TreeNode;

import org.openide.ErrorManager;
import org.openide.nodes.*;
import org.openide.util.Mutex;
import org.openide.util.Utilities;

/** Visual representation of one node. Holds necessary information about nodes
* like icon, name, description and also list of its children. 
* 

* There is at most one VisualizerNode for one node. All of them are held in a cache. *

* VisualizerNode provides a thread-safe layer between Nodes, which may fire * property changes on any thread, and the AWT dispatch thread * thread. * * @author Jaroslav Tulach */ final class VisualizerNode extends EventListenerList implements NodeListener, TreeNode, Runnable { /** one template to use for searching for visualizers */ private static final VisualizerNode TEMPLATE = new VisualizerNode (0); /** constant holding empty reference to children */ private static final Reference NO_REF = new WeakReference (null); /** cache of visializers (VisualizerNode, Reference (VisualizerNode)) */ private static WeakHashMap cache = new WeakHashMap (); /** empty visualizer */ public static final VisualizerNode EMPTY = getVisualizer (null, Node.EMPTY); /** queue processor to transfer requests to event queue */ private static final QP QUEUE = new QP (); private static final ErrorManager err = ErrorManager.getDefault().getInstance("org.openide.explorer.view.VisualizerNode"); // NOI18N /** Cached icon - pre-html, there was a separate cache in NodeRenderer, but * if we're keeping a weak cache of VisualizerNodes, there's no reason not * to keep it here */ private Icon icon = null; // bugfix #29435, getVisualizer is synchronized in place of be called only from EventQueue /** Finds VisualizerNode for given node. * @param ch the children this visualizer should belong to * @param n the node * @return the visualizer */ public static synchronized VisualizerNode getVisualizer (VisualizerChildren ch, Node n) { TEMPLATE.hashCode = System.identityHashCode (n); TEMPLATE.node = n; Reference r = (Reference)cache.get (TEMPLATE); TEMPLATE.hashCode = 0; TEMPLATE.node = null; VisualizerNode v = r == null ? null : (VisualizerNode)r.get (); if (v == null) { v = new VisualizerNode (n); cache.put (v, new WeakReference (v)); } if (ch != null) { v.parent = ch; } return v; } /** node. Do not modify!!! */ Node node; /** system hashcode of the node */ private int hashCode; /** visualizer children attached thru weak references Reference (VisualizerChildren) */ private Reference children = NO_REF; /** the VisualizerChildren that contains this VisualizerNode or null */ private VisualizerChildren parent; /** cached name */ private String name; /** cached display name */ private String displayName; private static final String UNKNOWN = new String(); /** cached short description */ private String shortDescription; static final long serialVersionUID =3726728244698316872L; /** Constructor that creates template for the node. */ private VisualizerNode (int hashCode) { this.hashCode = hashCode; this.node = null; } /** Creates new VisualizerNode * @param n node to refer to */ private VisualizerNode(Node n) { node = n; hashCode = System.identityHashCode (node); // attach as a listener node.addNodeListener (NodeOp.weakNodeListener (this, node)); // uiListener = WeakListener.propertyChange (this, null); // UIManager.addPropertyChangeListener (uiListener); name = UNKNOWN; displayName = UNKNOWN; shortDescription = UNKNOWN; } /** Returns cached short description. * @return short description of represented node */ public String getShortDescription() { String desc = shortDescription; if( desc == UNKNOWN ) { shortDescription = desc = node.getShortDescription (); } return desc; } /** Returns cached display name. * @return display name of represented node */ public String getDisplayName () { if (displayName == UNKNOWN) { displayName = node == null ? null : node.getDisplayName (); } return displayName; } /** Returns cached name. * @return name of represented node */ public String getName () { if (name == UNKNOWN) { name = node == null ? null : node.getName (); } return name; } /** Getter for list of children of this visualizer. * @return list of VisualizerNode objects */ public List getChildren () { VisualizerChildren ch = (VisualizerChildren)children.get (); if (ch == null && !node.isLeaf ()) { // initialize the nodes children before we enter // the readAccess section Node[] tmpInit = node.getChildren ().getNodes (); // go into lock to ensure that no childrenAdded, childrenRemoved, // childrenReordered notifications occures and that is why we do // not loose any changes ch = (VisualizerChildren)Children.MUTEX.readAccess (new Mutex.Action () { public Object run () { Node[] nodes = node.getChildren ().getNodes (); VisualizerChildren vc = new VisualizerChildren ( VisualizerNode.this, nodes ); notifyVisualizerChildrenChange (nodes.length, vc); return vc; } }); } assert ch == null || !ch.list.contains(null) : ch.list + " from " + node; return ch == null ? Collections.EMPTY_LIST : ch.list; } // // TreeNode interface (callable only from AWT-Event-Queue) // public int getIndex(final javax.swing.tree.TreeNode p1) { return getChildren ().indexOf (p1); } public boolean getAllowsChildren() { return !isLeaf (); } public javax.swing.tree.TreeNode getChildAt(int p1) { List ch = getChildren(); VisualizerNode vn = (VisualizerNode)ch.get (p1); assert vn != null : "Null child in " + ch + " from " + node; return vn; } public int getChildCount() { return getChildren ().size (); } public java.util.Enumeration children() { List l = getChildren(); assert !l.contains(null) : "Null child in " + l + " from " + node; return java.util.Collections.enumeration(l); } public boolean isLeaf() { return node.isLeaf (); } public javax.swing.tree.TreeNode getParent() { Node parent = node.getParentNode (); return parent == null ? null : getVisualizer (null, parent); } // ********************************************** // Can be called under Children.MUTEX.writeAccess // ********************************************** /** Fired when a set of new children is added. * @param ev event describing the action */ public void childrenAdded(NodeMemberEvent ev) { VisualizerChildren ch = (VisualizerChildren)children.get (); if (ch == null) return; QUEUE.runSafe (new VisualizerEvent.Added ( ch, ev.getDelta (), ev.getDeltaIndices () )); } /** Fired when a set of children is removed. * @param ev event describing the action */ public void childrenRemoved(NodeMemberEvent ev) { VisualizerChildren ch = (VisualizerChildren)children.get (); if (ch == null) return; QUEUE.runSafe (new VisualizerEvent.Removed (ch, ev.getDelta ()) ); } /** Fired when the order of children is changed. * @param ev event describing the change */ public void childrenReordered(NodeReorderEvent ev) { doChildrenReordered (ev.getPermutation ()); } // helper method (called from TreeTableView.sort) void doChildrenReordered (int[] perm) { VisualizerChildren ch = (VisualizerChildren)children.get (); if (ch == null) return; QUEUE.runSafe (new VisualizerEvent.Reordered (ch, perm)); } void reorderChildren (Comparator c) { assert SwingUtilities.isEventDispatchThread(); VisualizerChildren ch = (VisualizerChildren)children.get (); if (ch == null) { return; } new VisualizerEvent.Reordered (ch, c).run(); } /** Fired when the node is deleted. * @param ev event describing the node */ public void nodeDestroyed(NodeEvent ev) { // ignore for now } /** Change in the node properties (icon, etc.) */ public void propertyChange(final java.beans.PropertyChangeEvent evt) { String name = evt.getPropertyName (); boolean isIconChange = Node.PROP_ICON.equals(name) || Node.PROP_OPENED_ICON.equals(name); if ( Node.PROP_NAME.equals (name) || Node.PROP_DISPLAY_NAME.equals (name) || isIconChange ) { if (isIconChange) { //Ditch the cached icon type so the next call to getIcon() will //recreate the ImageIcon cachedIconType = -1; } if (Node.PROP_DISPLAY_NAME.equals(name)) { htmlDisplayName = null; } SwingUtilities.invokeLater (this); return; } // bugfix #37748, VisualizerNode ignores change of short desc if it is not read yet (set to UNKNOWN) if ( Node.PROP_SHORT_DESCRIPTION.equals (name) && shortDescription != UNKNOWN ) { SwingUtilities.invokeLater (this); return; } if ( Node.PROP_LEAF.equals( name ) ) { SwingUtilities.invokeLater( new Runnable() { public void run() { children = NO_REF; // notify models VisualizerNode parent = VisualizerNode.this; while (parent != null) { Object[] listeners = parent.getListenerList (); for (int i = listeners.length - 1; i >= 0; i -= 2) { ((NodeModel)listeners[i]).structuralChange (VisualizerNode.this); } parent = (VisualizerNode)parent.getParent (); } } } ); } } /** Update the state of this class by retrieving new name, etc. * And fire change to all listeners. Only by AWT-Event-Queue */ public void run () { name = node.getName (); displayName = node.getDisplayName (); shortDescription = UNKNOWN; // // notify models // VisualizerNode parent = this; while (parent != null) { Object[] listeners = parent.getListenerList (); for (int i = listeners.length - 1; i >= 0; i -= 2) { ((NodeModel)listeners[i]).update (this); } parent = (VisualizerNode)parent.getParent (); } } // // Access to VisualizerChildren // /** Notifies change in the amount of children. This is used to distinguish between * weak and hard reference. Called from VisualizerChildren * @param size amount of children * @param ch the children */ void notifyVisualizerChildrenChange (int size, VisualizerChildren ch) { if (size == 0) { // hold the children hard children = new StrongReference (ch); } else { children = new WeakReference (ch); } } // ******************************** // This can be called from anywhere // ******************************** /** Adds visualizer listener. */ public synchronized void addNodeModel (NodeModel l) { add (NodeModel.class, l); } /** Removes visualizer listener. */ public synchronized void removeNodeModel (NodeModel l) { remove (NodeModel.class, l); } /** Hash code */ public int hashCode () { return hashCode; } /** Equals two objects are equal if they have the same hash code */ public boolean equals (Object o) { if (!(o instanceof VisualizerNode)) return false; VisualizerNode v = (VisualizerNode)o; return v.node == node; } /** String name is taken from the node. */ public String toString () { return getDisplayName (); } private static final String NO_HTML_DISPLAYNAME="noHtmlDisplayName"; //NOI18N private String htmlDisplayName = null; public String getHtmlDisplayName() { if (htmlDisplayName == null) { htmlDisplayName = node.getHtmlDisplayName(); if (htmlDisplayName == null) { htmlDisplayName = NO_HTML_DISPLAYNAME; } } return htmlDisplayName == NO_HTML_DISPLAYNAME ? null : htmlDisplayName; } Icon getIcon(boolean opened, boolean large) { int newCacheType = getCacheType(opened, large); if (cachedIconType != newCacheType) { int iconType = large ? BeanInfo.ICON_COLOR_32x32 : BeanInfo.ICON_COLOR_16x16; Image image = opened ? node.getOpenedIcon( iconType) : node.getIcon( iconType); // bugfix #28515, check if getIcon contract isn't broken if (image == null) { String method = opened ? "getOpenedIcon" : "getIcon"; // NOI18N ErrorManager.getDefault ().log (ErrorManager.WARNING, "Node \"" + node.getName () + "\" [" + node.getClass().getName()+ "] cannot return null from " + method + "(). See Node." + method + " contract."); // NOI18N icon = getDefaultIcon (); } else { icon = new ImageIcon (image); } } cachedIconType = newCacheType; return icon; } /** Some simple bitmasking to determine the type of the cached icon. * Generally, it's worth caching one, but not a bunch - generally one will * be used repeatedly. */ private static final int getCacheType (boolean opened, boolean large) { return (opened ? 2 : 0) | (large ? 1 : 0); } /** loaded default icon */ private static Icon defaultIcon; private int cachedIconType = -1; /** default icon to use when none is present */ private static final String DEFAULT_ICON = "org/openide/resources/defaultNode.gif"; // NOI18N /** Loads default icon if not loaded. */ private static Icon getDefaultIcon () { if (defaultIcon == null) { defaultIcon = new ImageIcon(Utilities.loadImage(DEFAULT_ICON)); } return defaultIcon; } /** Strong reference. */ private static final class StrongReference extends WeakReference { private Object o; public StrongReference (Object o) { super (null); this.o = o; } public Object get () { return o; } } /** Class that processes runnables in event queue. It guarantees that * the order of processed objects will be exactly the same as they * arrived. */ private static final class QP extends Object implements Runnable { QP() {} /** queue of all requests (Runnable) that should be processed * AWT-Event queue. */ private LinkedList queue = null; /** Runs the runnable in event thread. * @param run what should run */ public void runSafe (Runnable run) { boolean isNew = false; synchronized (this) { // access to queue variable is synchronized if (queue == null) { queue = new LinkedList (); isNew = true; } queue.add (run); } if (isNew) { // either starts the processing of the queue immediatelly // (if we are in AWT-Event thread) or uses // SwingUtilities.invokeLater to do so Mutex.EVENT.writeAccess (this); } } /** Processes the queue. */ public void run () { Enumeration en; synchronized (this) { // access to queue variable is synchronized en = Collections.enumeration (queue); queue = null; } while (en.hasMoreElements ()) { Runnable r = (Runnable)en.nextElement (); r.run (); } } } }

... 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.