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