//////////////////////////////////////////////////////////////////////////////// // checkstyle: Checks Java source code for adherence to a set of rules. // Copyright (C) 2001-2017 the original author or authors. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //////////////////////////////////////////////////////////////////////////////// package com.puppycrawl.tools.checkstyle.api; import java.util.BitSet; import antlr.CommonASTWithHiddenTokens; import antlr.Token; import antlr.collections.AST; import com.puppycrawl.tools.checkstyle.utils.TokenUtils; /** * An extension of the CommonAST that records the line and column number. * * @author Oliver Burn * @author lkuehne * @see <a href="http://www.antlr.org/">ANTLR Website</a> */ public final class DetailAST extends CommonASTWithHiddenTokens { private static final long serialVersionUID = -2580884815577559874L; /** Constant to indicate if not calculated the child count. */ private static final int NOT_INITIALIZED = Integer.MIN_VALUE; /** The line number. **/ private int lineNo = NOT_INITIALIZED; /** The column number. **/ private int columnNo = NOT_INITIALIZED; /** Number of children. */ private int childCount = NOT_INITIALIZED; /** The parent token. */ private DetailAST parent; /** Previous sibling. */ private DetailAST previousSibling; /** * All token types in this branch. * Token 'x' (where x is an int) is in this branch * if branchTokenTypes.get(x) is true. */ private BitSet branchTokenTypes; @Override public void initialize(Token tok) { super.initialize(tok); lineNo = tok.getLine(); // expect columns to start @ 0 columnNo = tok.getColumn() - 1; } @Override public void initialize(AST ast) { final DetailAST detailAst = (DetailAST) ast; setText(detailAst.getText()); setType(detailAst.getType()); lineNo = detailAst.getLineNo(); columnNo = detailAst.getColumnNo(); hiddenAfter = detailAst.getHiddenAfter(); hiddenBefore = detailAst.getHiddenBefore(); } @Override public void setFirstChild(AST ast) { clearBranchTokenTypes(); clearChildCountCache(this); super.setFirstChild(ast); if (ast != null) { ((DetailAST) ast).setParent(this); } } @Override public void setNextSibling(AST ast) { clearBranchTokenTypes(); clearChildCountCache(parent); super.setNextSibling(ast); if (ast != null && parent != null) { ((DetailAST) ast).setParent(parent); } if (ast != null) { ((DetailAST) ast).previousSibling = this; } } /** * Add previous sibling. * @param ast * DetailAST object. */ public void addPreviousSibling(DetailAST ast) { clearBranchTokenTypes(); clearChildCountCache(parent); if (ast != null) { ast.setParent(parent); final DetailAST previousSiblingNode = previousSibling; if (previousSiblingNode != null) { ast.previousSibling = previousSiblingNode; previousSiblingNode.setNextSibling(ast); } else if (parent != null) { parent.setFirstChild(ast); } ast.setNextSibling(this); previousSibling = ast; } } /** * Add next sibling. * @param ast * DetailAST object. */ public void addNextSibling(DetailAST ast) { clearBranchTokenTypes(); clearChildCountCache(parent); if (ast != null) { ast.setParent(parent); final DetailAST nextSibling = getNextSibling(); if (nextSibling != null) { ast.setNextSibling(nextSibling); nextSibling.previousSibling = ast; } ast.previousSibling = this; setNextSibling(ast); } } @Override public void addChild(AST ast) { clearBranchTokenTypes(); clearChildCountCache(this); if (ast != null) { ((DetailAST) ast).setParent(this); ((DetailAST) ast).previousSibling = getLastChild(); } super.addChild(ast); } /** * Returns the number of child nodes one level below this node. That is is * does not recurse down the tree. * @return the number of child nodes */ public int getChildCount() { // lazy init if (childCount == NOT_INITIALIZED) { childCount = 0; AST child = getFirstChild(); while (child != null) { childCount += 1; child = child.getNextSibling(); } } return childCount; } /** * Returns the number of direct child tokens that have the specified type. * @param type the token type to match * @return the number of matching token */ public int getChildCount(int type) { int count = 0; for (AST ast = getFirstChild(); ast != null; ast = ast.getNextSibling()) { if (ast.getType() == type) { count++; } } return count; } /** * Set the parent token. * @param parent the parent token */ private void setParent(DetailAST parent) { clearBranchTokenTypes(); this.parent = parent; final DetailAST nextSibling = getNextSibling(); if (nextSibling != null) { nextSibling.setParent(parent); nextSibling.previousSibling = this; } } /** * Returns the parent token. * @return the parent token */ public DetailAST getParent() { return parent; } /** * Gets line number. * @return the line number */ public int getLineNo() { int resultNo = -1; if (lineNo == NOT_INITIALIZED) { // an inner AST that has been initialized // with initialize(String text) resultNo = findLineNo(getFirstChild()); if (resultNo < 0) { resultNo = findLineNo(getNextSibling()); } } if (resultNo < 0) { resultNo = lineNo; } return resultNo; } /** * Set line number. * @param lineNo * line number. */ public void setLineNo(int lineNo) { this.lineNo = lineNo; } /** * Gets column number. * @return the column number */ public int getColumnNo() { int resultNo = -1; if (columnNo == NOT_INITIALIZED) { // an inner AST that has been initialized // with initialize(String text) resultNo = findColumnNo(getFirstChild()); if (resultNo < 0) { resultNo = findColumnNo(getNextSibling()); } } if (resultNo < 0) { resultNo = columnNo; } return resultNo; } /** * Set column number. * @param columnNo * column number. */ public void setColumnNo(int columnNo) { this.columnNo = columnNo; } /** * Gets the last child node. * @return the last child node */ public DetailAST getLastChild() { DetailAST ast = getFirstChild(); while (ast != null && ast.getNextSibling() != null) { ast = ast.getNextSibling(); } return ast; } /** * Finds column number in the first non-comment node. * * @param ast DetailAST node. * @return Column number if non-comment node exists, -1 otherwise. */ private static int findColumnNo(DetailAST ast) { int resultNo = -1; DetailAST node = ast; while (node != null) { // comment node can't be start of any java statement/definition if (TokenUtils.isCommentType(node.getType())) { node = node.getNextSibling(); } else { resultNo = node.getColumnNo(); break; } } return resultNo; } /** * Finds line number in the first non-comment node. * * @param ast DetailAST node. * @return Line number if non-comment node exists, -1 otherwise. */ private static int findLineNo(DetailAST ast) { int resultNo = -1; DetailAST node = ast; while (node != null) { // comment node can't be start of any java statement/definition if (TokenUtils.isCommentType(node.getType())) { node = node.getNextSibling(); } else { resultNo = node.getLineNo(); break; } } return resultNo; } /** * @return the token types that occur in the branch as a sorted set. */ private BitSet getBranchTokenTypes() { // lazy init if (branchTokenTypes == null) { branchTokenTypes = new BitSet(); branchTokenTypes.set(getType()); // add union of all children DetailAST child = getFirstChild(); while (child != null) { final BitSet childTypes = child.getBranchTokenTypes(); branchTokenTypes.or(childTypes); child = child.getNextSibling(); } } return branchTokenTypes; } /** * Checks if this branch of the parse tree contains a token * of the provided type. * @param type a TokenType * @return true if and only if this branch (including this node) * contains a token of type {@code type}. */ public boolean branchContains(int type) { return getBranchTokenTypes().get(type); } /** * Returns the previous sibling or null if no such sibling exists. * @return the previous sibling or null if no such sibling exists. */ public DetailAST getPreviousSibling() { return previousSibling; } /** * Returns the first child token that makes a specified type. * @param type the token type to match * @return the matching token, or null if no match */ public DetailAST findFirstToken(int type) { DetailAST returnValue = null; for (DetailAST ast = getFirstChild(); ast != null; ast = ast.getNextSibling()) { if (ast.getType() == type) { returnValue = ast; break; } } return returnValue; } @Override public String toString() { return super.toString() + "[" + getLineNo() + "x" + getColumnNo() + "]"; } @Override public DetailAST getNextSibling() { return (DetailAST) super.getNextSibling(); } @Override public DetailAST getFirstChild() { return (DetailAST) super.getFirstChild(); } /** * Clears the child count for the ast instance. * @param ast The ast to clear. */ private static void clearChildCountCache(DetailAST ast) { if (ast != null) { ast.childCount = NOT_INITIALIZED; } } /** * Clears branchTokenTypes cache for all parents of the current DetailAST instance, and the * child count for the current DetailAST instance. */ private void clearBranchTokenTypes() { DetailAST prevParent = getParent(); while (prevParent != null) { prevParent.branchTokenTypes = null; prevParent = prevParent.getParent(); } } }