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