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}