001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2024 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.util.HashMap; 023import java.util.Map; 024 025import com.puppycrawl.tools.checkstyle.JavadocDetailNodeParser; 026import com.puppycrawl.tools.checkstyle.api.DetailAST; 027import com.puppycrawl.tools.checkstyle.api.DetailNode; 028import com.puppycrawl.tools.checkstyle.api.TokenTypes; 029import com.puppycrawl.tools.checkstyle.gui.MainFrameModel.ParseMode; 030import com.puppycrawl.tools.checkstyle.utils.JavadocUtil; 031import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 032 033/** 034 * The model that backs the parse tree in the GUI. 035 * 036 */ 037public class ParseTreeTablePresentation { 038 039 /** Exception message. */ 040 private static final String UNKNOWN_COLUMN_MSG = "Unknown column"; 041 042 /** Column names. */ 043 private static final String[] COLUMN_NAMES = { 044 "Tree", 045 "Type", 046 "Line", 047 "Column", 048 "Text", 049 }; 050 051 /** Cache to store already parsed Javadoc comments. Used for optimisation purposes. */ 052 private final Map<DetailAST, DetailNode> blockCommentToJavadocTree = new HashMap<>(); 053 054 /** The root node of the tree table model. */ 055 private DetailAST root; 056 057 /** Parsing mode. */ 058 private ParseMode parseMode; 059 060 /** 061 * Constructor initialise root node. 062 * 063 * @param parseTree DetailAST parse tree. 064 */ 065 public ParseTreeTablePresentation(DetailAST parseTree) { 066 root = parseTree; 067 } 068 069 /** 070 * Set parse tree. 071 * 072 * @param parseTree DetailAST parse tree. 073 */ 074 protected final void setRoot(DetailAST parseTree) { 075 root = parseTree; 076 } 077 078 /** 079 * Set parse mode. 080 * 081 * @param mode ParseMode enum 082 */ 083 protected void setParseMode(ParseMode mode) { 084 parseMode = mode; 085 } 086 087 /** 088 * Returns number of available columns. 089 * 090 * @return the number of available columns. 091 */ 092 public int getColumnCount() { 093 return COLUMN_NAMES.length; 094 } 095 096 /** 097 * Returns name for specified column number. 098 * 099 * @param column the column number 100 * @return the name for column number {@code column}. 101 */ 102 public String getColumnName(int column) { 103 return COLUMN_NAMES[column]; 104 } 105 106 /** 107 * Returns type of specified column number. 108 * 109 * @param column the column number 110 * @return the type for column number {@code column}. 111 * @throws IllegalStateException if an unknown column index was specified. 112 */ 113 // -@cs[ForbidWildcardAsReturnType] We need to satisfy javax.swing.table.AbstractTableModel 114 // public Class<?> getColumnClass(int columnIndex) {...} 115 public Class<?> getColumnClass(int column) { 116 final Class<?> columnClass; 117 118 switch (column) { 119 case 0: 120 columnClass = ParseTreeTableModel.class; 121 break; 122 case 1: 123 case 4: 124 columnClass = String.class; 125 break; 126 case 2: 127 case 3: 128 columnClass = Integer.class; 129 break; 130 default: 131 throw new IllegalStateException(UNKNOWN_COLUMN_MSG); 132 } 133 return columnClass; 134 } 135 136 /** 137 * Returns the value to be displayed for node at column number. 138 * 139 * @param node the node 140 * @param column the column number 141 * @return the value to be displayed for node {@code node}, at column number {@code column}. 142 */ 143 public Object getValueAt(Object node, int column) { 144 final Object result; 145 146 if (node instanceof DetailNode) { 147 result = getValueAtDetailNode((DetailNode) node, column); 148 } 149 else { 150 result = getValueAtDetailAST((DetailAST) node, column); 151 } 152 153 return result; 154 } 155 156 /** 157 * Returns the child of parent at index. 158 * 159 * @param parent the node to get a child from. 160 * @param index the index of a child. 161 * @return the child of parent at index. 162 */ 163 public Object getChild(Object parent, int index) { 164 final Object result; 165 166 if (parent instanceof DetailNode) { 167 result = ((DetailNode) parent).getChildren()[index]; 168 } 169 else { 170 result = getChildAtDetailAst((DetailAST) parent, index); 171 } 172 173 return result; 174 } 175 176 /** 177 * Returns the number of children of parent. 178 * 179 * @param parent the node to count children for. 180 * @return the number of children of the node parent. 181 */ 182 public int getChildCount(Object parent) { 183 final int result; 184 185 if (parent instanceof DetailNode) { 186 result = ((DetailNode) parent).getChildren().length; 187 } 188 else { 189 if (parseMode == ParseMode.JAVA_WITH_JAVADOC_AND_COMMENTS 190 && ((DetailAST) parent).getType() == TokenTypes.COMMENT_CONTENT 191 && JavadocUtil.isJavadocComment(((DetailAST) parent).getParent())) { 192 // getChildCount return 0 on COMMENT_CONTENT, 193 // but we need to attach javadoc tree, that is separate tree 194 result = 1; 195 } 196 else { 197 result = ((DetailAST) parent).getChildCount(); 198 } 199 } 200 201 return result; 202 } 203 204 /** 205 * Returns value of root. 206 * 207 * @return the root. 208 */ 209 public Object getRoot() { 210 return root; 211 } 212 213 /** 214 * Whether the node is a leaf. 215 * 216 * @param node the node to check. 217 * @return true if the node is a leaf. 218 */ 219 public boolean isLeaf(Object node) { 220 return getChildCount(node) == 0; 221 } 222 223 /** 224 * Return the index of child in parent. If either {@code parent} 225 * or {@code child} is {@code null}, returns -1. 226 * If either {@code parent} or {@code child} don't 227 * belong to this tree model, returns -1. 228 * 229 * @param parent a node in the tree, obtained from this data source. 230 * @param child the node we are interested in. 231 * @return the index of the child in the parent, or -1 if either 232 * {@code child} or {@code parent} are {@code null} 233 * or don't belong to this tree model. 234 */ 235 public int getIndexOfChild(Object parent, Object child) { 236 int index = -1; 237 for (int i = 0; i < getChildCount(parent); i++) { 238 if (getChild(parent, i).equals(child)) { 239 index = i; 240 break; 241 } 242 } 243 return index; 244 } 245 246 /** 247 * Indicates whether the value for node {@code node}, at column number {@code column} is 248 * editable. 249 * 250 * @param column the column number 251 * @return true if editable 252 */ 253 public boolean isCellEditable(int column) { 254 return false; 255 } 256 257 /** 258 * Gets child of DetailAST node at specified index. 259 * 260 * @param parent DetailAST node 261 * @param index child index 262 * @return child DetailsAST or DetailNode if child is Javadoc node 263 * and parseMode is JAVA_WITH_JAVADOC_AND_COMMENTS. 264 */ 265 private Object getChildAtDetailAst(DetailAST parent, int index) { 266 final Object result; 267 if (parseMode == ParseMode.JAVA_WITH_JAVADOC_AND_COMMENTS 268 && parent.getType() == TokenTypes.COMMENT_CONTENT 269 && JavadocUtil.isJavadocComment(parent.getParent())) { 270 result = getJavadocTree(parent.getParent()); 271 } 272 else { 273 int currentIndex = 0; 274 DetailAST child = parent.getFirstChild(); 275 while (currentIndex < index) { 276 child = child.getNextSibling(); 277 currentIndex++; 278 } 279 result = child; 280 } 281 282 return result; 283 } 284 285 /** 286 * Gets a value for DetailNode object. 287 * 288 * @param node DetailNode(Javadoc) node. 289 * @param column column index. 290 * @return value at specified column. 291 * @throws IllegalStateException if an unknown column index was specified. 292 */ 293 private static Object getValueAtDetailNode(DetailNode node, int column) { 294 final Object value; 295 296 switch (column) { 297 case 0: 298 // first column is tree model. no value needed 299 value = null; 300 break; 301 case 1: 302 value = JavadocUtil.getTokenName(node.getType()); 303 break; 304 case 2: 305 value = node.getLineNumber(); 306 break; 307 case 3: 308 value = node.getColumnNumber(); 309 break; 310 case 4: 311 value = node.getText(); 312 break; 313 default: 314 throw new IllegalStateException(UNKNOWN_COLUMN_MSG); 315 } 316 return value; 317 } 318 319 /** 320 * Gets a value for DetailAST object. 321 * 322 * @param ast DetailAST node. 323 * @param column column index. 324 * @return value at specified column. 325 * @throws IllegalStateException if an unknown column index was specified. 326 */ 327 private static Object getValueAtDetailAST(DetailAST ast, int column) { 328 final Object value; 329 330 switch (column) { 331 case 0: 332 // first column is tree model. no value needed 333 value = null; 334 break; 335 case 1: 336 value = TokenUtil.getTokenName(ast.getType()); 337 break; 338 case 2: 339 value = ast.getLineNo(); 340 break; 341 case 3: 342 value = ast.getColumnNo(); 343 break; 344 case 4: 345 value = ast.getText(); 346 break; 347 default: 348 throw new IllegalStateException(UNKNOWN_COLUMN_MSG); 349 } 350 return value; 351 } 352 353 /** 354 * Gets Javadoc (DetailNode) tree of specified block comments. 355 * 356 * @param blockComment Javadoc comment as a block comment 357 * @return root of DetailNode tree 358 */ 359 private DetailNode getJavadocTree(DetailAST blockComment) { 360 return blockCommentToJavadocTree.computeIfAbsent(blockComment, 361 ParseTreeTablePresentation::parseJavadocTree); 362 } 363 364 /** 365 * Parses Javadoc (DetailNode) tree of specified block comments. 366 * 367 * @param blockComment Javadoc comment as a block comment 368 * @return root of DetailNode tree 369 */ 370 private static DetailNode parseJavadocTree(DetailAST blockComment) { 371 return new JavadocDetailNodeParser().parseJavadocAsDetailNode(blockComment).getTree(); 372 } 373 374}