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