001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2016 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 childCount = NOT_INITIALIZED; 084 super.setFirstChild(ast); 085 if (ast != null) { 086 ((DetailAST) ast).setParent(this); 087 } 088 } 089 090 @Override 091 public void setNextSibling(AST ast) { 092 super.setNextSibling(ast); 093 if (ast != null && parent != null) { 094 ((DetailAST) ast).setParent(parent); 095 } 096 if (ast != null) { 097 ((DetailAST) ast).previousSibling = this; 098 } 099 } 100 101 /** 102 * Add previous sibling. 103 * @param ast 104 * DetailAST object. 105 */ 106 public void addPreviousSibling(DetailAST ast) { 107 if (ast != null) { 108 ast.setParent(parent); 109 final DetailAST previousSiblingNode = previousSibling; 110 111 if (previousSiblingNode != null) { 112 ast.previousSibling = previousSiblingNode; 113 previousSiblingNode.setNextSibling(ast); 114 } 115 else if (parent != null) { 116 parent.setFirstChild(ast); 117 } 118 119 ast.setNextSibling(this); 120 previousSibling = ast; 121 } 122 } 123 124 /** 125 * Add next sibling. 126 * @param ast 127 * DetailAST object. 128 */ 129 public void addNextSibling(DetailAST ast) { 130 if (ast != null) { 131 ast.setParent(parent); 132 final DetailAST nextSibling = getNextSibling(); 133 134 if (nextSibling != null) { 135 ast.setNextSibling(nextSibling); 136 nextSibling.previousSibling = ast; 137 } 138 139 ast.previousSibling = this; 140 setNextSibling(ast); 141 } 142 } 143 144 @Override 145 public void addChild(AST ast) { 146 super.addChild(ast); 147 if (ast != null) { 148 ((DetailAST) ast).setParent(this); 149 getFirstChild().setParent(this); 150 } 151 } 152 153 /** 154 * Returns the number of child nodes one level below this node. That is is 155 * does not recurse down the tree. 156 * @return the number of child nodes 157 */ 158 public int getChildCount() { 159 // lazy init 160 if (childCount == NOT_INITIALIZED) { 161 childCount = 0; 162 AST child = getFirstChild(); 163 164 while (child != null) { 165 childCount += 1; 166 child = child.getNextSibling(); 167 } 168 } 169 return childCount; 170 } 171 172 /** 173 * Returns the number of direct child tokens that have the specified type. 174 * @param type the token type to match 175 * @return the number of matching token 176 */ 177 public int getChildCount(int type) { 178 int count = 0; 179 for (AST ast = getFirstChild(); ast != null; ast = ast.getNextSibling()) { 180 if (ast.getType() == type) { 181 count++; 182 } 183 } 184 return count; 185 } 186 187 /** 188 * Set the parent token. 189 * @param parent the parent token 190 */ 191 private void setParent(DetailAST parent) { 192 this.parent = parent; 193 final DetailAST nextSibling = getNextSibling(); 194 if (nextSibling != null) { 195 nextSibling.setParent(parent); 196 nextSibling.previousSibling = this; 197 } 198 } 199 200 /** 201 * Returns the parent token. 202 * @return the parent token 203 */ 204 public DetailAST getParent() { 205 return parent; 206 } 207 208 /** 209 * Gets line number. 210 * @return the line number 211 */ 212 public int getLineNo() { 213 int resultNo = -1; 214 215 if (lineNo == NOT_INITIALIZED) { 216 // an inner AST that has been initialized 217 // with initialize(String text) 218 resultNo = findLineNo(getFirstChild()); 219 220 if (resultNo < 0) { 221 resultNo = findLineNo(getNextSibling()); 222 } 223 } 224 if (resultNo < 0) { 225 resultNo = lineNo; 226 } 227 return resultNo; 228 } 229 230 /** 231 * Set line number. 232 * @param lineNo 233 * line number. 234 */ 235 public void setLineNo(int lineNo) { 236 this.lineNo = lineNo; 237 } 238 239 /** 240 * Gets column number. 241 * @return the column number 242 */ 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 < 0) { 252 resultNo = findColumnNo(getNextSibling()); 253 } 254 } 255 if (resultNo < 0) { 256 resultNo = columnNo; 257 } 258 return resultNo; 259 } 260 261 /** 262 * Set column number. 263 * @param columnNo 264 * column number. 265 */ 266 public void setColumnNo(int columnNo) { 267 this.columnNo = columnNo; 268 } 269 270 /** 271 * Gets the last child node. 272 * @return the last child node 273 */ 274 public DetailAST getLastChild() { 275 DetailAST ast = getFirstChild(); 276 while (ast != null && ast.getNextSibling() != null) { 277 ast = ast.getNextSibling(); 278 } 279 return ast; 280 } 281 282 /** 283 * Finds column number in the first non-comment node. 284 * 285 * @param ast DetailAST node. 286 * @return Column number if non-comment node exists, -1 otherwise. 287 */ 288 private static int findColumnNo(DetailAST ast) { 289 int resultNo = -1; 290 DetailAST node = ast; 291 while (node != null) { 292 // comment node can't be start of any java statement/definition 293 if (TokenUtils.isCommentType(node.getType())) { 294 node = node.getNextSibling(); 295 } 296 else { 297 resultNo = node.getColumnNo(); 298 break; 299 } 300 } 301 return resultNo; 302 } 303 304 /** 305 * Finds line number in the first non-comment node. 306 * 307 * @param ast DetailAST node. 308 * @return Line number if non-comment node exists, -1 otherwise. 309 */ 310 private static int findLineNo(DetailAST ast) { 311 int resultNo = -1; 312 DetailAST node = ast; 313 while (node != null) { 314 // comment node can't be start of any java statement/definition 315 if (TokenUtils.isCommentType(node.getType())) { 316 node = node.getNextSibling(); 317 } 318 else { 319 resultNo = node.getLineNo(); 320 break; 321 } 322 } 323 return resultNo; 324 } 325 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 333 branchTokenTypes = new BitSet(); 334 branchTokenTypes.set(getType()); 335 336 // add union of all children 337 DetailAST child = getFirstChild(); 338 while (child != null) { 339 final BitSet childTypes = child.getBranchTokenTypes(); 340 branchTokenTypes.or(childTypes); 341 342 child = child.getNextSibling(); 343 } 344 } 345 return branchTokenTypes; 346 } 347 348 /** 349 * Checks if this branch of the parse tree contains a token 350 * of the provided type. 351 * @param type a TokenType 352 * @return true if and only if this branch (including this node) 353 * contains a token of type {@code type}. 354 */ 355 public boolean branchContains(int type) { 356 return getBranchTokenTypes().get(type); 357 } 358 359 /** 360 * Returns the previous sibling or null if no such sibling exists. 361 * @return the previous sibling or null if no such sibling exists. 362 */ 363 public DetailAST getPreviousSibling() { 364 return previousSibling; 365 } 366 367 /** 368 * Returns the first child token that makes a specified type. 369 * @param type the token type to match 370 * @return the matching token, or null if no match 371 */ 372 public DetailAST findFirstToken(int type) { 373 DetailAST returnValue = null; 374 for (DetailAST ast = getFirstChild(); ast != null; ast = ast.getNextSibling()) { 375 if (ast.getType() == type) { 376 returnValue = ast; 377 break; 378 } 379 } 380 return returnValue; 381 } 382 383 @Override 384 public String toString() { 385 return super.toString() + "[" + getLineNo() + "x" + getColumnNo() + "]"; 386 } 387 388 @Override 389 public DetailAST getNextSibling() { 390 return (DetailAST) super.getNextSibling(); 391 } 392 393 @Override 394 public DetailAST getFirstChild() { 395 return (DetailAST) super.getFirstChild(); 396 } 397 398}