/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2003, by :
* Corporate:
* Astrium SAS
* EADS CRC
* Individual:
* Nicolas Brodu
*
* $Id: SourceTree.java,v 1.13 2008/10/03 12:23:43 ogor Exp $
*
* Changes
* -------
* 25-Sep-2003 : Initial public release (NB);
*
*/
package simtools.ui;
import java.awt.Cursor;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;
import java.util.regex.Pattern;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.TransferHandler;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import simtools.data.DataInfo;
import simtools.data.DataSource;
import simtools.data.DataSourceCollection;
import simtools.data.DataSourcePool;
import simtools.data.DataSourcePoolEvent;
import simtools.data.DataSourcePoolListener;
/**
* A tree specialized in displaying and filtering data sources.
*
* Uses and listens to the data source pool of JSynoptic.
*
* @author Nicolas Brodu
*
* @version 1.0 2001
*/
public class SourceTree extends JTree implements DataSourcePoolListener, ActionListener {
protected DataSourcePool pool;
protected DefaultNode rootNode;
protected DefaultTreeModel treeModel;
protected JMenuItem sourceInfo;
protected JMenuItem sourceDelete;
protected double actionX, actionY;
protected FilterPattern filterPattern;
protected String currentFilterValue = "";
protected static HashMap sourceTreePool=new HashMap();
protected static Class sourceTreeClass=SourceTree.class;
/**
* A pool of source tree panels to reduce memory foot print
* Keep in mind that each JComponent can be displayed only one time
* @param id an identifier to define the reuse or not from the pool
* @return the source tree
*/
public static SourceTree getFromPool(String id){
if(sourceTreePool.get(id)==null){
try {
SourceTree res=(SourceTree)sourceTreeClass.newInstance();
sourceTreePool.put(id,res);
} catch (InstantiationException e) {
throw new RuntimeException("SourceTree.getFromPool "+e);
} catch (IllegalAccessException e) {
throw new RuntimeException("SourceTree.getFromPool "+e);
}
}
// reset the tree
SourceTree ret=(SourceTree)sourceTreePool.get(id);
if(ret.getVisibleRowCount()!=8) {
ret.setVisibleRowCount(8);
}
ret.setSelectedValue(null);
ret.setEnabled(true);
return ret;
}
public SourceTree(){
this(DataSourcePool.global);
}
public SourceTree(DataSourcePool pool) {
super();
rootNode = new DefaultNode();
treeModel = new DefaultTreeModel(rootNode);
setModel(treeModel);
// We don't want to see the root node, it is here to hold the other
// interesting ones
setRootVisible(false);
setShowsRootHandles(true);
this.pool = pool;
pool.addListener(this);
//filter
filterPattern = new SourceFilterPattern();
for (Iterator it = pool.dataSources().iterator(); it.hasNext(); ) {
addSource((DataSource)it.next());
}
for (Iterator it = pool.dataSourceCollections().iterator(); it.hasNext(); ) {
addCollection((DataSourceCollection)it.next());
}
treeModel.reload();
setTransferHandler(new SourceTransferHandler());
setDragEnabled(true);
setCellRenderer(createRenderer());
MouseListener ml = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
JComponent c = (JComponent)e.getSource();
if (c != SourceTree.this) return;
TreePath path = getPathForLocation(e.getX(), e.getY());
if ((e.getModifiers() & MouseEvent.BUTTON3_MASK) == MouseEvent.BUTTON3_MASK) {
if (path!=null) setSelectionPath(path);
boolean locked = false;
if (getSelectionPath()!=null) {
Object node = getSelectionPath().getLastPathComponent();
if (node instanceof SourceNode) {
if (((SourceNode)node).isLocked()) {
locked = true;
}
}
}
if (!locked) doPopup(e.getX(), e.getY());
return;
}
}
public void mouseExited(MouseEvent e) {
setCursor(Cursor.getDefaultCursor());
}
};
addMouseListener(ml);
MouseMotionListener mml = new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
JComponent c = (JComponent)e.getSource();
if (c != SourceTree.this) return;
TreePath path = getPathForLocation(e.getX(), e.getY());
if (path!=null) {
Object node = path.getLastPathComponent();
if (node instanceof SourceNode) {
if (((SourceNode)node).isLocked()) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
return;
}
}
}
setCursor(Cursor.getDefaultCursor());
}
};
addMouseMotionListener(mml);
registerKeyboardAction(
this,
"d",
KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0),
WHEN_FOCUSED
);
registerKeyboardAction(
this,
"d",
KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE,0),
WHEN_FOCUSED
);
}
/**
* Select the given user object in the tree, if it exists. Default SourceTree implementation
* only contains data source and collections, but any object is accepted (nothing will
* happen if it is not found).
*/
public void setSelectedValue(Object o) {
if (o==null) {
setSelectionPath(null);
return;
}
for (Enumeration e = rootNode.depthFirstEnumeration(); e.hasMoreElements();) {
Object elt = e.nextElement();
if (!(elt instanceof DefaultMutableTreeNode)) continue;
DefaultMutableTreeNode node = (DefaultMutableTreeNode)elt;
Object u = node.getUserObject();
if ((u!=null) && (u.equals(o))) {
TreePath path = new TreePath(node.getPath());
scrollPathToVisible(path);
setSelectionPath(path);
return;
}
}
}
/**
* Select the given source or collection in the tree, if it exists, using its id.
*/
public void setSelectedValueById(String id) {
if (id==null) return;
for (Enumeration e = rootNode.depthFirstEnumeration(); e.hasMoreElements();) {
Object elt = e.nextElement();
if (!(elt instanceof DefaultMutableTreeNode)) continue;
DefaultMutableTreeNode node = (DefaultMutableTreeNode)elt;
if (id.equals(DataInfo.getId(node.getUserObject()))) {
TreePath path = new TreePath(node.getPath());
scrollPathToVisible(path);
setSelectionPath(path);
return;
}
}
}
/**
* Runs through the tree to lock or unlock Nodes
*/
protected void recursiveLock(DefaultMutableTreeNode n, Object o, boolean state) {
if (n==null) return;
if ((n.getUserObject()==o) && (n instanceof SourceNode)) {
((SourceNode)n).setLocked(state);
return;
}
if (!n.getAllowsChildren()) return;
Enumeration e = n.children();
if (e==null) return;
while (e.hasMoreElements())
recursiveLock((DefaultMutableTreeNode)e.nextElement(), o, state);
}
/**
* Locks a Data Source or Data Source Collection
* The lock can be removed with the unlock function
* A locked Data Source or Collection cannot be used
* @param o The data source or data source collection to lock
*/
public void lock(Object o) {
recursiveLock(rootNode, o, true);
repaint();
}
/**
* Unlocks a Data Source or Data Source Collection
* @param o The data source or data source collection to lock
*/
public void unlock(Object o) {
recursiveLock(rootNode, o, false);
repaint();
}
/**
* Checks if a Data Source or Data Source Collection is locked
* @param o The data source or data source collection to look at
*/
public boolean isLocked(Object o) {
return recursiveIsLocked(rootNode, o);
}
/**
* Runs through the tree to check if a source or collection is lock or unlocked
*/
protected boolean recursiveIsLocked(DefaultMutableTreeNode n, Object o) {
if (n==null) return false;
if ((n.getUserObject()==o) && (n instanceof SourceNode)) {
return ((SourceNode)n).isLocked();
}
if (!n.getAllowsChildren()) return false;
Enumeration e = n.children();
if (e==null) return false;
while (e.hasMoreElements())
if (recursiveIsLocked((DefaultMutableTreeNode)e.nextElement(), o))
return true;
return false;
}
/** For subclasses to specialize. Does nothing by default. */
protected void doPopup(int x, int y) {
}
/**
* Iterates over the root node children and removes those with the
* specified user object
*/
protected void removeNodesWithUserObject(Object o) {
// Reset filter
setFilterValidity(false);
// Don't remove the nodes while iterating, safer to do it after?
// May be useless, a pity I'm too lazy to investigate...
Vector nodes = new Vector();
for (Enumeration e = rootNode.children(); e.hasMoreElements();) {
DefaultMutableTreeNode child = (DefaultMutableTreeNode)e.nextElement();
if (child.getUserObject()==o) nodes.add(child);
}
for (Iterator it = nodes.iterator(); it.hasNext();)
rootNode.remove((MutableTreeNode)it.next());
setFilterValidity(true);
}
protected void addSource(DataSource ds) {
// Unlike rootNode.add, this handles tree events for tree listeners
setFilterValidity(false);
if(ds.isCompound()){
treeModel.insertNodeInto(
new CompoundDataSourceNode(ds, ""),
rootNode,
rootNode.getChildCount()
);
} else{
treeModel.insertNodeInto(
new DataSourceNode(ds, ""),
rootNode,
rootNode.getChildCount()
);
}
setFilterValidity(true);
}
protected void addCollection(DataSourceCollection dsc) {
// Unlike rootNode.add, this handles tree events for tree listeners
setFilterValidity(false);
if(dsc.isCompound()){
treeModel.insertNodeInto(
new CompoundCollectionNode(dsc, ""),
rootNode,
rootNode.getChildCount()
);
}
else {
treeModel.insertNodeInto(
new CollectionNode(dsc, ""),
rootNode,
rootNode.getChildCount()
);
}
setFilterValidity(true);
}
protected void changeSource(DataSource ds, DataSource old) {
for (Enumeration e = rootNode.children(); e.hasMoreElements();) {
DefaultMutableTreeNode child = (DefaultMutableTreeNode)e.nextElement();
if (child.getUserObject()==old) ((DataSourceNode)child).changeSource(ds);
}
}
protected void changeCollection(DataSourceCollection dsc, DataSourceCollection old) {
for (Enumeration e = rootNode.children(); e.hasMoreElements();) {
DefaultMutableTreeNode child = (DefaultMutableTreeNode)e.nextElement();
if (child.getUserObject()==old) ((CollectionNode)child).changeCollection(dsc);
}
}
/**
* Return true if the selected values are child of the root node
* @return true if the selected values are child of the root node
*/
protected boolean selectionIsRootChild(){
boolean res = false;
TreePath[] paths = getSelectionPaths();
if (paths.length > 0){
res = true;
for(int i=0;i<paths.length && res; i++){
TreeNode node = (TreeNode)paths[i].getLastPathComponent();
res &= rootNode.isNodeChild(node);
}
}
return res;
}
public Object getSelectedSourceOrCollection() {
if (getSelectionPath()==null) return null;
Object node = getSelectionPath().getLastPathComponent();
if (node==null) return null;
if (node instanceof DataSourceNode) {
return ((DataSourceNode)node).getUserObject();
}
if (node instanceof CollectionNode) {
return ((CollectionNode)node).getUserObject();
}
return null;
}
// DataSourcePoolListener interface
public void dataSourcePoolNotification(DataSourcePoolEvent e) {
switch (e.action) {
case DataSourcePoolEvent.ADD_SOURCE:
addSource(e.source);
break;
case DataSourcePoolEvent.ADD_COLLECTION:
addCollection(e.collection);
break;
case DataSourcePoolEvent.REMOVE_SOURCE:
removeNodesWithUserObject(e.source);
break;
case DataSourcePoolEvent.REMOVE_COLLECTION:
removeNodesWithUserObject(e.collection);
break;
case DataSourcePoolEvent.CHANGE_SOURCE:
changeSource(e.source, e.oldSource);
break;
case DataSourcePoolEvent.CHANGE_COLLECTION:
changeCollection(e.collection, e.oldCollection);
break;
}
reload(true);
}
/**
*
* Re-assign tree nodes visibility regarding the current filter
* and notify all tree listeners that the model has changed.
*
* @param reloadAllNodes - if true all tree node visibility are updated, otherwise update only
* the visibility of visible nodes
*/
public void reload(boolean reloadAllNodes) {
// Keep selection
TreePath sel = this.getSelectionPath();
// Update all node visibility
if (reloadAllNodes){
setTreeNodeVisibility(rootNode, true);
}
updateTreeNodeVisibility(rootNode);
// reload model
treeModel.reload();
// restore selection, if still present
setSelectionPath(sel);
}
protected void setTreeNodeVisibility(DefaultNode node, boolean isVisible){
node.setIsVisible(isVisible);
Enumeration e = node.children();
while (e.hasMoreElements()) {
SourceNode childNode = (SourceNode) e.nextElement();
setTreeNodeVisibility(childNode, isVisible);
}
}
protected boolean updateTreeNodeVisibility(DefaultNode node){
boolean res = false;
if (node.isVisible){ // if node not visible do not change its visibility
res = filterPattern.acceptAll() || filterPattern.matches(node.getNodePath());
Enumeration e = node.children();
while (e.hasMoreElements()) {
SourceNode childNode = (SourceNode) e.nextElement();
res |= updateTreeNodeVisibility(childNode); // node is visible if at least one sub node is visible
}
// Set node visibility
node.setIsVisible(res);
}
return res;
}
public void removeAllTreeSelectionListeners(){
TreeSelectionListener[] sl=getTreeSelectionListeners();
if(sl!=null){
for(int i=0;i<sl.length;i++){
this.removeTreeSelectionListener(sl[i]);
}
}
}
// ActionListener interface
public void actionPerformed(ActionEvent e){
String cmd = e.getActionCommand();
if (cmd.equals("d") && selectionIsRootChild()) {
Object o = getSelectedSourceOrCollection();
if (!(o==null || isLocked(o))){
if (o instanceof DataSource) {
pool.removeDataSource((DataSource)o);
} else if (o instanceof DataSourceCollection) {
pool.removeDataSourceCollection((DataSourceCollection)o);
}
}
}
}
/** For subclasses to specialize*/
protected TreeCellRenderer createRenderer() {
return new DefaultTreeCellRenderer();
}
protected class DefaultNode extends DefaultMutableTreeNode {
/**
* A string representation of node path (e.g "a.b.test")
*/
String nodePath;
protected boolean isVisible = true;
public DefaultNode(){
super();
nodePath = "";
}
public DefaultNode(Object o, boolean allowsChildren, String parentNodePath) {
super(o, allowsChildren);
nodePath = parentNodePath + ( (!parentNodePath.equals("") && !getNodeId().equals("") )? "." : "" ) + getNodeId();
}
/**
* Used to get the string representation of this node when filtering the tree
* Sub classes can overload this method to specify the node text value
* @return
*/
protected String getNodeId(){
return toString();
}
/* (non-Javadoc)
* @see javax.swing.tree.DefaultMutableTreeNode#getChildAt(int)
*/
public TreeNode getChildAt(int index) throws ArrayIndexOutOfBoundsException{
TreeNode res = null;
if (children == null) {
throw new ArrayIndexOutOfBoundsException("node has no children");
}
int realIndex = -1;
int visibleIndex = -1;
Enumeration e = children.elements();
while (e.hasMoreElements() && (res == null)) {
SourceNode node = (SourceNode) e.nextElement();
if (node.isVisible() ) {
visibleIndex++;
}
realIndex++;
if (visibleIndex == index) {
res = (TreeNode) children.elementAt(realIndex);
}
}
if (res == null){
throw new ArrayIndexOutOfBoundsException("index unmatched");
}
return res;
}
/**
* Return whether or not the node shall be displayed in the source tree
* @return
*/
protected boolean isVisible(){
return isVisible;
}
protected void setIsVisible(boolean isVisible){
this.isVisible = isVisible;
}
/* (non-Javadoc)
* @see javax.swing.tree.DefaultMutableTreeNode#getChildCount()
*/
public int getChildCount(){
int count = 0;
if (children != null) {
Enumeration e = children.elements();
while (e.hasMoreElements()) {
SourceNode node = (SourceNode) e.nextElement();
if (node.isVisible() ) {
count++;
}
}
}
return count;
}
public String getNodePath(){
return nodePath;
}
}
/**
* A SourceNode is a mutable node which can be hidden and locked
* @author zxpletran007
*
*/
protected class SourceNode extends DefaultNode {
/**
* A node can be locked
*/
boolean locked;
/**
* @param userObject an Object provided by the user that constitutes
* the node's data
* @param allowsChildren if true, the node is allowed to have child
* nodes -- otherwise, it is always a leaf node
* @param parentNodePath - A string representation of parent node path.
*/
SourceNode(Object o, boolean allowsChildren, String parentNodePath) {
super(o, allowsChildren, parentNodePath);
setLocked(false);
}
public void setLocked(boolean b) {
locked = b;
}
public boolean getLocked() {
return locked;
}
/**
* returns true if this node is locked, or one of its parents
*/
public boolean isLocked() {
if (locked) return true;
TreeNode tn = getParent();
if (tn==null) return false;
if (tn instanceof SourceNode) return ((SourceNode)tn).isLocked();
return false;
}
}
protected class DataSourceNode extends SourceNode {
DataSourceNode(DataSource source, String parentNodePath) {
this(source, false, parentNodePath); // do not allow children
}
DataSourceNode(DataSource source, boolean allowsChildren, String parentNodePath) {
super(source, allowsChildren, parentNodePath);
}
public String toString() {
if (userObject==null) return "DataSource";
String ret = DataInfo.toString((DataSource)userObject);
if (ret!=null){
if (((DataSource)userObject).isAuxiliary()){
int dot = ret.lastIndexOf(".");
if (dot!=-1)
ret = ret.substring(dot+1);
}
return ret;
}
ret = userObject.getClass().getName();
int dot = ret.lastIndexOf(".");
if (dot!=-1) return ret.substring(dot+1);
return ret;
}
public void changeSource(DataSource ds) {
setUserObject(ds);
}
}
protected class CompoundDataSourceNode extends DataSourceNode{
CompoundDataSourceNode(DataSource source, String parentNodePath){
super(source, true, parentNodePath); // allow children
for(Iterator it=source.getAuxiliarySources().iterator();it.hasNext();)
add(new DataSourceNode((DataSource)it.next(), nodePath));
}
public void changeSource(DataSource ds) {
super.changeSource(ds);
removeAllChildren();
for(Iterator it=ds.getAuxiliarySources().iterator();it.hasNext();)
add(new DataSourceNode((DataSource)it.next(), nodePath));
}
public boolean isLocked() {
Enumeration e = children();
if (e!=null) while (e.hasMoreElements()) {
Object child = e.nextElement();
if (child instanceof SourceNode)
if (((SourceNode)child).getLocked()) return true;
}
return super.isLocked();
}
}
protected class CollectionNode extends SourceNode {
CollectionNode(Object o, String parentNodePath) {
super(o, true, parentNodePath); // allow children
}
CollectionNode(DataSourceCollection collection, String parentNodePath) {
this((Object)collection, parentNodePath);
setSourceNodes(collection.iterator());
}
public String toString() {
if (userObject==null) return "DataSourceCollection";
String ret = DataInfo.getLabel((DataSourceCollection)userObject);
if (ret!=null) return ret;
ret = userObject.getClass().getName();
int dot = ret.lastIndexOf(".");
if (dot!=-1) return ret.substring(dot+1);
return ret;
}
public void changeCollection(DataSourceCollection dsc) {
removeAllChildren();
setUserObject(dsc);
setSourceNodes(dsc.iterator());
}
protected void setSourceNodes(Iterator it){
while(it.hasNext()){
addDataSource((DataSource)it.next());
}
}
protected void addDataSource(DataSource ds){
DataSourceNode dsn;
if(ds.isCompound()){
dsn = new CompoundDataSourceNode(ds, nodePath);
}
else{
dsn = new DataSourceNode(ds, nodePath);
}
add(dsn);
}
/**
* A collection node is locked if one of its source is locked
*/
public boolean isLocked() {
Enumeration e = children();
if (e!=null) while (e.hasMoreElements()) {
Object child = e.nextElement();
if (child instanceof SourceNode)
if (((SourceNode)child).getLocked()) return true;
}
return super.isLocked();
}
}
protected class CompoundCollectionNode extends CollectionNode {
CompoundCollectionNode(DataSourceCollection collection, String parentNodePath) {
super((Object)collection, parentNodePath);
nodePath = parentNodePath; // we do not take in account a collection name in source filtering
setChildNodes(collection.getCollectionContainers().iterator());
}
CompoundCollectionNode(DataSourceCollection.Container ct, String parentNodePath) {
super((Object)ct, parentNodePath);
Collection c=ct.getChildren();
if(c!=null)
setChildNodes(c.iterator());
}
public String toString() {
if(userObject instanceof DataSourceCollection.Container){
return userObject.toString();
}
return super.toString();
}
/* (non-Javadoc)
* @see simtools.ui.SourceTree.DefaultNode#getNodeText()
*/
protected String getNodeId(){
String res;
if (( (userObject) instanceof DataSourceCollection.Container) && ((DataSourceCollection.Container)userObject).isVirtual()){
res = "";
} else {
res = toString();
}
return res;
}
private void removeAllChildrenRecursively(){
Enumeration e = children();
if (e!=null) while (e.hasMoreElements()) {
Object child = e.nextElement();
if (child instanceof CompoundCollectionNode){
((CompoundCollectionNode)child).removeAllChildrenRecursively();
}
}
removeAllChildren();
}
public void changeCollection(DataSourceCollection dsc) {
removeAllChildrenRecursively();
setUserObject(dsc);
setChildNodes(dsc.getCollectionContainers().iterator());
}
protected void setChildNodes(Iterator it){
while(it.hasNext()){
Object o=it.next();
if(o instanceof DataSourceCollection.Container){
add(new CompoundCollectionNode((DataSourceCollection.Container)o, nodePath));
}
else{
addDataSource((DataSource)o);
}
}
}
public boolean isLocked() {
Enumeration e = children();
if (e!=null) while (e.hasMoreElements()) {
Object child = e.nextElement();
if (child instanceof CompoundCollectionNode){
if(((CompoundCollectionNode)child).isLocked()){
return true;
}
}
else if (child instanceof SourceNode)
if (((SourceNode)child).getLocked()) return true;
}
return false;
}
}
protected class SourceTransferHandler extends TransferHandler {
protected Transferable createTransferable(JComponent c) {
if (c!=SourceTree.this) return null;
return new SourceTransferable();
}
public int getSourceActions(JComponent c){
return COPY;
}
}
protected class SourceTransferable implements Transferable {
/* (non-Javadoc)
* @see java.awt.datatransfer.Transferable#getTransferDataFlavors()
*/
public DataFlavor[] getTransferDataFlavors() {
Object node = getSelectionPath().getLastPathComponent();
if (node==null) return new DataFlavor[0];
//If there is a selected data, the available data flavors will be the data itself, and a string.
DataFlavor[] ret = new DataFlavor[2];
if (node instanceof DataSourceNode) ret[0] = new DataFlavor(DataSource.class, node.toString());
else if (node instanceof CollectionNode) ret[0] = new DataFlavor(DataSourceCollection.class, node.toString());
else ret[0] = DataFlavor.stringFlavor; // error, unknown object => as string
//Add the String dataFlavor, to also export data path.
ret[1] = DataFlavor.stringFlavor;
return ret;
}
public boolean isDataFlavorSupported(DataFlavor flavor) {
Object node = getSelectionPath().getLastPathComponent();
if (node==null) return false;
if (node instanceof DataSourceNode)
return DataSource.class.equals(flavor.getRepresentationClass());
if (node instanceof CollectionNode)
return DataSourceCollection.class.equals(flavor.getRepresentationClass());
return false;
}
/* (non-Javadoc)
* @see java.awt.datatransfer.Transferable#getTransferData(java.awt.datatransfer.DataFlavor)
*/
public Object getTransferData(DataFlavor flavor) {
//The result of the method
Object result = null;
//retrieve the selected data.
Object selectedData = getSelectedSourceOrCollection();
if(selectedData != null){
//In case of a dataSource, and dataSource flavor, return the dataSource
if ((selectedData instanceof DataSource) && DataSource.class.equals(flavor.getRepresentationClass())) {
result = selectedData;
}else if ((selectedData instanceof DataSourceCollection) && DataSourceCollection.class.equals(flavor.getRepresentationClass())) {
//Else for a dataSourceCollection, and if flavor is a collection, return collection.
result = selectedData;
}else if(flavor.equals(DataFlavor.stringFlavor)){
//Else, if we need a String representation.
//return the label of the data.
result = DataInfo.getLabel(selectedData);
}
}
//return the result
return result;
}
}
/**
* Set filter value and reload the tree
* @param filterValue
*/
public void setFilterValue(String filterValue) {
boolean reloadAll = !filterValue.startsWith(currentFilterValue);
currentFilterValue = filterValue;
filterPattern.setPattern(filterValue);
reload(reloadAll);
}
/**
* Set filter value and reload the tree
* @param filterValue
*/
public void setFilterValidity(boolean isValid) {
filterPattern.setValidity(isValid);
reload(true);
}
/**
* All nodes are expanded
*/
public void expandAll(){
expandAll(new TreePath(rootNode));
}
/**
* All nodes are collapsed
*/
public void collapseAll(){
int row = getRowCount() - 1;
while (row >= 0) {
collapseRow(row);
row--;
}
}
private void expandAll(TreePath parent) {
TreeNode node = (TreeNode)parent.getLastPathComponent();
boolean hasAuxiliaryDsNodes = node instanceof DataSourceNode && ((DataSource)((DataSourceNode) node).getUserObject()).isCompound();
if ((node.getChildCount() >= 0) && !hasAuxiliaryDsNodes) {
for (Enumeration e=node.children(); e.hasMoreElements(); ) {
TreeNode n = (TreeNode)e.nextElement();
TreePath path = parent.pathByAddingChild(n);
expandAll(path);
}
}
// Do not expand auxiliary sources
if (!hasAuxiliaryDsNodes){
expandPath(parent);
}
}
public static interface FilterPattern{
/**
* @return true if the pattern does accept any value
*/
public boolean acceptAll();
/**
* @param a given string value
* @return true if the pattern does accept he given string
*/
public boolean matches(String value);
/**
* Set the pattern value
* @param pattern
*/
public void setPattern(String pattern);
public void setValidity(boolean isValid);
}
/**
* This filter accepts any kind of string value.
* Following 2 characters have a special signification:
* <ul>
* <li> The wild card (*): equivalent to any sequence of character (zero to several characters)
* <li> The question mark card (?) : equivalent to any single character (one and only one character))
* </ul>
* @author zxpletran007
*
*/
public static class SourceFilterPattern implements FilterPattern{
protected Pattern pattern;
protected boolean isValid;
public SourceFilterPattern(){
this("");
}
public SourceFilterPattern(String pattern){
setPattern(pattern);
isValid = true;
}
public boolean matches(String value) {
if (!isValid){
return true;
} else {
return pattern.matcher(value.toUpperCase()).matches();
}
}
public void setPattern(String pattern) {
if (pattern == null){
pattern = "";
}
// Convert the given pattern into a regular Java expression
pattern = pattern.replaceAll("\\.", "\\\\.");
pattern = pattern.replaceAll("\\?", ".");
pattern = pattern.replaceAll("\\*", ".*");
pattern = pattern.replaceAll("\\^", "\\\\^");
pattern = pattern.replaceAll("\\+", "\\\\+");
pattern = pattern.replaceAll("\\(", "\\\\(");
pattern = pattern.replaceAll("\\)", "\\\\)");
pattern = pattern.replaceAll("\\[", "\\\\[");
pattern = pattern.replaceAll("\\]", "\\\\]");
pattern = pattern.replaceAll("\\{", "\\\\{");
pattern = pattern.replaceAll("\\}", "\\\\}");
// Add an additional wild card at the end of the given pattern
pattern = pattern.concat(".*");
// pattern is not case sensitive
pattern = pattern.toUpperCase();
this.pattern = Pattern.compile(pattern);
}
public boolean acceptAll(){
return !isValid || pattern.pattern().equals(".*");
}
public void setValidity(boolean isValid){
this.isValid = isValid;
}
}
public static void main(String args[]){
System.out.println(new SourceFilterPattern("ee.rr(*").matches("ee.rr(0") );
/*
System.out.println( "efcgabbc matches with *abbc = " + new SourceFilterPattern("*abbc").matches("efcgabbc") );
System.out.println( "efcgabbc matches with *abbc* = " + new SourceFilterPattern("*abbc*").matches("efcgabbc") );
System.out.println( "efcgabbcdf matches with *abbc* = " + new SourceFilterPattern("*abbc*").matches("efcgabbcdf") );
System.out.println( "efcgabbc matches with *abbc? = " + new SourceFilterPattern("*abbc?").matches("efcgabbc") );//false
System.out.println( "efcgabbcm matches with *abbc? = " + new SourceFilterPattern("*abbc?").matches("efcgabbcm") );
System.out.println( "a.bb.cc matches with *bb* = " + new SourceFilterPattern("*bb*").matches("a.bb.cc") );*/
}
}