1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.api;
21
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertNotSame;
25 import static org.junit.Assert.assertNull;
26 import static org.junit.Assert.assertTrue;
27
28 import java.io.File;
29 import java.io.Writer;
30 import java.lang.reflect.Method;
31 import java.nio.charset.StandardCharsets;
32 import java.nio.file.Files;
33 import java.text.MessageFormat;
34 import java.util.Arrays;
35 import java.util.BitSet;
36 import java.util.List;
37 import java.util.Locale;
38 import java.util.function.Consumer;
39
40 import org.junit.Rule;
41 import org.junit.Test;
42 import org.junit.rules.TemporaryFolder;
43 import org.powermock.reflect.Whitebox;
44
45 import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
46 import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
47 import com.puppycrawl.tools.checkstyle.JavaParser;
48 import com.puppycrawl.tools.checkstyle.checks.TodoCommentCheck;
49 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
50
51
52
53
54 public class DetailASTTest extends AbstractModuleTestSupport {
55
56 @Rule
57 public final TemporaryFolder temporaryFolder = new TemporaryFolder();
58
59 @Override
60 protected String getPackageLocation() {
61 return "com/puppycrawl/tools/checkstyle/api/detailast";
62 }
63
64 private static Method getSetParentMethod() throws Exception {
65 final Class<DetailAST> detailAstClass = DetailAST.class;
66 final Method setParentMethod =
67 detailAstClass.getDeclaredMethod("setParent", DetailAST.class);
68 setParentMethod.setAccessible(true);
69 return setParentMethod;
70 }
71
72 @Test
73 public void testGetChildCount() throws Exception {
74 final DetailAST root = new DetailAST();
75 final DetailAST firstLevelA = new DetailAST();
76 final DetailAST firstLevelB = new DetailAST();
77 final DetailAST secondLevelA = new DetailAST();
78
79 root.setFirstChild(firstLevelA);
80
81 final Method setParentMethod = getSetParentMethod();
82 setParentMethod.invoke(firstLevelA, root);
83 firstLevelA.setFirstChild(secondLevelA);
84 firstLevelA.setNextSibling(firstLevelB);
85
86 setParentMethod.invoke(firstLevelB, root);
87
88 setParentMethod.invoke(secondLevelA, root);
89
90 assertEquals("Invalid child count", 0, secondLevelA.getChildCount());
91 assertEquals("Invalid child count", 0, firstLevelB.getChildCount());
92 assertEquals("Invalid child count", 1, firstLevelA.getChildCount());
93 assertEquals("Invalid child count", 2, root.getChildCount());
94 assertEquals("Invalid child count", 2, root.getChildCount());
95
96 assertNull("Previous sibling should be null", root.getPreviousSibling());
97 assertNull("Previous sibling should be null", firstLevelA.getPreviousSibling());
98 assertNull("Previous sibling should be null", secondLevelA.getPreviousSibling());
99 assertEquals("Invalid previous sibling", firstLevelA, firstLevelB.getPreviousSibling());
100 }
101
102 @Test
103 public void testGetChildCountType() throws Exception {
104 final DetailAST root = new DetailAST();
105 final DetailAST firstLevelA = new DetailAST();
106 final DetailAST firstLevelB = new DetailAST();
107
108 root.setFirstChild(firstLevelA);
109
110 final Method setParentMethod = getSetParentMethod();
111 setParentMethod.invoke(firstLevelA, root);
112 firstLevelA.setNextSibling(firstLevelB);
113
114 firstLevelA.setType(TokenTypes.IDENT);
115 firstLevelB.setType(TokenTypes.EXPR);
116
117 setParentMethod.invoke(firstLevelB, root);
118
119 assertEquals("Invalid child count", 0, firstLevelB.getChildCount(0));
120 assertEquals("Invalid child count", 0, firstLevelA.getChildCount(TokenTypes.EXPR));
121 assertEquals("Invalid child count", 1, root.getChildCount(TokenTypes.IDENT));
122 assertEquals("Invalid child count", 1, root.getChildCount(TokenTypes.EXPR));
123 assertEquals("Invalid child count", 0, root.getChildCount(0));
124 }
125
126 @Test
127 public void testSetSiblingNull() throws Exception {
128 final DetailAST root = new DetailAST();
129 final DetailAST firstLevelA = new DetailAST();
130
131 root.setFirstChild(firstLevelA);
132
133 assertEquals("Invalid child count", 1, root.getChildCount());
134
135 getSetParentMethod().invoke(firstLevelA, root);
136 firstLevelA.addPreviousSibling(null);
137 firstLevelA.addNextSibling(null);
138
139 assertEquals("Invalid child count", 1, root.getChildCount());
140 }
141
142 @Test
143 public void testAddPreviousSibling() {
144 final DetailAST previousSibling = new DetailAST();
145 final DetailAST instance = new DetailAST();
146 final DetailAST parent = new DetailAST();
147
148 parent.setFirstChild(instance);
149
150 instance.addPreviousSibling(previousSibling);
151
152 assertEquals("unexpected result", previousSibling, instance.getPreviousSibling());
153 assertEquals("unexpected result", previousSibling, parent.getFirstChild());
154
155 final DetailAST newPreviousSibling = new DetailAST();
156
157 instance.addPreviousSibling(newPreviousSibling);
158
159 assertEquals("unexpected result", newPreviousSibling, instance.getPreviousSibling());
160 assertEquals("unexpected result", previousSibling, newPreviousSibling.getPreviousSibling());
161 assertEquals("unexpected result", newPreviousSibling, previousSibling.getNextSibling());
162 assertEquals("unexpected result", previousSibling, parent.getFirstChild());
163 }
164
165 @Test
166 public void testAddPreviousSiblingNullParent() {
167 final DetailAST child = new DetailAST();
168 final DetailAST newSibling = new DetailAST();
169
170 child.addPreviousSibling(newSibling);
171
172 assertEquals("Invalid child token", child, newSibling.getNextSibling());
173 assertEquals("Invalid child token", newSibling, child.getPreviousSibling());
174 }
175
176 @Test
177 public void testInsertSiblingBetween() throws Exception {
178 final DetailAST root = new DetailAST();
179 final DetailAST firstLevelA = new DetailAST();
180 final DetailAST firstLevelB = new DetailAST();
181 final DetailAST firstLevelC = new DetailAST();
182
183 assertEquals("Invalid child count", 0, root.getChildCount());
184
185 root.setFirstChild(firstLevelA);
186 final Method setParentMethod = getSetParentMethod();
187 setParentMethod.invoke(firstLevelA, root);
188
189 assertEquals("Invalid child count", 1, root.getChildCount());
190
191 firstLevelA.addNextSibling(firstLevelB);
192 setParentMethod.invoke(firstLevelB, root);
193
194 assertEquals("Invalid next sibling", firstLevelB, firstLevelA.getNextSibling());
195
196 firstLevelA.addNextSibling(firstLevelC);
197 setParentMethod.invoke(firstLevelC, root);
198
199 assertEquals("Invalid next sibling", firstLevelC, firstLevelA.getNextSibling());
200 }
201
202 @Test
203 public void testBranchContains() {
204 final DetailAST root = createToken(null, TokenTypes.CLASS_DEF);
205 final DetailAST modifiers = createToken(root, TokenTypes.MODIFIERS);
206 createToken(modifiers, TokenTypes.LITERAL_PUBLIC);
207
208 assertTrue("invalid result", root.branchContains(TokenTypes.LITERAL_PUBLIC));
209 assertFalse("invalid result", root.branchContains(TokenTypes.OBJBLOCK));
210 }
211
212 private static DetailAST createToken(DetailAST root, int type) {
213 final DetailAST result = new DetailAST();
214 result.setType(type);
215 if (root != null) {
216 root.addChild(result);
217 }
218 return result;
219 }
220
221 @Test
222 public void testClearBranchTokenTypes() throws Exception {
223 final DetailAST parent = new DetailAST();
224 final DetailAST child = new DetailAST();
225 parent.setFirstChild(child);
226
227 final List<Consumer<DetailAST>> clearBranchTokenTypesMethods = Arrays.asList(
228 child::setFirstChild,
229 child::setNextSibling,
230 child::addPreviousSibling,
231 child::addNextSibling,
232 child::addChild,
233 ast -> {
234 try {
235 Whitebox.invokeMethod(child, "setParent", ast);
236 }
237
238 catch (Exception exception) {
239 throw new IllegalStateException(exception);
240 }
241 }
242 );
243
244 for (Consumer<DetailAST> method : clearBranchTokenTypesMethods) {
245 final BitSet branchTokenTypes = Whitebox.invokeMethod(parent, "getBranchTokenTypes");
246 method.accept(null);
247 final BitSet branchTokenTypes2 = Whitebox.invokeMethod(parent, "getBranchTokenTypes");
248 assertEquals("Branch token types are not equal", branchTokenTypes, branchTokenTypes2);
249 assertNotSame("Branch token types should not be the same",
250 branchTokenTypes, branchTokenTypes2);
251 }
252 }
253
254 @Test
255 public void testCacheBranchTokenTypes() {
256 final DetailAST root = new DetailAST();
257 final BitSet bitSet = new BitSet();
258 bitSet.set(999);
259
260 Whitebox.setInternalState(root, "branchTokenTypes", bitSet);
261 assertTrue("Branch tokens has changed", root.branchContains(999));
262 }
263
264 @Test
265 public void testClearChildCountCache() {
266 final DetailAST parent = new DetailAST();
267 final DetailAST child = new DetailAST();
268 parent.setFirstChild(child);
269
270 final List<Consumer<DetailAST>> clearChildCountCacheMethods = Arrays.asList(
271 child::setNextSibling,
272 child::addPreviousSibling,
273 child::addNextSibling
274 );
275
276 for (Consumer<DetailAST> method : clearChildCountCacheMethods) {
277 final int startCount = parent.getChildCount();
278 method.accept(null);
279 final int intermediateCount = Whitebox.getInternalState(parent, "childCount");
280 final int finishCount = parent.getChildCount();
281 assertEquals("Child count has changed", startCount, finishCount);
282 assertEquals("Invalid child count", Integer.MIN_VALUE, intermediateCount);
283 }
284
285 final int startCount = child.getChildCount();
286 child.addChild(null);
287 final int intermediateCount = Whitebox.getInternalState(child, "childCount");
288 final int finishCount = child.getChildCount();
289 assertEquals("Child count has changed", startCount, finishCount);
290 assertEquals("Invalid child count", Integer.MIN_VALUE, intermediateCount);
291 }
292
293 @Test
294 public void testCacheGetChildCount() {
295 final DetailAST root = new DetailAST();
296
297 Whitebox.setInternalState(root, "childCount", 999);
298 assertEquals("Child count has changed", 999, root.getChildCount());
299 }
300
301 @Test
302 public void testAddNextSibling() {
303 final DetailAST parent = new DetailAST();
304 final DetailAST child = new DetailAST();
305 final DetailAST sibling = new DetailAST();
306 final DetailAST newSibling = new DetailAST();
307 parent.setFirstChild(child);
308 child.setNextSibling(sibling);
309 child.addNextSibling(newSibling);
310
311 assertEquals("Invalid parent", parent, newSibling.getParent());
312 assertEquals("Invalid next sibling", sibling, newSibling.getNextSibling());
313 assertEquals("Invalid child", newSibling, child.getNextSibling());
314 }
315
316 @Test
317 public void testAddNextSiblingNullParent() {
318 final DetailAST child = new DetailAST();
319 final DetailAST newSibling = new DetailAST();
320 final DetailAST oldParent = new DetailAST();
321 oldParent.addChild(newSibling);
322 child.addNextSibling(newSibling);
323
324 assertEquals("Invalid parent", oldParent, newSibling.getParent());
325 assertNull("Invalid next sibling", newSibling.getNextSibling());
326 assertEquals("Invalid child", newSibling, child.getNextSibling());
327 }
328
329 @Test
330 public void testGetLineNo() {
331 final DetailAST root1 = new DetailAST();
332 root1.setLineNo(1);
333 assertEquals("Invalid line number", 1, root1.getLineNo());
334
335 final DetailAST root2 = new DetailAST();
336 final DetailAST firstChild = new DetailAST();
337 firstChild.setLineNo(2);
338 root2.setFirstChild(firstChild);
339 assertEquals("Invalid line number", 2, root2.getLineNo());
340
341 final DetailAST root3 = new DetailAST();
342 final DetailAST nextSibling = new DetailAST();
343 nextSibling.setLineNo(3);
344 root3.setNextSibling(nextSibling);
345 assertEquals("Invalid line number", 3, root3.getLineNo());
346
347 final DetailAST root4 = new DetailAST();
348 final DetailAST comment = new DetailAST();
349 comment.setType(TokenTypes.SINGLE_LINE_COMMENT);
350 comment.setLineNo(3);
351 root4.setFirstChild(comment);
352 assertEquals("Invalid line number", Integer.MIN_VALUE, root4.getLineNo());
353 }
354
355 @Test
356 public void testGetColumnNo() {
357 final DetailAST root1 = new DetailAST();
358 root1.setColumnNo(1);
359 assertEquals("Invalid column number", 1, root1.getColumnNo());
360
361 final DetailAST root2 = new DetailAST();
362 final DetailAST firstChild = new DetailAST();
363 firstChild.setColumnNo(2);
364 root2.setFirstChild(firstChild);
365 assertEquals("Invalid column number", 2, root2.getColumnNo());
366
367 final DetailAST root3 = new DetailAST();
368 final DetailAST nextSibling = new DetailAST();
369 nextSibling.setColumnNo(3);
370 root3.setNextSibling(nextSibling);
371 assertEquals("Invalid column number", 3, root3.getColumnNo());
372
373 final DetailAST root4 = new DetailAST();
374 final DetailAST comment = new DetailAST();
375 comment.setType(TokenTypes.SINGLE_LINE_COMMENT);
376 comment.setColumnNo(3);
377 root4.setFirstChild(comment);
378 assertEquals("Invalid column number", Integer.MIN_VALUE, root4.getColumnNo());
379 }
380
381 @Test
382 public void testFindFirstToken() {
383 final DetailAST root = new DetailAST();
384 final DetailAST firstChild = new DetailAST();
385 firstChild.setType(TokenTypes.IDENT);
386 final DetailAST secondChild = new DetailAST();
387 secondChild.setType(TokenTypes.EXPR);
388 final DetailAST thirdChild = new DetailAST();
389 thirdChild.setType(TokenTypes.IDENT);
390
391 root.addChild(firstChild);
392 root.addChild(secondChild);
393 root.addChild(thirdChild);
394
395 assertNull("Invalid result", firstChild.findFirstToken(TokenTypes.IDENT));
396 assertEquals("Invalid result", firstChild, root.findFirstToken(TokenTypes.IDENT));
397 assertEquals("Invalid result", secondChild, root.findFirstToken(TokenTypes.EXPR));
398 assertNull("Invalid result", root.findFirstToken(0));
399 }
400
401 @Test
402 public void testManyComments() throws Exception {
403 final File file = temporaryFolder.newFile("InputDetailASTManyComments.java");
404
405 try (Writer bw = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8)) {
406 bw.write("class C {\n");
407 for (int i = 0; i <= 30000; i++) {
408 bw.write("// " + i + "\n");
409 }
410 bw.write("}\n");
411 }
412
413 final DefaultConfiguration checkConfig = createModuleConfig(TodoCommentCheck.class);
414
415 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
416 verify(checkConfig, file.getAbsolutePath(), expected);
417 }
418
419
420
421
422
423 @Test
424 public void testTreeStructure() throws Exception {
425 checkDir(new File("src/test/resources/com/puppycrawl/tools/checkstyle"));
426 }
427
428 @Test
429 public void testToString() {
430 final DetailAST ast = new DetailAST();
431 ast.setText("text");
432 ast.setColumnNo(0);
433 ast.setLineNo(0);
434 assertEquals("Invalid text", "text[0x0]", ast.toString());
435 }
436
437 private static void checkDir(File dir) throws Exception {
438 final File[] files = dir.listFiles(file -> {
439 return (file.getName().endsWith(".java")
440 || file.isDirectory())
441 && !file.getName().endsWith("InputGrammar.java");
442 });
443 for (File file : files) {
444 if (file.isFile()) {
445 checkFile(file.getCanonicalPath());
446 }
447 else if (file.isDirectory()) {
448 checkDir(file);
449 }
450 }
451 }
452
453 private static void checkFile(String filename) throws Exception {
454 final DetailAST rootAST =
455 JavaParser.parseFile(new File(filename), JavaParser.Options.WITHOUT_COMMENTS);
456 if (rootAST != null) {
457 checkTree(filename, rootAST);
458 }
459 }
460
461 private static void checkTree(final String filename, final DetailAST root) {
462 DetailAST curNode = root;
463 DetailAST parent = null;
464 DetailAST prev = null;
465 while (curNode != null) {
466 checkNode(curNode, parent, prev, filename, root);
467 DetailAST toVisit = curNode.getFirstChild();
468 if (toVisit == null) {
469 while (curNode != null && toVisit == null) {
470 toVisit = curNode.getNextSibling();
471 if (toVisit == null) {
472 curNode = curNode.getParent();
473 if (curNode != null) {
474 parent = curNode.getParent();
475 }
476 }
477 else {
478 prev = curNode;
479 curNode = toVisit;
480 }
481 }
482 }
483 else {
484 parent = curNode;
485 curNode = toVisit;
486 prev = null;
487 }
488 }
489 }
490
491 private static void checkNode(final DetailAST node,
492 final DetailAST parent,
493 final DetailAST prev,
494 final String filename,
495 final DetailAST root) {
496 final Object[] params = {
497 node, parent, prev, filename, root,
498 };
499 final MessageFormat badParentFormatter = new MessageFormat(
500 "Bad parent node={0} parent={1} filename={3} root={4}", Locale.ROOT);
501 final String badParentMsg = badParentFormatter.format(params);
502 assertEquals(badParentMsg, parent, node.getParent());
503 final MessageFormat badPrevFormatter = new MessageFormat(
504 "Bad prev node={0} prev={2} parent={1} filename={3} root={4}", Locale.ROOT);
505 final String badPrevMsg = badPrevFormatter.format(params);
506 assertEquals(badPrevMsg, prev, node.getPreviousSibling());
507 }
508
509 }