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