📄 smartnode.java
字号:
package com.ca.directory.jxplorer.tree;
import com.ca.commons.cbutil.CBResourceLoader;
import com.ca.commons.cbutil.CBUtility;
import com.ca.commons.naming.*;
import com.ca.directory.jxplorer.JXplorer;
import javax.naming.NamingEnumeration;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.datatransfer.*;
import java.io.File;
import java.io.IOException;
import java.text.CollationKey;
import java.text.Collator;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/*
* SmartNode is a utility class for storing class/value/icon combinations, as
* well as the standard MutableTreeNode information from the swing.tree package.
* combinations. Takes arguments of form 'cn=Fred+sn=Nurk: ...'
* and parses them to:
* distinguishedValue = 'Fred+Nurk'
*
* The class also tries to work out an appropriate icon for displaying the node.
* It uses the objectClass attributes, when these are known, or a default icon
* keyed to the ldap RDN attribute (i.e. 'c','o','cn' etc.)
*
* WARNING: At present, this only works for <b>single</b> valued RDNs!
*/
// Mmmm... wonder if it's worth trying to include an explicit DN for every node,
// rather than generating it from the tree?
// ans. Yes it would be DAMN useful, but there might be some stress keeping them
// all updated when parent nodes changed... better to have them able to work it
// out dynamically...
public class SmartNode extends DefaultMutableTreeNode implements Transferable, Comparable
{
private static RDN emptyRDN = new RDN();
public RDN rdn = emptyRDN;
/*
* A misnomer - this is actually the naming attribute of the RDN.
*/
String nodeObjectClass = null;
boolean dummy = false;
boolean root = false;
boolean alwaysRefresh = false;
boolean blankRoot = false; // if root is blank, use ROOTNAME or blank depending on context
//boolean objectClassSet = false; // whether a true object class is known.
boolean structural = false; // whether this is just to fill the tree out, but doesn't represent an entry... (i.e. in a search tree response)
static Hashtable icons = new Hashtable(16); // the icons displayed in the tree
static boolean useIcons;
public static final String ROOTNAME = "World";
public static final String DUMMYMESSAGE = "reading...";
ImageIcon icon = null;
JPopupMenu menu = null; // an optional popup menu.
private static boolean initialised = false;
final public static DataFlavor UNICODETEXT = DataFlavor.getTextPlainUnicodeFlavor();
DataFlavor[] flavours = { UNICODETEXT };
// get a single platform specific language collator for use in sorting.
private static Collator myCollator = Collator.getInstance();
// the collation key of the node, used for sorting.
private CollationKey collationKey;
private static Logger log = Logger.getLogger(SmartNode.class.getName());
/**
* Pre load the image icons, we'll be using them a lot.
*
* Image icons are stored in the 'icons' hashtable, keyed on their
* file name stem. (i.e. person.gif is stored with key 'person')
*/
// XXX Rewrite to use resource loader as well...!
// XXX (and possibly only create icons when needed?)
static public void init(CBResourceLoader resourceLoader)
{
if (initialised) return; // we only need to run this method once, but
initialised = true; // it's not an error to call it multiple times.
String iconPath = JXplorer.getProperty("dir.icons");
if (iconPath == null)
{
useIcons = false;
return;
}
String[] extensions = {"jpg","gif","jpeg"};
String[] iconFiles = CBUtility.readFilteredDirectory(iconPath, extensions);
/*
* Emergency BackUp : If the icon directory is bad, try to
* find a working one, and if successfull, save it as new
* default value.
*/
if (iconFiles == null)
{
log.warning("can't find icon directory " + iconPath + " trying to find /icons directory");
iconPath = JXplorer.localDir + "icons" + File.separator;
iconFiles = CBUtility.readFilteredDirectory(iconPath, extensions);
if (iconFiles == null)
{
log.warning("Can't find icon directory; check 'dir.icons=' line in dxconfig.txt.");
return;
}
log.warning("Recovered! - iconPath reset to " + iconPath);
JXplorer.myProperties.setProperty("dir.icons", iconPath);
}
for (int i=0; i<iconFiles.length; i++)
{
String stem = iconFiles[i].substring(0,iconFiles[i].lastIndexOf('.'));
// save icon names *in lower case*
icons.put(stem.toLowerCase(), new ImageIcon(iconPath + iconFiles[i]));
}
// get any extra icons available in resource files...
try
{
String[] extraIcons = resourceLoader.getPrefixedResources("icons/");
for (int i=0; i<extraIcons.length; i++)
{
String iconName = extraIcons[i];
String stem = iconName.substring(6);
int endpos = stem.lastIndexOf('.');
if (stem.length() > 0 && endpos != -1)
{
// save icon names *in lower case*
stem = stem.substring(0,endpos).toLowerCase();
byte[] b = resourceLoader.getResource(iconName);
icons.put(stem, new ImageIcon(b));
}
}
}
catch (Exception e)
{
log.warning("Error trying to load icons from resource files: " + e);
}
useIcons = icons.containsKey("default"); // only use icons if we have a fallback default
}
/**
* Constructor for dummy nodes; used to flag possibly expandable nodes
* when their children status is unknown (due to not having been read
* from the directory yet.)
*/
public SmartNode()
{
super();
log.finer("created null SmartNode (I)");
dummy = true;
nodeObjectClass="default";
//distinguishedValue = "null";
}
/**
* Simple constructor, for when objectClass attributes are not known
* @param rdnString the relative distinguished name, e.g. 'cn=fnord'
*/
public SmartNode(String rdnString)
{
super();
log.finer("created SmartNode (II) :" + rdnString);
update(rdnString);
}
/**
* Simple constructor, for when objectClass attributes are not known
* @param rdn the relative distinguished name, e.g. 'cn=fnord'
*/
public SmartNode(RDN rdn)
{
super();
log.finer("created SmartNode (IIb) :" + rdn);
update(rdn);
}
/**
* Copy constructor, for when an RDN is the same, but the tree position
* (and hence the full DN) is different. <b>Does Not</b> makes copies of children:
* use copyChildren() separately if you want this.
*
* @param S the node to copy for initial values.
*/
public SmartNode(SmartNode S)
{
super();
log.finer("created SmartNode (III) :" + S.toString());
//distinguishedValue = new String(S.distinguishedValue);
nodeObjectClass = new String(S.nodeObjectClass);
icon = S.icon;
update(S.getRDN());
dummy = S.dummy;
}
/**
* When objectClass attributes are known, we try to be cleverer getting
* the icon for this node.
*
* @param RDN the RDN of the new node (e.g. 'cn=fnord')
* @param objectClasses a javax.naming.directory Attribute containing
* a list of the node's ldap objectClasses.
*/
// XXX how to choose between conflicting object classes? At the moment
// XXX this picks up the first one that it can match an icon to...
public SmartNode(String RDN, DXAttribute objectClasses)
{
super();
log.finer("created SmartNode (IV) :" + RDN);
update(RDN);
setTrueObjectClass(objectClasses);
}
/**
* If available, it is best to use the object class of the
* entry (to determine which icon to display 'n stuff). This
* does a quick 'n dirty search if the object class attribute
* is known. Better is to manually set the deepest object class,
* but this requires knowledge of the schema 'n stuff...
*/
public void setTrueObjectClass(DXAttribute objectClasses)
{
try
{
NamingEnumeration obClasses = objectClasses.getAll();
while (obClasses.hasMoreElements()) // use non-error-checking form
{
String value = obClasses.nextElement().toString();
if (setTrueObjectClass(value))
break;
}
}
catch (javax.naming.NamingException e)
{
log.log(Level.WARNING, "Naming Exception parsing " + rdn +"\n", e);
}
}
/**
* This attempts to set the object class of the node to a particular
* value. It returns true if an icon is available for that object
* class, false otherwise.
*/
public boolean setTrueObjectClass(String value)
{
value = value.toLowerCase();
if (icons.containsKey(value))
{
nodeObjectClass = value;
icon = (ImageIcon) icons.get(nodeObjectClass);
return true;
}
else
return false;
}
/**
* Takes an ldap RDN string such as 'ou=DemoCorp', and breaks it into
* a nodeObjectClass ('ou') and a distinguished value ('DemoCorp'),
* and replaces the existing values of these variables.
*
* @param rdn the new RDN to replace the nodes current value.
*/
public void update(String rdn)
{
try
{
update(new RDN(rdn));
}
catch (Exception e)
{
log.warning("unexpected error in SmartNode:update() " + e.toString());
e.printStackTrace();
} // should never throw an exception...
}
public void update(RDN newRDN)
{
if (newRDN==null)
setRdn(emptyRDN);
else
setRdn(newRDN);
if (rdn.isEmpty()) // probably a root entry
{
nodeObjectClass="default";
}
if (nodeObjectClass == null) // in extremis, grab the ldap rdn attribute and use that for icons...
nodeObjectClass = rdn.getAtt(0); // use root 'cause DN has only one element...
// create a collation key for fast language-sensitive sorting...
// (note toLowerCase() for case insensitive sorting - remove for case sensitive...
// XXX this is where features such as sorting by object class, or sorting by naming attribute, can be
// XXX put...
// 'false' is the default
boolean sortByNamingAttribute = ("true".equals(JXplorer.getProperty("sort.by.naming.attribute")));
if (rdn.isMultiValued())
{
StringBuffer key = new StringBuffer(rdn.toString().length());
for (int i=0; i<rdn.size(); i++)
{
if (sortByNamingAttribute)
key.append(rdn.getRawVal(i)).append(rdn.getAtt(i));
else
key.append(rdn.getAtt(i)).append(rdn.getRawVal(i));
}
collationKey = myCollator.getCollationKey(key.toString().toLowerCase());
}
else
{
if (sortByNamingAttribute)
collationKey = myCollator.getCollationKey(nodeObjectClass + getDistinguishedValue().toLowerCase());
else
collationKey = myCollator.getCollationKey(getDistinguishedValue().toLowerCase() + nodeObjectClass);
}
}
/**
* A utility ftn that makes copies of all the child nodes
* given to it, and adds the copies to the current node.
* @param children an enumeration of SmartNode(s), to copy
* and add.
*/
public void copyChildren(Enumeration children)
{
while (children.hasMoreElements())
{
SmartNode A = new SmartNode((SmartNode)children.nextElement());
add(A);
}
}
/**
* Returns the RDN of the tree node as a String.
* @return the RDN as a string.
*/
public RDN getRDN() {
return (blankRoot==true)?emptyRDN:rdn; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -