001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2026 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018///////////////////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle;
021
022import java.util.BitSet;
023import java.util.List;
024
025import javax.annotation.Nullable;
026
027import org.antlr.v4.runtime.Token;
028
029import com.puppycrawl.tools.checkstyle.api.DetailAST;
030import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
031import com.puppycrawl.tools.checkstyle.utils.UnmodifiableCollectionUtil;
032
033/**
034 * The implementation of {@link DetailAST}. This should only be directly used to
035 * create custom AST nodes and in 'JavaAstVisitor.java'.
036 *
037 * @noinspection FieldNotUsedInToString
038 * @noinspectionreason FieldNotUsedInToString - We require a specific string format for
039 *      printing to CLI.
040 */
041public final class DetailAstImpl implements DetailAST {
042
043    /** Constant to indicate if not calculated the child count. */
044    private static final int NOT_INITIALIZED = Integer.MIN_VALUE;
045
046    /** The line number. */
047    private int lineNo = NOT_INITIALIZED;
048    /** The column number. */
049    private int columnNo = NOT_INITIALIZED;
050
051    /** Number of children. */
052    private int childCount;
053    /** The parent token. */
054    private DetailAstImpl parent;
055    /** Previous sibling. */
056    private DetailAstImpl previousSibling;
057
058    /** First child of this DetailAST. */
059    private DetailAstImpl firstChild;
060
061    /** First sibling of this DetailAST.*/
062    private DetailAstImpl nextSibling;
063
064    /** Text of this DetailAST. */
065    private String text;
066
067    /** The type of this DetailAST. */
068    private int type;
069
070    /**
071     * All tokens on COMMENTS channel to the left of the current token up to the
072     * preceding token on the DEFAULT_TOKEN_CHANNEL.
073     */
074    private List<Token> hiddenBefore;
075
076    /**
077     * All tokens on COMMENTS channel to the right of the current token up to the
078     * next token on the DEFAULT_TOKEN_CHANNEL.
079     */
080    private List<Token> hiddenAfter;
081
082    /**
083     * All token types in this branch.
084     * Token 'x' (where x is an int) is in this branch if branchTokenTypes.get(x) is true.
085     */
086    private BitSet branchTokenTypes;
087
088    /**
089     * Creates a new {@code DetailAstImpl} instance.
090     */
091    public DetailAstImpl() {
092        // no code by default
093    }
094
095    /**
096     * Initializes this DetailAstImpl.
097     *
098     * @param token the token to generate this DetailAstImpl from
099     */
100    public void initialize(Token token) {
101        text = token.getText();
102        type = token.getType();
103        lineNo = token.getLine();
104        columnNo = token.getCharPositionInLine();
105    }
106
107    /**
108     * Initializes this DetailAstImpl.
109     *
110     * @param tokenType the type of this DetailAstImpl
111     * @param tokenText the text of this DetailAstImpl
112     */
113    public void initialize(int tokenType, String tokenText) {
114        type = tokenType;
115        text = tokenText;
116    }
117
118    /**
119     * Add previous sibling.
120     *
121     * @param ast
122     *        DetailAST object.
123     */
124    public void addPreviousSibling(DetailAST ast) {
125        clearBranchTokenTypes();
126        clearChildCountCache(parent);
127        if (ast != null) {
128            // parent is set in setNextSibling or parent.setFirstChild
129            final DetailAstImpl previousSiblingNode = previousSibling;
130            final DetailAstImpl astImpl = (DetailAstImpl) ast;
131
132            if (previousSiblingNode != null) {
133                previousSiblingNode.setNextSibling(astImpl);
134            }
135            else if (parent != null) {
136                parent.setFirstChild(astImpl);
137            }
138
139            astImpl.setNextSibling(this);
140        }
141    }
142
143    /**
144     * Add next sibling, pushes other siblings back.
145     *
146     * @param ast DetailAST object.
147     */
148    public void addNextSibling(DetailAST ast) {
149        clearBranchTokenTypes();
150        clearChildCountCache(parent);
151        if (ast != null) {
152            // parent is set in setNextSibling
153            final DetailAstImpl sibling = nextSibling;
154            final DetailAstImpl astImpl = (DetailAstImpl) ast;
155            astImpl.setNextSibling(sibling);
156
157            setNextSibling(astImpl);
158        }
159    }
160
161    /**
162     * Adds a new child to the current AST.
163     *
164     * @param child to DetailAST to add as child
165     */
166    public void addChild(DetailAST child) {
167        clearBranchTokenTypes();
168        clearChildCountCache(this);
169        if (child != null) {
170            final DetailAstImpl astImpl = (DetailAstImpl) child;
171            astImpl.setParent(this);
172        }
173        DetailAST temp = firstChild;
174        if (temp == null) {
175            firstChild = (DetailAstImpl) child;
176        }
177        else {
178            while (temp.getNextSibling() != null) {
179                temp = temp.getNextSibling();
180            }
181
182            ((DetailAstImpl) temp).setNextSibling(child);
183        }
184    }
185
186    @Override
187    public int getChildCount() {
188        // lazy init
189        if (childCount == NOT_INITIALIZED) {
190            childCount = 0;
191            DetailAST child = firstChild;
192
193            while (child != null) {
194                childCount += 1;
195                child = child.getNextSibling();
196            }
197        }
198        return childCount;
199    }
200
201    @Override
202    public int getChildCount(int tokenType) {
203        int count = 0;
204        for (DetailAST ast = firstChild; ast != null; ast = ast.getNextSibling()) {
205            if (ast.getType() == tokenType) {
206                count++;
207            }
208        }
209        return count;
210    }
211
212    /**
213     * Set the parent token.
214     *
215     * @param parent the parent token
216     */
217    private void setParent(DetailAstImpl parent) {
218        DetailAstImpl instance = this;
219        do {
220            instance.clearBranchTokenTypes();
221            instance.parent = parent;
222            instance = instance.nextSibling;
223        } while (instance != null);
224    }
225
226    @Override
227    public DetailAST getParent() {
228        return parent;
229    }
230
231    @Override
232    public String getText() {
233        return text;
234    }
235
236    /**
237     * Sets the text for this DetailAstImpl.
238     *
239     * @param text the text field of this DetailAstImpl
240     */
241    public void setText(String text) {
242        this.text = text;
243    }
244
245    @Override
246    public int getType() {
247        return type;
248    }
249
250    /**
251     * Sets the type of this AST.
252     *
253     * @param type the token type of this DetailAstImpl
254     */
255    public void setType(int type) {
256        this.type = type;
257    }
258
259    @Override
260    public int getLineNo() {
261        int resultNo = -1;
262
263        if (lineNo == NOT_INITIALIZED) {
264            // an inner AST that has been initialized
265            // with initialize(String text)
266            resultNo = findLineNo(firstChild);
267
268            if (resultNo == -1) {
269                resultNo = findLineNo(nextSibling);
270            }
271        }
272        if (resultNo == -1) {
273            resultNo = lineNo;
274        }
275        return resultNo;
276    }
277
278    /**
279     * Set line number.
280     *
281     * @param lineNo
282     *        line number.
283     */
284    public void setLineNo(int lineNo) {
285        this.lineNo = lineNo;
286    }
287
288    @Override
289    public int getColumnNo() {
290        int resultNo = -1;
291
292        if (columnNo == NOT_INITIALIZED) {
293            // an inner AST that has been initialized
294            // with initialize(String text)
295            resultNo = findColumnNo(firstChild);
296
297            if (resultNo == -1) {
298                resultNo = findColumnNo(nextSibling);
299            }
300        }
301        if (resultNo == -1) {
302            resultNo = columnNo;
303        }
304        return resultNo;
305    }
306
307    /**
308     * Set column number.
309     *
310     * @param columnNo
311     *        column number.
312     */
313    public void setColumnNo(int columnNo) {
314        this.columnNo = columnNo;
315    }
316
317    @Override
318    public DetailAST getLastChild() {
319        DetailAstImpl ast = firstChild;
320        while (ast != null && ast.nextSibling != null) {
321            ast = ast.nextSibling;
322        }
323        return ast;
324    }
325
326    /**
327     * Finds column number in the first non-comment node.
328     *
329     * @param ast DetailAST node.
330     * @return Column number if non-comment node exists, -1 otherwise.
331     */
332    private static int findColumnNo(DetailAST ast) {
333        int resultNo = -1;
334        DetailAST node = ast;
335        while (node != null) {
336            // comment node can't be start of any java statement/definition
337            if (TokenUtil.isCommentType(node.getType())) {
338                node = node.getNextSibling();
339            }
340            else {
341                resultNo = node.getColumnNo();
342                break;
343            }
344        }
345        return resultNo;
346    }
347
348    /**
349     * Finds line number in the first non-comment node.
350     *
351     * @param ast DetailAST node.
352     * @return Line number if non-comment node exists, -1 otherwise.
353     */
354    private static int findLineNo(DetailAST ast) {
355        int resultNo = -1;
356        DetailAST node = ast;
357        while (node != null) {
358            // comment node can't be start of any java statement/definition
359            if (TokenUtil.isCommentType(node.getType())) {
360                node = node.getNextSibling();
361            }
362            else {
363                resultNo = node.getLineNo();
364                break;
365            }
366        }
367        return resultNo;
368    }
369
370    /**
371     * Returns token type with branch.
372     *
373     * @return the token types that occur in the branch as a sorted set.
374     */
375    private BitSet getBranchTokenTypes() {
376        // lazy init
377        if (branchTokenTypes == null) {
378            branchTokenTypes = new BitSet();
379            branchTokenTypes.set(type);
380
381            // add union of all children
382            DetailAstImpl child = firstChild;
383            while (child != null) {
384                final BitSet childTypes = child.getBranchTokenTypes();
385                branchTokenTypes.or(childTypes);
386
387                child = child.nextSibling;
388            }
389        }
390        return branchTokenTypes;
391    }
392
393    @Override
394    public boolean branchContains(int tokenType) {
395        return getBranchTokenTypes().get(tokenType);
396    }
397
398    @Override
399    public DetailAST getPreviousSibling() {
400        return previousSibling;
401    }
402
403    @Nullable
404    @Override
405    public DetailAST findFirstToken(int tokenType) {
406        DetailAST returnValue = null;
407        for (DetailAST ast = firstChild; ast != null; ast = ast.getNextSibling()) {
408            if (ast.getType() == tokenType) {
409                returnValue = ast;
410                break;
411            }
412        }
413        return returnValue;
414    }
415
416    @Override
417    public String toString() {
418        return text + "[" + getLineNo() + "x" + getColumnNo() + "]";
419    }
420
421    @Override
422    public DetailAstImpl getNextSibling() {
423        return nextSibling;
424    }
425
426    @Override
427    public DetailAstImpl getFirstChild() {
428        return firstChild;
429    }
430
431    @Override
432    public int getNumberOfChildren() {
433        return getChildCount();
434    }
435
436    @Override
437    public boolean hasChildren() {
438        return firstChild != null;
439    }
440
441    /**
442     * Clears the child count for the ast instance.
443     *
444     * @param ast The ast to clear.
445     */
446    private static void clearChildCountCache(DetailAstImpl ast) {
447        if (ast != null) {
448            ast.childCount = NOT_INITIALIZED;
449        }
450    }
451
452    /**
453     * Clears branchTokenTypes cache for all parents of the current DetailAST instance, and the
454     * child count for the current DetailAST instance.
455     */
456    private void clearBranchTokenTypes() {
457        DetailAstImpl prevParent = parent;
458        while (prevParent != null) {
459            prevParent.branchTokenTypes = null;
460            prevParent = prevParent.parent;
461        }
462    }
463
464    /**
465     * Sets the next sibling of this AST.
466     *
467     * @param nextSibling the DetailAST to set as sibling
468     */
469    public void setNextSibling(DetailAST nextSibling) {
470        clearBranchTokenTypes();
471        clearChildCountCache(parent);
472        this.nextSibling = (DetailAstImpl) nextSibling;
473        if (nextSibling != null && parent != null) {
474            ((DetailAstImpl) nextSibling).setParent(parent);
475        }
476        if (nextSibling != null) {
477            ((DetailAstImpl) nextSibling).previousSibling = this;
478        }
479    }
480
481    /**
482     * Sets the first child of this AST.
483     *
484     * @param firstChild the DetailAST to set as first child
485     */
486    public void setFirstChild(DetailAST firstChild) {
487        clearBranchTokenTypes();
488        clearChildCountCache(this);
489        this.firstChild = (DetailAstImpl) firstChild;
490        if (firstChild != null) {
491            ((DetailAstImpl) firstChild).setParent(this);
492        }
493    }
494
495    /**
496     * Removes all children of this AST.
497     */
498    public void removeChildren() {
499        firstChild = null;
500    }
501
502    /**
503     * Get list of tokens on COMMENTS channel to the left of the
504     * current token up to the preceding token on the DEFAULT_TOKEN_CHANNEL.
505     *
506     * @return list of comment tokens
507     */
508    public List<Token> getHiddenBefore() {
509        List<Token> returnList = null;
510        if (hiddenBefore != null) {
511            returnList = UnmodifiableCollectionUtil.unmodifiableList(hiddenBefore);
512        }
513        return returnList;
514    }
515
516    /**
517     * Get list tokens on COMMENTS channel to the right of the current
518     * token up to the next token on the DEFAULT_TOKEN_CHANNEL.
519     *
520     * @return list of comment tokens
521     */
522    public List<Token> getHiddenAfter() {
523        List<Token> returnList = null;
524        if (hiddenAfter != null) {
525            returnList = UnmodifiableCollectionUtil.unmodifiableList(hiddenAfter);
526        }
527        return returnList;
528    }
529
530    /**
531     * Sets the hiddenBefore token field.
532     *
533     * @param hiddenBefore comment token preceding this DetailAstImpl
534     */
535    public void setHiddenBefore(List<Token> hiddenBefore) {
536        this.hiddenBefore = UnmodifiableCollectionUtil.unmodifiableList(hiddenBefore);
537    }
538
539    /**
540     * Sets the hiddenAfter token field.
541     *
542     * @param hiddenAfter comment token following this DetailAstImpl
543     */
544    public void setHiddenAfter(List<Token> hiddenAfter) {
545        this.hiddenAfter = UnmodifiableCollectionUtil.unmodifiableList(hiddenAfter);
546    }
547
548}