001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2022 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 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 Source Location</a> 040 * 041 */ 042public class TreeTableModelAdapter extends AbstractTableModel { 043 044 /** A unique serial version identifier. */ 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 * 055 * @param treeTableModel Tree table model. 056 * @param tree JTree component. 057 */ 058 public TreeTableModelAdapter(ParseTreeTableModel treeTableModel, JTree tree) { 059 this.tree = tree; 060 this.treeTableModel = treeTableModel; 061 062 tree.addTreeExpansionListener(new UpdatingTreeExpansionListener()); 063 064 // Install a TreeModelListener that can update the table when 065 // mTree changes. We use delayedFireTableDataChanged as we can 066 // not be guaranteed the mTree will have finished processing 067 // the event before us. 068 treeTableModel.addTreeModelListener(new UpdatingTreeModelListener()); 069 } 070 071 // Wrappers, implementing TableModel interface. 072 073 @Override 074 public int getColumnCount() { 075 return treeTableModel.getColumnCount(); 076 } 077 078 @Override 079 public String getColumnName(int column) { 080 return treeTableModel.getColumnName(column); 081 } 082 083 @Override 084 public Class<?> getColumnClass(int column) { 085 return treeTableModel.getColumnClass(column); 086 } 087 088 @Override 089 public int getRowCount() { 090 return tree.getRowCount(); 091 } 092 093 @Override 094 public Object getValueAt(int row, int column) { 095 return treeTableModel.getValueAt(nodeForRow(row), column); 096 } 097 098 @Override 099 public boolean isCellEditable(int row, int column) { 100 return treeTableModel.isCellEditable(column); 101 } 102 103 /** 104 * Finds node for a given row. 105 * 106 * @param row Row for which to find a related node. 107 * @return Node for a given row. 108 */ 109 private Object nodeForRow(int row) { 110 final TreePath treePath = tree.getPathForRow(row); 111 return treePath.getLastPathComponent(); 112 } 113 114 /** 115 * TreeExpansionListener that can update the table when tree changes. 116 */ 117 private class UpdatingTreeExpansionListener implements TreeExpansionListener { 118 119 // Don't use fireTableRowsInserted() here; the selection model 120 // would get updated twice. 121 @Override 122 public void treeExpanded(TreeExpansionEvent event) { 123 fireTableDataChanged(); 124 } 125 126 @Override 127 public void treeCollapsed(TreeExpansionEvent event) { 128 fireTableDataChanged(); 129 } 130 131 } 132 133 /** 134 * TreeModelListener that can update the table when tree changes. 135 */ 136 private class UpdatingTreeModelListener implements TreeModelListener { 137 138 @Override 139 public void treeNodesChanged(TreeModelEvent event) { 140 delayedFireTableDataChanged(); 141 } 142 143 @Override 144 public void treeNodesInserted(TreeModelEvent event) { 145 delayedFireTableDataChanged(); 146 } 147 148 @Override 149 public void treeNodesRemoved(TreeModelEvent event) { 150 delayedFireTableDataChanged(); 151 } 152 153 @Override 154 public void treeStructureChanged(TreeModelEvent event) { 155 delayedFireTableDataChanged(); 156 } 157 158 /** 159 * Invokes fireTableDataChanged after all the pending events have been 160 * processed. SwingUtilities.invokeLater is used to handle this. 161 */ 162 private void delayedFireTableDataChanged() { 163 SwingUtilities.invokeLater(TreeTableModelAdapter.this::fireTableDataChanged); 164 } 165 166 } 167 168}