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