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