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