001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2016 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     * @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(new Runnable() {
117            @Override
118            public void run() {
119                fireTableDataChanged();
120            }
121        });
122    }
123
124    /**
125     * TreeExpansionListener that can update the table when tree changes.
126     */
127    private class UpdatingTreeExpansionListener implements TreeExpansionListener {
128        // Don't use fireTableRowsInserted() here; the selection model
129        // would get updated twice.
130        @Override
131        public void treeExpanded(TreeExpansionEvent event) {
132            fireTableDataChanged();
133        }
134
135        @Override
136        public void treeCollapsed(TreeExpansionEvent event) {
137            fireTableDataChanged();
138        }
139    }
140
141    /**
142     * TreeModelListener that can update the table when tree changes.
143     */
144    private class UpdatingTreeModelListener implements TreeModelListener {
145        @Override
146        public void treeNodesChanged(TreeModelEvent event) {
147            delayedFireTableDataChanged();
148        }
149
150        @Override
151        public void treeNodesInserted(TreeModelEvent event) {
152            delayedFireTableDataChanged();
153        }
154
155        @Override
156        public void treeNodesRemoved(TreeModelEvent event) {
157            delayedFireTableDataChanged();
158        }
159
160        @Override
161        public void treeStructureChanged(TreeModelEvent event) {
162            delayedFireTableDataChanged();
163        }
164    }
165}