/*
This file is part of the software framework JStatCom,
a library for building applications for data based analysis.
Copyright (C) 2000-2005 Markus Kraetzig
This library 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 library 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 library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
*/
package com.jstatcom.model.control;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import com.jstatcom.model.Symbol;
import com.jstatcom.model.SymbolTable;
/**
* Treenode to be used with SymbolTree for displaying
* SymbolTable objects a JTree.
*
* @author Markus Kraetzig
*/
public final class SymbolTableTreeNode extends DefaultMutableTreeNode {
/**
* SymbolTable represented by this tree node.
*/
public final SymbolTable symbolTable;
private final DefaultTreeModel symbolTreeModel;
private final DefaultMutableTreeNode parent;
/**
* Creates a tree node for a symbol.
*
* @param symbolTable
* the respective table
* @param symbolTreeModel
* the used tree model
* @throws IllegalArgumentException
* if (symbolTable == null)
*/
public SymbolTableTreeNode(SymbolTable symbolTable,
DefaultTreeModel symbolTreeModel) {
super(symbolTable.NAME, true);
if (symbolTable == null)
throw new IllegalArgumentException("Argument was null.");
this.symbolTreeModel = symbolTreeModel;
this.symbolTable = symbolTable;
SymbolTable parentTable = symbolTable.getParentTable();
if (parentTable == null)
this.parent = (DefaultMutableTreeNode) symbolTreeModel.getRoot();
else
this.parent = parentTable.getSymbolTableNode();
symbolTable.setSymbolTableNode(this);
if (getChildCount() > 0)
addSymbolTableNode(this, parent);
reloadInEDT();
}
/**
* Adds a symbol table tree node to this. It keeps the alphabetic order of
* the nodes. Symbols table tree nodes are always added before symbol nodes.
*
* @param symbolTableTreeNode
* @param parent
*/
private static void addSymbolTableNode(
SymbolTableTreeNode symbolTableTreeNode,
DefaultMutableTreeNode parent) {
if (parent.getChildCount() == 0) {
parent.add(symbolTableTreeNode);
return;
}
boolean inserted = false;
for (int i = 0; i < parent.getChildCount(); i++) {
Object item = parent.getChildAt(i);
// add before symbols
if (item instanceof SymbolTreeNode) {
parent.insert(symbolTableTreeNode, i);
inserted = true;
break;
} else if (item instanceof SymbolTableTreeNode) {
SymbolTableTreeNode node = (SymbolTableTreeNode) item;
if (node.symbolTable.NAME
.compareTo(symbolTableTreeNode.symbolTable.NAME) >= 0) {
parent.insert(symbolTableTreeNode, i);
inserted = true;
break;
}
}
}
// was not yet added
if (!inserted)
parent.add(symbolTableTreeNode);
}
/**
* Adds a symbol to this node. It keeps the alphabetic order of the nodes.
* Symbols are always added after symbol tables.
*
* @param symbol
* to be put in tree
*/
public void addSymbol(Symbol symbol) {
synchronized (parent) {
synchronized (this) {
boolean wasEmpty = (getChildCount() == 0);
boolean inserted = false;
for (int i = 0; i < getChildCount(); i++) {
Object item = getChildAt(i);
if (item instanceof SymbolTreeNode) {
SymbolTreeNode node = (SymbolTreeNode) item;
if (node.symbol.NAME.compareTo(symbol.NAME) >= 0) {
insert(new SymbolTreeNode(symbol), i);
inserted = true;
break;
}
}
}
// was not yet added
if (!inserted)
add(new SymbolTreeNode(symbol));
boolean alreadUpdated = false; // economize updates
if (wasEmpty) {
addSymbolTableNode(this, parent);
if (parent instanceof SymbolTableTreeNode)
alreadUpdated = ((SymbolTableTreeNode) parent)
.updateAfterAdd();
}
if (!alreadUpdated)
reloadInEDT();
}
}
}
/**
* Removes a symbol node with name from this table node.
*
* @param name
* the identifier of the symbol to remove
*/
public void removeSymbol(String name) {
synchronized (parent) {
synchronized (this) {
boolean lastElement = (getChildCount() == 1);
boolean alreadUpdated = false; // economize updates
for (int i = 0; i < getChildCount(); i++) {
Object item = getChildAt(i);
if (item instanceof SymbolTreeNode) {
SymbolTreeNode node = (SymbolTreeNode) getChildAt(i);
if (node.symbol.NAME.compareToIgnoreCase(name) == 0) {
remove(i);
if (lastElement) {
((DefaultMutableTreeNode) getParent())
.remove(this);
if (parent instanceof SymbolTableTreeNode)
alreadUpdated = ((SymbolTableTreeNode) parent)
.updateAfterRemove();
}
if (!alreadUpdated)
reloadInEDT();
return;
}
}
}
}
}
}
/**
* Removes all children that are SymbolTreeNode instances
* from this node. It removes itself from the parent node
* if (getChildCount() == 0) afterwards. It might still be
* that SymbolTableTreeNode instances are contained in this
* node.
*/
public void clear() {
if (getChildCount() == 0)
return;
for (int i = 0; i < getChildCount(); i++) {
Object item = getChildAt(i);
if (item instanceof SymbolTreeNode)
remove(i--);
}
boolean alreadUpdated = false; // economize updates
if (getChildCount() == 0)
parent.remove(this);
if (parent instanceof SymbolTableTreeNode)
alreadUpdated = ((SymbolTableTreeNode) parent).updateAfterRemove();
if (!alreadUpdated)
reloadInEDT();
}
/**
* Adds parent(s) if necessary.
*
* @return true if model was reloaded from parent of this
*/
private boolean updateAfterAdd() {
// checks whether parent was empty before child was added
boolean wasEmpty = getChildCount() == 1;
boolean alreadUpdated = false; // economize updates
if (wasEmpty) {
addSymbolTableNode(this, parent);
// recurse upwards
if (parent instanceof SymbolTableTreeNode)
alreadUpdated = ((SymbolTableTreeNode) parent).updateAfterAdd();
if (!alreadUpdated)
symbolTreeModel.reload(parent);
return true;
}
return false;
}
/**
* Removes parent(s) if necessary.
*
* @return true if model was reloaded from parent of parent
*/
private boolean updateAfterRemove() {
boolean empty = (getChildCount() == 0);
boolean alreadUpdated = false; // economize updates
if (empty) {
parent.remove(this);
// recurse upwards
if (parent instanceof SymbolTableTreeNode)
alreadUpdated = ((SymbolTableTreeNode) parent)
.updateAfterRemove();
if (!alreadUpdated)
reloadInEDT();
return true;
}
return false;
}
/**
* Reloads model to inform listeners synched with Swing
* EventDispatchingThread.
*
* @param node
* reload model from this node
*/
private void reloadInEDT() {
if (SwingUtilities.isEventDispatchThread())
symbolTreeModel.reload(parent);
else {
Runnable runner = new Runnable() {
public void run() {
symbolTreeModel.reload(parent);
}
};
SwingUtilities.invokeLater(runner);
}
}
}
Ads help maintain this website.