View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2015 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  package com.puppycrawl.tools.checkstyle.api;
20  
21  import java.util.BitSet;
22  
23  import antlr.CommonASTWithHiddenTokens;
24  import antlr.Token;
25  import antlr.collections.AST;
26  
27  /**
28   * An extension of the CommonAST that records the line and column
29   * number.  The idea was taken from <a target="_top"
30   * href="http://www.jguru.com/jguru/faq/view.jsp?EID=62654">Java Guru
31   * FAQ: How can I include line numbers in automatically generated
32   * ASTs?</a>.
33   * @author Oliver Burn
34   * @author lkuehne
35   * @see <a href="http://www.antlr.org/">ANTLR Website</a>
36   */
37  public final class DetailAST extends CommonASTWithHiddenTokens
38  {
39      /** For Serialisation that will never happen. */
40      private static final long serialVersionUID = -2580884815577559874L;
41  
42      /** constant to indicate if not calculated the child count */
43      private static final int NOT_INITIALIZED = Integer.MIN_VALUE;
44  
45      /** the line number **/
46      private int lineNo = NOT_INITIALIZED;
47      /** the column number **/
48      private int columnNo = NOT_INITIALIZED;
49  
50      /** number of children */
51      private int childCount = NOT_INITIALIZED;
52      /** the parent token */
53      private DetailAST parent;
54      /** previous sibling */
55      private DetailAST previousSibling;
56  
57      /**
58       * All token types in this branch.
59       * Token 'x' (where x is an int) is in this branch
60       * if branchTokenTypes.get(x) is true.
61       */
62      private BitSet branchTokenTypes;
63  
64      @Override
65      public void initialize(Token tok)
66      {
67          super.initialize(tok);
68          lineNo = tok.getLine();
69          columnNo = tok.getColumn() - 1; // expect columns to start @ 0
70      }
71  
72      @Override
73      public void initialize(AST ast)
74      {
75          final DetailAST da = (DetailAST) ast;
76          setText(da.getText());
77          setType(da.getType());
78          lineNo = da.getLineNo();
79          columnNo = da.getColumnNo();
80          hiddenAfter = da.getHiddenAfter();
81          hiddenBefore = da.getHiddenBefore();
82      }
83  
84      @Override
85      public void setFirstChild(AST ast)
86      {
87          childCount = NOT_INITIALIZED;
88          super.setFirstChild(ast);
89          if (ast != null) {
90              ((DetailAST) ast).setParent(this);
91          }
92      }
93  
94      @Override
95      public void setNextSibling(AST ast)
96      {
97          super.setNextSibling(ast);
98          if (ast != null && parent != null) {
99              ((DetailAST) ast).setParent(parent);
100         }
101         if (ast != null) {
102             ((DetailAST) ast).setPreviousSibling(this);
103         }
104     }
105 
106     /**
107      * Add previous sibling.
108      * @param ast
109      *        DetailAST object.
110      */
111     public void addPreviousSibling(DetailAST ast)
112     {
113         if (ast != null) {
114             ast.setParent(parent);
115             final DetailAST previousSibling = this.getPreviousSibling();
116 
117             if (previousSibling != null) {
118                 ast.setPreviousSibling(previousSibling);
119                 previousSibling.setNextSibling(ast);
120             }
121             else if (parent != null) {
122                 parent.setFirstChild(ast);
123             }
124 
125             ast.setNextSibling(this);
126             this.setPreviousSibling(ast);
127         }
128     }
129 
130     /**
131      * Add next sibling.
132      * @param ast
133      *        DetailAST object.
134      */
135     public void addNextSibling(DetailAST ast)
136     {
137         if (ast != null) {
138             ast.setParent(parent);
139             final DetailAST nextSibling = this.getNextSibling();
140 
141             if (nextSibling != null) {
142                 ast.setNextSibling(nextSibling);
143                 nextSibling.setPreviousSibling(ast);
144             }
145 
146             ast.setPreviousSibling(this);
147             this.setNextSibling(ast);
148         }
149     }
150 
151     /**
152      * Sets previous sibling.
153      * @param ast a previous sibling
154      */
155     void setPreviousSibling(DetailAST ast)
156     {
157         previousSibling = ast;
158     }
159 
160     @Override
161     public void addChild(AST ast)
162     {
163         super.addChild(ast);
164         if (ast != null) {
165             ((DetailAST) ast).setParent(this);
166             getFirstChild().setParent(this);
167         }
168     }
169 
170     /**
171      * Returns the number of child nodes one level below this node. That is is
172      * does not recurse down the tree.
173      * @return the number of child nodes
174      */
175     public int getChildCount()
176     {
177         // lazy init
178         if (childCount == NOT_INITIALIZED) {
179             childCount = 0;
180             AST child = getFirstChild();
181 
182             while (child != null) {
183                 childCount += 1;
184                 child = child.getNextSibling();
185             }
186         }
187         return childCount;
188     }
189 
190     /**
191      * Set the parent token.
192      * @param parent the parent token
193      */
194     // TODO: should be private but that breaks the DetailASTTest
195     // until we manage parent in DetailAST instead of externally
196     void setParent(DetailAST parent)
197     {
198         // TODO: Check visibility, could be private
199         // if set in setFirstChild() and friends
200         this.parent = parent;
201         final DetailAST nextSibling = getNextSibling();
202         if (nextSibling != null) {
203             nextSibling.setParent(parent);
204             nextSibling.setPreviousSibling(this);
205         }
206     }
207 
208     /**
209      * Returns the parent token.
210      * @return the parent token
211      */
212     public DetailAST getParent()
213     {
214         return parent;
215     }
216 
217     /** @return the line number **/
218     public int getLineNo()
219     {
220         if (lineNo == NOT_INITIALIZED) {
221             // an inner AST that has been initialized
222             // with initialize(String text)
223             DetailAST child = getFirstChild();
224             while (child != null) {
225                 // comment node can't be start of any java statement/definition
226                 if (TokenTypes.isCommentType(child.getType())) {
227                     child = child.getNextSibling();
228                 }
229                 else {
230                     return child.getLineNo();
231                 }
232             }
233 
234             DetailAST sibling = getNextSibling();
235             while (sibling != null) {
236                 // comment node can't be start of any java statement/definition
237                 if (TokenTypes.isCommentType(sibling.getType())) {
238                     sibling = sibling.getNextSibling();
239                 }
240                 else {
241                     return sibling.getLineNo();
242                 }
243             }
244         }
245         return lineNo;
246     }
247 
248     /**
249      * Set line number.
250      * @param lineNo
251      *        line number.
252      */
253     public void setLineNo(int lineNo)
254     {
255         this.lineNo = lineNo;
256     }
257 
258     /** @return the column number **/
259     public int getColumnNo()
260     {
261         if (columnNo == NOT_INITIALIZED) {
262             // an inner AST that has been initialized
263             // with initialize(String text)
264             DetailAST child = getFirstChild();
265             while (child != null) {
266                 // comment node can't be start of any java statement/definition
267                 if (TokenTypes.isCommentType(child.getType())) {
268                     child = child.getNextSibling();
269                 }
270                 else {
271                     return child.getColumnNo();
272                 }
273             }
274 
275             DetailAST sibling = getNextSibling();
276             while (sibling != null) {
277                 // comment node can't be start of any java statement/definition
278                 if (TokenTypes.isCommentType(sibling.getType())) {
279                     sibling = sibling.getNextSibling();
280                 }
281                 else {
282                     return sibling.getColumnNo();
283                 }
284             }
285         }
286         return columnNo;
287     }
288 
289     /**
290      * Set column number.
291      * @param columnNo
292      *        column number.
293      */
294     public void setColumnNo(int columnNo)
295     {
296         this.columnNo = columnNo;
297     }
298 
299     /** @return the last child node */
300     public DetailAST getLastChild()
301     {
302         DetailAST ast = getFirstChild();
303         while (ast != null && ast.getNextSibling() != null) {
304             ast = ast.getNextSibling();
305         }
306         return ast;
307     }
308 
309     /**
310      * @return the token types that occur in the branch as a sorted set.
311      */
312     private BitSet getBranchTokenTypes()
313     {
314         // lazy init
315         if (branchTokenTypes == null) {
316 
317             branchTokenTypes = new BitSet();
318             branchTokenTypes.set(getType());
319 
320             // add union of all childs
321             DetailAST child = getFirstChild();
322             while (child != null) {
323                 final BitSet childTypes = child.getBranchTokenTypes();
324                 branchTokenTypes.or(childTypes);
325 
326                 child = child.getNextSibling();
327             }
328         }
329         return branchTokenTypes;
330     }
331 
332     /**
333      * Checks if this branch of the parse tree contains a token
334      * of the provided type.
335      * @param type a TokenType
336      * @return true if and only if this branch (including this node)
337      * contains a token of type <code>type</code>.
338      */
339     public boolean branchContains(int type)
340     {
341         return getBranchTokenTypes().get(type);
342     }
343 
344     /**
345      * Returns the number of direct child tokens that have the specified type.
346      * @param type the token type to match
347      * @return the number of matching token
348      */
349     public int getChildCount(int type)
350     {
351         int count = 0;
352         for (AST i = getFirstChild(); i != null; i = i.getNextSibling()) {
353             if (i.getType() == type) {
354                 count++;
355             }
356         }
357         return count;
358     }
359 
360     /**
361      * Returns the previous sibling or null if no such sibling exists.
362      * @return the previous sibling or null if no such sibling exists.
363      */
364     public DetailAST getPreviousSibling()
365     {
366         return previousSibling;
367     }
368 
369     /**
370      * Returns the first child token that makes a specified type.
371      * @param type the token type to match
372      * @return the matching token, or null if no match
373      */
374     public DetailAST findFirstToken(int type)
375     {
376         DetailAST retVal = null;
377         for (DetailAST i = getFirstChild(); i != null; i = i.getNextSibling()) {
378             if (i.getType() == type) {
379                 retVal = i;
380                 break;
381             }
382         }
383         return retVal;
384     }
385 
386     @Override
387     public String toString()
388     {
389         return super.toString() + "[" + getLineNo() + "x" + getColumnNo() + "]";
390     }
391 
392     @Override
393     public DetailAST getNextSibling()
394     {
395         return (DetailAST) super.getNextSibling();
396     }
397 
398     @Override
399     public DetailAST getFirstChild()
400     {
401         return (DetailAST) super.getFirstChild();
402     }
403 
404 }