001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2018 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 */
042public class TreeTableModelAdapter extends AbstractTableModel {
043
044    private static final long serialVersionUID = 8269213416115369275L;
045
046    /** JTree component. */
047    private final JTree tree;
048    /** Tree table model. */
049    private final transient ParseTreeTableModel treeTableModel;
050
051    /**
052     * Initialise tree and treeTableModel class attributes.
053     * @param treeTableModel Tree table model.
054     * @param tree JTree component.
055     */
056    public TreeTableModelAdapter(ParseTreeTableModel treeTableModel, JTree tree) {
057        this.tree = tree;
058        this.treeTableModel = treeTableModel;
059
060        tree.addTreeExpansionListener(new UpdatingTreeExpansionListener());
061
062        // Install a TreeModelListener that can update the table when
063        // mTree changes. We use delayedFireTableDataChanged as we can
064        // not be guaranteed the mTree will have finished processing
065        // the event before us.
066        treeTableModel.addTreeModelListener(new UpdatingTreeModelListener());
067    }
068
069    // Wrappers, implementing TableModel interface.
070
071    @Override
072    public int getColumnCount() {
073        return treeTableModel.getColumnCount();
074    }
075
076    @Override
077    public String getColumnName(int column) {
078        return treeTableModel.getColumnName(column);
079    }
080
081    @Override
082    public Class<?> getColumnClass(int column) {
083        return treeTableModel.getColumnClass(column);
084    }
085
086    @Override
087    public int getRowCount() {
088        return tree.getRowCount();
089    }
090
091    @Override
092    public Object getValueAt(int row, int column) {
093        return treeTableModel.getValueAt(nodeForRow(row), column);
094    }
095
096    @Override
097    public boolean isCellEditable(int row, int column) {
098        return treeTableModel.isCellEditable(column);
099    }
100
101    /**
102     * Finds node for a given row.
103     * @param row Row for which to find a related node.
104     * @return Node for a given row.
105     */
106    private Object nodeForRow(int row) {
107        final TreePath treePath = tree.getPathForRow(row);
108        return treePath.getLastPathComponent();
109    }
110
111    /**
112     * Invokes fireTableDataChanged after all the pending events have been
113     * processed. SwingUtilities.invokeLater is used to handle this.
114     */
115    private void delayedFireTableDataChanged() {
116        SwingUtilities.invokeLater(this::fireTableDataChanged);
117    }
118
119    /**
120     * TreeExpansionListener that can update the table when tree changes.
121     */
122    private class UpdatingTreeExpansionListener implements TreeExpansionListener {
123
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    /**
139     * TreeModelListener that can update the table when tree changes.
140     */
141    private class UpdatingTreeModelListener implements TreeModelListener {
142
143        @Override
144        public void treeNodesChanged(TreeModelEvent event) {
145            delayedFireTableDataChanged();
146        }
147
148        @Override
149        public void treeNodesInserted(TreeModelEvent event) {
150            delayedFireTableDataChanged();
151        }
152
153        @Override
154        public void treeNodesRemoved(TreeModelEvent event) {
155            delayedFireTableDataChanged();
156        }
157
158        @Override
159        public void treeStructureChanged(TreeModelEvent event) {
160            delayedFireTableDataChanged();
161        }
162
163    }
164
165}