001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 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;
023
024import antlr.CommonASTWithHiddenTokens;
025import antlr.Token;
026import antlr.collections.AST;
027import com.puppycrawl.tools.checkstyle.api.DetailAST;
028import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
029
030/**
031 * The implementation of {@link DetailAST}. This should only be directly used to
032 * create custom AST nodes.
033 * @noinspection FieldNotUsedInToString, SerializableHasSerializationMethods
034 */
035public final class DetailAstImpl extends CommonASTWithHiddenTokens implements DetailAST {
036
037    private static final long serialVersionUID = -2580884815577559874L;
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    /**
055     * All token types in this branch.
056     * Token 'x' (where x is an int) is in this branch
057     * if branchTokenTypes.get(x) is true.
058     */
059    private BitSet branchTokenTypes;
060
061    @Override
062    public void initialize(Token tok) {
063        super.initialize(tok);
064        lineNo = tok.getLine();
065
066        // expect columns to start @ 0
067        columnNo = tok.getColumn() - 1;
068    }
069
070    @Override
071    public void initialize(AST ast) {
072        final DetailAstImpl detailAst = (DetailAstImpl) ast;
073        setText(detailAst.getText());
074        setType(detailAst.getType());
075        lineNo = detailAst.getLineNo();
076        columnNo = detailAst.getColumnNo();
077        hiddenAfter = detailAst.getHiddenAfter();
078        hiddenBefore = detailAst.getHiddenBefore();
079    }
080
081    @Override
082    public void setFirstChild(AST ast) {
083        clearBranchTokenTypes();
084        clearChildCountCache(this);
085        super.setFirstChild(ast);
086        if (ast != null) {
087            ((DetailAstImpl) ast).setParent(this);
088        }
089    }
090
091    @Override
092    public void setNextSibling(AST ast) {
093        clearBranchTokenTypes();
094        clearChildCountCache(parent);
095        super.setNextSibling(ast);
096        if (ast != null && parent != null) {
097            ((DetailAstImpl) ast).setParent(parent);
098        }
099        if (ast != null) {
100            ((DetailAstImpl) ast).previousSibling = this;
101        }
102    }
103
104    /**
105     * Add previous sibling.
106     * @param ast
107     *        DetailAST object.
108     */
109    public void addPreviousSibling(DetailAST ast) {
110        clearBranchTokenTypes();
111        clearChildCountCache(parent);
112        if (ast != null) {
113            //parent is set in setNextSibling or parent.setFirstChild
114            final DetailAstImpl previousSiblingNode = previousSibling;
115            final DetailAstImpl astImpl = (DetailAstImpl) ast;
116
117            if (previousSiblingNode != null) {
118                astImpl.previousSibling = previousSiblingNode;
119                previousSiblingNode.setNextSibling(astImpl);
120            }
121            else if (parent != null) {
122                parent.setFirstChild(astImpl);
123            }
124
125            astImpl.setNextSibling(this);
126            previousSibling = astImpl;
127        }
128    }
129
130    /**
131     * Add next sibling.
132     * @param ast
133     *        DetailAST object.
134     */
135    public void addNextSibling(DetailAST ast) {
136        clearBranchTokenTypes();
137        clearChildCountCache(parent);
138        if (ast != null) {
139            //parent is set in setNextSibling
140            final DetailAstImpl nextSibling = getNextSibling();
141            final DetailAstImpl astImpl = (DetailAstImpl) ast;
142
143            if (nextSibling != null) {
144                astImpl.setNextSibling(nextSibling);
145                nextSibling.previousSibling = astImpl;
146            }
147
148            astImpl.previousSibling = this;
149            setNextSibling(astImpl);
150        }
151    }
152
153    @Override
154    public void addChild(AST ast) {
155        clearBranchTokenTypes();
156        clearChildCountCache(this);
157        if (ast != null) {
158            final DetailAstImpl astImpl = (DetailAstImpl) ast;
159            astImpl.setParent(this);
160            astImpl.previousSibling = (DetailAstImpl) getLastChild();
161        }
162        super.addChild(ast);
163    }
164
165    @Override
166    public int getChildCount() {
167        // lazy init
168        if (childCount == NOT_INITIALIZED) {
169            childCount = 0;
170            AST child = getFirstChild();
171
172            while (child != null) {
173                childCount += 1;
174                child = child.getNextSibling();
175            }
176        }
177        return childCount;
178    }
179
180    @Override
181    public int getChildCount(int type) {
182        int count = 0;
183        for (AST ast = getFirstChild(); ast != null; ast = ast.getNextSibling()) {
184            if (ast.getType() == type) {
185                count++;
186            }
187        }
188        return count;
189    }
190
191    /**
192     * Set the parent token.
193     * @param parent the parent token
194     */
195    private void setParent(DetailAstImpl parent) {
196        DetailAstImpl instance = this;
197        do {
198            instance.clearBranchTokenTypes();
199            instance.parent = parent;
200            instance = instance.getNextSibling();
201        } while (instance != null);
202    }
203
204    @Override
205    public DetailAST getParent() {
206        return parent;
207    }
208
209    @Override
210    public int getLineNo() {
211        int resultNo = -1;
212
213        if (lineNo == NOT_INITIALIZED) {
214            // an inner AST that has been initialized
215            // with initialize(String text)
216            resultNo = findLineNo(getFirstChild());
217
218            if (resultNo == -1) {
219                resultNo = findLineNo(getNextSibling());
220            }
221        }
222        if (resultNo == -1) {
223            resultNo = lineNo;
224        }
225        return resultNo;
226    }
227
228    /**
229     * Set line number.
230     * @param lineNo
231     *        line number.
232     */
233    public void setLineNo(int lineNo) {
234        this.lineNo = lineNo;
235    }
236
237    @Override
238    public int getColumnNo() {
239        int resultNo = -1;
240
241        if (columnNo == NOT_INITIALIZED) {
242            // an inner AST that has been initialized
243            // with initialize(String text)
244            resultNo = findColumnNo(getFirstChild());
245
246            if (resultNo == -1) {
247                resultNo = findColumnNo(getNextSibling());
248            }
249        }
250        if (resultNo == -1) {
251            resultNo = columnNo;
252        }
253        return resultNo;
254    }
255
256    /**
257     * Set column number.
258     * @param columnNo
259     *        column number.
260     */
261    public void setColumnNo(int columnNo) {
262        this.columnNo = columnNo;
263    }
264
265    @Override
266    public DetailAST getLastChild() {
267        DetailAST ast = getFirstChild();
268        while (ast != null && ast.getNextSibling() != null) {
269            ast = ast.getNextSibling();
270        }
271        return ast;
272    }
273
274    /**
275     * Finds column number in the first non-comment node.
276     *
277     * @param ast DetailAST node.
278     * @return Column number if non-comment node exists, -1 otherwise.
279     */
280    private static int findColumnNo(DetailAST ast) {
281        int resultNo = -1;
282        DetailAST node = ast;
283        while (node != null) {
284            // comment node can't be start of any java statement/definition
285            if (TokenUtil.isCommentType(node.getType())) {
286                node = node.getNextSibling();
287            }
288            else {
289                resultNo = node.getColumnNo();
290                break;
291            }
292        }
293        return resultNo;
294    }
295
296    /**
297     * Finds line number in the first non-comment node.
298     *
299     * @param ast DetailAST node.
300     * @return Line number if non-comment node exists, -1 otherwise.
301     */
302    private static int findLineNo(DetailAST ast) {
303        int resultNo = -1;
304        DetailAST node = ast;
305        while (node != null) {
306            // comment node can't be start of any java statement/definition
307            if (TokenUtil.isCommentType(node.getType())) {
308                node = node.getNextSibling();
309            }
310            else {
311                resultNo = node.getLineNo();
312                break;
313            }
314        }
315        return resultNo;
316    }
317
318    /**
319     * Returns token type with branch.
320     * @return the token types that occur in the branch as a sorted set.
321     */
322    private BitSet getBranchTokenTypes() {
323        // lazy init
324        if (branchTokenTypes == null) {
325            branchTokenTypes = new BitSet();
326            branchTokenTypes.set(getType());
327
328            // add union of all children
329            DetailAstImpl child = getFirstChild();
330            while (child != null) {
331                final BitSet childTypes = child.getBranchTokenTypes();
332                branchTokenTypes.or(childTypes);
333
334                child = child.getNextSibling();
335            }
336        }
337        return branchTokenTypes;
338    }
339
340    @Override
341    public boolean branchContains(int type) {
342        return getBranchTokenTypes().get(type);
343    }
344
345    @Override
346    public DetailAST getPreviousSibling() {
347        return previousSibling;
348    }
349
350    @Override
351    public DetailAST findFirstToken(int type) {
352        DetailAST returnValue = null;
353        for (DetailAST ast = getFirstChild(); ast != null; ast = ast.getNextSibling()) {
354            if (ast.getType() == type) {
355                returnValue = ast;
356                break;
357            }
358        }
359        return returnValue;
360    }
361
362    @Override
363    public String toString() {
364        return super.toString() + "[" + getLineNo() + "x" + getColumnNo() + "]";
365    }
366
367    @Override
368    public DetailAstImpl getNextSibling() {
369        return (DetailAstImpl) super.getNextSibling();
370    }
371
372    @Override
373    public DetailAstImpl getFirstChild() {
374        return (DetailAstImpl) super.getFirstChild();
375    }
376
377    @Override
378    public boolean hasChildren() {
379        return getFirstChild() != null;
380    }
381
382    /**
383     * Clears the child count for the ast instance.
384     * @param ast The ast to clear.
385     */
386    private static void clearChildCountCache(DetailAstImpl ast) {
387        if (ast != null) {
388            ast.childCount = NOT_INITIALIZED;
389        }
390    }
391
392    /**
393     * Clears branchTokenTypes cache for all parents of the current DetailAST instance, and the
394     * child count for the current DetailAST instance.
395     */
396    private void clearBranchTokenTypes() {
397        DetailAstImpl prevParent = parent;
398        while (prevParent != null) {
399            prevParent.branchTokenTypes = null;
400            prevParent = prevParent.parent;
401        }
402    }
403
404}