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