001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 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; 023 024import antlr.CommonASTWithHiddenTokens; 025import antlr.Token; 026import antlr.collections.AST; 027import com.puppycrawl.tools.checkstyle.api.DetailAST; 028import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 029 030/** 031 * The implementation of {@link DetailAST}. This should only be directly used to 032 * create custom AST nodes. 033 * 034 * @noinspection FieldNotUsedInToString, SerializableHasSerializationMethods 035 */ 036public final class DetailAstImpl extends CommonASTWithHiddenTokens implements DetailAST { 037 038 private static final long serialVersionUID = -2580884815577559874L; 039 040 /** Constant to indicate if not calculated the child count. */ 041 private static final int NOT_INITIALIZED = Integer.MIN_VALUE; 042 043 /** The line number. **/ 044 private int lineNo = NOT_INITIALIZED; 045 /** The column number. **/ 046 private int columnNo = NOT_INITIALIZED; 047 048 /** Number of children. */ 049 private int childCount = NOT_INITIALIZED; 050 /** The parent token. */ 051 private DetailAstImpl parent; 052 /** Previous sibling. */ 053 private DetailAstImpl previousSibling; 054 055 /** 056 * All token types in this branch. 057 * Token 'x' (where x is an int) is in this branch 058 * if branchTokenTypes.get(x) is true. 059 */ 060 private BitSet branchTokenTypes; 061 062 @Override 063 public void initialize(Token tok) { 064 super.initialize(tok); 065 lineNo = tok.getLine(); 066 067 // expect columns to start @ 0 068 columnNo = tok.getColumn() - 1; 069 } 070 071 @Override 072 public void initialize(AST ast) { 073 final DetailAstImpl detailAst = (DetailAstImpl) ast; 074 setText(detailAst.getText()); 075 setType(detailAst.getType()); 076 lineNo = detailAst.getLineNo(); 077 columnNo = detailAst.getColumnNo(); 078 hiddenAfter = detailAst.getHiddenAfter(); 079 hiddenBefore = detailAst.getHiddenBefore(); 080 } 081 082 @Override 083 public void setFirstChild(AST ast) { 084 clearBranchTokenTypes(); 085 clearChildCountCache(this); 086 super.setFirstChild(ast); 087 if (ast != null) { 088 ((DetailAstImpl) ast).setParent(this); 089 } 090 } 091 092 @Override 093 public void setNextSibling(AST ast) { 094 clearBranchTokenTypes(); 095 clearChildCountCache(parent); 096 super.setNextSibling(ast); 097 if (ast != null && parent != null) { 098 ((DetailAstImpl) ast).setParent(parent); 099 } 100 if (ast != null) { 101 ((DetailAstImpl) ast).previousSibling = this; 102 } 103 } 104 105 /** 106 * Add previous sibling. 107 * 108 * @param ast 109 * DetailAST object. 110 */ 111 public void addPreviousSibling(DetailAST ast) { 112 clearBranchTokenTypes(); 113 clearChildCountCache(parent); 114 if (ast != null) { 115 // parent is set in setNextSibling or parent.setFirstChild 116 final DetailAstImpl previousSiblingNode = previousSibling; 117 final DetailAstImpl astImpl = (DetailAstImpl) ast; 118 119 if (previousSiblingNode != null) { 120 astImpl.previousSibling = previousSiblingNode; 121 previousSiblingNode.setNextSibling(astImpl); 122 } 123 else if (parent != null) { 124 parent.setFirstChild(astImpl); 125 } 126 127 astImpl.setNextSibling(this); 128 previousSibling = astImpl; 129 } 130 } 131 132 /** 133 * Add next sibling. 134 * 135 * @param ast 136 * DetailAST object. 137 */ 138 public void addNextSibling(DetailAST ast) { 139 clearBranchTokenTypes(); 140 clearChildCountCache(parent); 141 if (ast != null) { 142 // parent is set in setNextSibling 143 final DetailAstImpl nextSibling = getNextSibling(); 144 final DetailAstImpl astImpl = (DetailAstImpl) ast; 145 146 if (nextSibling != null) { 147 astImpl.setNextSibling(nextSibling); 148 nextSibling.previousSibling = astImpl; 149 } 150 151 astImpl.previousSibling = this; 152 setNextSibling(astImpl); 153 } 154 } 155 156 @Override 157 public void addChild(AST ast) { 158 clearBranchTokenTypes(); 159 clearChildCountCache(this); 160 if (ast != null) { 161 final DetailAstImpl astImpl = (DetailAstImpl) ast; 162 astImpl.setParent(this); 163 astImpl.previousSibling = (DetailAstImpl) getLastChild(); 164 } 165 super.addChild(ast); 166 } 167 168 @Override 169 public int getChildCount() { 170 // lazy init 171 if (childCount == NOT_INITIALIZED) { 172 childCount = 0; 173 AST child = getFirstChild(); 174 175 while (child != null) { 176 childCount += 1; 177 child = child.getNextSibling(); 178 } 179 } 180 return childCount; 181 } 182 183 @Override 184 public int getChildCount(int type) { 185 int count = 0; 186 for (AST ast = getFirstChild(); ast != null; ast = ast.getNextSibling()) { 187 if (ast.getType() == type) { 188 count++; 189 } 190 } 191 return count; 192 } 193 194 /** 195 * Set the parent token. 196 * 197 * @param parent the parent token 198 */ 199 private void setParent(DetailAstImpl parent) { 200 DetailAstImpl instance = this; 201 do { 202 instance.clearBranchTokenTypes(); 203 instance.parent = parent; 204 instance = instance.getNextSibling(); 205 } while (instance != null); 206 } 207 208 @Override 209 public DetailAST getParent() { 210 return parent; 211 } 212 213 @Override 214 public int getLineNo() { 215 int resultNo = -1; 216 217 if (lineNo == NOT_INITIALIZED) { 218 // an inner AST that has been initialized 219 // with initialize(String text) 220 resultNo = findLineNo(getFirstChild()); 221 222 if (resultNo == -1) { 223 resultNo = findLineNo(getNextSibling()); 224 } 225 } 226 if (resultNo == -1) { 227 resultNo = lineNo; 228 } 229 return resultNo; 230 } 231 232 /** 233 * Set line number. 234 * 235 * @param lineNo 236 * line number. 237 */ 238 public void setLineNo(int lineNo) { 239 this.lineNo = lineNo; 240 } 241 242 @Override 243 public int getColumnNo() { 244 int resultNo = -1; 245 246 if (columnNo == NOT_INITIALIZED) { 247 // an inner AST that has been initialized 248 // with initialize(String text) 249 resultNo = findColumnNo(getFirstChild()); 250 251 if (resultNo == -1) { 252 resultNo = findColumnNo(getNextSibling()); 253 } 254 } 255 if (resultNo == -1) { 256 resultNo = columnNo; 257 } 258 return resultNo; 259 } 260 261 /** 262 * Set column number. 263 * 264 * @param columnNo 265 * column number. 266 */ 267 public void setColumnNo(int columnNo) { 268 this.columnNo = columnNo; 269 } 270 271 @Override 272 public DetailAST getLastChild() { 273 DetailAST ast = getFirstChild(); 274 while (ast != null && ast.getNextSibling() != null) { 275 ast = ast.getNextSibling(); 276 } 277 return ast; 278 } 279 280 /** 281 * Finds column number in the first non-comment node. 282 * 283 * @param ast DetailAST node. 284 * @return Column number if non-comment node exists, -1 otherwise. 285 */ 286 private static int findColumnNo(DetailAST ast) { 287 int resultNo = -1; 288 DetailAST node = ast; 289 while (node != null) { 290 // comment node can't be start of any java statement/definition 291 if (TokenUtil.isCommentType(node.getType())) { 292 node = node.getNextSibling(); 293 } 294 else { 295 resultNo = node.getColumnNo(); 296 break; 297 } 298 } 299 return resultNo; 300 } 301 302 /** 303 * Finds line number in the first non-comment node. 304 * 305 * @param ast DetailAST node. 306 * @return Line number if non-comment node exists, -1 otherwise. 307 */ 308 private static int findLineNo(DetailAST ast) { 309 int resultNo = -1; 310 DetailAST node = ast; 311 while (node != null) { 312 // comment node can't be start of any java statement/definition 313 if (TokenUtil.isCommentType(node.getType())) { 314 node = node.getNextSibling(); 315 } 316 else { 317 resultNo = node.getLineNo(); 318 break; 319 } 320 } 321 return resultNo; 322 } 323 324 /** 325 * Returns token type with branch. 326 * 327 * @return the token types that occur in the branch as a sorted set. 328 */ 329 private BitSet getBranchTokenTypes() { 330 // lazy init 331 if (branchTokenTypes == null) { 332 branchTokenTypes = new BitSet(); 333 branchTokenTypes.set(getType()); 334 335 // add union of all children 336 DetailAstImpl child = getFirstChild(); 337 while (child != null) { 338 final BitSet childTypes = child.getBranchTokenTypes(); 339 branchTokenTypes.or(childTypes); 340 341 child = child.getNextSibling(); 342 } 343 } 344 return branchTokenTypes; 345 } 346 347 @Override 348 public boolean branchContains(int type) { 349 return getBranchTokenTypes().get(type); 350 } 351 352 @Override 353 public DetailAST getPreviousSibling() { 354 return previousSibling; 355 } 356 357 @Override 358 public DetailAST findFirstToken(int type) { 359 DetailAST returnValue = null; 360 for (DetailAST ast = getFirstChild(); ast != null; ast = ast.getNextSibling()) { 361 if (ast.getType() == type) { 362 returnValue = ast; 363 break; 364 } 365 } 366 return returnValue; 367 } 368 369 @Override 370 public String toString() { 371 return super.toString() + "[" + getLineNo() + "x" + getColumnNo() + "]"; 372 } 373 374 @Override 375 public DetailAstImpl getNextSibling() { 376 return (DetailAstImpl) super.getNextSibling(); 377 } 378 379 @Override 380 public DetailAstImpl getFirstChild() { 381 return (DetailAstImpl) super.getFirstChild(); 382 } 383 384 @Override 385 public boolean hasChildren() { 386 return getFirstChild() != null; 387 } 388 389 /** 390 * Clears the child count for the ast instance. 391 * 392 * @param ast The ast to clear. 393 */ 394 private static void clearChildCountCache(DetailAstImpl ast) { 395 if (ast != null) { 396 ast.childCount = NOT_INITIALIZED; 397 } 398 } 399 400 /** 401 * Clears branchTokenTypes cache for all parents of the current DetailAST instance, and the 402 * child count for the current DetailAST instance. 403 */ 404 private void clearBranchTokenTypes() { 405 DetailAstImpl prevParent = parent; 406 while (prevParent != null) { 407 prevParent.branchTokenTypes = null; 408 prevParent = prevParent.parent; 409 } 410 } 411 412}