jadex.base.gui.asynctree.AbstractTreeNode Maven / Gradle / Ivy
package jadex.base.gui.asynctree;
import jadex.commons.SUtil;
import jadex.commons.future.Future;
import jadex.commons.future.IFuture;
import jadex.commons.gui.TreeExpansionHandler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.TreePath;
/**
* Basic node object.
*/
public abstract class AbstractTreeNode implements ITreeNode
{
//-------- attributes --------
/** The parent node. */
protected final ITreeNode parent;
/** The tree model. */
protected final AsyncTreeModel model;
/** The tree. */
// Hack!!! Model should not have access to ui, required for refresh only on expanded nodes.
protected final JTree tree;
/** The cached children. */
private List children;
/** Flag to indicate search in progress. */
protected boolean searching;
/** Flag to indicate recursive refresh. */
protected boolean recurse;
/** Flag to indicate that children were added / removed during ongoing search (->restart search). */
protected boolean dirty;
/** The children future (result of next search). */
protected Future> childrenfuture;
//-------- constructors --------
/**
* Create a node.
*/
public AbstractTreeNode(ITreeNode parent, AsyncTreeModel model, JTree tree)
{
this.parent = parent;
this.model = model;
this.tree = tree;
// model.registerNode(this);
}
//-------- IComponentTreeNode interface --------
/**
* Called when the node is removed or the tree is closed.
*/
public void dispose()
{
}
/**
* Get the parent node.
*/
public ITreeNode getParent()
{
return parent;
}
/**
* Get the child count.
*/
public int getChildCount()
{
assert SwingUtilities.isEventDispatchThread();// || Starter.isShutdown();
if(children==null && !searching)
{
searching = true;
// System.out.println("searchChildren: "+getId());
searchChildren();
}
return children==null ? 0 : children.size();
}
/**
* Get the given child.
*/
public ITreeNode getChild(int index)
{
assert SwingUtilities.isEventDispatchThread();// || Starter.isShutdown();
if(children==null && !searching)
{
searching = true;
// System.out.println("searchChildren: "+getId());
searchChildren();
}
return children==null ? null : (ITreeNode)children.get(index);
}
/**
* Get the index of a child.
*/
public int getIndexOfChild(ITreeNode child)
{
assert SwingUtilities.isEventDispatchThread();// || Starter.isShutdown();
if(children==null && !searching)
{
searching = true;
// System.out.println("searchChildren: "+getId());
searchChildren();
}
return children==null ? -1 : children.indexOf(child);
}
/**
* Check if the node is a leaf.
*/
public boolean isLeaf()
{
assert SwingUtilities.isEventDispatchThread();// || Starter.isShutdown();
return getChildCount()==0;
}
/**
* Refresh the node.
* @param recurse Recursively refresh subnodes, if true.
*/
public void refresh(boolean recurse)
{
assert SwingUtilities.isEventDispatchThread();// || Starter.isShutdown();
// System.out.println("ATN refresh: "+getId());
if(!searching)
{
searching = true;
this.recurse = recurse;
// System.out.println("searchChildren: "+getId());
searchChildren();
}
else
{
// If search in progress upgrade to recursive, but do not downgrade.
this.recurse = this.recurse || recurse;
dirty=true;
}
tree.repaint();
}
/**
* Get the cached children, i.e. do not start any background processes for updating the children.
*/
public List getCachedChildren()
{
assert SwingUtilities.isEventDispatchThread();// || Starter.isShutdown();
// Conditional does not work with generic Collections.emptyMap() method?
return children!=null ? children : (List)Collections.EMPTY_LIST;
}
/**
* Get the current children, i.e. start a new update process and provide the result as a future.
*/
public IFuture> getChildren()
{
assert SwingUtilities.isEventDispatchThread();// || Starter.isShutdown();
if(childrenfuture==null)
{
childrenfuture = new Future>();
}
IFuture> ret = childrenfuture;
// System.out.println("searchChildren: "+getId());
searchChildren(); // might reset childrenfuture.
return ret;
}
/**
* True, if the node has properties that can be displayed.
*/
public boolean hasProperties()
{
return false;
}
/**
* Get or create a component displaying the node properties.
* Only to be called if hasProperties() is true;
*/
public JComponent getPropertiesComponent()
{
throw new UnsupportedOperationException("Node has no properties: "+this);
}
//-------- template methods --------
/**
* Get the icon for a node.
*/
public abstract Icon getIcon();
/**
* Get tooltip text.
*/
public abstract String getTooltipText();
/**
* Asynchronously search for children.
* Called once for each node.
* Should call setChildren() once children are found.
*/
protected abstract void searchChildren();
/**
* Set the children.
* No children should be represented as empty list to avoid
* ongoing search for children.
*/
protected void setChildren(List newchildren)
{
assert SwingUtilities.isEventDispatchThread();// || Starter.isShutdown();
List oldcs = children!=null ? new ArrayList(children) : null;
List newcs = newchildren!=null ? new ArrayList(newchildren) : null;
assert false || checkChildren(oldcs, newcs);
searching = false;
if(dirty)
{
// Restart search when nodes have been added/removed in the mean time.
dirty = false;
// System.out.println("searchChildren: "+getId());
searchChildren();
}
else
{
// System.err.println(""+model.hashCode()+" setChildren executing: "+parent+"/"+AbstractTreeNode.this+", "+children+", "+newcs);
boolean dorecurse = recurse;
recurse = false;
if(children==null)
children = new ArrayList();
// New update algorithm (can cope with changing orderings)
boolean changed = false;
// Traverse through source list and remove changed nodes
int newidx = 0; // Pointer to target list.
int removed = 0; // Counter for correct tree event index
for(int i=0; oldcs!=null && i move on
newidx++;
}
else
{
// Node removed or moved -> remove (moved nodes will be re-added later at correct position)
children.remove(node);
model.deregisterNode(node);
model.fireNodeRemoved(AbstractTreeNode.this, node, i-removed);
removed++;
changed = true;
}
}
// Traverse through target list and add missing nodes
for(int i=0; newcs!=null && i=children.size() || !node.equals(children.get(i)))
{
children.add(i, node);
model.addNode(node);
model.fireNodeAdded(AbstractTreeNode.this, node, i);
changed = true;
}
}
// List toadd = new ArrayList();
// for(int i=0; newcs!=null && i=children.size() || !node.equals(children.get(i)))
// {
// toadd.add(node);
// }
// }
// // Add missing or moved node.
// if(toadd.size()>0)
// {
// for(ITreeNode node: toadd)
// {
// boolean ins = false;
// for(int i=0; i=0)
// {
//// System.out.println("adding: "+node.toString());
// children.add(i, node);
// model.addNode(node);
// model.fireNodeAdded(AbstractTreeNode.this, node, i);
// ins = true;
// changed = true;
// }
// }
// if(!ins)
// {
// children.add(node);
// model.addNode(node);
// model.fireNodeAdded(AbstractTreeNode.this, node, children.size()-1);
// changed = true;
// }
// }
// }
if(changed)
model.fireNodeChanged(AbstractTreeNode.this);
// Old update algorithm (only when order does not change)
// List added = new ArrayList();
// List removed = new ArrayList();
// if(oldcs!=null)
// {
// removed.addAll(oldcs);
// }
// if(newcs!=null)
// {
// added.addAll(newcs);
// removed.removeAll(newcs);
// }
// if(oldcs!=null)
// {
// added.removeAll(oldcs);
// }
//
// if(!removed.isEmpty())
// {
// for(int i=removed.size()-1; i>=0; i--)
// {
// ITreeNode node = (ITreeNode)removed.get(i);
// int index = oldcs.indexOf(node);
// Object r = children.remove(index);
// assert SUtil.equals(r, node) : "Node inconsistency: "+oldcs+", "+newcs;
// model.deregisterNode(node);
// model.fireNodeRemoved(AbstractTreeNode.this, node, index);
// }
// if(added.isEmpty())
// model.fireNodeChanged(AbstractTreeNode.this);
// }
// if(!added.isEmpty())
// {
// for(int i=0; i(children));
childrenfuture = null;
}
if(dorecurse && TreeExpansionHandler.isTreeExpanded(tree, new TreePath(model.buildTreePath(AbstractTreeNode.this).toArray())))
{
for(int i=0; children!=null && i1)
{
for(int i=0; i=index2)
// {
// throw new RuntimeException("Found unequal ordering:\n oldcs = "+oldcs+"\nnewcs = "+newcs);
// }
// }
// }
// }
return true;
}
/**
* Get the model.
*/
public AsyncTreeModel getModel()
{
return model;
}
/**
* Get the tree.
*/
public JTree getTree()
{
return tree;
}
/**
* Add a child and update the tree.
* Must be called from swing thread.
*/
public void addChild(int index, ITreeNode node)
{
assert SwingUtilities.isEventDispatchThread();// || Starter.isShutdown();
// Ignore when node already removed.
if(!model.isZombieNode(node.getId()))
{
if(children==null)
children = new ArrayList();
children.add(index, node);
model.addNode(node);
model.fireNodeAdded(this, node, index);
if(searching)
dirty = true;
// if(node.getId().toString().startsWith("ANDTest@"))
// System.out.println("Node added: "+node+", "+children);
}
else
{
model.removeZombieNode(node);
}
}
/**
* Add a child and update the tree.
* Must be called from swing thread.
*/
public void addChild(ITreeNode node)
{
assert SwingUtilities.isEventDispatchThread();// || Starter.isShutdown();
// model.registerNode(node);
addChild(getCachedChildren().size(), node);
}
/**
* Remove a child and update the tree.
* Must be called from swing thread.
*/
public void removeChild(ITreeNode node)
{
assert SwingUtilities.isEventDispatchThread();// || Starter.isShutdown();
int index = getIndexOfChild(node);
if(index!=-1)
{
// boolean removed =
children.remove(node);
// if(node.getId().toString().startsWith("ANDTest@"))
// System.out.println("removed: "+node+", "+removed);
model.deregisterNode(node);
model.fireNodeRemoved(this, node, index);
if(searching)
dirty = true;
}
else
{
getModel().addZombieNode(node.getId());
}
}
/**
* Remove all children.
*/
public void removeAllChildren()
{
if(children!=null && children.size()>0)
{
int[] indices = new int[children.size()];
for(int i=0; i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy