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