001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 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.List; 024 025import javax.annotation.Nullable; 026 027import org.antlr.v4.runtime.Token; 028 029import com.puppycrawl.tools.checkstyle.api.DetailAST; 030import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 031import com.puppycrawl.tools.checkstyle.utils.UnmodifiableCollectionUtil; 032 033/** 034 * The implementation of {@link DetailAST}. This should only be directly used to 035 * create custom AST nodes and in 'JavaAstVisitor.java'. 036 * 037 * @noinspection FieldNotUsedInToString 038 * @noinspectionreason FieldNotUsedInToString - We require a specific string format for 039 * printing to CLI. 040 */ 041public final class DetailAstImpl implements DetailAST { 042 043 /** Constant to indicate if not calculated the child count. */ 044 private static final int NOT_INITIALIZED = Integer.MIN_VALUE; 045 046 /** The line number. */ 047 private int lineNo = NOT_INITIALIZED; 048 /** The column number. */ 049 private int columnNo = NOT_INITIALIZED; 050 051 /** Number of children. */ 052 private int childCount; 053 /** The parent token. */ 054 private DetailAstImpl parent; 055 /** Previous sibling. */ 056 private DetailAstImpl previousSibling; 057 058 /** First child of this DetailAST. */ 059 private DetailAstImpl firstChild; 060 061 /** First sibling of this DetailAST.*/ 062 private DetailAstImpl nextSibling; 063 064 /** Text of this DetailAST. */ 065 private String text; 066 067 /** The type of this DetailAST. */ 068 private int type; 069 070 /** 071 * All tokens on COMMENTS channel to the left of the current token up to the 072 * preceding token on the DEFAULT_TOKEN_CHANNEL. 073 */ 074 private List<Token> hiddenBefore; 075 076 /** 077 * All tokens on COMMENTS channel to the right of the current token up to the 078 * next token on the DEFAULT_TOKEN_CHANNEL. 079 */ 080 private List<Token> hiddenAfter; 081 082 /** 083 * All token types in this branch. 084 * Token 'x' (where x is an int) is in this branch if branchTokenTypes.get(x) is true. 085 */ 086 private BitSet branchTokenTypes; 087 088 /** 089 * Creates a new {@code DetailAstImpl} instance. 090 */ 091 public DetailAstImpl() { 092 // no code by default 093 } 094 095 /** 096 * Initializes this DetailAstImpl. 097 * 098 * @param token the token to generate this DetailAstImpl from 099 */ 100 public void initialize(Token token) { 101 text = token.getText(); 102 type = token.getType(); 103 lineNo = token.getLine(); 104 columnNo = token.getCharPositionInLine(); 105 } 106 107 /** 108 * Initializes this DetailAstImpl. 109 * 110 * @param tokenType the type of this DetailAstImpl 111 * @param tokenText the text of this DetailAstImpl 112 */ 113 public void initialize(int tokenType, String tokenText) { 114 type = tokenType; 115 text = tokenText; 116 } 117 118 /** 119 * Add previous sibling. 120 * 121 * @param ast 122 * DetailAST object. 123 */ 124 public void addPreviousSibling(DetailAST ast) { 125 clearBranchTokenTypes(); 126 clearChildCountCache(parent); 127 if (ast != null) { 128 // parent is set in setNextSibling or parent.setFirstChild 129 final DetailAstImpl previousSiblingNode = previousSibling; 130 final DetailAstImpl astImpl = (DetailAstImpl) ast; 131 132 if (previousSiblingNode != null) { 133 previousSiblingNode.setNextSibling(astImpl); 134 } 135 else if (parent != null) { 136 parent.setFirstChild(astImpl); 137 } 138 139 astImpl.setNextSibling(this); 140 } 141 } 142 143 /** 144 * Add next sibling, pushes other siblings back. 145 * 146 * @param ast DetailAST object. 147 */ 148 public void addNextSibling(DetailAST ast) { 149 clearBranchTokenTypes(); 150 clearChildCountCache(parent); 151 if (ast != null) { 152 // parent is set in setNextSibling 153 final DetailAstImpl sibling = nextSibling; 154 final DetailAstImpl astImpl = (DetailAstImpl) ast; 155 astImpl.setNextSibling(sibling); 156 157 setNextSibling(astImpl); 158 } 159 } 160 161 /** 162 * Adds a new child to the current AST. 163 * 164 * @param child to DetailAST to add as child 165 */ 166 public void addChild(DetailAST child) { 167 clearBranchTokenTypes(); 168 clearChildCountCache(this); 169 if (child != null) { 170 final DetailAstImpl astImpl = (DetailAstImpl) child; 171 astImpl.setParent(this); 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 @Nullable 404 @Override 405 public DetailAST findFirstToken(int tokenType) { 406 DetailAST returnValue = null; 407 for (DetailAST ast = firstChild; ast != null; ast = ast.getNextSibling()) { 408 if (ast.getType() == tokenType) { 409 returnValue = ast; 410 break; 411 } 412 } 413 return returnValue; 414 } 415 416 @Override 417 public String toString() { 418 return text + "[" + getLineNo() + "x" + getColumnNo() + "]"; 419 } 420 421 @Override 422 public DetailAstImpl getNextSibling() { 423 return nextSibling; 424 } 425 426 @Override 427 public DetailAstImpl getFirstChild() { 428 return firstChild; 429 } 430 431 @Override 432 public int getNumberOfChildren() { 433 return getChildCount(); 434 } 435 436 @Override 437 public boolean hasChildren() { 438 return firstChild != null; 439 } 440 441 /** 442 * Clears the child count for the ast instance. 443 * 444 * @param ast The ast to clear. 445 */ 446 private static void clearChildCountCache(DetailAstImpl ast) { 447 if (ast != null) { 448 ast.childCount = NOT_INITIALIZED; 449 } 450 } 451 452 /** 453 * Clears branchTokenTypes cache for all parents of the current DetailAST instance, and the 454 * child count for the current DetailAST instance. 455 */ 456 private void clearBranchTokenTypes() { 457 DetailAstImpl prevParent = parent; 458 while (prevParent != null) { 459 prevParent.branchTokenTypes = null; 460 prevParent = prevParent.parent; 461 } 462 } 463 464 /** 465 * Sets the next sibling of this AST. 466 * 467 * @param nextSibling the DetailAST to set as sibling 468 */ 469 public void setNextSibling(DetailAST nextSibling) { 470 clearBranchTokenTypes(); 471 clearChildCountCache(parent); 472 this.nextSibling = (DetailAstImpl) nextSibling; 473 if (nextSibling != null && parent != null) { 474 ((DetailAstImpl) nextSibling).setParent(parent); 475 } 476 if (nextSibling != null) { 477 ((DetailAstImpl) nextSibling).previousSibling = this; 478 } 479 } 480 481 /** 482 * Sets the first child of this AST. 483 * 484 * @param firstChild the DetailAST to set as first child 485 */ 486 public void setFirstChild(DetailAST firstChild) { 487 clearBranchTokenTypes(); 488 clearChildCountCache(this); 489 this.firstChild = (DetailAstImpl) firstChild; 490 if (firstChild != null) { 491 ((DetailAstImpl) firstChild).setParent(this); 492 } 493 } 494 495 /** 496 * Removes all children of this AST. 497 */ 498 public void removeChildren() { 499 firstChild = null; 500 } 501 502 /** 503 * Get list of tokens on COMMENTS channel to the left of the 504 * current token up to the preceding token on the DEFAULT_TOKEN_CHANNEL. 505 * 506 * @return list of comment tokens 507 */ 508 public List<Token> getHiddenBefore() { 509 List<Token> returnList = null; 510 if (hiddenBefore != null) { 511 returnList = UnmodifiableCollectionUtil.unmodifiableList(hiddenBefore); 512 } 513 return returnList; 514 } 515 516 /** 517 * Get list tokens on COMMENTS channel to the right of the current 518 * token up to the next token on the DEFAULT_TOKEN_CHANNEL. 519 * 520 * @return list of comment tokens 521 */ 522 public List<Token> getHiddenAfter() { 523 List<Token> returnList = null; 524 if (hiddenAfter != null) { 525 returnList = UnmodifiableCollectionUtil.unmodifiableList(hiddenAfter); 526 } 527 return returnList; 528 } 529 530 /** 531 * Sets the hiddenBefore token field. 532 * 533 * @param hiddenBefore comment token preceding this DetailAstImpl 534 */ 535 public void setHiddenBefore(List<Token> hiddenBefore) { 536 this.hiddenBefore = UnmodifiableCollectionUtil.unmodifiableList(hiddenBefore); 537 } 538 539 /** 540 * Sets the hiddenAfter token field. 541 * 542 * @param hiddenAfter comment token following this DetailAstImpl 543 */ 544 public void setHiddenAfter(List<Token> hiddenAfter) { 545 this.hiddenAfter = UnmodifiableCollectionUtil.unmodifiableList(hiddenAfter); 546 } 547 548}