View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2014  Oliver Burn
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   * @version 1.0
36   * @see <a href="http://www.antlr.org/">ANTLR Website</a>
37   */
38  public final class DetailAST extends CommonASTWithHiddenTokens
39  {
40      /** For Serialisation that will never happen. */
41      private static final long serialVersionUID = -2580884815577559874L;
42  
43      /** constant to indicate if not calculated the child count */
44      private static final int NOT_INITIALIZED = Integer.MIN_VALUE;
45  
46      /** the line number **/
47      private int lineNo = NOT_INITIALIZED;
48      /** the column number **/
49      private int columnNo = NOT_INITIALIZED;
50  
51      /** number of children */
52      private int childCount = NOT_INITIALIZED;
53      /** the parent token */
54      private DetailAST parent;
55      /** previous sibling */
56      private DetailAST previousSibling;
57  
58      /**
59       * All token types in this branch.
60       * Token 'x' (where x is an int) is in this branch
61       * if branchTokenTypes.get(x) is true.
62       */
63      private BitSet branchTokenTypes;
64  
65      @Override
66      public void initialize(Token tok)
67      {
68          super.initialize(tok);
69          lineNo = tok.getLine();
70          columnNo = tok.getColumn() - 1; // expect columns to start @ 0
71      }
72  
73      @Override
74      public void initialize(AST ast)
75      {
76          final DetailAST da = (DetailAST) ast;
77          setText(da.getText());
78          setType(da.getType());
79          lineNo = da.getLineNo();
80          columnNo = da.getColumnNo();
81          hiddenAfter = da.getHiddenAfter();
82          hiddenBefore = da.getHiddenBefore();
83      }
84  
85      @Override
86      public void setFirstChild(AST ast)
87      {
88          childCount = NOT_INITIALIZED;
89          super.setFirstChild(ast);
90          if (ast != null) {
91              ((DetailAST) ast).setParent(this);
92          }
93      }
94  
95      @Override
96      public void setNextSibling(AST ast)
97      {
98          super.setNextSibling(ast);
99          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 }