swingx-master/swingx-core/src/test/java/org/jdesktop/swingx/test/ComponentTreeTableModel.java
/*
 * Created on 20.06.2005
 *
 */
package org.jdesktop.swingx.test;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;

import javax.swing.tree.TreePath;

import org.jdesktop.swingx.JXFrame;
import org.jdesktop.swingx.treetable.AbstractTreeTableModel;

/**
 * A static snapshot of a container hierarchy.
 * 
 * NOTE: does not listen to any property changes of
 * contained components - cell updates are arbitrary on
 * repaint only!
 * 
 * @author Jeanette Winzenburg
 */
public class ComponentTreeTableModel extends AbstractTreeTableModel {


    public ComponentTreeTableModel(Container root) {
        super(root);
        setRoot(root);
    }

    public void setRoot(Container root) {
        if (root == null) {
            root = new JXFrame();
        }
        this.root = root;
        modelSupport.fireNewRoot();
    }
    
    
    //  ------------------TreeModel
    
    
    @Override
    public Container getRoot() {
        // TODO Auto-generated method stub
        return (Container) super.getRoot();
    }

    public Object getChild(Object parent, int index) {
        return ((Container) parent).getComponent(index);
    }
    
    public int getChildCount(Object parent) {
        return parent instanceof Container ? ((Container) parent).getComponentCount() : 0;
    }
    
    public int getIndexOfChild(Object parent, Object child) {
        Component[] children = ((Container) parent).getComponents();
        for (int i = 0; i < children.length; i++) {
            if (children[i].equals(child)) {
                return i;
            }
        }
        return -1;
    }
    
    @Override
    public boolean isLeaf(Object node) {
        return getChildCount(node) == 0;
    }

    /**
     * This method is called by the "tree" part to render the hierarchical
     * column. <p>
     * 
     * PENDING: currently need to comment to let the treetable show the
     *   correct value. Need to dig why ...
     * 
     * @param node
     *            the node to convert
     * @return the {@code node} as a string
     */
//    public String convertValueToText(Object node) {
//        return String.valueOf(getValueAt(node, getHierarchicalColumn()));
//    }
    

    @Override
    public void valueForPathChanged(TreePath path, Object newValue) {
        Object node = path.getLastPathComponent();
        setValueAt(newValue, node, getHierarchicalColumn());
    }


//------------------ TreeTableModel    

    @Override
    public Class<?> getColumnClass(int column) {
        switch (column) {
        case 0:
            return Object.class;
        case 1:
            return Class.class;
        case 2:
            return Dimension.class;
        case 3:
            return Integer.class;
        default:
            return Object.class;
        }
    }
    
    public int getColumnCount() {
        return 4;
    }
    
    @Override
    public String getColumnName(int column) {
        switch (column) {
        case 0:
            return "Name";
        case 1:
            return "Type";
        case 2:
            return "Size";
        case 3:
            return "Width";
        default:
            return "Column " + column;
        }
    }
    
    public Object getValueAt(Object node, int column) {
        Component comp = (Component) node;
        switch (column) {
        case 0:
            return comp.getName();
        case 1:
            return comp.getClass();
        case 2:
            return comp.getSize();
        case 3: 
            return comp.getWidth();
        default:
            return null;
        }
    }

    
    @Override
    public boolean isCellEditable(Object node, int column) {
        // fake accessing the node: TreeModel can expect to 
        // be messaged with nodes that belong to the 
        // hierarchical structure 
        // introduced for testing #270-swingx: 
        // NPE for invisible rows (if parent is collapsed.
        ((Component) node).getName();
        return column == 0 || column == 3;
    }

    @Override
    public void setValueAt(Object value, Object node, int column) {
        if (!isCellEditable(node, column)) return;
        
        Component comp = (Component) node;
        if (column == 0) {
            comp.setName(String.valueOf(value));
        } else if (column == 3) {
            comp.setSize(new Integer((Integer) value), comp.getHeight());
        }
        nodeChanged(comp);
    }


    private void nodeChanged(Component comp) {
        TreePath path = getPathToRoot(comp);
        modelSupport.firePathChanged(path);
    }

    /**
     * exposed for test convenience.
     * 
     * @param node
     * @return a {@code TreePath} representation from {@code root} to
     *         {@code node}
     */
    public TreePath getPathToRoot(Component node) {
        return new TreePath(getPathToRoot(node, 0));
    }

    private Component[] getPathToRoot(Component node, int depth) {
        Component[]              retNodes;
        // This method recurses, traversing towards the root in order
        // size the array. On the way back, it fills in the nodes,
        // starting from the root and working back to the original node.

        /* Check for null, in case someone passed in a null node, or
           they passed in an element that isn't rooted at root. */
        if(node == null) {
            if(depth == 0)
                return null;
            else
                retNodes = new Component[depth];
        }
        else {
            depth++;
            if(node == root)
                retNodes = new Component[depth];
            else
                retNodes = getPathToRoot(node.getParent(), depth);
            retNodes[retNodes.length - depth] = node;
        }
        return retNodes;
    }

    
}
