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