1 ////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code for adherence to a set of rules.
3 // Copyright (C) 2001-2014 Oliver Burn
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ////////////////////////////////////////////////////////////////////////////////
19 package com.puppycrawl.tools.checkstyle.api;
20
21 import java.util.BitSet;
22
23 import antlr.CommonASTWithHiddenTokens;
24 import antlr.Token;
25 import antlr.collections.AST;
26
27 /**
28 * An extension of the CommonAST that records the line and column
29 * number. The idea was taken from <a target="_top"
30 * href="http://www.jguru.com/jguru/faq/view.jsp?EID=62654">Java Guru
31 * FAQ: How can I include line numbers in automatically generated
32 * ASTs?</a>.
33 * @author Oliver Burn
34 * @author lkuehne
35 * @version 1.0
36 * @see <a href="http://www.antlr.org/">ANTLR Website</a>
37 */
38 public final class DetailAST extends CommonASTWithHiddenTokens
39 {
40 /** For Serialisation that will never happen. */
41 private static final long serialVersionUID = -2580884815577559874L;
42
43 /** constant to indicate if not calculated the child count */
44 private static final int NOT_INITIALIZED = Integer.MIN_VALUE;
45
46 /** the line number **/
47 private int lineNo = NOT_INITIALIZED;
48 /** the column number **/
49 private int columnNo = NOT_INITIALIZED;
50
51 /** number of children */
52 private int childCount = NOT_INITIALIZED;
53 /** the parent token */
54 private DetailAST parent;
55 /** previous sibling */
56 private DetailAST previousSibling;
57
58 /**
59 * All token types in this branch.
60 * Token 'x' (where x is an int) is in this branch
61 * if branchTokenTypes.get(x) is true.
62 */
63 private BitSet branchTokenTypes;
64
65 @Override
66 public void initialize(Token tok)
67 {
68 super.initialize(tok);
69 lineNo = tok.getLine();
70 columnNo = tok.getColumn() - 1; // expect columns to start @ 0
71 }
72
73 @Override
74 public void initialize(AST ast)
75 {
76 final DetailAST da = (DetailAST) ast;
77 setText(da.getText());
78 setType(da.getType());
79 lineNo = da.getLineNo();
80 columnNo = da.getColumnNo();
81 hiddenAfter = da.getHiddenAfter();
82 hiddenBefore = da.getHiddenBefore();
83 }
84
85 @Override
86 public void setFirstChild(AST ast)
87 {
88 childCount = NOT_INITIALIZED;
89 super.setFirstChild(ast);
90 if (ast != null) {
91 ((DetailAST) ast).setParent(this);
92 }
93 }
94
95 @Override
96 public void setNextSibling(AST ast)
97 {
98 super.setNextSibling(ast);
99 if ((ast != null) && (parent != null)) {
100 ((DetailAST) ast).setParent(parent);
101 }
102 if (ast != null) {
103 ((DetailAST) ast).setPreviousSibling(this);
104 }
105 }
106
107 /**
108 * Add previous sibling.
109 * @param ast
110 * DetailAST object.
111 */
112 public void addPreviousSibling(DetailAST ast)
113 {
114 if (ast != null) {
115 ast.setParent(parent);
116 final DetailAST previousSibling = this.getPreviousSibling();
117
118 if (previousSibling != null) {
119 ast.setPreviousSibling(previousSibling);
120 previousSibling.setNextSibling(ast);
121 }
122 else if (parent != null) {
123 parent.setFirstChild(ast);
124 }
125
126 ast.setNextSibling(this);
127 this.setPreviousSibling(ast);
128 }
129 }
130
131 /**
132 * Add next sibling.
133 * @param ast
134 * DetailAST object.
135 */
136 public void addNextSibling(DetailAST ast)
137 {
138 if (ast != null) {
139 ast.setParent(parent);
140 final DetailAST nextSibling = this.getNextSibling();
141
142 if (nextSibling != null) {
143 ast.setNextSibling(nextSibling);
144 nextSibling.setPreviousSibling(ast);
145 }
146
147 ast.setPreviousSibling(this);
148 this.setNextSibling(ast);
149 }
150 }
151
152 /**
153 * Sets previous sibling.
154 * @param ast a previous sibling
155 */
156 void setPreviousSibling(DetailAST ast)
157 {
158 previousSibling = ast;
159 }
160
161 @Override
162 public void addChild(AST ast)
163 {
164 super.addChild(ast);
165 if (ast != null) {
166 ((DetailAST) ast).setParent(this);
167 (getFirstChild()).setParent(this);
168 }
169 }
170
171 /**
172 * Returns the number of child nodes one level below this node. That is is
173 * does not recurse down the tree.
174 * @return the number of child nodes
175 */
176 public int getChildCount()
177 {
178 // lazy init
179 if (childCount == NOT_INITIALIZED) {
180 childCount = 0;
181 AST child = getFirstChild();
182
183 while (child != null) {
184 childCount += 1;
185 child = child.getNextSibling();
186 }
187 }
188 return childCount;
189 }
190
191 /**
192 * Set the parent token.
193 * @param parent the parent token
194 */
195 // TODO: should be private but that breaks the DetailASTTest
196 // until we manage parent in DetailAST instead of externally
197 void setParent(DetailAST parent)
198 {
199 // TODO: Check visibility, could be private
200 // if set in setFirstChild() and friends
201 this.parent = parent;
202 final DetailAST nextSibling = getNextSibling();
203 if (nextSibling != null) {
204 nextSibling.setParent(parent);
205 nextSibling.setPreviousSibling(this);
206 }
207 }
208
209 /**
210 * Returns the parent token.
211 * @return the parent token
212 */
213 public DetailAST getParent()
214 {
215 return parent;
216 }
217
218 /** @return the line number **/
219 public int getLineNo()
220 {
221 if (lineNo == NOT_INITIALIZED) {
222 // an inner AST that has been initialized
223 // with initialize(String text)
224 DetailAST child = getFirstChild();
225 while (child != null) {
226 // comment node can't be start of any java statement/definition
227 if (TokenTypes.isCommentType(child.getType())) {
228 child = child.getNextSibling();
229 }
230 else {
231 return child.getLineNo();
232 }
233 }
234
235 DetailAST sibling = getNextSibling();
236 while (sibling != null) {
237 // comment node can't be start of any java statement/definition
238 if (TokenTypes.isCommentType(sibling.getType())) {
239 sibling = sibling.getNextSibling();
240 }
241 else {
242 return sibling.getLineNo();
243 }
244 }
245 }
246 return lineNo;
247 }
248
249 /**
250 * Set line number.
251 * @param lineNo
252 * line number.
253 */
254 public void setLineNo(int lineNo)
255 {
256 this.lineNo = lineNo;
257 }
258
259 /** @return the column number **/
260 public int getColumnNo()
261 {
262 if (columnNo == NOT_INITIALIZED) {
263 // an inner AST that has been initialized
264 // with initialize(String text)
265 DetailAST child = getFirstChild();
266 while (child != null) {
267 // comment node can't be start of any java statement/definition
268 if (TokenTypes.isCommentType(child.getType())) {
269 child = child.getNextSibling();
270 }
271 else {
272 return child.getColumnNo();
273 }
274 }
275
276 DetailAST sibling = getNextSibling();
277 while (sibling != null) {
278 // comment node can't be start of any java statement/definition
279 if (TokenTypes.isCommentType(sibling.getType())) {
280 sibling = sibling.getNextSibling();
281 }
282 else {
283 return sibling.getColumnNo();
284 }
285 }
286 }
287 return columnNo;
288 }
289
290 /**
291 * Set column number.
292 * @param columnNo
293 * column number.
294 */
295 public void setColumnNo(int columnNo)
296 {
297 this.columnNo = columnNo;
298 }
299
300 /** @return the last child node */
301 public DetailAST getLastChild()
302 {
303 DetailAST ast = getFirstChild();
304 while ((ast != null) && (ast.getNextSibling() != null)) {
305 ast = ast.getNextSibling();
306 }
307 return ast;
308 }
309
310 /**
311 * @return the token types that occur in the branch as a sorted set.
312 */
313 private BitSet getBranchTokenTypes()
314 {
315 // lazy init
316 if (branchTokenTypes == null) {
317
318 branchTokenTypes = new BitSet();
319 branchTokenTypes.set(getType());
320
321 // add union of all childs
322 DetailAST child = getFirstChild();
323 while (child != null) {
324 final BitSet childTypes = child.getBranchTokenTypes();
325 branchTokenTypes.or(childTypes);
326
327 child = child.getNextSibling();
328 }
329 }
330 return branchTokenTypes;
331 }
332
333 /**
334 * Checks if this branch of the parse tree contains a token
335 * of the provided type.
336 * @param type a TokenType
337 * @return true if and only if this branch (including this node)
338 * contains a token of type <code>type</code>.
339 */
340 public boolean branchContains(int type)
341 {
342 return getBranchTokenTypes().get(type);
343 }
344
345 /**
346 * Returns the number of direct child tokens that have the specified type.
347 * @param type the token type to match
348 * @return the number of matching token
349 */
350 public int getChildCount(int type)
351 {
352 int count = 0;
353 for (AST i = getFirstChild(); i != null; i = i.getNextSibling()) {
354 if (i.getType() == type) {
355 count++;
356 }
357 }
358 return count;
359 }
360
361 /**
362 * Returns the previous sibling or null if no such sibling exists.
363 * @return the previous sibling or null if no such sibling exists.
364 */
365 public DetailAST getPreviousSibling()
366 {
367 return previousSibling;
368 }
369
370 /**
371 * Returns the first child token that makes a specified type.
372 * @param type the token type to match
373 * @return the matching token, or null if no match
374 */
375 public DetailAST findFirstToken(int type)
376 {
377 DetailAST retVal = null;
378 for (DetailAST i = getFirstChild(); i != null; i = i.getNextSibling()) {
379 if (i.getType() == type) {
380 retVal = i;
381 break;
382 }
383 }
384 return retVal;
385 }
386
387 @Override
388 public String toString()
389 {
390 return super.toString() + "[" + getLineNo() + "x" + getColumnNo() + "]";
391 }
392
393 @Override
394 public DetailAST getNextSibling()
395 {
396 return (DetailAST) super.getNextSibling();
397 }
398
399 @Override
400 public DetailAST getFirstChild()
401 {
402 return (DetailAST) super.getFirstChild();
403 }
404
405 }