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
085     * if branchTokenTypes.get(x) is true.
086     */
087    private BitSet branchTokenTypes;
088
089    /**
090     * Creates a new {@code DetailAstImpl} instance.
091     */
092    public DetailAstImpl() {
093        // no code by default
094    }
095
096    /**
097     * Initializes this DetailAstImpl.
098     *
099     * @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}