|
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.netbeans.modules.xml.tree.nodes;
import java.awt.Image;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.io.StringReader;
import java.util.Arrays;
import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyChangeEvent;
import java.beans.IntrospectionException;
import java.beans.PropertyChangeListener;
import org.xml.sax.*; // debug
import org.openide.ErrorManager;
import org.openide.actions.*;
import org.openide.nodes.*;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataNode;
import org.openide.cookies.SaveCookie;
import org.openide.util.actions.SystemAction;
import org.openide.util.actions.NodeAction;
import org.openide.util.Utilities;
import org.openide.util.WeakListener;
import org.openide.util.datatransfer.ExTransferable;
import org.openide.util.datatransfer.TransferListener;
import org.netbeans.tax.*;
import org.netbeans.tax.io.XMLStringResult;
import org.netbeans.modules.xml.tree.lib.OrderedBeanNode;
import org.netbeans.modules.xml.tree.lib.BeanUtil;
import org.netbeans.modules.xml.tree.lib.StringUtil;
import org.netbeans.modules.xml.tree.lib.GuiUtil;
import org.openide.util.HelpCtx;
/**
*
* @author Libor Kramolis
* @version 0.1
*/
abstract class AbstractObjectNode extends OrderedBeanNode implements TreeObjectNode, DataNodeCookie {
/** */
public static final XMLDataFlavor XML_NODE_COPY_FLAVOR =
new XMLDataFlavor (AbstractObjectNode.class, "XML_NODE_COPY_FLAVOR"); // NOI18N
/** */
public static final XMLDataFlavor XML_NODE_CUT_FLAVOR =
new XMLDataFlavor (AbstractObjectNode.class, "XML_NODE_CUT_FLAVOR"); // NOI18N
/** */
protected static final String ICON_DIR_BASE = "org/netbeans/modules/xml/tree/resources/"; // NOI18N
/** */
private static final String NODE_TYPE_PREFIX_SEPARATOR = " : "; // NOI18N
/** Data Node */
private DataNode dataNode;
/** */
private final TreeListener treeListener;
/** */
private final DataObjectListener doListener;
/** */
private Image mergeIcon;
/** */
private short cutIconStack;
/** Cache system actions. */
private SystemAction[] systemActions;
// debug
protected static final ErrorManager clipboardEM = ErrorManager.getDefault().getInstance ("org.netbeans.modules.xml.tree.nodes:clipboard"); // NOI18N
//
// init
//
/**
* Creates new AbstractObjectNode with particular children.
*/
protected AbstractObjectNode (TreeObject treeObject, Children children, String iconName) throws IntrospectionException {
super (treeObject, children);
cutIconStack = 0;
treeListener = new TreeListener();
doListener = new DataObjectListener();
init (treeObject);
updateIconBase (iconName);
}
/**
* Creates new AbstractObjectNode with particular children.
*/
protected AbstractObjectNode (TreeObject treeObject, String iconName) throws IntrospectionException {
this (treeObject, Children.LEAF, iconName);
}
/**
* Attach weak property change listener and init.
*/
private void init (TreeObject peer) {
setSynchronizeName (false);
peer.addPropertyChangeListener (WeakListener.propertyChange (treeListener, peer));
updateNodeNames();
if ( hasCustomizer() ) {
setDefaultAction (SystemAction.get (CustomizeAction.class));
}
}
//
// from TreeObjectNode
//
/**
* @return related tree object.
*/
public final TreeObject getTreeObject () {
return (TreeObject) getBean();
}
//
// util
//
/** Is this node read only? This affect canCut, canDestroy, canRename actions.
*/
protected boolean isReadOnly () {
return getTreeObject().isReadOnly();
}
/**
*/
protected boolean canPaste () {
return !!! isReadOnly();
}
/**
*/
protected boolean canChangeOrder () {
return false;
}
/**
*/
protected boolean canMoveUpDown () {
return false;
}
//
// Customizer
//
/**
*/
public boolean hasCustomizer () {
return !!! isReadOnly();
}
//
// name support
//
/** Name of property used as node name. Used to listen on it.
*/
abstract protected String getPresentableNamePropertyName ();
/** Set new value to property which is used as node name.
*/
abstract protected void setPresentableNameProperty (String name) throws TreeException;
/**
*/
private void updatePresentableNameProperty () {
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::updatePresentableNameProperty: this.getName = " + getName());//, new RuntimeException()); // NOI18N
try {
setPresentableNameProperty (getName());
} catch (TreeException exc) {
updateNodeNames();
GuiUtil.notifyTreeException (exc);
}
}
/**
*/
private void updateNodeNames () {
setNameImpl (createName());
setDisplayName (createDisplayName());
updateShortDescription();
}
/**
*/
private void updateShortDescription () {
setShortDescription (createShortDescription());
}
/**
* @return node name prefix or null.
*/
protected String getNodeTypePrefix () {
return null;
}
/**
*/
protected boolean checkName (String name) {
return true;
}
/**
*/
private void setNameImpl (String name) {
super.setName (name);
updatePresentableNameProperty();
}
/**
*/
public final void setName (String name) {
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::setName: '" + name + "'"); // NOI18N
if ( checkName (name) ) {
setNameImpl (name);
}
}
/**
*/
abstract protected String createName ();
/**
*/
protected String createDisplayName () {
return StringUtil.printableValue (createName());
}
/**
*/
protected final String createShortDescription () {
StringBuffer sb = new StringBuffer();
if (getNodeTypePrefix() != null) {
sb.append (getNodeTypePrefix()).append (NODE_TYPE_PREFIX_SEPARATOR);
}
sb.append (createNodePreview());
return sb.toString();
}
/**
*/
protected String createNodePreview () {
return createDisplayName();
}
//
// icon
//
/**
*/
protected final void updateIconBase (String iconName) {
setIconBase (ICON_DIR_BASE + iconName);
}
/**
*/
protected final void setMergeIcon (Image img) {
this.mergeIcon = img;
fireIconChange ();
fireOpenedIconChange ();
}
/**
*/
public Image getIcon (int type) {
Image img = super.getIcon (type);
if ( isReadOnly() ) {
Image roBadge = Utilities.loadImage ("org/netbeans/modules/xml/tree/resources/ro-badge.gif"); // NOI18N
img = Utilities.mergeImages (img, roBadge, 16, 8);
}
if ( mergeIcon == null ) {
return img;
}
return Utilities.mergeImages (img, mergeIcon, 0, 0);
}
/**
*/
protected final void activateCutIcon () {
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::activateCutIcon: cutIconStack = " + cutIconStack + " ++"); // NOI18N
if ( cutIconStack == 0 ) {
setMergeIcon (GuiUtil.getCutBadgeIcon());
}
cutIconStack++;
}
/**
*/
protected final void deactivateCutIcon () {
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::DEactivateCutIcon: cutIconStack = " + cutIconStack + " --"); // NOI18N
cutIconStack--;
if ( cutIconStack == 0 ) {
setMergeIcon (null);
}
}
public HelpCtx getHelpCtx() {
return new HelpCtx (this.getClass());
}
//
// actions
//
/** Node impl can add its specific actions.
* @return node specific actions or null.
*/
protected SystemAction[] createNodeSpecificActions () {
return null;
}
/**
*/
protected final SystemAction[] createActions() {
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::createActions: this = " + this); // NOI18N
if ( systemActions == null ) {
List nodeActions = new LinkedList();
boolean separator = false;
// new actions
if ( (getNewTypes() != null) && (getNewTypes().length != 0) ) {
nodeActions.add (SystemAction.get (NewAction.class));
nodeActions.add (null);
}
// node specific actions
SystemAction[] nodeSpecificActions = createNodeSpecificActions();
if (nodeSpecificActions != null) {
nodeActions.addAll (Arrays.asList (nodeSpecificActions));
nodeActions.add (null);
}
// change order actions
if ( canChangeOrder() ) {
nodeActions.add (SystemAction.get (ReorderAction.class));
separator = true;
}
if ( canMoveUpDown() ) {
nodeActions.add (SystemAction.get (MoveUpAction.class));
nodeActions.add (SystemAction.get (MoveDownAction.class));
separator = true;
}
if ( separator ) {
nodeActions.add (null);
separator = false;
}
// clipboard actions
if ( canCut() ) {
nodeActions.add (SystemAction.get (CutAction.class));
separator = true;
}
if ( canCopy() ) {
nodeActions.add (SystemAction.get (CopyAction.class));
separator = true;
}
if ( ( !!! isLeaf() ) && canPaste() ) {
nodeActions.add (SystemAction.get (PasteAction.class));
separator = true;
}
if ( separator ) {
nodeActions.add (null);
separator = false;
}
// delete, rename actions
if ( canDestroy() ) {
nodeActions.add (SystemAction.get (DeleteAction.class));
separator = true;
}
if ( canRename() ) {
nodeActions.add (SystemAction.get (RenameAction.class));
separator = true;
}
if ( separator ) {
nodeActions.add (null);
separator = false;
}
// properties action
nodeActions.add (SystemAction.get (PropertiesAction.class));
//
if ( Util.THIS.isLoggable() ) {
nodeActions.add (SystemAction.get (ToolsAction.class));
nodeActions.add( new GotoAction() {
public void performAction(Node[] activatedNodes) {
Object peer = getBean();
Util.THIS.debug("Dumping " + AbstractObjectNode.this.getDisplayName() + " /" + AbstractObjectNode.this.getName() + "/"); // NOI18N
Util.THIS.debug("\tTree model listener " + treeListener); // NOI18N
Util.THIS.debug("\tThis node listener " + doListener); // NOI18N
Util.THIS.debug("\tRaw peer " + peer.getClass() + System.identityHashCode(peer)); // NOI18N
Util.THIS.debug("\tString peer " + peer.toString()); // NOI18N
if (peer instanceof TreeNode) {
Object parent = ((TreeNode)peer).getOwnerDocument();
if (parent == null) {
Util.THIS.debug("\tMISSING OWNER DOCUMENT!"); // NOI18N
Util.THIS.debug("\tTraversing parents:"); // NOI18N
for(TreeParentNode p = ((TreeChild)peer).getParentNode(); p != null; p = p.getParentNode() ) {
Util.THIS.debug("\t\tRaw parent " + p.getClass() + System.identityHashCode(p)); // NOI18N
}
} else {
Util.THIS.debug("\tRaw document " + parent.getClass() + System.identityHashCode(parent)); // NOI18N
Util.THIS.debug("\tDocument listeners: " + ((TreeObject)parent).listListeners()); // NOI18N
}
parent = ((TreeChild)peer).getParentNode();
if (parent == null) {
Util.THIS.debug("\tMISSING PARENT DOCUMENT!"); // NOI18N
} else {
Util.THIS.debug("\tRaw parent " + parent.getClass() + System.identityHashCode(parent)); // NOI18N
Util.THIS.debug("\tChildren: "); // NOI18N
for (int max = ((TreeParentNode)parent).getChildrenNumber(), i = 0; i is 'addPropertyChangeListener' public: " + java.lang.reflect.Modifier.isPublic (method.getModifiers())); // NOI18N
} catch (Exception exc) {
Util.THIS.debug (exc);
}
}
public boolean enable(Node[] nodes) {
return true;
}
});
}
//
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug (" ::createActions: nodeActions = " + nodeActions); // NOI18N
systemActions = (SystemAction[])nodeActions.toArray (new SystemAction[0]);
}
return systemActions;
}
//
// DataNode caching
//
/**
* Determine associated DataNode/Object at runtime.
* It would cache the DataNode.
* Cache results in dataNode field because of expensivity.
* @return first adjacent DataNode or null if can not be found
*/
public final DataNode getDataNode () {
String thisClassName = this.getClass().getName();
if (dataNode != null) {
return dataNode; // cache hit
}
// try to populate cache
Node parent = getParentNode();
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug (" [ " + thisClassName + " ] ::getDataNode: parent = " + parent); // NOI18N
if (parent != null) {
DataNodeCookie cake = (DataNodeCookie) parent.getCookie (DataNodeCookie.class);
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug (" [ " + thisClassName + " ] ::getDataNode: parent's DataNodeCookie = " + cake); // NOI18N
if (cake != null) {
setDataNode (cake.getDataNode());
}
}
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug (" [ " + thisClassName + " ] ::getDataNode: dataNode = " + dataNode); // NOI18N
return dataNode;
}
/**
*/
protected void setDataNode (DataNode dataNode) {
this.dataNode = dataNode;
addDataObjectListener();
}
//
// cookie
//
/**
*/
private void addDataObjectListener () {
if (dataNode != null) {
DataObject dataObject = dataNode.getDataObject();
dataObject.addPropertyChangeListener (WeakListener.propertyChange (doListener, dataObject));
}
}
protected void superFireCookieChange () {
super.fireCookieChange();
}
/**
* Delegate DataObjectCookies to DataNode.
* Add itself into cookie set as DataNodeCookie.
*/
public final Cookie getCookie (Class type) {
if (type == SaveCookie.class || DataObject.class == type) {
DataNode node = getDataNode();
if (node != null)
return node.getCookie (type);
}
// go over FilterNodes
if (DataNodeCookie.class == type) {
return this;
}
return super.getCookie (type);
}
//
// node operation
//
/**
*/
public boolean canDestroy () {
boolean canDestroy = (!!! isReadOnly());
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::canDestroy : " + canDestroy); // NOI18N
return canDestroy;
}
/**
*/
public void destroy () throws IOException {
try {
getTreeObject().removeFromContext();
// super.destroy(); //!!! -- why yes / why not
} catch (ReadOnlyException exc) {
GuiUtil.notifyTreeException (exc); //!!! -- what about declared IOException?
}
}
/**
*/
public boolean canRename () {
boolean canRename = (!!! isReadOnly());
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::canRename : " + canRename); // NOI18N
return canRename;
}
//
// BeanNode
//
/**
*/
protected void createProperties (Object bean, BeanInfo info) {
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::createProperties: bean = " + bean); // NOI18N
if ( bean instanceof TreeObject ) {
Node.Property[][] properties = BeanUtil.computeProperties ((TreeObject)bean, info);
Sheet sets = getSheet ();
Sheet.Set pset = Sheet.createPropertiesSet ();
pset.put (properties[0]);
sets.put (pset);
if (properties[1].length != 0) {
Sheet.Set eset = Sheet.createExpertSet ();
eset.put (properties[1]);
sets.put (eset);
}
} else {
super.createProperties (bean, info);
}
}
//
// Clipboard operations
//
/**
*/
public boolean canCopy () {
boolean canCopy = true;
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::canCopy : " + canCopy); // NOI18N
return canCopy;
}
/**
*/
public boolean canCut () {
boolean canCut = (!!! isReadOnly());
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::canCut : " + canCut); // NOI18N
return canCut;
}
/**
*/
public final Transferable clipboardCopy () throws IOException {
// debug
clipboardEM.log ("Clipboard copy: " + this.getDisplayName());
TreeObject clone = (TreeObject)getTreeObject().clone();
ExTransferable transferable = ExTransferable.create (new XMLTransferable (XML_NODE_COPY_FLAVOR, clone));
// debug
clipboardEM.log ("Copy transferable [ " + System.identityHashCode (transferable) + " ] = " + transferable);
return transferable;
}
/**
*/
public final Transferable clipboardCut () throws IOException {
// debug
clipboardEM.log ("Clipboard cut: " + this.getDisplayName());
activateCutIcon();
ExTransferable transferable = ExTransferable.create (new XMLTransferable (XML_NODE_CUT_FLAVOR, getTreeObject()));
// listen on ownershipLost and deactivate cut icon
transferable.addTransferListener (new TransferListener () {
public void accepted (int action) {
// debug
clipboardEM.log ("Clipboard accepted [" + action + "]: " + AbstractObjectNode.this.getDisplayName());
}
public void rejected () {
// debug
clipboardEM.log ("Clipboard rejected: " + AbstractObjectNode.this.getDisplayName());
}
public void ownershipLost () {
// debug
clipboardEM.log ("Clipboard ownershipLost: " + AbstractObjectNode.this.getDisplayName());
AbstractObjectNode.this.deactivateCutIcon();
}
});
// debug
clipboardEM.log ("Cut transferable [ " + System.identityHashCode (transferable) + " ] = " + transferable);
return transferable;
}
//
// class XMLDataFlavor
//
/**
*
*/
public static class XMLDataFlavor extends DataFlavor {
/** Serial Version UID */
private static final long serialVersionUID = 2137534426987675449L;
//
// init
//
/** */
XMLDataFlavor (Class representationClass, String name) {
super (representationClass, name);
}
//
// itself
//
/**
*/
public boolean equals (Object obj) {
if ( (obj instanceof XMLDataFlavor) == false )
return false;
XMLDataFlavor xmlDF = (XMLDataFlavor)obj;
return ( this.getHumanPresentableName().equals (xmlDF.getHumanPresentableName())
&& super.equals (obj) );
}
} // end: class XMLDataFlavor
//
// class XMLTransferable
//
/**
*
*/
static class XMLTransferable implements Transferable {
/** */
private TreeObject treeObject;
/** */
private XMLDataFlavor xmlFlavor;
//
// init
//
/** */
public XMLTransferable (XMLDataFlavor flavor, TreeObject treeObject) {
this.xmlFlavor = flavor;
this.treeObject = treeObject;
}
//
// from Transferable
//
/**
*/
public DataFlavor[] getTransferDataFlavors() {
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::XMLTransferable::getTransferDataFlavors: xmlFlavor = " + xmlFlavor); // NOI18N
return new DataFlavor[] { xmlFlavor, DataFlavor.stringFlavor }; // XMLDataFlavor + String flavor
}
/**
*/
public boolean isDataFlavorSupported (DataFlavor flavor) {
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::XMLTransferable::isDataFlavorSupported: flavor = " + flavor); // NOI18N
if ( DataFlavor.stringFlavor.equals (flavor) ) { // String flavor
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::XMLTransferable::isDataFlavorSupported: is stringFlavor"); // NOI18N
return true;
}
if (xmlFlavor.equals ((Object)flavor)) {
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::XMLTransferable::isDataFlavorSupported: is XMLDataFlavor"); // NOI18N
return true;
}
return false;
}
/**
*/
public Object getTransferData (DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if ( isDataFlavorSupported (flavor) ) {
if (DataFlavor.stringFlavor == flavor) { // String flavor
try {
if ( treeObject instanceof TreeNode ) {
return XMLStringResult.toString ((TreeNode)treeObject);
} else {
return ""; //??? -- what to do? // NOI18N
}
} catch (Exception exc) {
GuiUtil.notifyException (exc);
}
} else if (xmlFlavor == flavor) {//flavor instanceof XMLDataFlavor) {
return treeObject;
}
}
throw new UnsupportedFlavorException (flavor);
}
} // end: class XMLTransferable
//
// class TreeListener
//
/**
* class TreeListener listen on
*/
private class TreeListener implements PropertyChangeListener {
/** */
public void propertyChange (PropertyChangeEvent pche) {
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::TreeListener::propertyChange: propertyName = '" + pche.getPropertyName() + "'"); // NOI18N
if (pche.getPropertyName().equals (AbstractObjectNode.this.getPresentableNamePropertyName())) {
//[WARNING]: AbstractObjectNode.this.getPresentableNamePropertyName() can return null
AbstractObjectNode.this.updateNodeNames();
} else if (TreeNode.PROP_NODE.equals (pche.getPropertyName())) {
AbstractObjectNode.this.updateShortDescription();
}
}
} // end: class TreeListener
//
// class DataObjectListener
//
/**
* class DataObjectListener listen on
*/
private class DataObjectListener implements PropertyChangeListener {
/** */
public void propertyChange (PropertyChangeEvent pche) {
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("AbstractObjectNode::DataObjectListener::propertyChange: propertyName = '" + pche.getPropertyName() + "'"); // NOI18N
if ( Node.PROP_COOKIE.equals (pche.getPropertyName()) ) {
AbstractObjectNode.this.superFireCookieChange();
}
}
} // end: class DataObjectListener
}
|