DetailAstImpl.java
///////////////////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
// Copyright (C) 2001-2022 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;
import java.util.BitSet;
import java.util.Collections;
import java.util.List;
import org.antlr.v4.runtime.Token;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
/**
* The implementation of {@link DetailAST}. This should only be directly used to
* create custom AST nodes and in 'JavaAstVisitor.java'.
*
* @noinspection FieldNotUsedInToString
*/
public final class DetailAstImpl implements DetailAST {
/** 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 DetailAstImpl parent;
/** Previous sibling. */
private DetailAstImpl previousSibling;
/** First child of this DetailAST. */
private DetailAstImpl firstChild;
/** First sibling of this DetailAST.*/
private DetailAstImpl nextSibling;
/** Text of this DetailAST. */
private String text;
/** The type of this DetailAST. */
private int type;
/**
* All tokens on COMMENTS channel to the left of the current token up to the
* preceding token on the DEFAULT_TOKEN_CHANNEL.
*/
private List<Token> hiddenBefore;
/**
* All tokens on COMMENTS channel to the right of the current token up to the
* next token on the DEFAULT_TOKEN_CHANNEL.
*/
private List<Token> hiddenAfter;
/**
* 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;
/**
* Initializes this DetailAstImpl.
*
* @param tokenType the type of this DetailAstImpl
* @param tokenText the text of this DetailAstImpl
*/
public void initialize(int tokenType, String tokenText) {
type = tokenType;
text = tokenText;
}
/**
* Initializes this DetailAstImpl.
*
* @param token the token to generate this DetailAstImpl from
*/
public void initialize(Token token) {
text = token.getText();
type = token.getType();
lineNo = token.getLine();
columnNo = token.getCharPositionInLine();
}
/**
* Add previous sibling.
*
* @param ast
* DetailAST object.
*/
public void addPreviousSibling(DetailAST ast) {
clearBranchTokenTypes();
clearChildCountCache(parent);
if (ast != null) {
// parent is set in setNextSibling or parent.setFirstChild
final DetailAstImpl previousSiblingNode = previousSibling;
final DetailAstImpl astImpl = (DetailAstImpl) ast;
if (previousSiblingNode != null) {
astImpl.previousSibling = previousSiblingNode;
previousSiblingNode.setNextSibling(astImpl);
}
else if (parent != null) {
parent.setFirstChild(astImpl);
}
astImpl.setNextSibling(this);
previousSibling = astImpl;
}
}
/**
* Add next sibling, pushes other siblings back.
*
* @param ast DetailAST object.
*/
public void addNextSibling(DetailAST ast) {
clearBranchTokenTypes();
clearChildCountCache(parent);
if (ast != null) {
// parent is set in setNextSibling
final DetailAstImpl sibling = nextSibling;
final DetailAstImpl astImpl = (DetailAstImpl) ast;
if (sibling != null) {
astImpl.setNextSibling(sibling);
sibling.previousSibling = astImpl;
}
astImpl.previousSibling = this;
setNextSibling(astImpl);
}
}
/**
* Adds a new child to the current AST.
*
* @param child to DetailAST to add as child
*/
public void addChild(DetailAST child) {
clearBranchTokenTypes();
clearChildCountCache(this);
if (child != null) {
final DetailAstImpl astImpl = (DetailAstImpl) child;
astImpl.setParent(this);
astImpl.previousSibling = (DetailAstImpl) getLastChild();
}
DetailAST temp = firstChild;
if (temp == null) {
firstChild = (DetailAstImpl) child;
}
else {
while (temp.getNextSibling() != null) {
temp = temp.getNextSibling();
}
((DetailAstImpl) temp).setNextSibling(child);
}
}
@Override
public int getChildCount() {
// lazy init
if (childCount == NOT_INITIALIZED) {
childCount = 0;
DetailAST child = firstChild;
while (child != null) {
childCount += 1;
child = child.getNextSibling();
}
}
return childCount;
}
@Override
public int getChildCount(int tokenType) {
int count = 0;
for (DetailAST ast = firstChild; ast != null; ast = ast.getNextSibling()) {
if (ast.getType() == tokenType) {
count++;
}
}
return count;
}
/**
* Set the parent token.
*
* @param parent the parent token
*/
private void setParent(DetailAstImpl parent) {
DetailAstImpl instance = this;
do {
instance.clearBranchTokenTypes();
instance.parent = parent;
instance = instance.nextSibling;
} while (instance != null);
}
@Override
public DetailAST getParent() {
return parent;
}
@Override
public String getText() {
return text;
}
/**
* Sets the text for this DetailAstImpl.
*
* @param text the text field of this DetailAstImpl
*/
public void setText(String text) {
this.text = text;
}
@Override
public int getType() {
return type;
}
/**
* Sets the type of this AST.
*
* @param type the token type of this DetailAstImpl
*/
public void setType(int type) {
this.type = type;
}
@Override
public int getLineNo() {
int resultNo = -1;
if (lineNo == NOT_INITIALIZED) {
// an inner AST that has been initialized
// with initialize(String text)
resultNo = findLineNo(firstChild);
if (resultNo == -1) {
resultNo = findLineNo(nextSibling);
}
}
if (resultNo == -1) {
resultNo = lineNo;
}
return resultNo;
}
/**
* Set line number.
*
* @param lineNo
* line number.
*/
public void setLineNo(int lineNo) {
this.lineNo = lineNo;
}
@Override
public int getColumnNo() {
int resultNo = -1;
if (columnNo == NOT_INITIALIZED) {
// an inner AST that has been initialized
// with initialize(String text)
resultNo = findColumnNo(firstChild);
if (resultNo == -1) {
resultNo = findColumnNo(nextSibling);
}
}
if (resultNo == -1) {
resultNo = columnNo;
}
return resultNo;
}
/**
* Set column number.
*
* @param columnNo
* column number.
*/
public void setColumnNo(int columnNo) {
this.columnNo = columnNo;
}
@Override
public DetailAST getLastChild() {
DetailAstImpl ast = firstChild;
while (ast != null && ast.nextSibling != null) {
ast = ast.nextSibling;
}
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 (TokenUtil.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 (TokenUtil.isCommentType(node.getType())) {
node = node.getNextSibling();
}
else {
resultNo = node.getLineNo();
break;
}
}
return resultNo;
}
/**
* Returns token type with branch.
*
* @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(type);
// add union of all children
DetailAstImpl child = firstChild;
while (child != null) {
final BitSet childTypes = child.getBranchTokenTypes();
branchTokenTypes.or(childTypes);
child = child.nextSibling;
}
}
return branchTokenTypes;
}
@Override
public boolean branchContains(int tokenType) {
return getBranchTokenTypes().get(tokenType);
}
@Override
public DetailAST getPreviousSibling() {
return previousSibling;
}
@Override
public DetailAST findFirstToken(int tokenType) {
DetailAST returnValue = null;
for (DetailAST ast = firstChild; ast != null; ast = ast.getNextSibling()) {
if (ast.getType() == tokenType) {
returnValue = ast;
break;
}
}
return returnValue;
}
@Override
public String toString() {
return text + "[" + getLineNo() + "x" + getColumnNo() + "]";
}
@Override
public DetailAstImpl getNextSibling() {
return nextSibling;
}
@Override
public DetailAstImpl getFirstChild() {
return firstChild;
}
@Override
public int getNumberOfChildren() {
return getChildCount();
}
@Override
public boolean hasChildren() {
return firstChild != null;
}
/**
* Clears the child count for the ast instance.
*
* @param ast The ast to clear.
*/
private static void clearChildCountCache(DetailAstImpl 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() {
DetailAstImpl prevParent = parent;
while (prevParent != null) {
prevParent.branchTokenTypes = null;
prevParent = prevParent.parent;
}
}
/**
* Sets the next sibling of this AST.
*
* @param nextSibling the DetailAST to set as sibling
*/
public void setNextSibling(DetailAST nextSibling) {
clearBranchTokenTypes();
clearChildCountCache(parent);
this.nextSibling = (DetailAstImpl) nextSibling;
if (nextSibling != null && parent != null) {
((DetailAstImpl) nextSibling).setParent(parent);
}
if (nextSibling != null) {
((DetailAstImpl) nextSibling).previousSibling = this;
}
}
/**
* Sets the first child of this AST.
*
* @param firstChild the DetailAST to set as first child
*/
public void setFirstChild(DetailAST firstChild) {
clearBranchTokenTypes();
clearChildCountCache(this);
this.firstChild = (DetailAstImpl) firstChild;
if (firstChild != null) {
((DetailAstImpl) firstChild).setParent(this);
}
}
/**
* Removes all children of this AST.
*/
public void removeChildren() {
firstChild = null;
}
/**
* Get list of tokens on COMMENTS channel to the left of the
* current token up to the preceding token on the DEFAULT_TOKEN_CHANNEL.
*
* @return list of comment tokens
*/
public List<Token> getHiddenBefore() {
List<Token> returnList = null;
if (hiddenBefore != null) {
returnList = Collections.unmodifiableList(hiddenBefore);
}
return returnList;
}
/**
* Get list tokens on COMMENTS channel to the right of the current
* token up to the next token on the DEFAULT_TOKEN_CHANNEL.
*
* @return list of comment tokens
*/
public List<Token> getHiddenAfter() {
List<Token> returnList = null;
if (hiddenAfter != null) {
returnList = Collections.unmodifiableList(hiddenAfter);
}
return returnList;
}
/**
* Sets the hiddenBefore token field.
*
* @param hiddenBefore comment token preceding this DetailAstImpl
*/
public void setHiddenBefore(List<Token> hiddenBefore) {
this.hiddenBefore = Collections.unmodifiableList(hiddenBefore);
}
/**
* Sets the hiddenAfter token field.
*
* @param hiddenAfter comment token following this DetailAstImpl
*/
public void setHiddenAfter(List<Token> hiddenAfter) {
this.hiddenAfter = Collections.unmodifiableList(hiddenAfter);
}
}