001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2025 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
085     * if branchTokenTypes.get(x) is true.
086     */
087    private BitSet branchTokenTypes;
088
089    /**
090     * Initializes this DetailAstImpl.
091     *
092     * @param tokenType the type of this DetailAstImpl
093     * @param tokenText the text of this DetailAstImpl
094     */
095    public void initialize(int tokenType, String tokenText) {
096        type = tokenType;
097        text = tokenText;
098    }
099
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}