001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2017 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.gui;
021
022import javax.swing.JTree;
023import javax.swing.SwingUtilities;
024import javax.swing.event.TreeExpansionEvent;
025import javax.swing.event.TreeExpansionListener;
026import javax.swing.event.TreeModelEvent;
027import javax.swing.event.TreeModelListener;
028import javax.swing.table.AbstractTableModel;
029import javax.swing.tree.TreePath;
030
031/**
032 * This is a wrapper class takes a TreeTableModel and implements
033 * the table model interface. The implementation is trivial, with
034 * all of the event dispatching support provided by the superclass:
035 * the AbstractTableModel.
036 *
037 * <a href=
038 * "https://docs.oracle.com/cd/E48246_01/apirefs.1111/e13403/oracle/ide/controls/TreeTableModel.html">
039 * Original&nbsp;Source&nbsp;Location</a>
040 *
041 * @author Philip Milne
042 * @author Scott Violet
043 */
044public class TreeTableModelAdapter extends AbstractTableModel {
045    private static final long serialVersionUID = 8269213416115369275L;
046
047    /** JTree component. */
048    private final JTree tree;
049    /** Tree table model. */
050    private final transient ParseTreeTableModel treeTableModel;
051
052    /**
053     * Initialise tree and treeTableModel class attributes.
054     * @param treeTableModel Tree table model.
055     * @param tree JTree component.
056     */
057    public TreeTableModelAdapter(ParseTreeTableModel treeTableModel, JTree tree) {
058        this.tree = tree;
059        this.treeTableModel = treeTableModel;
060
061        tree.addTreeExpansionListener(new UpdatingTreeExpansionListener());
062
063        // Install a TreeModelListener that can update the table when
064        // mTree changes. We use delayedFireTableDataChanged as we can
065        // not be guaranteed the mTree will have finished processing
066        // the event before us.
067        treeTableModel.addTreeModelListener(new UpdatingTreeModelListener());
068    }
069
070    // Wrappers, implementing TableModel interface.
071
072    @Override
073    public int getColumnCount() {
074        return treeTableModel.getColumnCount();
075    }
076
077    @Override
078    public String getColumnName(int column) {
079        return treeTableModel.getColumnName(column);
080    }
081
082    @Override
083    public Class<?> getColumnClass(int column) {
084        return treeTableModel.getColumnClass(column);
085    }
086
087    @Override
088    public int getRowCount() {
089        return tree.getRowCount();
090    }
091
092    @Override
093    public Object getValueAt(int row, int column) {
094        return treeTableModel.getValueAt(nodeForRow(row), column);
095    }
096
097    @Override
098    public boolean isCellEditable(int row, int column) {
099        return treeTableModel.isCellEditable(column);
100    }
101
102    /**
103     * Finds node for a given row.
104     * @param row Row for which to find a related node.
105     * @return Node for a given row.
106     */
107    private Object nodeForRow(int row) {
108        final TreePath treePath = tree.getPathForRow(row);
109        return treePath.getLastPathComponent();
110    }
111
112    /**
113     * Invokes fireTableDataChanged after all the pending events have been
114     * processed. SwingUtilities.invokeLater is used to handle this.
115     */
116    private void delayedFireTableDataChanged() {
117        SwingUtilities.invokeLater(this::fireTableDataChanged);
118    }
119
120    /**
121     * TreeExpansionListener that can update the table when tree changes.
122     */
123    private class UpdatingTreeExpansionListener implements TreeExpansionListener {
124        // Don't use fireTableRowsInserted() here; the selection model
125        // would get updated twice.
126        @Override
127        public void treeExpanded(TreeExpansionEvent event) {
128            fireTableDataChanged();
129        }
130
131        @Override
132        public void treeCollapsed(TreeExpansionEvent event) {
133            fireTableDataChanged();
134        }
135    }
136
137    /**
138     * TreeModelListener that can update the table when tree changes.
139     */
140    private class UpdatingTreeModelListener implements TreeModelListener {
141        @Override
142        public void treeNodesChanged(TreeModelEvent event) {
143            delayedFireTableDataChanged();
144        }
145
146        @Override
147        public void treeNodesInserted(TreeModelEvent event) {
148            delayedFireTableDataChanged();
149        }
150
151        @Override
152        public void treeNodesRemoved(TreeModelEvent event) {
153            delayedFireTableDataChanged();
154        }
155
156        @Override
157        public void treeStructureChanged(TreeModelEvent event) {
158            delayedFireTableDataChanged();
159        }
160    }
161}