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