001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2019 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; 021 022import java.util.BitSet; 023 024import antlr.CommonASTWithHiddenTokens; 025import antlr.Token; 026import antlr.collections.AST; 027import com.puppycrawl.tools.checkstyle.api.DetailAST; 028import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 029 030/** 031 * The implementation of {@link DetailAST}. This should only be directly used to 032 * create custom AST nodes. 033 * @noinspection FieldNotUsedInToString, SerializableHasSerializationMethods 034 */ 035public final class DetailAstImpl extends CommonASTWithHiddenTokens implements DetailAST { 036 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 DetailAstImpl parent; 051 /** Previous sibling. */ 052 private DetailAstImpl 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 DetailAstImpl detailAst = (DetailAstImpl) 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 ((DetailAstImpl) 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 ((DetailAstImpl) ast).setParent(parent); 098 } 099 if (ast != null) { 100 ((DetailAstImpl) 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 //parent is set in setNextSibling or parent.setFirstChild 114 final DetailAstImpl previousSiblingNode = previousSibling; 115 final DetailAstImpl astImpl = (DetailAstImpl) ast; 116 117 if (previousSiblingNode != null) { 118 astImpl.previousSibling = previousSiblingNode; 119 previousSiblingNode.setNextSibling(astImpl); 120 } 121 else if (parent != null) { 122 parent.setFirstChild(astImpl); 123 } 124 125 astImpl.setNextSibling(this); 126 previousSibling = astImpl; 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 DetailAstImpl nextSibling = getNextSibling(); 141 final DetailAstImpl astImpl = (DetailAstImpl) ast; 142 143 if (nextSibling != null) { 144 astImpl.setNextSibling(nextSibling); 145 nextSibling.previousSibling = astImpl; 146 } 147 148 astImpl.previousSibling = this; 149 setNextSibling(astImpl); 150 } 151 } 152 153 @Override 154 public void addChild(AST ast) { 155 clearBranchTokenTypes(); 156 clearChildCountCache(this); 157 if (ast != null) { 158 final DetailAstImpl astImpl = (DetailAstImpl) ast; 159 astImpl.setParent(this); 160 astImpl.previousSibling = (DetailAstImpl) getLastChild(); 161 } 162 super.addChild(ast); 163 } 164 165 @Override 166 public int getChildCount() { 167 // lazy init 168 if (childCount == NOT_INITIALIZED) { 169 childCount = 0; 170 AST child = getFirstChild(); 171 172 while (child != null) { 173 childCount += 1; 174 child = child.getNextSibling(); 175 } 176 } 177 return childCount; 178 } 179 180 @Override 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 private void setParent(DetailAstImpl parent) { 196 DetailAstImpl instance = this; 197 do { 198 instance.clearBranchTokenTypes(); 199 instance.parent = parent; 200 instance = instance.getNextSibling(); 201 } while (instance != null); 202 } 203 204 @Override 205 public DetailAST getParent() { 206 return parent; 207 } 208 209 @Override 210 public int getLineNo() { 211 int resultNo = -1; 212 213 if (lineNo == NOT_INITIALIZED) { 214 // an inner AST that has been initialized 215 // with initialize(String text) 216 resultNo = findLineNo(getFirstChild()); 217 218 if (resultNo == -1) { 219 resultNo = findLineNo(getNextSibling()); 220 } 221 } 222 if (resultNo == -1) { 223 resultNo = lineNo; 224 } 225 return resultNo; 226 } 227 228 /** 229 * Set line number. 230 * @param lineNo 231 * line number. 232 */ 233 public void setLineNo(int lineNo) { 234 this.lineNo = lineNo; 235 } 236 237 @Override 238 public int getColumnNo() { 239 int resultNo = -1; 240 241 if (columnNo == NOT_INITIALIZED) { 242 // an inner AST that has been initialized 243 // with initialize(String text) 244 resultNo = findColumnNo(getFirstChild()); 245 246 if (resultNo == -1) { 247 resultNo = findColumnNo(getNextSibling()); 248 } 249 } 250 if (resultNo == -1) { 251 resultNo = columnNo; 252 } 253 return resultNo; 254 } 255 256 /** 257 * Set column number. 258 * @param columnNo 259 * column number. 260 */ 261 public void setColumnNo(int columnNo) { 262 this.columnNo = columnNo; 263 } 264 265 @Override 266 public DetailAST getLastChild() { 267 DetailAST ast = getFirstChild(); 268 while (ast != null && ast.getNextSibling() != null) { 269 ast = ast.getNextSibling(); 270 } 271 return ast; 272 } 273 274 /** 275 * Finds column number in the first non-comment node. 276 * 277 * @param ast DetailAST node. 278 * @return Column number if non-comment node exists, -1 otherwise. 279 */ 280 private static int findColumnNo(DetailAST ast) { 281 int resultNo = -1; 282 DetailAST node = ast; 283 while (node != null) { 284 // comment node can't be start of any java statement/definition 285 if (TokenUtil.isCommentType(node.getType())) { 286 node = node.getNextSibling(); 287 } 288 else { 289 resultNo = node.getColumnNo(); 290 break; 291 } 292 } 293 return resultNo; 294 } 295 296 /** 297 * Finds line number in the first non-comment node. 298 * 299 * @param ast DetailAST node. 300 * @return Line number if non-comment node exists, -1 otherwise. 301 */ 302 private static int findLineNo(DetailAST ast) { 303 int resultNo = -1; 304 DetailAST node = ast; 305 while (node != null) { 306 // comment node can't be start of any java statement/definition 307 if (TokenUtil.isCommentType(node.getType())) { 308 node = node.getNextSibling(); 309 } 310 else { 311 resultNo = node.getLineNo(); 312 break; 313 } 314 } 315 return resultNo; 316 } 317 318 /** 319 * Returns token type with branch. 320 * @return the token types that occur in the branch as a sorted set. 321 */ 322 private BitSet getBranchTokenTypes() { 323 // lazy init 324 if (branchTokenTypes == null) { 325 branchTokenTypes = new BitSet(); 326 branchTokenTypes.set(getType()); 327 328 // add union of all children 329 DetailAstImpl child = getFirstChild(); 330 while (child != null) { 331 final BitSet childTypes = child.getBranchTokenTypes(); 332 branchTokenTypes.or(childTypes); 333 334 child = child.getNextSibling(); 335 } 336 } 337 return branchTokenTypes; 338 } 339 340 @Override 341 public boolean branchContains(int type) { 342 return getBranchTokenTypes().get(type); 343 } 344 345 @Override 346 public DetailAST getPreviousSibling() { 347 return previousSibling; 348 } 349 350 @Override 351 public DetailAST findFirstToken(int type) { 352 DetailAST returnValue = null; 353 for (DetailAST ast = getFirstChild(); ast != null; ast = ast.getNextSibling()) { 354 if (ast.getType() == type) { 355 returnValue = ast; 356 break; 357 } 358 } 359 return returnValue; 360 } 361 362 @Override 363 public String toString() { 364 return super.toString() + "[" + getLineNo() + "x" + getColumnNo() + "]"; 365 } 366 367 @Override 368 public DetailAstImpl getNextSibling() { 369 return (DetailAstImpl) super.getNextSibling(); 370 } 371 372 @Override 373 public DetailAstImpl getFirstChild() { 374 return (DetailAstImpl) super.getFirstChild(); 375 } 376 377 /** 378 * Clears the child count for the ast instance. 379 * @param ast The ast to clear. 380 */ 381 private static void clearChildCountCache(DetailAstImpl ast) { 382 if (ast != null) { 383 ast.childCount = NOT_INITIALIZED; 384 } 385 } 386 387 /** 388 * Clears branchTokenTypes cache for all parents of the current DetailAST instance, and the 389 * child count for the current DetailAST instance. 390 */ 391 private void clearBranchTokenTypes() { 392 DetailAstImpl prevParent = parent; 393 while (prevParent != null) { 394 prevParent.branchTokenTypes = null; 395 prevParent = prevParent.parent; 396 } 397 } 398 399}