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