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