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.CommonAST;
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 CommonAST
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 mLineNo = NOT_INITIALIZED;
048    /** the column number **/
049    private int mColumnNo = NOT_INITIALIZED;
050
051    /** number of children */
052    private int mChildCount = NOT_INITIALIZED;
053    /** the parent token */
054    private DetailAST mParent;
055    /** previous sibling */
056    private DetailAST mPreviousSibling;
057
058    /**
059     * All token types in this branch.
060     * Token 'x' (where x is an int) is in this branch
061     * if mBranchTokenTypes.get(x) is true.
062     */
063    private BitSet mBranchTokenTypes;
064
065    @Override
066    public void initialize(Token aTok)
067    {
068        super.initialize(aTok);
069        mLineNo = aTok.getLine();
070        mColumnNo = aTok.getColumn() - 1; // expect columns to start @ 0
071    }
072
073    @Override
074    public void initialize(AST aAST)
075    {
076        final DetailAST da = (DetailAST) aAST;
077        setText(da.getText());
078        setType(da.getType());
079        mLineNo = da.getLineNo();
080        mColumnNo = da.getColumnNo();
081    }
082
083    @Override
084    public void setFirstChild(AST aAST)
085    {
086        mChildCount = NOT_INITIALIZED;
087        super.setFirstChild(aAST);
088        if (aAST != null) {
089            ((DetailAST) aAST).setParent(this);
090        }
091    }
092
093    @Override
094    public void setNextSibling(AST aAST)
095    {
096        super.setNextSibling(aAST);
097        if ((aAST != null) && (mParent != null)) {
098            ((DetailAST) aAST).setParent(mParent);
099        }
100        if (aAST != null) {
101            ((DetailAST) aAST).setPreviousSibling(this);
102        }
103    }
104
105    /**
106     * Sets previous sibling.
107     * @param aAST a previous sibling
108     */
109    void setPreviousSibling(DetailAST aAST)
110    {
111        mPreviousSibling = aAST;
112    }
113
114    @Override
115    public void addChild(AST aAST)
116    {
117        super.addChild(aAST);
118        if (aAST != null) {
119            ((DetailAST) aAST).setParent(this);
120            (getFirstChild()).setParent(this);
121        }
122    }
123
124    /**
125     * Returns the number of child nodes one level below this node. That is is
126     * does not recurse down the tree.
127     * @return the number of child nodes
128     */
129    public int getChildCount()
130    {
131        // lazy init
132        if (mChildCount == NOT_INITIALIZED) {
133            mChildCount = 0;
134            AST child = getFirstChild();
135
136            while (child != null) {
137                mChildCount += 1;
138                child = child.getNextSibling();
139            }
140        }
141        return mChildCount;
142    }
143
144    /**
145     * Set the parent token.
146     * @param aParent the parent token
147     */
148    // TODO: should be private but that breaks the DetailASTTest
149    // until we manage parent in DetailAST instead of externally
150    void setParent(DetailAST aParent)
151    {
152        // TODO: Check visibility, could be private
153        // if set in setFirstChild() and friends
154        mParent = aParent;
155        final DetailAST nextSibling = getNextSibling();
156        if (nextSibling != null) {
157            nextSibling.setParent(aParent);
158            nextSibling.setPreviousSibling(this);
159        }
160    }
161
162    /**
163     * Returns the parent token.
164     * @return the parent token
165     */
166    public DetailAST getParent()
167    {
168        return mParent;
169    }
170
171    /** @return the line number **/
172    public int getLineNo()
173    {
174        if (mLineNo == NOT_INITIALIZED) {
175            // an inner AST that has been initialized
176            // with initialize(String text)
177            final DetailAST child = getFirstChild();
178            final DetailAST sibling = getNextSibling();
179            if (child != null) {
180                return child.getLineNo();
181            }
182            else if (sibling != null) {
183                return sibling.getLineNo();
184            }
185        }
186        return mLineNo;
187    }
188
189    /** @return the column number **/
190    public int getColumnNo()
191    {
192        if (mColumnNo == NOT_INITIALIZED) {
193            // an inner AST that has been initialized
194            // with initialize(String text)
195            final DetailAST child = getFirstChild();
196            final DetailAST sibling = getNextSibling();
197            if (child != null) {
198                return child.getColumnNo();
199            }
200            else if (sibling != null) {
201                return sibling.getColumnNo();
202            }
203        }
204        return mColumnNo;
205    }
206
207    /** @return the last child node */
208    public DetailAST getLastChild()
209    {
210        DetailAST ast = getFirstChild();
211        while ((ast != null) && (ast.getNextSibling() != null)) {
212            ast = ast.getNextSibling();
213        }
214        return ast;
215    }
216
217    /**
218     * @return the token types that occur in the branch as a sorted set.
219     */
220    private BitSet getBranchTokenTypes()
221    {
222        // lazy init
223        if (mBranchTokenTypes == null) {
224
225            mBranchTokenTypes = new BitSet();
226            mBranchTokenTypes.set(getType());
227
228            // add union of all childs
229            DetailAST child = getFirstChild();
230            while (child != null) {
231                final BitSet childTypes = child.getBranchTokenTypes();
232                mBranchTokenTypes.or(childTypes);
233
234                child = child.getNextSibling();
235            }
236        }
237        return mBranchTokenTypes;
238    }
239
240    /**
241     * Checks if this branch of the parse tree contains a token
242     * of the provided type.
243     * @param aType a TokenType
244     * @return true if and only if this branch (including this node)
245     * contains a token of type <code>aType</code>.
246     */
247    public boolean branchContains(int aType)
248    {
249        return getBranchTokenTypes().get(aType);
250    }
251
252    /**
253     * Returns the number of direct child tokens that have the specified type.
254     * @param aType the token type to match
255     * @return the number of matching token
256     */
257    public int getChildCount(int aType)
258    {
259        int count = 0;
260        for (AST i = getFirstChild(); i != null; i = i.getNextSibling()) {
261            if (i.getType() == aType) {
262                count++;
263            }
264        }
265        return count;
266    }
267
268    /**
269     * Returns the previous sibling or null if no such sibling exists.
270     * @return the previous sibling or null if no such sibling exists.
271     */
272    public DetailAST getPreviousSibling()
273    {
274        return mPreviousSibling;
275    }
276
277    /**
278     * Returns the first child token that makes a specified type.
279     * @param aType the token type to match
280     * @return the matching token, or null if no match
281     */
282    public DetailAST findFirstToken(int aType)
283    {
284        DetailAST retVal = null;
285        for (DetailAST i = getFirstChild(); i != null; i = i.getNextSibling()) {
286            if (i.getType() == aType) {
287                retVal = i;
288                break;
289            }
290        }
291        return retVal;
292    }
293
294    @Override
295    public String toString()
296    {
297        return super.toString() + "[" + getLineNo() + "x" + getColumnNo() + "]";
298    }
299
300    @Override
301    public DetailAST getNextSibling()
302    {
303        return (DetailAST) super.getNextSibling();
304    }
305
306    @Override
307    public DetailAST getFirstChild()
308    {
309        return (DetailAST) super.getFirstChild();
310    }
311}