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