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.api; 021 022import java.util.BitSet; 023 024import antlr.CommonASTWithHiddenTokens; 025import antlr.Token; 026import antlr.collections.AST; 027import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 028 029/** 030 * An extension of the CommonAST that records the line and column number. 031 * 032 * @see <a href="https://www.antlr.org/">ANTLR Website</a> 033 * @noinspection FieldNotUsedInToString, SerializableHasSerializationMethods 034 */ 035public final class DetailAST extends CommonASTWithHiddenTokens { 036 037 private static final long serialVersionUID = -2580884815577559874L; 038 039 /** Constant to indicate if not calculated the child count. */ 040 private static final int NOT_INITIALIZED = Integer.MIN_VALUE; 041 042 /** The line number. **/ 043 private int lineNo = NOT_INITIALIZED; 044 /** The column number. **/ 045 private int columnNo = NOT_INITIALIZED; 046 047 /** Number of children. */ 048 private int childCount = NOT_INITIALIZED; 049 /** The parent token. */ 050 private DetailAST parent; 051 /** Previous sibling. */ 052 private DetailAST previousSibling; 053 054 /** 055 * All token types in this branch. 056 * Token 'x' (where x is an int) is in this branch 057 * if branchTokenTypes.get(x) is true. 058 */ 059 private BitSet branchTokenTypes; 060 061 @Override 062 public void initialize(Token tok) { 063 super.initialize(tok); 064 lineNo = tok.getLine(); 065 066 // expect columns to start @ 0 067 columnNo = tok.getColumn() - 1; 068 } 069 070 @Override 071 public void initialize(AST ast) { 072 final DetailAST detailAst = (DetailAST) ast; 073 setText(detailAst.getText()); 074 setType(detailAst.getType()); 075 lineNo = detailAst.getLineNo(); 076 columnNo = detailAst.getColumnNo(); 077 hiddenAfter = detailAst.getHiddenAfter(); 078 hiddenBefore = detailAst.getHiddenBefore(); 079 } 080 081 @Override 082 public void setFirstChild(AST ast) { 083 clearBranchTokenTypes(); 084 clearChildCountCache(this); 085 super.setFirstChild(ast); 086 if (ast != null) { 087 ((DetailAST) ast).setParent(this); 088 } 089 } 090 091 @Override 092 public void setNextSibling(AST ast) { 093 clearBranchTokenTypes(); 094 clearChildCountCache(parent); 095 super.setNextSibling(ast); 096 if (ast != null && parent != null) { 097 ((DetailAST) ast).setParent(parent); 098 } 099 if (ast != null) { 100 ((DetailAST) ast).previousSibling = this; 101 } 102 } 103 104 /** 105 * Add previous sibling. 106 * @param ast 107 * DetailAST object. 108 */ 109 public void addPreviousSibling(DetailAST ast) { 110 clearBranchTokenTypes(); 111 clearChildCountCache(parent); 112 if (ast != null) { 113 //parent is set in setNextSibling or parent.setFirstChild 114 final DetailAST previousSiblingNode = previousSibling; 115 116 if (previousSiblingNode != null) { 117 ast.previousSibling = previousSiblingNode; 118 previousSiblingNode.setNextSibling(ast); 119 } 120 else if (parent != null) { 121 parent.setFirstChild(ast); 122 } 123 124 ast.setNextSibling(this); 125 previousSibling = ast; 126 } 127 } 128 129 /** 130 * Add next sibling. 131 * @param ast 132 * DetailAST object. 133 */ 134 public void addNextSibling(DetailAST ast) { 135 clearBranchTokenTypes(); 136 clearChildCountCache(parent); 137 if (ast != null) { 138 //parent is set in setNextSibling 139 final DetailAST nextSibling = getNextSibling(); 140 141 if (nextSibling != null) { 142 ast.setNextSibling(nextSibling); 143 nextSibling.previousSibling = ast; 144 } 145 146 ast.previousSibling = this; 147 setNextSibling(ast); 148 } 149 } 150 151 @Override 152 public void addChild(AST ast) { 153 clearBranchTokenTypes(); 154 clearChildCountCache(this); 155 if (ast != null) { 156 ((DetailAST) ast).setParent(this); 157 ((DetailAST) ast).previousSibling = getLastChild(); 158 } 159 super.addChild(ast); 160 } 161 162 /** 163 * Returns the number of child nodes one level below this node. That is is 164 * does not recurse down the tree. 165 * @return the number of child nodes 166 */ 167 public int getChildCount() { 168 // lazy init 169 if (childCount == NOT_INITIALIZED) { 170 childCount = 0; 171 AST child = getFirstChild(); 172 173 while (child != null) { 174 childCount += 1; 175 child = child.getNextSibling(); 176 } 177 } 178 return childCount; 179 } 180 181 /** 182 * Returns the number of direct child tokens that have the specified type. 183 * @param type the token type to match 184 * @return the number of matching token 185 */ 186 public int getChildCount(int type) { 187 int count = 0; 188 for (AST ast = getFirstChild(); ast != null; ast = ast.getNextSibling()) { 189 if (ast.getType() == type) { 190 count++; 191 } 192 } 193 return count; 194 } 195 196 /** 197 * Set the parent token. 198 * @param parent the parent token 199 */ 200 private void setParent(DetailAST parent) { 201 DetailAST instance = this; 202 do { 203 instance.clearBranchTokenTypes(); 204 instance.parent = parent; 205 final DetailAST nextSibling = instance.getNextSibling(); 206 if (nextSibling != null) { 207 nextSibling.previousSibling = instance; 208 } 209 210 instance = nextSibling; 211 } while (instance != null); 212 } 213 214 /** 215 * Returns the parent token. 216 * @return the parent token 217 */ 218 public DetailAST getParent() { 219 return parent; 220 } 221 222 /** 223 * Gets line number. 224 * @return the line number 225 */ 226 public int getLineNo() { 227 int resultNo = -1; 228 229 if (lineNo == NOT_INITIALIZED) { 230 // an inner AST that has been initialized 231 // with initialize(String text) 232 resultNo = findLineNo(getFirstChild()); 233 234 if (resultNo == -1) { 235 resultNo = findLineNo(getNextSibling()); 236 } 237 } 238 if (resultNo == -1) { 239 resultNo = lineNo; 240 } 241 return resultNo; 242 } 243 244 /** 245 * Set line number. 246 * @param lineNo 247 * line number. 248 */ 249 public void setLineNo(int lineNo) { 250 this.lineNo = lineNo; 251 } 252 253 /** 254 * Gets column number. 255 * @return the column number 256 */ 257 public int getColumnNo() { 258 int resultNo = -1; 259 260 if (columnNo == NOT_INITIALIZED) { 261 // an inner AST that has been initialized 262 // with initialize(String text) 263 resultNo = findColumnNo(getFirstChild()); 264 265 if (resultNo == -1) { 266 resultNo = findColumnNo(getNextSibling()); 267 } 268 } 269 if (resultNo == -1) { 270 resultNo = columnNo; 271 } 272 return resultNo; 273 } 274 275 /** 276 * Set column number. 277 * @param columnNo 278 * column number. 279 */ 280 public void setColumnNo(int columnNo) { 281 this.columnNo = columnNo; 282 } 283 284 /** 285 * Gets the last child node. 286 * @return the last child node 287 */ 288 public DetailAST getLastChild() { 289 DetailAST ast = getFirstChild(); 290 while (ast != null && ast.getNextSibling() != null) { 291 ast = ast.getNextSibling(); 292 } 293 return ast; 294 } 295 296 /** 297 * Finds column number in the first non-comment node. 298 * 299 * @param ast DetailAST node. 300 * @return Column number if non-comment node exists, -1 otherwise. 301 */ 302 private static int findColumnNo(DetailAST ast) { 303 int resultNo = -1; 304 DetailAST node = ast; 305 while (node != null) { 306 // comment node can't be start of any java statement/definition 307 if (TokenUtil.isCommentType(node.getType())) { 308 node = node.getNextSibling(); 309 } 310 else { 311 resultNo = node.getColumnNo(); 312 break; 313 } 314 } 315 return resultNo; 316 } 317 318 /** 319 * Finds line number in the first non-comment node. 320 * 321 * @param ast DetailAST node. 322 * @return Line number if non-comment node exists, -1 otherwise. 323 */ 324 private static int findLineNo(DetailAST ast) { 325 int resultNo = -1; 326 DetailAST node = ast; 327 while (node != null) { 328 // comment node can't be start of any java statement/definition 329 if (TokenUtil.isCommentType(node.getType())) { 330 node = node.getNextSibling(); 331 } 332 else { 333 resultNo = node.getLineNo(); 334 break; 335 } 336 } 337 return resultNo; 338 } 339 340 /** 341 * Returns token type with branch. 342 * @return the token types that occur in the branch as a sorted set. 343 */ 344 private BitSet getBranchTokenTypes() { 345 // lazy init 346 if (branchTokenTypes == null) { 347 branchTokenTypes = new BitSet(); 348 branchTokenTypes.set(getType()); 349 350 // add union of all children 351 DetailAST child = getFirstChild(); 352 while (child != null) { 353 final BitSet childTypes = child.getBranchTokenTypes(); 354 branchTokenTypes.or(childTypes); 355 356 child = child.getNextSibling(); 357 } 358 } 359 return branchTokenTypes; 360 } 361 362 /** 363 * Checks if this branch of the parse tree contains a token 364 * of the provided type. 365 * @param type a TokenType 366 * @return true if and only if this branch (including this node) 367 * contains a token of type {@code type}. 368 */ 369 public boolean branchContains(int type) { 370 return getBranchTokenTypes().get(type); 371 } 372 373 /** 374 * Returns the previous sibling or null if no such sibling exists. 375 * @return the previous sibling or null if no such sibling exists. 376 */ 377 public DetailAST getPreviousSibling() { 378 return previousSibling; 379 } 380 381 /** 382 * Returns the first child token that makes a specified type. 383 * @param type the token type to match 384 * @return the matching token, or null if no match 385 */ 386 public DetailAST findFirstToken(int type) { 387 DetailAST returnValue = null; 388 for (DetailAST ast = getFirstChild(); ast != null; ast = ast.getNextSibling()) { 389 if (ast.getType() == type) { 390 returnValue = ast; 391 break; 392 } 393 } 394 return returnValue; 395 } 396 397 @Override 398 public String toString() { 399 return super.toString() + "[" + getLineNo() + "x" + getColumnNo() + "]"; 400 } 401 402 @Override 403 public DetailAST getNextSibling() { 404 return (DetailAST) super.getNextSibling(); 405 } 406 407 @Override 408 public DetailAST getFirstChild() { 409 return (DetailAST) super.getFirstChild(); 410 } 411 412 /** 413 * Clears the child count for the ast instance. 414 * @param ast The ast to clear. 415 */ 416 private static void clearChildCountCache(DetailAST ast) { 417 if (ast != null) { 418 ast.childCount = NOT_INITIALIZED; 419 } 420 } 421 422 /** 423 * Clears branchTokenTypes cache for all parents of the current DetailAST instance, and the 424 * child count for the current DetailAST instance. 425 */ 426 private void clearBranchTokenTypes() { 427 DetailAST prevParent = parent; 428 while (prevParent != null) { 429 prevParent.branchTokenTypes = null; 430 prevParent = prevParent.parent; 431 } 432 } 433 434}