001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2022 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; 021 022import java.util.BitSet; 023import java.util.Collections; 024import java.util.List; 025 026import org.antlr.v4.runtime.Token; 027 028import com.puppycrawl.tools.checkstyle.api.DetailAST; 029import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 030 031/** 032 * The implementation of {@link DetailAST}. This should only be directly used to 033 * create custom AST nodes and in 'JavaAstVisitor.java'. 034 * 035 * @noinspection FieldNotUsedInToString 036 * @noinspectionreason FieldNotUsedInToString - We require a specific string format for 037 * printing to CLI. 038 */ 039public final class DetailAstImpl implements DetailAST { 040 041 /** Constant to indicate if not calculated the child count. */ 042 private static final int NOT_INITIALIZED = Integer.MIN_VALUE; 043 044 /** The line number. **/ 045 private int lineNo = NOT_INITIALIZED; 046 /** The column number. **/ 047 private int columnNo = NOT_INITIALIZED; 048 049 /** Number of children. */ 050 private int childCount = NOT_INITIALIZED; 051 /** The parent token. */ 052 private DetailAstImpl parent; 053 /** Previous sibling. */ 054 private DetailAstImpl previousSibling; 055 056 /** First child of this DetailAST. */ 057 private DetailAstImpl firstChild; 058 059 /** First sibling of this DetailAST.*/ 060 private DetailAstImpl nextSibling; 061 062 /** Text of this DetailAST. */ 063 private String text; 064 065 /** The type of this DetailAST. */ 066 private int type; 067 068 /** 069 * All tokens on COMMENTS channel to the left of the current token up to the 070 * preceding token on the DEFAULT_TOKEN_CHANNEL. 071 */ 072 private List<Token> hiddenBefore; 073 074 /** 075 * All tokens on COMMENTS channel to the right of the current token up to the 076 * next token on the DEFAULT_TOKEN_CHANNEL. 077 */ 078 private List<Token> hiddenAfter; 079 080 /** 081 * All token types in this branch. 082 * Token 'x' (where x is an int) is in this branch 083 * if branchTokenTypes.get(x) is true. 084 */ 085 private BitSet branchTokenTypes; 086 087 /** 088 * Initializes this DetailAstImpl. 089 * 090 * @param tokenType the type of this DetailAstImpl 091 * @param tokenText the text of this DetailAstImpl 092 */ 093 public void initialize(int tokenType, String tokenText) { 094 type = tokenType; 095 text = tokenText; 096 } 097 098 /** 099 * Initializes this DetailAstImpl. 100 * 101 * @param token the token to generate this DetailAstImpl from 102 */ 103 public void initialize(Token token) { 104 text = token.getText(); 105 type = token.getType(); 106 lineNo = token.getLine(); 107 columnNo = token.getCharPositionInLine(); 108 } 109 110 /** 111 * Add previous sibling. 112 * 113 * @param ast 114 * DetailAST object. 115 */ 116 public void addPreviousSibling(DetailAST ast) { 117 clearBranchTokenTypes(); 118 clearChildCountCache(parent); 119 if (ast != null) { 120 // parent is set in setNextSibling or parent.setFirstChild 121 final DetailAstImpl previousSiblingNode = previousSibling; 122 final DetailAstImpl astImpl = (DetailAstImpl) ast; 123 124 if (previousSiblingNode != null) { 125 astImpl.previousSibling = previousSiblingNode; 126 previousSiblingNode.setNextSibling(astImpl); 127 } 128 else if (parent != null) { 129 parent.setFirstChild(astImpl); 130 } 131 132 astImpl.setNextSibling(this); 133 previousSibling = astImpl; 134 } 135 } 136 137 /** 138 * Add next sibling, pushes other siblings back. 139 * 140 * @param ast DetailAST object. 141 */ 142 public void addNextSibling(DetailAST ast) { 143 clearBranchTokenTypes(); 144 clearChildCountCache(parent); 145 if (ast != null) { 146 // parent is set in setNextSibling 147 final DetailAstImpl sibling = nextSibling; 148 final DetailAstImpl astImpl = (DetailAstImpl) ast; 149 150 if (sibling != null) { 151 astImpl.setNextSibling(sibling); 152 sibling.previousSibling = astImpl; 153 } 154 155 astImpl.previousSibling = this; 156 setNextSibling(astImpl); 157 } 158 } 159 160 /** 161 * Adds a new child to the current AST. 162 * 163 * @param child to DetailAST to add as child 164 */ 165 public void addChild(DetailAST child) { 166 clearBranchTokenTypes(); 167 clearChildCountCache(this); 168 if (child != null) { 169 final DetailAstImpl astImpl = (DetailAstImpl) child; 170 astImpl.setParent(this); 171 astImpl.previousSibling = (DetailAstImpl) getLastChild(); 172 } 173 DetailAST temp = firstChild; 174 if (temp == null) { 175 firstChild = (DetailAstImpl) child; 176 } 177 else { 178 while (temp.getNextSibling() != null) { 179 temp = temp.getNextSibling(); 180 } 181 182 ((DetailAstImpl) temp).setNextSibling(child); 183 } 184 } 185 186 @Override 187 public int getChildCount() { 188 // lazy init 189 if (childCount == NOT_INITIALIZED) { 190 childCount = 0; 191 DetailAST child = firstChild; 192 193 while (child != null) { 194 childCount += 1; 195 child = child.getNextSibling(); 196 } 197 } 198 return childCount; 199 } 200 201 @Override 202 public int getChildCount(int tokenType) { 203 int count = 0; 204 for (DetailAST ast = firstChild; ast != null; ast = ast.getNextSibling()) { 205 if (ast.getType() == tokenType) { 206 count++; 207 } 208 } 209 return count; 210 } 211 212 /** 213 * Set the parent token. 214 * 215 * @param parent the parent token 216 */ 217 private void setParent(DetailAstImpl parent) { 218 DetailAstImpl instance = this; 219 do { 220 instance.clearBranchTokenTypes(); 221 instance.parent = parent; 222 instance = instance.nextSibling; 223 } while (instance != null); 224 } 225 226 @Override 227 public DetailAST getParent() { 228 return parent; 229 } 230 231 @Override 232 public String getText() { 233 return text; 234 } 235 236 /** 237 * Sets the text for this DetailAstImpl. 238 * 239 * @param text the text field of this DetailAstImpl 240 */ 241 public void setText(String text) { 242 this.text = text; 243 } 244 245 @Override 246 public int getType() { 247 return type; 248 } 249 250 /** 251 * Sets the type of this AST. 252 * 253 * @param type the token type of this DetailAstImpl 254 */ 255 public void setType(int type) { 256 this.type = type; 257 } 258 259 @Override 260 public int getLineNo() { 261 int resultNo = -1; 262 263 if (lineNo == NOT_INITIALIZED) { 264 // an inner AST that has been initialized 265 // with initialize(String text) 266 resultNo = findLineNo(firstChild); 267 268 if (resultNo == -1) { 269 resultNo = findLineNo(nextSibling); 270 } 271 } 272 if (resultNo == -1) { 273 resultNo = lineNo; 274 } 275 return resultNo; 276 } 277 278 /** 279 * Set line number. 280 * 281 * @param lineNo 282 * line number. 283 */ 284 public void setLineNo(int lineNo) { 285 this.lineNo = lineNo; 286 } 287 288 @Override 289 public int getColumnNo() { 290 int resultNo = -1; 291 292 if (columnNo == NOT_INITIALIZED) { 293 // an inner AST that has been initialized 294 // with initialize(String text) 295 resultNo = findColumnNo(firstChild); 296 297 if (resultNo == -1) { 298 resultNo = findColumnNo(nextSibling); 299 } 300 } 301 if (resultNo == -1) { 302 resultNo = columnNo; 303 } 304 return resultNo; 305 } 306 307 /** 308 * Set column number. 309 * 310 * @param columnNo 311 * column number. 312 */ 313 public void setColumnNo(int columnNo) { 314 this.columnNo = columnNo; 315 } 316 317 @Override 318 public DetailAST getLastChild() { 319 DetailAstImpl ast = firstChild; 320 while (ast != null && ast.nextSibling != null) { 321 ast = ast.nextSibling; 322 } 323 return ast; 324 } 325 326 /** 327 * Finds column number in the first non-comment node. 328 * 329 * @param ast DetailAST node. 330 * @return Column number if non-comment node exists, -1 otherwise. 331 */ 332 private static int findColumnNo(DetailAST ast) { 333 int resultNo = -1; 334 DetailAST node = ast; 335 while (node != null) { 336 // comment node can't be start of any java statement/definition 337 if (TokenUtil.isCommentType(node.getType())) { 338 node = node.getNextSibling(); 339 } 340 else { 341 resultNo = node.getColumnNo(); 342 break; 343 } 344 } 345 return resultNo; 346 } 347 348 /** 349 * Finds line number in the first non-comment node. 350 * 351 * @param ast DetailAST node. 352 * @return Line number if non-comment node exists, -1 otherwise. 353 */ 354 private static int findLineNo(DetailAST ast) { 355 int resultNo = -1; 356 DetailAST node = ast; 357 while (node != null) { 358 // comment node can't be start of any java statement/definition 359 if (TokenUtil.isCommentType(node.getType())) { 360 node = node.getNextSibling(); 361 } 362 else { 363 resultNo = node.getLineNo(); 364 break; 365 } 366 } 367 return resultNo; 368 } 369 370 /** 371 * Returns token type with branch. 372 * 373 * @return the token types that occur in the branch as a sorted set. 374 */ 375 private BitSet getBranchTokenTypes() { 376 // lazy init 377 if (branchTokenTypes == null) { 378 branchTokenTypes = new BitSet(); 379 branchTokenTypes.set(type); 380 381 // add union of all children 382 DetailAstImpl child = firstChild; 383 while (child != null) { 384 final BitSet childTypes = child.getBranchTokenTypes(); 385 branchTokenTypes.or(childTypes); 386 387 child = child.nextSibling; 388 } 389 } 390 return branchTokenTypes; 391 } 392 393 @Override 394 public boolean branchContains(int tokenType) { 395 return getBranchTokenTypes().get(tokenType); 396 } 397 398 @Override 399 public DetailAST getPreviousSibling() { 400 return previousSibling; 401 } 402 403 @Override 404 public DetailAST findFirstToken(int tokenType) { 405 DetailAST returnValue = null; 406 for (DetailAST ast = firstChild; ast != null; ast = ast.getNextSibling()) { 407 if (ast.getType() == tokenType) { 408 returnValue = ast; 409 break; 410 } 411 } 412 return returnValue; 413 } 414 415 @Override 416 public String toString() { 417 return text + "[" + getLineNo() + "x" + getColumnNo() + "]"; 418 } 419 420 @Override 421 public DetailAstImpl getNextSibling() { 422 return nextSibling; 423 } 424 425 @Override 426 public DetailAstImpl getFirstChild() { 427 return firstChild; 428 } 429 430 @Override 431 public int getNumberOfChildren() { 432 return getChildCount(); 433 } 434 435 @Override 436 public boolean hasChildren() { 437 return firstChild != null; 438 } 439 440 /** 441 * Clears the child count for the ast instance. 442 * 443 * @param ast The ast to clear. 444 */ 445 private static void clearChildCountCache(DetailAstImpl ast) { 446 if (ast != null) { 447 ast.childCount = NOT_INITIALIZED; 448 } 449 } 450 451 /** 452 * Clears branchTokenTypes cache for all parents of the current DetailAST instance, and the 453 * child count for the current DetailAST instance. 454 */ 455 private void clearBranchTokenTypes() { 456 DetailAstImpl prevParent = parent; 457 while (prevParent != null) { 458 prevParent.branchTokenTypes = null; 459 prevParent = prevParent.parent; 460 } 461 } 462 463 /** 464 * Sets the next sibling of this AST. 465 * 466 * @param nextSibling the DetailAST to set as sibling 467 */ 468 public void setNextSibling(DetailAST nextSibling) { 469 clearBranchTokenTypes(); 470 clearChildCountCache(parent); 471 this.nextSibling = (DetailAstImpl) nextSibling; 472 if (nextSibling != null && parent != null) { 473 ((DetailAstImpl) nextSibling).setParent(parent); 474 } 475 if (nextSibling != null) { 476 ((DetailAstImpl) nextSibling).previousSibling = this; 477 } 478 } 479 480 /** 481 * Sets the first child of this AST. 482 * 483 * @param firstChild the DetailAST to set as first child 484 */ 485 public void setFirstChild(DetailAST firstChild) { 486 clearBranchTokenTypes(); 487 clearChildCountCache(this); 488 this.firstChild = (DetailAstImpl) firstChild; 489 if (firstChild != null) { 490 ((DetailAstImpl) firstChild).setParent(this); 491 } 492 } 493 494 /** 495 * Removes all children of this AST. 496 */ 497 public void removeChildren() { 498 firstChild = null; 499 } 500 501 /** 502 * Get list of tokens on COMMENTS channel to the left of the 503 * current token up to the preceding token on the DEFAULT_TOKEN_CHANNEL. 504 * 505 * @return list of comment tokens 506 */ 507 public List<Token> getHiddenBefore() { 508 List<Token> returnList = null; 509 if (hiddenBefore != null) { 510 returnList = Collections.unmodifiableList(hiddenBefore); 511 } 512 return returnList; 513 } 514 515 /** 516 * Get list tokens on COMMENTS channel to the right of the current 517 * token up to the next token on the DEFAULT_TOKEN_CHANNEL. 518 * 519 * @return list of comment tokens 520 */ 521 public List<Token> getHiddenAfter() { 522 List<Token> returnList = null; 523 if (hiddenAfter != null) { 524 returnList = Collections.unmodifiableList(hiddenAfter); 525 } 526 return returnList; 527 } 528 529 /** 530 * Sets the hiddenBefore token field. 531 * 532 * @param hiddenBefore comment token preceding this DetailAstImpl 533 */ 534 public void setHiddenBefore(List<Token> hiddenBefore) { 535 this.hiddenBefore = Collections.unmodifiableList(hiddenBefore); 536 } 537 538 /** 539 * Sets the hiddenAfter token field. 540 * 541 * @param hiddenAfter comment token following this DetailAstImpl 542 */ 543 public void setHiddenAfter(List<Token> hiddenAfter) { 544 this.hiddenAfter = Collections.unmodifiableList(hiddenAfter); 545 } 546}