001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2018 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.JavadocUtils; 033import com.puppycrawl.tools.checkstyle.utils.TokenUtils; 034 035/** 036 * The model that backs the parse tree in the GUI. 037 * 038 * @author Lars Kühne 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(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 columnClass = String.class; 123 break; 124 case 2: 125 columnClass = Integer.class; 126 break; 127 case 3: 128 columnClass = Integer.class; 129 break; 130 case 4: 131 columnClass = String.class; 132 break; 133 default: 134 throw new IllegalStateException(UNKNOWN_COLUMN_MSG); 135 } 136 return columnClass; 137 } 138 139 /** 140 * Returns the value to be displayed for node at column number. 141 * @param node the node 142 * @param column the column number 143 * @return the value to be displayed for node {@code node}, at column number {@code column}. 144 */ 145 public Object getValueAt(Object node, int column) { 146 final Object result; 147 148 if (node instanceof DetailNode) { 149 result = getValueAtDetailNode((DetailNode) node, column); 150 } 151 else { 152 result = getValueAtDetailAST((DetailAST) node, column); 153 } 154 155 return result; 156 } 157 158 /** 159 * Returns the child of parent at index. 160 * @param parent the node to get a child from. 161 * @param index the index of a child. 162 * @return the child of parent at index. 163 */ 164 public Object getChild(Object parent, int index) { 165 final Object result; 166 167 if (parent instanceof DetailNode) { 168 result = ((DetailNode) parent).getChildren()[index]; 169 } 170 else { 171 result = getChildAtDetailAst((DetailAST) parent, index); 172 } 173 174 return result; 175 } 176 177 /** 178 * Returns the number of children of parent. 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 && ((AST) parent).getType() == TokenTypes.COMMENT_CONTENT 191 && JavadocUtils.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 * @return the root. 207 */ 208 public Object getRoot() { 209 return root; 210 } 211 212 /** 213 * Whether the node is a leaf. 214 * @param node the node to check. 215 * @return true if the node is a leaf. 216 */ 217 public boolean isLeaf(Object node) { 218 return getChildCount(node) == 0; 219 } 220 221 /** 222 * Return the index of child in parent. If either {@code parent} 223 * or {@code child} is {@code null}, returns -1. 224 * If either {@code parent} or {@code child} don't 225 * belong to this tree model, returns -1. 226 * 227 * @param parent a node in the tree, obtained from this data source. 228 * @param child the node we are interested in. 229 * @return the index of the child in the parent, or -1 if either 230 * {@code child} or {@code parent} are {@code null} 231 * or don't belong to this tree model. 232 */ 233 public int getIndexOfChild(Object parent, Object child) { 234 int index = -1; 235 for (int i = 0; i < getChildCount(parent); i++) { 236 if (getChild(parent, i).equals(child)) { 237 index = i; 238 break; 239 } 240 } 241 return index; 242 } 243 244 /** 245 * Indicates whether the the value for node {@code node}, at column number {@code column} is 246 * editable. 247 * @param column the column number 248 * @return true if editable 249 */ 250 public boolean isCellEditable(int column) { 251 return false; 252 } 253 254 /** 255 * Creates artificial tree root. 256 * @return artificial tree root. 257 */ 258 private static DetailAST createArtificialTreeRoot() { 259 final ASTFactory factory = new ASTFactory(); 260 factory.setASTNodeClass(DetailAST.class.getName()); 261 return (DetailAST) factory.create(TokenTypes.EOF, "ROOT"); 262 } 263 264 /** 265 * Gets child of DetailAST node at specified index. 266 * @param parent DetailAST node 267 * @param index child index 268 * @return child DetailsAST or DetailNode if child is Javadoc node 269 * and parseMode is JAVA_WITH_JAVADOC_AND_COMMENTS. 270 */ 271 private Object getChildAtDetailAst(DetailAST parent, int index) { 272 final Object result; 273 if (parseMode == ParseMode.JAVA_WITH_JAVADOC_AND_COMMENTS 274 && parent.getType() == TokenTypes.COMMENT_CONTENT 275 && JavadocUtils.isJavadocComment(parent.getParent())) { 276 result = getJavadocTree(parent.getParent()); 277 } 278 else { 279 int currentIndex = 0; 280 DetailAST child = parent.getFirstChild(); 281 while (currentIndex < index) { 282 child = child.getNextSibling(); 283 currentIndex++; 284 } 285 result = child; 286 } 287 288 return result; 289 } 290 291 /** 292 * Gets a value for DetailNode object. 293 * @param node DetailNode(Javadoc) node. 294 * @param column column index. 295 * @return value at specified column. 296 */ 297 private static Object getValueAtDetailNode(DetailNode node, int column) { 298 final Object value; 299 300 switch (column) { 301 case 0: 302 // first column is tree model. no value needed 303 value = null; 304 break; 305 case 1: 306 value = JavadocUtils.getTokenName(node.getType()); 307 break; 308 case 2: 309 value = node.getLineNumber(); 310 break; 311 case 3: 312 value = node.getColumnNumber(); 313 break; 314 case 4: 315 value = node.getText(); 316 break; 317 default: 318 throw new IllegalStateException(UNKNOWN_COLUMN_MSG); 319 } 320 return value; 321 } 322 323 /** 324 * Gets a value for DetailAST object. 325 * @param ast DetailAST node. 326 * @param column column index. 327 * @return value at specified column. 328 */ 329 private static Object getValueAtDetailAST(DetailAST ast, int column) { 330 final Object value; 331 332 switch (column) { 333 case 0: 334 // first column is tree model. no value needed 335 value = null; 336 break; 337 case 1: 338 value = TokenUtils.getTokenName(ast.getType()); 339 break; 340 case 2: 341 value = ast.getLineNo(); 342 break; 343 case 3: 344 value = ast.getColumnNo(); 345 break; 346 case 4: 347 value = ast.getText(); 348 break; 349 default: 350 throw new IllegalStateException(UNKNOWN_COLUMN_MSG); 351 } 352 return value; 353 } 354 355 /** 356 * Gets Javadoc (DetailNode) tree of specified block comments. 357 * @param blockComment Javadoc comment as a block comment 358 * @return DetailNode tree 359 */ 360 private DetailNode getJavadocTree(DetailAST blockComment) { 361 DetailNode javadocTree = blockCommentToJavadocTree.get(blockComment); 362 if (javadocTree == null) { 363 javadocTree = new JavadocDetailNodeParser().parseJavadocAsDetailNode(blockComment) 364 .getTree(); 365 blockCommentToJavadocTree.put(blockComment, javadocTree); 366 } 367 return javadocTree; 368 } 369 370}