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