View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 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;
21  
22  import java.util.BitSet;
23  import java.util.List;
24  
25  import org.antlr.v4.runtime.Token;
26  
27  import com.puppycrawl.tools.checkstyle.api.DetailAST;
28  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
29  import com.puppycrawl.tools.checkstyle.utils.UnmodifiableCollectionUtil;
30  
31  /**
32   * The implementation of {@link DetailAST}. This should only be directly used to
33   * create custom AST nodes and in 'JavaAstVisitor.java'.
34   *
35   * @noinspection FieldNotUsedInToString
36   * @noinspectionreason FieldNotUsedInToString - We require a specific string format for
37   *      printing to CLI.
38   */
39  public final class DetailAstImpl implements DetailAST {
40  
41      /** Constant to indicate if not calculated the child count. */
42      private static final int NOT_INITIALIZED = Integer.MIN_VALUE;
43  
44      /** The line number. **/
45      private int lineNo = NOT_INITIALIZED;
46      /** The column number. **/
47      private int columnNo = NOT_INITIALIZED;
48  
49      /** Number of children. */
50      private int childCount;
51      /** The parent token. */
52      private DetailAstImpl parent;
53      /** Previous sibling. */
54      private DetailAstImpl previousSibling;
55  
56      /** First child of this DetailAST. */
57      private DetailAstImpl firstChild;
58  
59      /** First sibling of this DetailAST.*/
60      private DetailAstImpl nextSibling;
61  
62      /** Text of this DetailAST. */
63      private String text;
64  
65      /** The type of this DetailAST. */
66      private int type;
67  
68      /**
69       * All tokens on COMMENTS channel to the left of the current token up to the
70       * preceding token on the DEFAULT_TOKEN_CHANNEL.
71       */
72      private List<Token> hiddenBefore;
73  
74      /**
75       * All tokens on COMMENTS channel to the right of the current token up to the
76       * next token on the DEFAULT_TOKEN_CHANNEL.
77       */
78      private List<Token> hiddenAfter;
79  
80      /**
81       * All token types in this branch.
82       * Token 'x' (where x is an int) is in this branch
83       * if branchTokenTypes.get(x) is true.
84       */
85      private BitSet branchTokenTypes;
86  
87      /**
88       * Initializes this DetailAstImpl.
89       *
90       * @param tokenType the type of this DetailAstImpl
91       * @param tokenText the text of this DetailAstImpl
92       */
93      public void initialize(int tokenType, String tokenText) {
94          type = tokenType;
95          text = tokenText;
96      }
97  
98      /**
99       * Initializes this DetailAstImpl.
100      *
101      * @param token the token to generate this DetailAstImpl from
102      */
103     public void initialize(Token token) {
104         text = token.getText();
105         type = token.getType();
106         lineNo = token.getLine();
107         columnNo = token.getCharPositionInLine();
108     }
109 
110     /**
111      * Add previous sibling.
112      *
113      * @param ast
114      *        DetailAST object.
115      */
116     public void addPreviousSibling(DetailAST ast) {
117         clearBranchTokenTypes();
118         clearChildCountCache(parent);
119         if (ast != null) {
120             // parent is set in setNextSibling or parent.setFirstChild
121             final DetailAstImpl previousSiblingNode = previousSibling;
122             final DetailAstImpl astImpl = (DetailAstImpl) ast;
123 
124             if (previousSiblingNode != null) {
125                 previousSiblingNode.setNextSibling(astImpl);
126             }
127             else if (parent != null) {
128                 parent.setFirstChild(astImpl);
129             }
130 
131             astImpl.setNextSibling(this);
132         }
133     }
134 
135     /**
136      * Add next sibling, pushes other siblings back.
137      *
138      * @param ast DetailAST object.
139      */
140     public void addNextSibling(DetailAST ast) {
141         clearBranchTokenTypes();
142         clearChildCountCache(parent);
143         if (ast != null) {
144             // parent is set in setNextSibling
145             final DetailAstImpl sibling = nextSibling;
146             final DetailAstImpl astImpl = (DetailAstImpl) ast;
147             astImpl.setNextSibling(sibling);
148 
149             setNextSibling(astImpl);
150         }
151     }
152 
153     /**
154      * Adds a new child to the current AST.
155      *
156      * @param child to DetailAST to add as child
157      */
158     public void addChild(DetailAST child) {
159         clearBranchTokenTypes();
160         clearChildCountCache(this);
161         if (child != null) {
162             final DetailAstImpl astImpl = (DetailAstImpl) child;
163             astImpl.setParent(this);
164         }
165         DetailAST temp = firstChild;
166         if (temp == null) {
167             firstChild = (DetailAstImpl) child;
168         }
169         else {
170             while (temp.getNextSibling() != null) {
171                 temp = temp.getNextSibling();
172             }
173 
174             ((DetailAstImpl) temp).setNextSibling(child);
175         }
176     }
177 
178     @Override
179     public int getChildCount() {
180         // lazy init
181         if (childCount == NOT_INITIALIZED) {
182             childCount = 0;
183             DetailAST child = firstChild;
184 
185             while (child != null) {
186                 childCount += 1;
187                 child = child.getNextSibling();
188             }
189         }
190         return childCount;
191     }
192 
193     @Override
194     public int getChildCount(int tokenType) {
195         int count = 0;
196         for (DetailAST ast = firstChild; ast != null; ast = ast.getNextSibling()) {
197             if (ast.getType() == tokenType) {
198                 count++;
199             }
200         }
201         return count;
202     }
203 
204     /**
205      * Set the parent token.
206      *
207      * @param parent the parent token
208      */
209     private void setParent(DetailAstImpl parent) {
210         DetailAstImpl instance = this;
211         do {
212             instance.clearBranchTokenTypes();
213             instance.parent = parent;
214             instance = instance.nextSibling;
215         } while (instance != null);
216     }
217 
218     @Override
219     public DetailAST getParent() {
220         return parent;
221     }
222 
223     @Override
224     public String getText() {
225         return text;
226     }
227 
228     /**
229      * Sets the text for this DetailAstImpl.
230      *
231      * @param text the text field of this DetailAstImpl
232      */
233     public void setText(String text) {
234         this.text = text;
235     }
236 
237     @Override
238     public int getType() {
239         return type;
240     }
241 
242     /**
243      * Sets the type of this AST.
244      *
245      * @param type the token type of this DetailAstImpl
246      */
247     public void setType(int type) {
248         this.type = type;
249     }
250 
251     @Override
252     public int getLineNo() {
253         int resultNo = -1;
254 
255         if (lineNo == NOT_INITIALIZED) {
256             // an inner AST that has been initialized
257             // with initialize(String text)
258             resultNo = findLineNo(firstChild);
259 
260             if (resultNo == -1) {
261                 resultNo = findLineNo(nextSibling);
262             }
263         }
264         if (resultNo == -1) {
265             resultNo = lineNo;
266         }
267         return resultNo;
268     }
269 
270     /**
271      * Set line number.
272      *
273      * @param lineNo
274      *        line number.
275      */
276     public void setLineNo(int lineNo) {
277         this.lineNo = lineNo;
278     }
279 
280     @Override
281     public int getColumnNo() {
282         int resultNo = -1;
283 
284         if (columnNo == NOT_INITIALIZED) {
285             // an inner AST that has been initialized
286             // with initialize(String text)
287             resultNo = findColumnNo(firstChild);
288 
289             if (resultNo == -1) {
290                 resultNo = findColumnNo(nextSibling);
291             }
292         }
293         if (resultNo == -1) {
294             resultNo = columnNo;
295         }
296         return resultNo;
297     }
298 
299     /**
300      * Set column number.
301      *
302      * @param columnNo
303      *        column number.
304      */
305     public void setColumnNo(int columnNo) {
306         this.columnNo = columnNo;
307     }
308 
309     @Override
310     public DetailAST getLastChild() {
311         DetailAstImpl ast = firstChild;
312         while (ast != null && ast.nextSibling != null) {
313             ast = ast.nextSibling;
314         }
315         return ast;
316     }
317 
318     /**
319      * Finds column number in the first non-comment node.
320      *
321      * @param ast DetailAST node.
322      * @return Column number if non-comment node exists, -1 otherwise.
323      */
324     private static int findColumnNo(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 (TokenUtil.isCommentType(node.getType())) {
330                 node = node.getNextSibling();
331             }
332             else {
333                 resultNo = node.getColumnNo();
334                 break;
335             }
336         }
337         return resultNo;
338     }
339 
340     /**
341      * Finds line number in the first non-comment node.
342      *
343      * @param ast DetailAST node.
344      * @return Line number if non-comment node exists, -1 otherwise.
345      */
346     private static int findLineNo(DetailAST ast) {
347         int resultNo = -1;
348         DetailAST node = ast;
349         while (node != null) {
350             // comment node can't be start of any java statement/definition
351             if (TokenUtil.isCommentType(node.getType())) {
352                 node = node.getNextSibling();
353             }
354             else {
355                 resultNo = node.getLineNo();
356                 break;
357             }
358         }
359         return resultNo;
360     }
361 
362     /**
363      * Returns token type with branch.
364      *
365      * @return the token types that occur in the branch as a sorted set.
366      */
367     private BitSet getBranchTokenTypes() {
368         // lazy init
369         if (branchTokenTypes == null) {
370             branchTokenTypes = new BitSet();
371             branchTokenTypes.set(type);
372 
373             // add union of all children
374             DetailAstImpl child = firstChild;
375             while (child != null) {
376                 final BitSet childTypes = child.getBranchTokenTypes();
377                 branchTokenTypes.or(childTypes);
378 
379                 child = child.nextSibling;
380             }
381         }
382         return branchTokenTypes;
383     }
384 
385     @Override
386     public boolean branchContains(int tokenType) {
387         return getBranchTokenTypes().get(tokenType);
388     }
389 
390     @Override
391     public DetailAST getPreviousSibling() {
392         return previousSibling;
393     }
394 
395     @Override
396     public DetailAST findFirstToken(int tokenType) {
397         DetailAST returnValue = null;
398         for (DetailAST ast = firstChild; ast != null; ast = ast.getNextSibling()) {
399             if (ast.getType() == tokenType) {
400                 returnValue = ast;
401                 break;
402             }
403         }
404         return returnValue;
405     }
406 
407     @Override
408     public String toString() {
409         return text + "[" + getLineNo() + "x" + getColumnNo() + "]";
410     }
411 
412     @Override
413     public DetailAstImpl getNextSibling() {
414         return nextSibling;
415     }
416 
417     @Override
418     public DetailAstImpl getFirstChild() {
419         return firstChild;
420     }
421 
422     @Override
423     public int getNumberOfChildren() {
424         return getChildCount();
425     }
426 
427     @Override
428     public boolean hasChildren() {
429         return firstChild != null;
430     }
431 
432     /**
433      * Clears the child count for the ast instance.
434      *
435      * @param ast The ast to clear.
436      */
437     private static void clearChildCountCache(DetailAstImpl ast) {
438         if (ast != null) {
439             ast.childCount = NOT_INITIALIZED;
440         }
441     }
442 
443     /**
444      * Clears branchTokenTypes cache for all parents of the current DetailAST instance, and the
445      * child count for the current DetailAST instance.
446      */
447     private void clearBranchTokenTypes() {
448         DetailAstImpl prevParent = parent;
449         while (prevParent != null) {
450             prevParent.branchTokenTypes = null;
451             prevParent = prevParent.parent;
452         }
453     }
454 
455     /**
456      * Sets the next sibling of this AST.
457      *
458      * @param nextSibling the DetailAST to set as sibling
459      */
460     public void setNextSibling(DetailAST nextSibling) {
461         clearBranchTokenTypes();
462         clearChildCountCache(parent);
463         this.nextSibling = (DetailAstImpl) nextSibling;
464         if (nextSibling != null && parent != null) {
465             ((DetailAstImpl) nextSibling).setParent(parent);
466         }
467         if (nextSibling != null) {
468             ((DetailAstImpl) nextSibling).previousSibling = this;
469         }
470     }
471 
472     /**
473      * Sets the first child of this AST.
474      *
475      * @param firstChild the DetailAST to set as first child
476      */
477     public void setFirstChild(DetailAST firstChild) {
478         clearBranchTokenTypes();
479         clearChildCountCache(this);
480         this.firstChild = (DetailAstImpl) firstChild;
481         if (firstChild != null) {
482             ((DetailAstImpl) firstChild).setParent(this);
483         }
484     }
485 
486     /**
487      * Removes all children of this AST.
488      */
489     public void removeChildren() {
490         firstChild = null;
491     }
492 
493     /**
494      * Get list of tokens on COMMENTS channel to the left of the
495      * current token up to the preceding token on the DEFAULT_TOKEN_CHANNEL.
496      *
497      * @return list of comment tokens
498      */
499     public List<Token> getHiddenBefore() {
500         List<Token> returnList = null;
501         if (hiddenBefore != null) {
502             returnList = UnmodifiableCollectionUtil.unmodifiableList(hiddenBefore);
503         }
504         return returnList;
505     }
506 
507     /**
508      * Get list tokens on COMMENTS channel to the right of the current
509      * token up to the next token on the DEFAULT_TOKEN_CHANNEL.
510      *
511      * @return list of comment tokens
512      */
513     public List<Token> getHiddenAfter() {
514         List<Token> returnList = null;
515         if (hiddenAfter != null) {
516             returnList = UnmodifiableCollectionUtil.unmodifiableList(hiddenAfter);
517         }
518         return returnList;
519     }
520 
521     /**
522      * Sets the hiddenBefore token field.
523      *
524      * @param hiddenBefore comment token preceding this DetailAstImpl
525      */
526     public void setHiddenBefore(List<Token> hiddenBefore) {
527         this.hiddenBefore = UnmodifiableCollectionUtil.unmodifiableList(hiddenBefore);
528     }
529 
530     /**
531      * Sets the hiddenAfter token field.
532      *
533      * @param hiddenAfter comment token following this DetailAstImpl
534      */
535     public void setHiddenAfter(List<Token> hiddenAfter) {
536         this.hiddenAfter = UnmodifiableCollectionUtil.unmodifiableList(hiddenAfter);
537     }
538 }