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