View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2018 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  
20  package com.puppycrawl.tools.checkstyle.api;
21  
22  import java.util.BitSet;
23  
24  import antlr.CommonASTWithHiddenTokens;
25  import antlr.Token;
26  import antlr.collections.AST;
27  import com.puppycrawl.tools.checkstyle.utils.TokenUtils;
28  
29  /**
30   * An extension of the CommonAST that records the line and column number.
31   *
32   * @see <a href="http://www.antlr.org/">ANTLR Website</a>
33   * @noinspection FieldNotUsedInToString, SerializableHasSerializationMethods
34   */
35  public final class DetailAST extends CommonASTWithHiddenTokens {
36  
37      private static final long serialVersionUID = -2580884815577559874L;
38  
39      /** Constant to indicate if not calculated the child count. */
40      private static final int NOT_INITIALIZED = Integer.MIN_VALUE;
41  
42      /** The line number. **/
43      private int lineNo = NOT_INITIALIZED;
44      /** The column number. **/
45      private int columnNo = NOT_INITIALIZED;
46  
47      /** Number of children. */
48      private int childCount = NOT_INITIALIZED;
49      /** The parent token. */
50      private DetailAST parent;
51      /** Previous sibling. */
52      private DetailAST previousSibling;
53  
54      /**
55       * All token types in this branch.
56       * Token 'x' (where x is an int) is in this branch
57       * if branchTokenTypes.get(x) is true.
58       */
59      private BitSet branchTokenTypes;
60  
61      @Override
62      public void initialize(Token tok) {
63          super.initialize(tok);
64          lineNo = tok.getLine();
65  
66          // expect columns to start @ 0
67          columnNo = tok.getColumn() - 1;
68      }
69  
70      @Override
71      public void initialize(AST ast) {
72          final DetailAST detailAst = (DetailAST) ast;
73          setText(detailAst.getText());
74          setType(detailAst.getType());
75          lineNo = detailAst.getLineNo();
76          columnNo = detailAst.getColumnNo();
77          hiddenAfter = detailAst.getHiddenAfter();
78          hiddenBefore = detailAst.getHiddenBefore();
79      }
80  
81      @Override
82      public void setFirstChild(AST ast) {
83          clearBranchTokenTypes();
84          clearChildCountCache(this);
85          super.setFirstChild(ast);
86          if (ast != null) {
87              ((DetailAST) ast).setParent(this);
88          }
89      }
90  
91      @Override
92      public void setNextSibling(AST ast) {
93          clearBranchTokenTypes();
94          clearChildCountCache(parent);
95          super.setNextSibling(ast);
96          if (ast != null && parent != null) {
97              ((DetailAST) ast).setParent(parent);
98          }
99          if (ast != null) {
100             ((DetailAST) ast).previousSibling = this;
101         }
102     }
103 
104     /**
105      * Add previous sibling.
106      * @param ast
107      *        DetailAST object.
108      */
109     public void addPreviousSibling(DetailAST ast) {
110         clearBranchTokenTypes();
111         clearChildCountCache(parent);
112         if (ast != null) {
113             //parent is set in setNextSibling or parent.setFirstChild
114             final DetailAST previousSiblingNode = previousSibling;
115 
116             if (previousSiblingNode != null) {
117                 ast.previousSibling = previousSiblingNode;
118                 previousSiblingNode.setNextSibling(ast);
119             }
120             else if (parent != null) {
121                 parent.setFirstChild(ast);
122             }
123 
124             ast.setNextSibling(this);
125             previousSibling = ast;
126         }
127     }
128 
129     /**
130      * Add next sibling.
131      * @param ast
132      *        DetailAST object.
133      */
134     public void addNextSibling(DetailAST ast) {
135         clearBranchTokenTypes();
136         clearChildCountCache(parent);
137         if (ast != null) {
138             //parent is set in setNextSibling
139             final DetailAST nextSibling = getNextSibling();
140 
141             if (nextSibling != null) {
142                 ast.setNextSibling(nextSibling);
143                 nextSibling.previousSibling = ast;
144             }
145 
146             ast.previousSibling = this;
147             setNextSibling(ast);
148         }
149     }
150 
151     @Override
152     public void addChild(AST ast) {
153         clearBranchTokenTypes();
154         clearChildCountCache(this);
155         if (ast != null) {
156             ((DetailAST) ast).setParent(this);
157             ((DetailAST) ast).previousSibling = getLastChild();
158         }
159         super.addChild(ast);
160     }
161 
162     /**
163      * Returns the number of child nodes one level below this node. That is is
164      * does not recurse down the tree.
165      * @return the number of child nodes
166      */
167     public int getChildCount() {
168         // lazy init
169         if (childCount == NOT_INITIALIZED) {
170             childCount = 0;
171             AST child = getFirstChild();
172 
173             while (child != null) {
174                 childCount += 1;
175                 child = child.getNextSibling();
176             }
177         }
178         return childCount;
179     }
180 
181     /**
182      * Returns the number of direct child tokens that have the specified type.
183      * @param type the token type to match
184      * @return the number of matching token
185      */
186     public int getChildCount(int type) {
187         int count = 0;
188         for (AST ast = getFirstChild(); ast != null; ast = ast.getNextSibling()) {
189             if (ast.getType() == type) {
190                 count++;
191             }
192         }
193         return count;
194     }
195 
196     /**
197      * Set the parent token.
198      * @param parent the parent token
199      */
200     private void setParent(DetailAST parent) {
201         DetailAST instance = this;
202         do {
203             instance.clearBranchTokenTypes();
204             instance.parent = parent;
205             final DetailAST nextSibling = instance.getNextSibling();
206             if (nextSibling != null) {
207                 nextSibling.previousSibling = instance;
208             }
209 
210             instance = nextSibling;
211         } while (instance != null);
212     }
213 
214     /**
215      * Returns the parent token.
216      * @return the parent token
217      */
218     public DetailAST getParent() {
219         return parent;
220     }
221 
222     /**
223      * Gets line number.
224      * @return the line number
225      */
226     public int getLineNo() {
227         int resultNo = -1;
228 
229         if (lineNo == NOT_INITIALIZED) {
230             // an inner AST that has been initialized
231             // with initialize(String text)
232             resultNo = findLineNo(getFirstChild());
233 
234             if (resultNo == -1) {
235                 resultNo = findLineNo(getNextSibling());
236             }
237         }
238         if (resultNo == -1) {
239             resultNo = lineNo;
240         }
241         return resultNo;
242     }
243 
244     /**
245      * Set line number.
246      * @param lineNo
247      *        line number.
248      */
249     public void setLineNo(int lineNo) {
250         this.lineNo = lineNo;
251     }
252 
253     /**
254      * Gets column number.
255      * @return the column number
256      */
257     public int getColumnNo() {
258         int resultNo = -1;
259 
260         if (columnNo == NOT_INITIALIZED) {
261             // an inner AST that has been initialized
262             // with initialize(String text)
263             resultNo = findColumnNo(getFirstChild());
264 
265             if (resultNo == -1) {
266                 resultNo = findColumnNo(getNextSibling());
267             }
268         }
269         if (resultNo == -1) {
270             resultNo = columnNo;
271         }
272         return resultNo;
273     }
274 
275     /**
276      * Set column number.
277      * @param columnNo
278      *        column number.
279      */
280     public void setColumnNo(int columnNo) {
281         this.columnNo = columnNo;
282     }
283 
284     /**
285      * Gets the last child node.
286      * @return the last child node
287      */
288     public DetailAST getLastChild() {
289         DetailAST ast = getFirstChild();
290         while (ast != null && ast.getNextSibling() != null) {
291             ast = ast.getNextSibling();
292         }
293         return ast;
294     }
295 
296     /**
297      * Finds column number in the first non-comment node.
298      *
299      * @param ast DetailAST node.
300      * @return Column number if non-comment node exists, -1 otherwise.
301      */
302     private static int findColumnNo(DetailAST ast) {
303         int resultNo = -1;
304         DetailAST node = ast;
305         while (node != null) {
306             // comment node can't be start of any java statement/definition
307             if (TokenUtils.isCommentType(node.getType())) {
308                 node = node.getNextSibling();
309             }
310             else {
311                 resultNo = node.getColumnNo();
312                 break;
313             }
314         }
315         return resultNo;
316     }
317 
318     /**
319      * Finds line number in the first non-comment node.
320      *
321      * @param ast DetailAST node.
322      * @return Line number if non-comment node exists, -1 otherwise.
323      */
324     private static int findLineNo(DetailAST ast) {
325         int resultNo = -1;
326         DetailAST node = ast;
327         while (node != null) {
328             // comment node can't be start of any java statement/definition
329             if (TokenUtils.isCommentType(node.getType())) {
330                 node = node.getNextSibling();
331             }
332             else {
333                 resultNo = node.getLineNo();
334                 break;
335             }
336         }
337         return resultNo;
338     }
339 
340     /**
341      * Returns token type with branch.
342      * @return the token types that occur in the branch as a sorted set.
343      */
344     private BitSet getBranchTokenTypes() {
345         // lazy init
346         if (branchTokenTypes == null) {
347             branchTokenTypes = new BitSet();
348             branchTokenTypes.set(getType());
349 
350             // add union of all children
351             DetailAST child = getFirstChild();
352             while (child != null) {
353                 final BitSet childTypes = child.getBranchTokenTypes();
354                 branchTokenTypes.or(childTypes);
355 
356                 child = child.getNextSibling();
357             }
358         }
359         return branchTokenTypes;
360     }
361 
362     /**
363      * Checks if this branch of the parse tree contains a token
364      * of the provided type.
365      * @param type a TokenType
366      * @return true if and only if this branch (including this node)
367      *     contains a token of type {@code type}.
368      */
369     public boolean branchContains(int type) {
370         return getBranchTokenTypes().get(type);
371     }
372 
373     /**
374      * Returns the previous sibling or null if no such sibling exists.
375      * @return the previous sibling or null if no such sibling exists.
376      */
377     public DetailAST getPreviousSibling() {
378         return previousSibling;
379     }
380 
381     /**
382      * Returns the first child token that makes a specified type.
383      * @param type the token type to match
384      * @return the matching token, or null if no match
385      */
386     public DetailAST findFirstToken(int type) {
387         DetailAST returnValue = null;
388         for (DetailAST ast = getFirstChild(); ast != null; ast = ast.getNextSibling()) {
389             if (ast.getType() == type) {
390                 returnValue = ast;
391                 break;
392             }
393         }
394         return returnValue;
395     }
396 
397     @Override
398     public String toString() {
399         return super.toString() + "[" + getLineNo() + "x" + getColumnNo() + "]";
400     }
401 
402     @Override
403     public DetailAST getNextSibling() {
404         return (DetailAST) super.getNextSibling();
405     }
406 
407     @Override
408     public DetailAST getFirstChild() {
409         return (DetailAST) super.getFirstChild();
410     }
411 
412     /**
413      * Clears the child count for the ast instance.
414      * @param ast The ast to clear.
415      */
416     private static void clearChildCountCache(DetailAST ast) {
417         if (ast != null) {
418             ast.childCount = NOT_INITIALIZED;
419         }
420     }
421 
422     /**
423      * Clears branchTokenTypes cache for all parents of the current DetailAST instance, and the
424      * child count for the current DetailAST instance.
425      */
426     private void clearBranchTokenTypes() {
427         DetailAST prevParent = parent;
428         while (prevParent != null) {
429             prevParent.branchTokenTypes = null;
430             prevParent = prevParent.parent;
431         }
432     }
433 
434 }