001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2023 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.Collections;
024import java.util.List;
025
026import org.antlr.v4.runtime.Token;
027
028import com.puppycrawl.tools.checkstyle.api.DetailAST;
029import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
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 = NOT_INITIALIZED;
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                astImpl.previousSibling = previousSiblingNode;
126                previousSiblingNode.setNextSibling(astImpl);
127            }
128            else if (parent != null) {
129                parent.setFirstChild(astImpl);
130            }
131
132            astImpl.setNextSibling(this);
133            previousSibling = astImpl;
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
150            if (sibling != null) {
151                astImpl.setNextSibling(sibling);
152                sibling.previousSibling = astImpl;
153            }
154
155            astImpl.previousSibling = this;
156            setNextSibling(astImpl);
157        }
158    }
159
160    /**
161     * Adds a new child to the current AST.
162     *
163     * @param child to DetailAST to add as child
164     */
165    public void addChild(DetailAST child) {
166        clearBranchTokenTypes();
167        clearChildCountCache(this);
168        if (child != null) {
169            final DetailAstImpl astImpl = (DetailAstImpl) child;
170            astImpl.setParent(this);
171            astImpl.previousSibling = (DetailAstImpl) getLastChild();
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    @Override
404    public DetailAST findFirstToken(int tokenType) {
405        DetailAST returnValue = null;
406        for (DetailAST ast = firstChild; ast != null; ast = ast.getNextSibling()) {
407            if (ast.getType() == tokenType) {
408                returnValue = ast;
409                break;
410            }
411        }
412        return returnValue;
413    }
414
415    @Override
416    public String toString() {
417        return text + "[" + getLineNo() + "x" + getColumnNo() + "]";
418    }
419
420    @Override
421    public DetailAstImpl getNextSibling() {
422        return nextSibling;
423    }
424
425    @Override
426    public DetailAstImpl getFirstChild() {
427        return firstChild;
428    }
429
430    @Override
431    public int getNumberOfChildren() {
432        return getChildCount();
433    }
434
435    @Override
436    public boolean hasChildren() {
437        return firstChild != null;
438    }
439
440    /**
441     * Clears the child count for the ast instance.
442     *
443     * @param ast The ast to clear.
444     */
445    private static void clearChildCountCache(DetailAstImpl ast) {
446        if (ast != null) {
447            ast.childCount = NOT_INITIALIZED;
448        }
449    }
450
451    /**
452     * Clears branchTokenTypes cache for all parents of the current DetailAST instance, and the
453     * child count for the current DetailAST instance.
454     */
455    private void clearBranchTokenTypes() {
456        DetailAstImpl prevParent = parent;
457        while (prevParent != null) {
458            prevParent.branchTokenTypes = null;
459            prevParent = prevParent.parent;
460        }
461    }
462
463    /**
464     * Sets the next sibling of this AST.
465     *
466     * @param nextSibling the DetailAST to set as sibling
467     */
468    public void setNextSibling(DetailAST nextSibling) {
469        clearBranchTokenTypes();
470        clearChildCountCache(parent);
471        this.nextSibling = (DetailAstImpl) nextSibling;
472        if (nextSibling != null && parent != null) {
473            ((DetailAstImpl) nextSibling).setParent(parent);
474        }
475        if (nextSibling != null) {
476            ((DetailAstImpl) nextSibling).previousSibling = this;
477        }
478    }
479
480    /**
481     * Sets the first child of this AST.
482     *
483     * @param firstChild the DetailAST to set as first child
484     */
485    public void setFirstChild(DetailAST firstChild) {
486        clearBranchTokenTypes();
487        clearChildCountCache(this);
488        this.firstChild = (DetailAstImpl) firstChild;
489        if (firstChild != null) {
490            ((DetailAstImpl) firstChild).setParent(this);
491        }
492    }
493
494    /**
495     * Removes all children of this AST.
496     */
497    public void removeChildren() {
498        firstChild = null;
499    }
500
501    /**
502     * Get list of tokens on COMMENTS channel to the left of the
503     * current token up to the preceding token on the DEFAULT_TOKEN_CHANNEL.
504     *
505     * @return list of comment tokens
506     */
507    public List<Token> getHiddenBefore() {
508        List<Token> returnList = null;
509        if (hiddenBefore != null) {
510            returnList = Collections.unmodifiableList(hiddenBefore);
511        }
512        return returnList;
513    }
514
515    /**
516     * Get list tokens on COMMENTS channel to the right of the current
517     * token up to the next token on the DEFAULT_TOKEN_CHANNEL.
518     *
519     * @return list of comment tokens
520     */
521    public List<Token> getHiddenAfter() {
522        List<Token> returnList = null;
523        if (hiddenAfter != null) {
524            returnList = Collections.unmodifiableList(hiddenAfter);
525        }
526        return returnList;
527    }
528
529    /**
530     * Sets the hiddenBefore token field.
531     *
532     * @param hiddenBefore comment token preceding this DetailAstImpl
533     */
534    public void setHiddenBefore(List<Token> hiddenBefore) {
535        this.hiddenBefore = Collections.unmodifiableList(hiddenBefore);
536    }
537
538    /**
539     * Sets the hiddenAfter token field.
540     *
541     * @param hiddenAfter comment token following this DetailAstImpl
542     */
543    public void setHiddenAfter(List<Token> hiddenAfter) {
544        this.hiddenAfter = Collections.unmodifiableList(hiddenAfter);
545    }
546}