components/visual-panels/core/src/java/util/com/oracle/solaris/vp/util/swing/tree/TreeTableModelAdapter.java
author Dan Labrecque <dan.labrecque@oracle.com>
Thu, 24 May 2012 04:16:47 -0400
changeset 827 0944d8c0158b
permissions -rw-r--r--
7169052 Integrate Visual Panels into Userland

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
 */

package com.oracle.solaris.vp.util.swing.tree;

import java.util.*;
import javax.swing.ListModel;
import javax.swing.event.*;
import javax.swing.table.TableModel;
import javax.swing.tree.TreeNode;
import com.oracle.solaris.vp.util.swing.ListDataListenerAdapter;
import com.oracle.solaris.vp.util.swing.event.TableModelListeners;

/**
 * The {@code TreeTableModelAdapter} class maps a {@link TreeTableModel} onto a
 * {@code TableModel} and a {@code ListModel}.
 */
@SuppressWarnings({"serial"})
public class TreeTableModelAdapter implements TableModel, ListModel,
    TreeModelListener {

    //
    // Instance data
    //

    private List<ModelRowData> rows = new LinkedList<ModelRowData>();
    private TreeTableModel model;
    private TableModelListeners listeners = new TableModelListeners();

    private Map<ListDataListener, ListDataListenerAdapter> listenerMap =
	new HashMap<ListDataListener, ListDataListenerAdapter>();

    private int listColumn;

    //
    // Constructors
    //

    public TreeTableModelAdapter(TreeTableModel model) {
	this.model = model;

	addRows(0, (TreeNode)model.getRoot(), 0);
	model.addTreeModelListener(this);
    }

    //
    // TableModel methods
    //

    @Override
    public void addTableModelListener(TableModelListener l) {
	listeners.add(l);
    }

    @Override
    public Class<?> getColumnClass(int column) {
	return model.getColumnClass(column);
    }

    @Override
    public int getColumnCount() {
	return model.getColumnCount();
    }

    @Override
    public String getColumnName(int column) {
	return model.getColumnName(column);
    }

    @Override
    public int getRowCount() {
	return rows.size();
    }

    @Override
    public Object getValueAt(int row, int column) {
	return model.getValueAt(getTreeNode(row), column);
    }

    @Override
    public boolean isCellEditable(int row, int column) {
	return model.isCellEditable(getTreeNode(row), column);
    }

    @Override
    public void removeTableModelListener(TableModelListener l) {
	listeners.remove(l);
    }

    @Override
    public void setValueAt(Object value, int row, int column) {
	model.setValueAt(value, getTreeNode(row), column);
    }

    //
    // ListModel methods
    //

    @Override
    public void addListDataListener(ListDataListener l) {
	synchronized (listenerMap) {
	    if (!listenerMap.containsKey(l)) {
		ListDataListenerAdapter adapter =
		    new ListDataListenerAdapter(l);

		addTableModelListener(adapter);
		listenerMap.put(l, adapter);
	    }
	}
    }

    /**
     * Returns the value of the cell in the {@link #getListColumn list column}
     * of the given row.
     */
    @Override
    public Object getElementAt(int row) {
	return getValueAt(row, getListColumn());
    }

    @Override
    public int getSize() {
	return getRowCount();
    }

    @Override
    public void removeListDataListener(ListDataListener l) {
	synchronized (listenerMap) {
	    ListDataListenerAdapter adapter = listenerMap.get(l);
	    if (adapter != null) {
		removeTableModelListener(adapter);
		listenerMap.remove(l);
	    }
	}
    }

    //
    // TreeModelListener methods
    //

    @Override
    public void treeNodesChanged(TreeModelEvent e) {
	synchronized (rows) {
	    for (Object child : e.getChildren()) {
		TreeNode node = (TreeNode)child;
		int index = indexOf(node);

		TableModelEvent event = new TableModelEvent(
		    this, index, index, TableModelEvent.ALL_COLUMNS,
		    TableModelEvent.UPDATE);

		fireTableChanged(event);
	    }
	}
    }

    @Override
    public void treeNodesInserted(TreeModelEvent e) {
	TreeNode node = (TreeNode)e.getTreePath().getLastPathComponent();
	ModelRowData row = getRow(indexOf(node));
	int level = row.getLevel() + 1;

	int[] indexes = e.getChildIndices();
	Object[] children = e.getChildren();

	synchronized (rows) {
	    for (int i = 0; i < indexes.length; i++) {
		int first = indexOf(node, indexes[i]);
		TreeNode child = (TreeNode)children[i];
		int last = addRows(first, child, level) - 1;

		TableModelEvent event = new TableModelEvent(
		    this, first, last, TableModelEvent.ALL_COLUMNS,
		    TableModelEvent.INSERT);

		fireTableChanged(event);
	    }
	}
    }

    @Override
    public void treeNodesRemoved(TreeModelEvent e) {
	TreeNode node = (TreeNode)e.getTreePath().getLastPathComponent();
	Object[] children = e.getChildren();

	synchronized (rows) {
	    for (int i = children.length - 1; i >= 0; i--) {
		TreeNode child = (TreeNode)children[i];
		int first = indexOf(child);
		int last = indexOf(child, -1) - 1;

		for (int j = last; j >= first; j--) {
		    rows.remove(j);
		}

		TableModelEvent event = new TableModelEvent(
		    this, first, last, TableModelEvent.ALL_COLUMNS,
		    TableModelEvent.DELETE);

		fireTableChanged(event);
	    }
	}
    }

    @Override
    public void treeStructureChanged(TreeModelEvent e) {
	TableModelEvent event = new TableModelEvent(
	    this, TableModelEvent.HEADER_ROW);

	fireTableChanged(event);
    }

    //
    // TreeTableModelAdapter methods
    //

    public void fireTableChanged(TableModelEvent e) {
	listeners.tableChanged(e);
    }

    /**
     * Gets the column to returned by the {@code ListModel} method {@link
     * #getElementAt}.
     *
     * @see	    #setListColumn
     */
    public int getListColumn() {
	return listColumn;
    }

    protected ModelRowData getParent(ModelRowData row) {
	synchronized (rows) {
	    TreeNode parent = row.getTreeNode().getParent();
	    if (parent != null) {
		return getRow(parent);
	    }
	    return null;
	}
    }

    protected ModelRowData getRow(int row) {
	synchronized (rows) {
	    return rows.get(row);
	}
    }

    protected ModelRowData getRow(TreeNode node) {
	synchronized (rows) {
	    for (ModelRowData row : rows) {
		if (row.getTreeNode() == node) {
		    return row;
		}
	    }
	}
	return null;
    }

    public TreeNode getTreeNode(int row) {
	return getRow(row).getTreeNode();
    }

    public TreeTableModel getTreeTableModel() {
	return model;
    }

    public int indexOf(TreeNode node) {
	for (int i = 0, n = rows.size(); i < n; i++) {
	    ModelRowData row = getRow(i);
	    if (row.getTreeNode() == node) {
		return i;
	    }
	}
	return -1;
    }

    /**
     * Sets the column to returned by the {@code ListModel} method {@link
     * #getElementAt}.  The default value is 0.
     *
     * @see	    #getListColumn
     */
    public void setListColumn(int listColumn) {
	this.listColumn = listColumn;
    }

    //
    // Private methods
    //

    /**
     * Adds the given {@code TreeNode} and its children to the list of rows;
     * returns the current insertion point.
     */
    private int addRows(int index, TreeNode node, int level) {
	synchronized (rows) {
	    ModelRowData row = new ModelRowData(node, level);
	    rows.add(index, row);
	    index++;
	    level++;
	    for (Enumeration i = node.children(); i.hasMoreElements();) {
		TreeNode child = (TreeNode)i.nextElement();
		index = addRows(index, child, level);
	    }
	    return index;
	}
    }

    /**
     * Gets the index of the {@code (childIndex + 1)}th child of the given node.
     *
     * @return	    an index, or -1 if the given {@code TreeNode} is not found
     */
    private int indexOf(TreeNode node, int childIndex) {
	synchronized (rows) {
	    int i = indexOf(node);
	    if (i != -1) {
		ModelRowData row = getRow(i++);
		if (row.getTreeNode() == node) {

		    for (int n = rows.size(); i < n; i++) {
			int level = getRow(i).getLevel();
			if (level <= row.getLevel()) {
			    break;
			}

			if (level == row.getLevel() + 1) {
			    childIndex--;
			    if (childIndex == -1) {
				break;
			    }
			}
		    }
		}
	    }

	    return i;
	}
    }
}