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 * @noinspection FieldNotUsedInToString, SerializableHasSerializationMethods 036 */ 037public final class DetailAST extends CommonASTWithHiddenTokens { 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 DetailAST parent; 052 /** Previous sibling. */ 053 private DetailAST 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 DetailAST detailAst = (DetailAST) 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 ((DetailAST) 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 ((DetailAST) ast).setParent(parent); 099 } 100 if (ast != null) { 101 ((DetailAST) ast).previousSibling = this; 102 } 103 } 104 105 /** 106 * Add previous sibling. 107 * @param ast 108 * DetailAST object. 109 */ 110 public void addPreviousSibling(DetailAST ast) { 111 clearBranchTokenTypes(); 112 clearChildCountCache(parent); 113 if (ast != null) { 114 //parent is set in setNextSibling or parent.setFirstChild 115 final DetailAST previousSiblingNode = previousSibling; 116 117 if (previousSiblingNode != null) { 118 ast.previousSibling = previousSiblingNode; 119 previousSiblingNode.setNextSibling(ast); 120 } 121 else if (parent != null) { 122 parent.setFirstChild(ast); 123 } 124 125 ast.setNextSibling(this); 126 previousSibling = ast; 127 } 128 } 129 130 /** 131 * Add next sibling. 132 * @param ast 133 * DetailAST object. 134 */ 135 public void addNextSibling(DetailAST ast) { 136 clearBranchTokenTypes(); 137 clearChildCountCache(parent); 138 if (ast != null) { 139 //parent is set in setNextSibling 140 final DetailAST nextSibling = getNextSibling(); 141 142 if (nextSibling != null) { 143 ast.setNextSibling(nextSibling); 144 nextSibling.previousSibling = ast; 145 } 146 147 ast.previousSibling = this; 148 setNextSibling(ast); 149 } 150 } 151 152 @Override 153 public void addChild(AST ast) { 154 clearBranchTokenTypes(); 155 clearChildCountCache(this); 156 if (ast != null) { 157 ((DetailAST) ast).setParent(this); 158 ((DetailAST) ast).previousSibling = getLastChild(); 159 } 160 super.addChild(ast); 161 } 162 163 /** 164 * Returns the number of child nodes one level below this node. That is is 165 * does not recurse down the tree. 166 * @return the number of child nodes 167 */ 168 public int getChildCount() { 169 // lazy init 170 if (childCount == NOT_INITIALIZED) { 171 childCount = 0; 172 AST child = getFirstChild(); 173 174 while (child != null) { 175 childCount += 1; 176 child = child.getNextSibling(); 177 } 178 } 179 return childCount; 180 } 181 182 /** 183 * Returns the number of direct child tokens that have the specified type. 184 * @param type the token type to match 185 * @return the number of matching token 186 */ 187 public int getChildCount(int type) { 188 int count = 0; 189 for (AST ast = getFirstChild(); ast != null; ast = ast.getNextSibling()) { 190 if (ast.getType() == type) { 191 count++; 192 } 193 } 194 return count; 195 } 196 197 /** 198 * Set the parent token. 199 * @param parent the parent token 200 */ 201 private void setParent(DetailAST parent) { 202 DetailAST instance = this; 203 do { 204 instance.clearBranchTokenTypes(); 205 instance.parent = parent; 206 final DetailAST nextSibling = instance.getNextSibling(); 207 if (nextSibling != null) { 208 nextSibling.previousSibling = instance; 209 } 210 211 instance = nextSibling; 212 } while (instance != null); 213 } 214 215 /** 216 * Returns the parent token. 217 * @return the parent token 218 */ 219 public DetailAST getParent() { 220 return parent; 221 } 222 223 /** 224 * Gets line number. 225 * @return the line number 226 */ 227 public int getLineNo() { 228 int resultNo = -1; 229 230 if (lineNo == NOT_INITIALIZED) { 231 // an inner AST that has been initialized 232 // with initialize(String text) 233 resultNo = findLineNo(getFirstChild()); 234 235 if (resultNo == -1) { 236 resultNo = findLineNo(getNextSibling()); 237 } 238 } 239 if (resultNo == -1) { 240 resultNo = lineNo; 241 } 242 return resultNo; 243 } 244 245 /** 246 * Set line number. 247 * @param lineNo 248 * line number. 249 */ 250 public void setLineNo(int lineNo) { 251 this.lineNo = lineNo; 252 } 253 254 /** 255 * Gets column number. 256 * @return the column number 257 */ 258 public int getColumnNo() { 259 int resultNo = -1; 260 261 if (columnNo == NOT_INITIALIZED) { 262 // an inner AST that has been initialized 263 // with initialize(String text) 264 resultNo = findColumnNo(getFirstChild()); 265 266 if (resultNo == -1) { 267 resultNo = findColumnNo(getNextSibling()); 268 } 269 } 270 if (resultNo == -1) { 271 resultNo = columnNo; 272 } 273 return resultNo; 274 } 275 276 /** 277 * Set column number. 278 * @param columnNo 279 * column number. 280 */ 281 public void setColumnNo(int columnNo) { 282 this.columnNo = columnNo; 283 } 284 285 /** 286 * Gets the last child node. 287 * @return the last child node 288 */ 289 public DetailAST getLastChild() { 290 DetailAST ast = getFirstChild(); 291 while (ast != null && ast.getNextSibling() != null) { 292 ast = ast.getNextSibling(); 293 } 294 return ast; 295 } 296 297 /** 298 * Finds column number in the first non-comment node. 299 * 300 * @param ast DetailAST node. 301 * @return Column number if non-comment node exists, -1 otherwise. 302 */ 303 private static int findColumnNo(DetailAST ast) { 304 int resultNo = -1; 305 DetailAST node = ast; 306 while (node != null) { 307 // comment node can't be start of any java statement/definition 308 if (TokenUtils.isCommentType(node.getType())) { 309 node = node.getNextSibling(); 310 } 311 else { 312 resultNo = node.getColumnNo(); 313 break; 314 } 315 } 316 return resultNo; 317 } 318 319 /** 320 * Finds line number in the first non-comment node. 321 * 322 * @param ast DetailAST node. 323 * @return Line number if non-comment node exists, -1 otherwise. 324 */ 325 private static int findLineNo(DetailAST ast) { 326 int resultNo = -1; 327 DetailAST node = ast; 328 while (node != null) { 329 // comment node can't be start of any java statement/definition 330 if (TokenUtils.isCommentType(node.getType())) { 331 node = node.getNextSibling(); 332 } 333 else { 334 resultNo = node.getLineNo(); 335 break; 336 } 337 } 338 return resultNo; 339 } 340 341 /** 342 * Returns token type with branch. 343 * @return the token types that occur in the branch as a sorted set. 344 */ 345 private BitSet getBranchTokenTypes() { 346 // lazy init 347 if (branchTokenTypes == null) { 348 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}