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