001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2017 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 ParseTreeTablePModel { 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 * @param parseTree DetailAST parse tree. 067 */ 068 public ParseTreeTablePModel(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 * @return the number of available columns. 091 */ 092 public int getColumnCount() { 093 return COLUMN_NAMES.length; 094 } 095 096 /** 097 * @param column the column number 098 * @return the name for column number {@code column}. 099 */ 100 public String getColumnName(int column) { 101 return COLUMN_NAMES[column]; 102 } 103 104 /** 105 * @param column the column number 106 * @return the type for column number {@code column}. 107 */ 108 // -@cs[ForbidWildcardAsReturnType] We need to satisfy javax.swing.table.AbstractTableModel 109 // public Class<?> getColumnClass(int columnIndex) {...} 110 public Class<?> getColumnClass(int column) { 111 final Class<?> columnClass; 112 113 switch (column) { 114 case 0: 115 columnClass = ParseTreeTableModel.class; 116 break; 117 case 1: 118 columnClass = String.class; 119 break; 120 case 2: 121 columnClass = Integer.class; 122 break; 123 case 3: 124 columnClass = Integer.class; 125 break; 126 case 4: 127 columnClass = String.class; 128 break; 129 default: 130 throw new IllegalStateException(UNKNOWN_COLUMN_MSG); 131 } 132 return columnClass; 133 } 134 135 /** 136 * @param node the node 137 * @param column the column number 138 * @return the value to be displayed for node {@code node}, at column number {@code column}. 139 */ 140 public Object getValueAt(Object node, int column) { 141 final Object result; 142 143 if (node instanceof DetailNode) { 144 result = getValueAtDetailNode((DetailNode) node, column); 145 } 146 else { 147 result = getValueAtDetailAST((DetailAST) node, column); 148 } 149 150 return result; 151 } 152 153 /** 154 * Returns the child of parent at index. 155 * @param parent the node to get a child from. 156 * @param index the index of a child. 157 * @return the child of parent at index. 158 */ 159 public Object getChild(Object parent, int index) { 160 final Object result; 161 162 if (parent instanceof DetailNode) { 163 result = ((DetailNode) parent).getChildren()[index]; 164 } 165 else { 166 result = getChildAtDetailAst((DetailAST) parent, index); 167 } 168 169 return result; 170 } 171 172 /** 173 * Returns the number of children of parent. 174 * @param parent the node to count children for. 175 * @return the number of children of the node parent. 176 */ 177 public int getChildCount(Object parent) { 178 final int result; 179 180 if (parent instanceof DetailNode) { 181 result = ((DetailNode) parent).getChildren().length; 182 } 183 else { 184 if (parseMode == ParseMode.JAVA_WITH_JAVADOC_AND_COMMENTS 185 && ((AST) parent).getType() == TokenTypes.COMMENT_CONTENT 186 && JavadocUtils.isJavadocComment(((DetailAST) parent).getParent())) { 187 //getChildCount return 0 on COMMENT_CONTENT, 188 //but we need to attach javadoc tree, that is separate tree 189 result = 1; 190 } 191 else { 192 result = ((DetailAST) parent).getChildCount(); 193 } 194 } 195 196 return result; 197 } 198 199 /** 200 * @return the root. 201 */ 202 public Object getRoot() { 203 return root; 204 } 205 206 /** 207 * Whether the node is a leaf. 208 * @param node the node to check. 209 * @return true if the node is a leaf. 210 */ 211 public boolean isLeaf(Object node) { 212 return getChildCount(node) == 0; 213 } 214 215 /** 216 * Return the index of child in parent. If either <code>parent</code> 217 * or <code>child</code> is <code>null</code>, returns -1. 218 * If either <code>parent</code> or <code>child</code> don't 219 * belong to this tree model, returns -1. 220 * 221 * @param parent a node in the tree, obtained from this data source. 222 * @param child the node we are interested in. 223 * @return the index of the child in the parent, or -1 if either 224 * <code>child</code> or <code>parent</code> are <code>null</code> 225 * or don't belong to this tree model. 226 */ 227 public int getIndexOfChild(Object parent, Object child) { 228 int index = -1; 229 for (int i = 0; i < getChildCount(parent); i++) { 230 if (getChild(parent, i).equals(child)) { 231 index = i; 232 break; 233 } 234 } 235 return index; 236 } 237 238 /** 239 * Indicates whether the the value for node {@code node}, at column number {@code column} is 240 * editable. 241 * @param column the column number 242 * @return true if editable 243 */ 244 public boolean isCellEditable(int column) { 245 return false; 246 } 247 248 /** 249 * Creates artificial tree root. 250 * @return artificial tree root. 251 */ 252 private static DetailAST createArtificialTreeRoot() { 253 final ASTFactory factory = new ASTFactory(); 254 factory.setASTNodeClass(DetailAST.class.getName()); 255 return (DetailAST) factory.create(TokenTypes.EOF, "ROOT"); 256 } 257 258 /** 259 * Gets child of DetailAST node at specified index. 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 && JavadocUtils.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 * @param node DetailNode(Javadoc) node. 288 * @param column column index. 289 * @return value at specified column. 290 */ 291 private Object getValueAtDetailNode(DetailNode node, int column) { 292 final Object value; 293 294 switch (column) { 295 case 0: 296 // first column is tree model. no value needed 297 value = null; 298 break; 299 case 1: 300 value = JavadocUtils.getTokenName(node.getType()); 301 break; 302 case 2: 303 value = node.getLineNumber(); 304 break; 305 case 3: 306 value = node.getColumnNumber(); 307 break; 308 case 4: 309 value = node.getText(); 310 break; 311 default: 312 throw new IllegalStateException(UNKNOWN_COLUMN_MSG); 313 } 314 return value; 315 } 316 317 /** 318 * Gets a value for DetailAST object. 319 * @param ast DetailAST node. 320 * @param column column index. 321 * @return value at specified column. 322 */ 323 private Object getValueAtDetailAST(DetailAST ast, int column) { 324 final Object value; 325 326 switch (column) { 327 case 0: 328 // first column is tree model. no value needed 329 value = null; 330 break; 331 case 1: 332 value = TokenUtils.getTokenName(ast.getType()); 333 break; 334 case 2: 335 value = ast.getLineNo(); 336 break; 337 case 3: 338 value = ast.getColumnNo(); 339 break; 340 case 4: 341 value = ast.getText(); 342 break; 343 default: 344 throw new IllegalStateException(UNKNOWN_COLUMN_MSG); 345 } 346 return value; 347 } 348 349 /** 350 * Gets Javadoc (DetailNode) tree of specified block comments. 351 * @param blockComment Javadoc comment as a block comment 352 * @return DetailNode tree 353 */ 354 private DetailNode getJavadocTree(DetailAST blockComment) { 355 DetailNode javadocTree = blockCommentToJavadocTree.get(blockComment); 356 if (javadocTree == null) { 357 javadocTree = new JavadocDetailNodeParser().parseJavadocAsDetailNode(blockComment) 358 .getTree(); 359 blockCommentToJavadocTree.put(blockComment, javadocTree); 360 } 361 return javadocTree; 362 } 363}