001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 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 java.io.Serial; 023 024import javax.swing.JTree; 025import javax.swing.SwingUtilities; 026import javax.swing.event.TreeExpansionEvent; 027import javax.swing.event.TreeExpansionListener; 028import javax.swing.event.TreeModelEvent; 029import javax.swing.event.TreeModelListener; 030import javax.swing.table.AbstractTableModel; 031import javax.swing.tree.TreePath; 032 033/** 034 * This is a wrapper class takes a TreeTableModel and implements 035 * the table model interface. The implementation is trivial, with 036 * all the event dispatching support provided by the superclass: 037 * the AbstractTableModel. 038 * <a href= 039 * "https://docs.oracle.com/cd/E48246_01/apirefs.1111/e13403/oracle/ide/controls/TreeTableModel.html"> 040 * Original Source Location</a> 041 * 042 */ 043public class TreeTableModelAdapter extends AbstractTableModel { 044 045 /** A unique serial version identifier. */ 046 @Serial 047 private static final long serialVersionUID = 8269213416115369275L; 048 049 /** JTree component. */ 050 private final JTree tree; 051 /** Tree table model. */ 052 private final transient ParseTreeTableModel treeTableModel; 053 054 /** 055 * Initialise tree and treeTableModel class attributes. 056 * 057 * @param treeTableModel Tree table model. 058 * @param tree JTree component. 059 */ 060 public TreeTableModelAdapter(ParseTreeTableModel treeTableModel, JTree tree) { 061 this.tree = tree; 062 this.treeTableModel = treeTableModel; 063 064 tree.addTreeExpansionListener(new UpdatingTreeExpansionListener()); 065 066 // Install a TreeModelListener that can update the table when 067 // mTree changes. We use delayedFireTableDataChanged as we can 068 // not be guaranteed the mTree will have finished processing 069 // the event before us. 070 treeTableModel.addTreeModelListener(new UpdatingTreeModelListener()); 071 } 072 073 // Wrappers, implementing TableModel interface. 074 075 @Override 076 public int getColumnCount() { 077 return treeTableModel.getColumnCount(); 078 } 079 080 @Override 081 public String getColumnName(int column) { 082 return treeTableModel.getColumnName(column); 083 } 084 085 @Override 086 public Class<?> getColumnClass(int column) { 087 return treeTableModel.getColumnClass(column); 088 } 089 090 @Override 091 public int getRowCount() { 092 return tree.getRowCount(); 093 } 094 095 @Override 096 public Object getValueAt(int row, int column) { 097 return treeTableModel.getValueAt(nodeForRow(row), column); 098 } 099 100 @Override 101 public boolean isCellEditable(int row, int column) { 102 return treeTableModel.isCellEditable(column); 103 } 104 105 /** 106 * Finds node for a given row. 107 * 108 * @param row Row for which to find a related node. 109 * @return Node for a given row. 110 */ 111 private Object nodeForRow(int row) { 112 final TreePath treePath = tree.getPathForRow(row); 113 return treePath.getLastPathComponent(); 114 } 115 116 /** 117 * TreeExpansionListener that can update the table when tree changes. 118 */ 119 private final class UpdatingTreeExpansionListener implements TreeExpansionListener { 120 /** 121 * Creates a new {@code UpdatingTreeExpansionListener} instance. 122 */ 123 private UpdatingTreeExpansionListener() { 124 // no code by default 125 } 126 127 // Don't use fireTableRowsInserted() here; the selection model 128 // would get updated twice. 129 @Override 130 public void treeExpanded(TreeExpansionEvent event) { 131 fireTableDataChanged(); 132 } 133 134 @Override 135 public void treeCollapsed(TreeExpansionEvent event) { 136 fireTableDataChanged(); 137 } 138 139 } 140 141 /** 142 * TreeModelListener that can update the table when tree changes. 143 */ 144 private final class UpdatingTreeModelListener implements TreeModelListener { 145 /** 146 * Creates a new {@code UpdatingTreeModelListener} instance. 147 */ 148 private UpdatingTreeModelListener() { 149 // no code by default 150 } 151 152 @Override 153 public void treeNodesChanged(TreeModelEvent event) { 154 delayedFireTableDataChanged(); 155 } 156 157 @Override 158 public void treeNodesInserted(TreeModelEvent event) { 159 delayedFireTableDataChanged(); 160 } 161 162 @Override 163 public void treeNodesRemoved(TreeModelEvent event) { 164 delayedFireTableDataChanged(); 165 } 166 167 @Override 168 public void treeStructureChanged(TreeModelEvent event) { 169 delayedFireTableDataChanged(); 170 } 171 172 /** 173 * Invokes fireTableDataChanged after all the pending events have been 174 * processed. SwingUtilities.invokeLater is used to handle this. 175 */ 176 private void delayedFireTableDataChanged() { 177 SwingUtilities.invokeLater(TreeTableModelAdapter.this::fireTableDataChanged); 178 } 179 180 } 181 182}