/*
 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.