View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
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  
20  package com.puppycrawl.tools.checkstyle.xpath;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
24  import static com.puppycrawl.tools.checkstyle.utils.XpathUtil.getXpathItems;
25  
26  import java.io.File;
27  import java.util.List;
28  
29  import org.junit.jupiter.api.Test;
30  
31  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
32  import com.puppycrawl.tools.checkstyle.JavaParser;
33  import com.puppycrawl.tools.checkstyle.api.DetailAST;
34  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
35  import net.sf.saxon.om.NodeInfo;
36  
37  public class XpathMapperTest extends AbstractModuleTestSupport {
38  
39      @Override
40      public String getPackageLocation() {
41          return "com/puppycrawl/tools/checkstyle/xpath/xpathmapper";
42      }
43  
44      @Test
45      public void testNodeOrdering() throws Exception {
46          final String xpath = "//METHOD_DEF/SLIST/*";
47          final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
48          final List<NodeInfo> nodes = getXpathItems(xpath, rootNode);
49          for (int i = 1; i < nodes.size(); i++) {
50              final NodeInfo curr = nodes.get(i);
51              final NodeInfo prev = nodes.get(i - 1);
52  
53              if (curr.getLineNumber() == prev.getLineNumber()) {
54                  assertWithMessage("Column number is not in document order")
55                      .that(curr.getColumnNumber())
56                      .isGreaterThan(prev.getColumnNumber());
57              }
58              else {
59                  assertWithMessage("Line number is not in document order")
60                      .that(curr.getLineNumber())
61                      .isGreaterThan(prev.getLineNumber());
62              }
63          }
64      }
65  
66      @Test
67      public void testFullPath() throws Exception {
68          final String xpath = "/COMPILATION_UNIT/CLASS_DEF/OBJBLOCK"
69                  + "/METHOD_DEF[1]/SLIST/VARIABLE_DEF[2]";
70          final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
71          final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
72          final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
73                  TokenTypes.COMPILATION_UNIT)
74                  .findFirstToken(TokenTypes.CLASS_DEF)
75                  .findFirstToken(TokenTypes.OBJBLOCK)
76                  .findFirstToken(TokenTypes.METHOD_DEF)
77                  .findFirstToken(TokenTypes.SLIST)
78                  .findFirstToken(TokenTypes.VARIABLE_DEF)
79                  .getNextSibling()
80                  .getNextSibling();
81          final DetailAST[] expected = {expectedVariableDefNode};
82          assertWithMessage("Result nodes differ from expected")
83                  .that(actual)
84                  .isEqualTo(expected);
85      }
86  
87      @Test
88      public void testParent() throws Exception {
89          final String xpath = "(//VARIABLE_DEF)[1]/..";
90          final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
91          final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
92          final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
93                  TokenTypes.COMPILATION_UNIT)
94                  .findFirstToken(TokenTypes.CLASS_DEF)
95                  .findFirstToken(TokenTypes.OBJBLOCK)
96                  .findFirstToken(TokenTypes.METHOD_DEF)
97                  .findFirstToken(TokenTypes.SLIST);
98          final DetailAST[] expected = {expectedVariableDefNode};
99          assertWithMessage("Result nodes differ from expected")
100                 .that(actual)
101                 .isEqualTo(expected);
102     }
103 
104     @Test
105     public void testCurlyBrackets() throws Exception {
106         final String xpath = "(//RCURLY)[2]";
107         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
108         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
109         final DetailAST expectedCurlyNode = getSiblingByType(rootNode.getUnderlyingNode(),
110                         TokenTypes.COMPILATION_UNIT)
111                 .findFirstToken(TokenTypes.CLASS_DEF)
112                 .findFirstToken(TokenTypes.OBJBLOCK)
113                 .findFirstToken(TokenTypes.METHOD_DEF)
114                 .findFirstToken(TokenTypes.SLIST)
115                 .findFirstToken(TokenTypes.RCURLY);
116         final DetailAST[] expected = {expectedCurlyNode};
117         assertWithMessage("Result nodes differ from expected")
118                 .that(actual)
119                 .isEqualTo(expected);
120     }
121 
122     @Test
123     public void testOr() throws Exception {
124         final String xpath = "//CLASS_DEF | //METHOD_DEF";
125         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
126         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
127         final DetailAST expectedClassDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
128                 TokenTypes.COMPILATION_UNIT)
129                 .findFirstToken(TokenTypes.CLASS_DEF);
130         final DetailAST expectedMethodDefNode1 = expectedClassDefNode
131                 .findFirstToken(TokenTypes.OBJBLOCK)
132                 .findFirstToken(TokenTypes.METHOD_DEF);
133         final DetailAST expectedMethodDefNode2 = expectedMethodDefNode1.getNextSibling();
134         final DetailAST[] expected = {expectedClassDefNode, expectedMethodDefNode1,
135             expectedMethodDefNode2};
136         assertWithMessage("Result nodes differ from expected")
137                 .that(actual)
138                 .isEqualTo(expected);
139     }
140 
141     @Test
142     public void testComplexQueryOne() throws Exception {
143         final String xpath = "//CLASS_DEF | //CLASS_DEF/OBJBLOCK";
144         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
145         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
146         final DetailAST expectedClassDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
147                 TokenTypes.COMPILATION_UNIT)
148                 .findFirstToken(TokenTypes.CLASS_DEF);
149         final DetailAST expectedObjblockNode = expectedClassDefNode
150                 .findFirstToken(TokenTypes.OBJBLOCK);
151         final DetailAST[] expected = {expectedClassDefNode, expectedObjblockNode};
152         assertWithMessage("Result nodes differ from expected")
153                 .that(actual)
154                 .isEqualTo(expected);
155     }
156 
157     @Test
158     public void testComplexQueryTwo() throws Exception {
159         final String xpath = "//PACKAGE_DEF | //PACKAGE_DEF/ANNOTATIONS";
160         final RootNode rootNode = getRootNode("InputXpathMapperAnnotation.java");
161         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
162         final DetailAST expectedPackageDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
163                 TokenTypes.COMPILATION_UNIT)
164                 .findFirstToken(TokenTypes.PACKAGE_DEF);
165         final DetailAST expectedAnnotationsNode = expectedPackageDefNode
166                 .findFirstToken(TokenTypes.ANNOTATIONS);
167         final DetailAST[] expected = {expectedPackageDefNode, expectedAnnotationsNode};
168         assertWithMessage("Result nodes differ from expected")
169                 .that(actual)
170                 .isEqualTo(expected);
171     }
172 
173     @Test
174     public void testComplexQueryThree() throws Exception {
175         final String xpath = "//CLASS_DEF | //CLASS_DEF//METHOD_DEF |"
176                 + " /COMPILATION_UNIT/CLASS_DEF/OBJBLOCK";
177         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
178         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
179         final DetailAST expectedClassDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
180                 TokenTypes.COMPILATION_UNIT)
181                 .findFirstToken(TokenTypes.CLASS_DEF);
182         final DetailAST expectedObjblockNode = expectedClassDefNode
183                 .findFirstToken(TokenTypes.OBJBLOCK);
184         final DetailAST expectedMethodDefNode = expectedObjblockNode
185                 .findFirstToken(TokenTypes.METHOD_DEF);
186         final DetailAST expectedMethodDefNode2 = expectedObjblockNode
187                 .findFirstToken(TokenTypes.METHOD_DEF)
188                 .getNextSibling();
189         final DetailAST[] expected = {expectedClassDefNode, expectedObjblockNode,
190             expectedMethodDefNode, expectedMethodDefNode2};
191         assertWithMessage("Result nodes differ from expected")
192                 .that(actual)
193                 .isEqualTo(expected);
194     }
195 
196     @Test
197     public void testAttributeOr() throws Exception {
198         final String xpath = "//METHOD_DEF[./IDENT[@text='getSomeMethod'] "
199                 + "or ./IDENT[@text='nonExistentMethod']]";
200         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
201         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
202         final DetailAST expectedClassDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
203                 TokenTypes.COMPILATION_UNIT)
204                 .findFirstToken(TokenTypes.CLASS_DEF);
205         final DetailAST expectedMethodDefNode = expectedClassDefNode
206                 .findFirstToken(TokenTypes.OBJBLOCK)
207                 .findFirstToken(TokenTypes.METHOD_DEF)
208                 .getNextSibling();
209         final DetailAST[] expected = {expectedMethodDefNode};
210         assertWithMessage("Result nodes differ from expected")
211                 .that(actual)
212                 .isEqualTo(expected);
213     }
214 
215     @Test
216     public void testAttributeAnd() throws Exception {
217         final String xpath = "//METHOD_DEF[./IDENT[@text='callSomeMethod'] and "
218                 + "../..[./IDENT[@text='InputXpathMapperAst']]]";
219         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
220         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
221         final DetailAST expectedClassDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
222                 TokenTypes.COMPILATION_UNIT)
223                 .findFirstToken(TokenTypes.CLASS_DEF);
224         final DetailAST expectedMethodDefNode = expectedClassDefNode
225                 .findFirstToken(TokenTypes.OBJBLOCK)
226                 .findFirstToken(TokenTypes.METHOD_DEF);
227         final DetailAST[] expected = {expectedMethodDefNode};
228         assertWithMessage("Result nodes differ from expected")
229                 .that(actual)
230                 .isEqualTo(expected);
231     }
232 
233     @Test
234     public void testQueryAllElementsWithAttribute() throws Exception {
235         final String xpath = "//*[./IDENT[@text]]";
236         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
237         final List<NodeInfo> nodes = getXpathItems(xpath, rootNode);
238         assertWithMessage("Invalid number of nodes")
239                 .that(nodes)
240                 .hasSize(18);
241     }
242 
243     @Test
244     public void testQueryElementByIndex() throws Exception {
245         final String xpath = "(//VARIABLE_DEF)[1]";
246         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
247         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
248         assertWithMessage("Invalid number of nodes")
249                 .that(actual)
250                 .hasLength(1);
251         final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
252                 TokenTypes.COMPILATION_UNIT)
253                 .findFirstToken(TokenTypes.CLASS_DEF)
254                 .findFirstToken(TokenTypes.OBJBLOCK)
255                 .findFirstToken(TokenTypes.METHOD_DEF)
256                 .findFirstToken(TokenTypes.SLIST)
257                 .findFirstToken(TokenTypes.VARIABLE_DEF);
258         final DetailAST[] expected = {expectedVariableDefNode};
259         assertWithMessage("Result nodes differ from expected")
260                 .that(actual)
261                 .isEqualTo(expected);
262     }
263 
264     @Test
265     public void testQueryAllVariableDefinitionsWithAttribute() throws Exception {
266         final String xpath = "//VARIABLE_DEF[./IDENT[@*]]";
267         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
268         final List<NodeInfo> nodes = getXpathItems(xpath, rootNode);
269         assertWithMessage("Invalid number of nodes")
270                 .that(nodes)
271                 .hasSize(4);
272     }
273 
274     @Test
275     public void testQueryAllVariableDefWrongAttribute() throws Exception {
276         final String xpath = "//VARIABLE_DEF[@qwe]";
277         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
278         final List<NodeInfo> nodes = getXpathItems(xpath, rootNode);
279         assertWithMessage("Invalid number of nodes")
280                 .that(nodes)
281                 .hasSize(0);
282     }
283 
284     @Test
285     public void testQueryAllMethodDefinitionsInContext() throws Exception {
286         final String objectXpath = "//CLASS_DEF[./IDENT[@text='InputXpathMapperAst']]//OBJBLOCK";
287         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
288         final List<NodeInfo> objectNodes = getXpathItems(objectXpath, rootNode);
289         assertWithMessage("Invalid number of nodes")
290                 .that(objectNodes)
291                 .hasSize(1);
292         final AbstractNode objNode = (AbstractNode) objectNodes.getFirst();
293         final String methodsXpath = "METHOD_DEF";
294         final List<NodeInfo> methodsNodes = getXpathItems(methodsXpath, objNode);
295         assertWithMessage("Invalid number of nodes")
296                 .that(methodsNodes)
297                 .hasSize(2);
298         final DetailAST[] actual = convertToArray(methodsNodes);
299         final DetailAST expectedMethodDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
300                 TokenTypes.COMPILATION_UNIT)
301                 .findFirstToken(TokenTypes.CLASS_DEF)
302                 .findFirstToken(TokenTypes.OBJBLOCK)
303                 .findFirstToken(TokenTypes.METHOD_DEF);
304         final DetailAST[] expected = {expectedMethodDefNode,
305             expectedMethodDefNode.getNextSibling()};
306         assertWithMessage("Result nodes differ from expected")
307                 .that(actual)
308                 .isEqualTo(expected);
309         assertWithMessage("Invalid token type")
310                 .that(actual[0].getType())
311                 .isEqualTo(TokenTypes.METHOD_DEF);
312         assertWithMessage("Invalid token type")
313                 .that(actual[1].getType())
314                 .isEqualTo(TokenTypes.METHOD_DEF);
315     }
316 
317     @Test
318     public void testQueryAllClassDefinitions() throws Exception {
319         final String xpath = "/COMPILATION_UNIT/CLASS_DEF";
320         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
321         final List<NodeInfo> nodes = getXpathItems(xpath, rootNode);
322         assertWithMessage("Invalid number of nodes")
323                 .that(nodes)
324                 .hasSize(1);
325         final AbstractNode classDefNode = (AbstractNode) nodes.getFirst();
326         assertWithMessage("Invalid line number")
327                 .that(classDefNode.getLineNumber())
328                 .isEqualTo(3);
329         assertWithMessage("Invalid column number")
330                 .that(classDefNode.getColumnNumber())
331                 .isEqualTo(0);
332         final DetailAST[] actual = convertToArray(nodes);
333         final DetailAST expectedClassDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
334                 TokenTypes.COMPILATION_UNIT)
335                 .findFirstToken(TokenTypes.CLASS_DEF);
336         final DetailAST[] expected = {expectedClassDefNode};
337         assertWithMessage("Result nodes differ from expected")
338                 .that(actual)
339                 .isEqualTo(expected);
340     }
341 
342     @Test
343     public void testQueryByMethodName() throws Exception {
344         final String xpath = "//METHOD_DEF[./IDENT[@text='getSomeMethod']]";
345         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
346         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
347         final DetailAST expectedMethodDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
348                 TokenTypes.COMPILATION_UNIT)
349                 .findFirstToken(TokenTypes.CLASS_DEF)
350                 .findFirstToken(TokenTypes.OBJBLOCK)
351                 .findFirstToken(TokenTypes.METHOD_DEF)
352                 .getNextSibling();
353         final DetailAST[] expected = {expectedMethodDefNode};
354         assertWithMessage("Result nodes differ from expected")
355                 .that(actual)
356                 .isEqualTo(expected);
357     }
358 
359     @Test
360     public void testQueryMethodDefinitionsByClassName() throws Exception {
361         final String xpath = "//CLASS_DEF[./IDENT[@text='InputXpathMapperAst']]"
362                 + "//OBJBLOCK//METHOD_DEF";
363         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
364         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
365         final DetailAST expectedMethodDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
366                 TokenTypes.COMPILATION_UNIT)
367                 .findFirstToken(TokenTypes.CLASS_DEF)
368                 .findFirstToken(TokenTypes.OBJBLOCK)
369                 .findFirstToken(TokenTypes.METHOD_DEF);
370         final DetailAST[] expected = {expectedMethodDefNode,
371             expectedMethodDefNode.getNextSibling()};
372         assertWithMessage("Result nodes differ from expected")
373                 .that(actual)
374                 .isEqualTo(expected);
375         assertWithMessage("Invalid token type")
376                 .that(actual[0].getType())
377                 .isEqualTo(TokenTypes.METHOD_DEF);
378         assertWithMessage("Invalid token type")
379                 .that(actual[1].getType())
380                 .isEqualTo(TokenTypes.METHOD_DEF);
381     }
382 
383     @Test
384     public void testQueryByClassNameAndMethodName() throws Exception {
385         final String xpath = "//CLASS_DEF[./IDENT[@text='InputXpathMapperAst']]//OBJBLOCK"
386                 + "//METHOD_DEF[./IDENT[@text='getSomeMethod']]";
387         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
388         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
389         final DetailAST expectedMethodDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
390                 TokenTypes.COMPILATION_UNIT)
391                 .findFirstToken(TokenTypes.CLASS_DEF)
392                 .findFirstToken(TokenTypes.OBJBLOCK)
393                 .findFirstToken(TokenTypes.METHOD_DEF)
394                 .getNextSibling();
395         final DetailAST[] expected = {expectedMethodDefNode};
396         assertWithMessage("Result nodes differ from expected")
397                 .that(actual)
398                 .isEqualTo(expected);
399     }
400 
401     @Test
402     public void testQueryClassDefinitionByClassName() throws Exception {
403         final String xpath = "//CLASS_DEF[./IDENT[@text='InputXpathMapperAst']]";
404         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
405         final List<NodeInfo> nodes = getXpathItems(xpath, rootNode);
406         final DetailAST[] actual = convertToArray(nodes);
407         final DetailAST expectedClassDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
408                 TokenTypes.COMPILATION_UNIT)
409                 .findFirstToken(TokenTypes.CLASS_DEF);
410         final DetailAST[] expected = {expectedClassDefNode};
411         final ElementNode classDefNode = (ElementNode) nodes.getFirst();
412         assertWithMessage("Invalid node name")
413                 .that(classDefNode.getLocalPart())
414                 .isEqualTo("CLASS_DEF");
415         assertWithMessage("Result nodes differ from expected")
416                 .that(actual)
417                 .isEqualTo(expected);
418     }
419 
420     @Test
421     public void testQueryWrongClassName() throws Exception {
422         final String xpath = "/CLASS_DEF[@text='WrongName']";
423         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
424         final List<NodeInfo> nodes = getXpathItems(xpath, rootNode);
425         assertWithMessage("Should return true, because no item matches xpath")
426                 .that(nodes)
427                 .isEmpty();
428     }
429 
430     @Test
431     public void testQueryWrongXpath() throws Exception {
432         final String xpath = "/WRONG_XPATH";
433         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
434         final List<NodeInfo> nodes = getXpathItems(xpath, rootNode);
435         assertWithMessage("Should return true, because no item matches xpath")
436                 .that(nodes)
437                 .isEmpty();
438     }
439 
440     @Test
441     public void testQueryAncestor() throws Exception {
442         final String xpath = "//VARIABLE_DEF[./IDENT[@text='another']]/ancestor::METHOD_DEF";
443         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
444         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
445         final DetailAST expectedMethodDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
446                 TokenTypes.COMPILATION_UNIT)
447                 .findFirstToken(TokenTypes.CLASS_DEF)
448                 .findFirstToken(TokenTypes.OBJBLOCK)
449                 .findFirstToken(TokenTypes.METHOD_DEF);
450         final DetailAST[] expected = {expectedMethodDefNode};
451         assertWithMessage("Result nodes differ from expected")
452                 .that(actual)
453                 .isEqualTo(expected);
454     }
455 
456     @Test
457     public void testQueryAncestorOrSelf() throws Exception {
458         final String xpath = "//VARIABLE_DEF[./IDENT[@text='another']]"
459                 + "/ancestor-or-self::VARIABLE_DEF";
460         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
461         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
462         final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
463                 TokenTypes.COMPILATION_UNIT)
464                 .findFirstToken(TokenTypes.CLASS_DEF)
465                 .findFirstToken(TokenTypes.OBJBLOCK)
466                 .findFirstToken(TokenTypes.METHOD_DEF)
467                 .findFirstToken(TokenTypes.SLIST)
468                 .findFirstToken(TokenTypes.VARIABLE_DEF)
469                 .getNextSibling()
470                 .getNextSibling();
471         final DetailAST[] expected = {expectedVariableDefNode};
472         assertWithMessage("Result nodes differ from expected")
473                 .that(actual)
474                 .isEqualTo(expected);
475     }
476 
477     @Test
478     public void testQueryDescendant() throws Exception {
479         final String xpath = "//METHOD_DEF/descendant::EXPR";
480         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
481         final List<NodeInfo> nodes = getXpathItems(xpath, rootNode);
482         assertWithMessage("Invalid number of nodes")
483                 .that(nodes)
484                 .hasSize(6);
485     }
486 
487     @Test
488     public void testQueryDescendantOrSelf() throws Exception {
489         final String xpath = "//METHOD_DEF/descendant-or-self::METHOD_DEF";
490         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
491         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
492         final DetailAST expectedMethodDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
493                 TokenTypes.COMPILATION_UNIT)
494                 .findFirstToken(TokenTypes.CLASS_DEF)
495                 .findFirstToken(TokenTypes.OBJBLOCK)
496                 .findFirstToken(TokenTypes.METHOD_DEF);
497         final DetailAST[] expected = {expectedMethodDefNode,
498             expectedMethodDefNode.getNextSibling()};
499         assertWithMessage("Result nodes differ from expected")
500                 .that(actual)
501                 .isEqualTo(expected);
502         assertWithMessage("Invalid token type")
503                 .that(actual[0].getType())
504                 .isEqualTo(TokenTypes.METHOD_DEF);
505         assertWithMessage("Invalid token type")
506                 .that(actual[1].getType())
507                 .isEqualTo(TokenTypes.METHOD_DEF);
508     }
509 
510     @Test
511     public void testQueryNoChild() throws Exception {
512         final String xpath = "//RCURLY/METHOD_DEF";
513         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
514         final List<NodeInfo> nodes = getXpathItems(xpath, rootNode);
515         assertWithMessage("Should return true, because no item matches xpath")
516                 .that(nodes)
517                 .isEmpty();
518     }
519 
520     @Test
521     public void testQueryNoDescendant() throws Exception {
522         final String xpath = "//RCURLY/descendant::METHOD_DEF";
523         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
524         final List<NodeInfo> nodes = getXpathItems(xpath, rootNode);
525         assertWithMessage("Should return true, because no item matches xpath")
526                 .that(nodes)
527                 .isEmpty();
528     }
529 
530     @Test
531     public void testQueryRootNotImplementedAxis() throws Exception {
532         final String xpath = "//namespace::*";
533         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
534         final UnsupportedOperationException exc =
535                 getExpectedThrowable(UnsupportedOperationException.class, () -> {
536                     getXpathItems(xpath, rootNode);
537                 });
538         assertWithMessage("Invalid exception")
539                 .that(exc.getMessage())
540                 .isEqualTo("Operation is not supported");
541     }
542 
543     @Test
544     public void testQueryElementNotImplementedAxis() throws Exception {
545         final String xpath = "/COMPILATION_UNIT/CLASS_DEF//namespace::*";
546         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
547         final UnsupportedOperationException exc =
548                 getExpectedThrowable(UnsupportedOperationException.class, () -> {
549                     getXpathItems(xpath, rootNode);
550                 });
551         assertWithMessage("Invalid exception")
552                 .that(exc.getMessage())
553                 .isEqualTo("Operation is not supported");
554     }
555 
556     @Test
557     public void testQuerySelf() throws Exception {
558         final String objectXpath = "//CLASS_DEF[./IDENT[@text='InputXpathMapperAst']]//OBJBLOCK";
559         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
560         final List<NodeInfo> objectNodes = getXpathItems(objectXpath, rootNode);
561         assertWithMessage("Invalid number of nodes")
562                 .that(objectNodes)
563                 .hasSize(1);
564         final AbstractNode objNode = (AbstractNode) objectNodes.getFirst();
565         final String methodsXpath = "self::OBJBLOCK";
566         final DetailAST[] actual = convertToArray(getXpathItems(methodsXpath, objNode));
567         final DetailAST expectedObjBlockNode = getSiblingByType(rootNode.getUnderlyingNode(),
568                 TokenTypes.COMPILATION_UNIT)
569                 .findFirstToken(TokenTypes.CLASS_DEF)
570                 .findFirstToken(TokenTypes.OBJBLOCK);
571         final DetailAST[] expected = {expectedObjBlockNode};
572         assertWithMessage("Result nodes differ from expected")
573                 .that(actual)
574                 .isEqualTo(expected);
575     }
576 
577     @Test
578     public void testQueryNonExistentAttribute() throws Exception {
579         final String xpath = "//CLASS_DEF[./IDENT[@text='InputXpathMapperAst']]";
580         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
581         final List<NodeInfo> nodes = getXpathItems(xpath, rootNode);
582         final ElementNode classDefNode = (ElementNode) nodes.getFirst();
583         assertWithMessage("Not existing attribute should have null value")
584                 .that(classDefNode.getAttributeValue("", "noneExistingAttribute"))
585                 .isNull();
586     }
587 
588     @Test
589     public void testQueryRootSelf() throws Exception {
590         final String xpath = "self::node()";
591         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
592         final List<NodeInfo> nodes = getXpathItems(xpath, rootNode);
593         assertWithMessage("Invalid number of nodes")
594                 .that(nodes)
595                 .hasSize(1);
596     }
597 
598     @Test
599     public void testQueryAnnotation() throws Exception {
600         final String xpath = "//ANNOTATION[./IDENT[@text='Deprecated']]";
601         final RootNode rootNode = getRootNode("InputXpathMapperAnnotation.java");
602         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
603         final DetailAST expectedAnnotationNode = getSiblingByType(rootNode.getUnderlyingNode(),
604                 TokenTypes.COMPILATION_UNIT)
605                 .findFirstToken(TokenTypes.CLASS_DEF)
606                 .findFirstToken(TokenTypes.MODIFIERS)
607                 .findFirstToken(TokenTypes.ANNOTATION);
608         final DetailAST[] expected = {expectedAnnotationNode};
609         assertWithMessage("Result nodes differ from expected")
610                 .that(actual)
611                 .isEqualTo(expected);
612     }
613 
614     @Test
615     public void testQueryNonExistentAnnotation() throws Exception {
616         final String xpath = "//ANNOTATION[@text='SpringBootApplication']";
617         final RootNode rootNode = getRootNode("InputXpathMapperAnnotation.java");
618         final List<NodeInfo> nodes = getXpathItems(xpath, rootNode);
619         assertWithMessage("Invalid number of nodes")
620                 .that(nodes)
621                 .hasSize(0);
622     }
623 
624     @Test
625     public void testQueryEnumDef() throws Exception {
626         final String xpath = "/COMPILATION_UNIT/ENUM_DEF";
627         final RootNode enumRootNode = getRootNode("InputXpathMapperEnum.java");
628         final DetailAST[] actual = convertToArray(getXpathItems(xpath, enumRootNode));
629         final DetailAST expectedEnumDefNode = getSiblingByType(enumRootNode.getUnderlyingNode(),
630                 TokenTypes.COMPILATION_UNIT)
631                 .findFirstToken(TokenTypes.ENUM_DEF);
632         final DetailAST[] expected = {expectedEnumDefNode};
633         assertWithMessage("Result nodes differ from expected")
634                 .that(actual)
635                 .isEqualTo(expected);
636     }
637 
638     @Test
639     public void testQueryEnumElementsNumber() throws Exception {
640         final String xpath = "/COMPILATION_UNIT/ENUM_DEF/OBJBLOCK/ENUM_CONSTANT_DEF";
641         final RootNode enumRootNode = getRootNode("InputXpathMapperEnum.java");
642         final List<NodeInfo> nodes = getXpathItems(xpath, enumRootNode);
643         assertWithMessage("Invalid number of nodes")
644                 .that(nodes)
645                 .hasSize(3);
646     }
647 
648     @Test
649     public void testQueryEnumElementByName() throws Exception {
650         final String xpath = "//*[./IDENT[@text='TWO']]";
651         final RootNode enumRootNode = getRootNode("InputXpathMapperEnum.java");
652         final DetailAST[] actual = convertToArray(getXpathItems(xpath, enumRootNode));
653         final DetailAST expectedEnumConstantDefNode = getSiblingByType(
654                 enumRootNode.getUnderlyingNode(),
655                 TokenTypes.COMPILATION_UNIT)
656                 .findFirstToken(TokenTypes.ENUM_DEF)
657                 .findFirstToken(TokenTypes.OBJBLOCK)
658                 .findFirstToken(TokenTypes.ENUM_CONSTANT_DEF)
659                 .getNextSibling()
660                 .getNextSibling();
661         final DetailAST[] expected = {expectedEnumConstantDefNode};
662         assertWithMessage("Result nodes differ from expected")
663                 .that(actual)
664                 .isEqualTo(expected);
665     }
666 
667     @Test
668     public void testQueryInterfaceDef() throws Exception {
669         final String xpath = "//INTERFACE_DEF";
670         final RootNode interfaceRootNode = getRootNode("InputXpathMapperInterface.java");
671         final DetailAST[] actual = convertToArray(getXpathItems(xpath, interfaceRootNode));
672         final DetailAST expectedInterfaceDefNode = getSiblingByType(
673                 interfaceRootNode.getUnderlyingNode(),
674                 TokenTypes.COMPILATION_UNIT)
675                 .findFirstToken(TokenTypes.INTERFACE_DEF);
676         final DetailAST[] expected = {expectedInterfaceDefNode};
677         assertWithMessage("Result nodes differ from expected")
678                 .that(actual)
679                 .isEqualTo(expected);
680     }
681 
682     @Test
683     public void testQueryInterfaceMethodDefNumber() throws Exception {
684         final String xpath = "//INTERFACE_DEF/OBJBLOCK/METHOD_DEF";
685         final RootNode interfaceRootNode = getRootNode("InputXpathMapperInterface.java");
686         final List<NodeInfo> nodes = getXpathItems(xpath, interfaceRootNode);
687         assertWithMessage("Invalid number of nodes")
688                 .that(nodes)
689                 .hasSize(4);
690     }
691 
692     @Test
693     public void testQueryInterfaceParameterDef() throws Exception {
694         final String xpath = "//PARAMETER_DEF[./IDENT[@text='someVariable']]/../..";
695         final RootNode interfaceRootNode = getRootNode("InputXpathMapperInterface.java");
696         final DetailAST[] actual = convertToArray(getXpathItems(xpath, interfaceRootNode));
697         final DetailAST expectedMethodDefNode = getSiblingByType(
698                 interfaceRootNode.getUnderlyingNode(),
699                 TokenTypes.COMPILATION_UNIT)
700                 .findFirstToken(TokenTypes.INTERFACE_DEF)
701                 .findFirstToken(TokenTypes.OBJBLOCK)
702                 .findFirstToken(TokenTypes.METHOD_DEF)
703                 .getNextSibling();
704         final DetailAST[] expected = {expectedMethodDefNode};
705         assertWithMessage("Result nodes differ from expected")
706                 .that(actual)
707                 .isEqualTo(expected);
708     }
709 
710     @Test
711     public void testIdent() throws Exception {
712         final String xpath = "//CLASS_DEF/IDENT[@text='InputXpathMapperAst']";
713         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
714         final List<NodeInfo> nodes = getXpathItems(xpath, rootNode);
715         final DetailAST[] actual = convertToArray(nodes);
716         final DetailAST expectedIdentNode = getSiblingByType(rootNode.getUnderlyingNode(),
717                 TokenTypes.COMPILATION_UNIT)
718                 .findFirstToken(TokenTypes.CLASS_DEF)
719                 .findFirstToken(TokenTypes.IDENT);
720 
721         final DetailAST[] expected = {expectedIdentNode};
722         assertWithMessage("Result nodes differ from expected")
723                 .that(actual)
724                 .isEqualTo(expected);
725     }
726 
727     @Test
728     public void testIdentByText() throws Exception {
729         final String xpath = "//IDENT[@text='puppycrawl']";
730         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
731         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
732         final DetailAST expectedMethodDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
733                 TokenTypes.COMPILATION_UNIT)
734                 .findFirstToken(TokenTypes.PACKAGE_DEF)
735                 .findFirstToken(TokenTypes.DOT)
736                 .findFirstToken(TokenTypes.DOT)
737                 .findFirstToken(TokenTypes.DOT)
738                 .findFirstToken(TokenTypes.DOT)
739                 .findFirstToken(TokenTypes.DOT)
740                 .findFirstToken(TokenTypes.IDENT)
741                 .getNextSibling();
742         final DetailAST[] expected = {expectedMethodDefNode};
743         assertWithMessage("Result nodes differ from expected")
744                 .that(actual)
745                 .isEqualTo(expected);
746     }
747 
748     @Test
749     public void testNumVariableByItsValue() throws Exception {
750         final String xpath = "//VARIABLE_DEF[.//NUM_INT[@text=123]]";
751         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
752         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
753         final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
754                 TokenTypes.COMPILATION_UNIT)
755                 .findFirstToken(TokenTypes.CLASS_DEF)
756                 .findFirstToken(TokenTypes.OBJBLOCK)
757                 .findFirstToken(TokenTypes.METHOD_DEF)
758                 .findFirstToken(TokenTypes.SLIST)
759                 .findFirstToken(TokenTypes.VARIABLE_DEF);
760         final DetailAST[] expected = {expectedVariableDefNode};
761         assertWithMessage("Result nodes differ from expected")
762                 .that(actual)
763                 .isEqualTo(expected);
764     }
765 
766     @Test
767     public void testStringVariableByItsValue() throws Exception {
768         final String xpath = "//VARIABLE_DEF[./ASSIGN/EXPR"
769                 + "/STRING_LITERAL[@text='HelloWorld']]";
770         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
771         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
772         final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
773                 TokenTypes.COMPILATION_UNIT)
774                 .findFirstToken(TokenTypes.CLASS_DEF)
775                 .findFirstToken(TokenTypes.OBJBLOCK)
776                 .findFirstToken(TokenTypes.METHOD_DEF)
777                 .findFirstToken(TokenTypes.SLIST)
778                 .findFirstToken(TokenTypes.VARIABLE_DEF)
779                 .getNextSibling()
780                 .getNextSibling();
781         final DetailAST[] expected = {expectedVariableDefNode};
782         assertWithMessage("Result nodes differ from expected")
783                 .that(actual)
784                 .isEqualTo(expected);
785     }
786 
787     @Test
788     public void testSameNodesByNameAndByText() throws Exception {
789         final String xpath1 = "//VARIABLE_DEF[./IDENT[@text='another']]/ASSIGN/EXPR/STRING_LITERAL";
790         final String xpath2 = "//VARIABLE_DEF/ASSIGN/EXPR/STRING_LITERAL[@text='HelloWorld']";
791         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
792         final DetailAST[] actual1 = convertToArray(getXpathItems(xpath1, rootNode));
793         final DetailAST[] actual2 = convertToArray(getXpathItems(xpath2, rootNode));
794         assertWithMessage("Result nodes differ from expected")
795                 .that(actual2)
796                 .isEqualTo(actual1);
797     }
798 
799     @Test
800     public void testMethodDefByAnnotationValue() throws Exception {
801         final String xpath = "//METHOD_DEF[.//ANNOTATION[./IDENT[@text='SuppressWarnings']"
802                 + " and .//*[@text='good']]]";
803         final RootNode rootNode = getRootNode("InputXpathMapperAnnotation.java");
804         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
805         final DetailAST expectedAnnotationNode = getSiblingByType(rootNode.getUnderlyingNode(),
806                 TokenTypes.COMPILATION_UNIT)
807                 .findFirstToken(TokenTypes.CLASS_DEF)
808                 .findFirstToken(TokenTypes.OBJBLOCK)
809                 .findFirstToken(TokenTypes.METHOD_DEF)
810                 .getNextSibling();
811         final DetailAST[] expected = {expectedAnnotationNode};
812         assertWithMessage("Result nodes differ from expected")
813                 .that(actual)
814                 .isEqualTo(expected);
815     }
816 
817     @Test
818     public void testFirstImport() throws Exception {
819         final String xpath = "/COMPILATION_UNIT/IMPORT[1]";
820         final RootNode rootNode = getRootNode("InputXpathMapperPositions.java");
821         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
822         final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
823                 TokenTypes.COMPILATION_UNIT)
824                 .findFirstToken(TokenTypes.IMPORT);
825         final DetailAST[] expected = {expectedVariableDefNode};
826         assertWithMessage("Result nodes differ from expected")
827                 .that(actual)
828                 .isEqualTo(expected);
829     }
830 
831     @Test
832     public void testSecondImport() throws Exception {
833         final String xpath = "/COMPILATION_UNIT/IMPORT[2]";
834         final RootNode rootNode = getRootNode("InputXpathMapperPositions.java");
835         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
836         final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
837                 TokenTypes.COMPILATION_UNIT)
838                 .findFirstToken(TokenTypes.IMPORT)
839                 .getNextSibling();
840         final DetailAST[] expected = {expectedVariableDefNode};
841         assertWithMessage("Result nodes differ from expected")
842                 .that(actual)
843                 .isEqualTo(expected);
844     }
845 
846     @Test
847     public void testThirdImport() throws Exception {
848         final String xpath = "/COMPILATION_UNIT/IMPORT[3]";
849         final RootNode rootNode = getRootNode("InputXpathMapperPositions.java");
850         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
851         final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
852                 TokenTypes.COMPILATION_UNIT)
853                 .findFirstToken(TokenTypes.IMPORT)
854                 .getNextSibling()
855                 .getNextSibling();
856         final DetailAST[] expected = {expectedVariableDefNode};
857         assertWithMessage("Result nodes differ from expected")
858                 .that(actual)
859                 .isEqualTo(expected);
860     }
861 
862     @Test
863     public void testLastImport() throws Exception {
864         final String xpath = "/COMPILATION_UNIT/IMPORT[9]";
865         final RootNode rootNode = getRootNode("InputXpathMapperPositions.java");
866         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
867         final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
868                 TokenTypes.COMPILATION_UNIT)
869                 .findFirstToken(TokenTypes.IMPORT)
870                 .getNextSibling()
871                 .getNextSibling()
872                 .getNextSibling()
873                 .getNextSibling()
874                 .getNextSibling()
875                 .getNextSibling()
876                 .getNextSibling()
877                 .getNextSibling();
878         final DetailAST[] expected = {expectedVariableDefNode};
879         assertWithMessage("Result nodes differ from expected")
880                 .that(actual)
881                 .isEqualTo(expected);
882     }
883 
884     @Test
885     public void testFirstCaseGroup() throws Exception {
886         final String xpath = "//CLASS_DEF[./IDENT[@text='InputXpathMapperPositions']]"
887                 + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='switchMethod']]"
888                 + "/SLIST/LITERAL_SWITCH/CASE_GROUP[1]";
889         final RootNode rootNode = getRootNode("InputXpathMapperPositions.java");
890         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
891         final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
892                 TokenTypes.COMPILATION_UNIT)
893                 .findFirstToken(TokenTypes.CLASS_DEF)
894                 .findFirstToken(TokenTypes.OBJBLOCK)
895                 .findFirstToken(TokenTypes.METHOD_DEF)
896                 .findFirstToken(TokenTypes.SLIST)
897                 .findFirstToken(TokenTypes.LITERAL_SWITCH)
898                 .findFirstToken(TokenTypes.CASE_GROUP);
899         final DetailAST[] expected = {expectedVariableDefNode};
900         assertWithMessage("Result nodes differ from expected")
901                 .that(actual)
902                 .isEqualTo(expected);
903     }
904 
905     @Test
906     public void testSecondCaseGroup() throws Exception {
907         final String xpath = "//CLASS_DEF[./IDENT[@text='InputXpathMapperPositions']]"
908                 + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='switchMethod']]"
909                 + "/SLIST/LITERAL_SWITCH/CASE_GROUP[2]";
910         final RootNode rootNode = getRootNode("InputXpathMapperPositions.java");
911         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
912         final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
913                 TokenTypes.COMPILATION_UNIT)
914                 .findFirstToken(TokenTypes.CLASS_DEF)
915                 .findFirstToken(TokenTypes.OBJBLOCK)
916                 .findFirstToken(TokenTypes.METHOD_DEF)
917                 .findFirstToken(TokenTypes.SLIST)
918                 .findFirstToken(TokenTypes.LITERAL_SWITCH)
919                 .findFirstToken(TokenTypes.CASE_GROUP)
920                 .getNextSibling();
921         final DetailAST[] expected = {expectedVariableDefNode};
922         assertWithMessage("Result nodes differ from expected")
923                 .that(actual)
924                 .isEqualTo(expected);
925     }
926 
927     @Test
928     public void testThirdCaseGroup() throws Exception {
929         final String xpath = "//CLASS_DEF[./IDENT[@text='InputXpathMapperPositions']]"
930                 + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='switchMethod']]"
931                 + "/SLIST/LITERAL_SWITCH/CASE_GROUP[3]";
932         final RootNode rootNode = getRootNode("InputXpathMapperPositions.java");
933         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
934         final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
935                 TokenTypes.COMPILATION_UNIT)
936                 .findFirstToken(TokenTypes.CLASS_DEF)
937                 .findFirstToken(TokenTypes.OBJBLOCK)
938                 .findFirstToken(TokenTypes.METHOD_DEF)
939                 .findFirstToken(TokenTypes.SLIST)
940                 .findFirstToken(TokenTypes.LITERAL_SWITCH)
941                 .findFirstToken(TokenTypes.CASE_GROUP)
942                 .getNextSibling()
943                 .getNextSibling();
944         final DetailAST[] expected = {expectedVariableDefNode};
945         assertWithMessage("Result nodes differ from expected")
946                 .that(actual)
947                 .isEqualTo(expected);
948     }
949 
950     @Test
951     public void testFourthCaseGroup() throws Exception {
952         final String xpath = "//CLASS_DEF[./IDENT[@text='InputXpathMapperPositions']]"
953                 + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='switchMethod']]"
954                 + "/SLIST/LITERAL_SWITCH/CASE_GROUP[4]";
955         final RootNode rootNode = getRootNode("InputXpathMapperPositions.java");
956         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
957         final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
958                 TokenTypes.COMPILATION_UNIT)
959                 .findFirstToken(TokenTypes.CLASS_DEF)
960                 .findFirstToken(TokenTypes.OBJBLOCK)
961                 .findFirstToken(TokenTypes.METHOD_DEF)
962                 .findFirstToken(TokenTypes.SLIST)
963                 .findFirstToken(TokenTypes.LITERAL_SWITCH)
964                 .findFirstToken(TokenTypes.CASE_GROUP)
965                 .getNextSibling()
966                 .getNextSibling()
967                 .getNextSibling();
968         final DetailAST[] expected = {expectedVariableDefNode};
969         assertWithMessage("Result nodes differ from expected")
970                 .that(actual)
971                 .isEqualTo(expected);
972     }
973 
974     @Test
975     public void testQueryElementFollowingSibling() throws Exception {
976         final String xpath = "//METHOD_DEF/following-sibling::*";
977         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
978         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
979         final DetailAST expectedMethodDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
980                 TokenTypes.COMPILATION_UNIT)
981                 .findFirstToken(TokenTypes.CLASS_DEF)
982                 .findFirstToken(TokenTypes.OBJBLOCK)
983                 .findFirstToken(TokenTypes.METHOD_DEF)
984                 .getNextSibling();
985         final DetailAST[] expected = {expectedMethodDefNode,
986                 expectedMethodDefNode.getNextSibling()};
987         assertWithMessage("Result nodes differ from expected")
988                 .that(actual)
989                 .isEqualTo(expected);
990         assertWithMessage("Invalid token type")
991                 .that(actual[0].getType())
992                 .isEqualTo(TokenTypes.METHOD_DEF);
993         assertWithMessage("Invalid token type")
994                 .that(actual[1].getType())
995                 .isEqualTo(TokenTypes.RCURLY);
996     }
997 
998     @Test
999     public void testQueryElementNoFollowingSibling() throws Exception {
1000         final String xpath = "//CLASS_DEF/following-sibling::*";
1001         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
1002         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
1003         assertWithMessage("Invalid number of nodes")
1004                 .that(actual)
1005                 .isEmpty();
1006     }
1007 
1008     @Test
1009     public void testQueryElementFollowingSiblingRcurly() throws Exception {
1010         final String xpath = "//METHOD_DEF/following-sibling::RCURLY";
1011         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
1012         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
1013         final DetailAST expectedRightCurlyNode = getSiblingByType(rootNode.getUnderlyingNode(),
1014                 TokenTypes.COMPILATION_UNIT)
1015                 .findFirstToken(TokenTypes.CLASS_DEF)
1016                 .findFirstToken(TokenTypes.OBJBLOCK)
1017                 .findFirstToken(TokenTypes.METHOD_DEF)
1018                 .getNextSibling().getNextSibling();
1019         final DetailAST[] expected = {expectedRightCurlyNode};
1020         assertWithMessage("Result nodes differ from expected")
1021                 .that(actual)
1022                 .isEqualTo(expected);
1023     }
1024 
1025     @Test
1026     public void testQueryElementFollowing() throws Exception {
1027         final String xpath = "//IDENT[@text='variable']/following::*";
1028         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
1029         final List<NodeInfo> actual = getXpathItems(xpath, rootNode);
1030         assertWithMessage("Number of result nodes differ from expected")
1031                 .that(actual.size())
1032                 .isEqualTo(60);
1033     }
1034 
1035     @Test
1036     public void testQueryElementFollowingTwo() throws Exception {
1037         final String xpath = "//LITERAL_RETURN[.//STRING_LITERAL[@text='HelloWorld']]/following::*";
1038         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
1039         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
1040         final DetailAST expectedMethodDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
1041                 TokenTypes.COMPILATION_UNIT)
1042                 .findFirstToken(TokenTypes.CLASS_DEF)
1043                 .findFirstToken(TokenTypes.OBJBLOCK)
1044                 .findFirstToken(TokenTypes.METHOD_DEF)
1045                 .getNextSibling();
1046         final DetailAST expectedRcurlyNodeOne = expectedMethodDefNode.getNextSibling();
1047         final DetailAST expectedRcurlyNodeTwo = expectedMethodDefNode
1048                 .findFirstToken(TokenTypes.SLIST)
1049                 .findFirstToken(TokenTypes.LITERAL_RETURN)
1050                 .getNextSibling();
1051         final DetailAST[] expected = {expectedRcurlyNodeOne, expectedRcurlyNodeTwo};
1052         assertWithMessage("Result nodes differ from expected")
1053                 .that(actual)
1054                 .isEqualTo(expected);
1055     }
1056 
1057     @Test
1058     public void testQueryElementFollowingMethodDef() throws Exception {
1059         final String xpath = "//PACKAGE_DEF/following::METHOD_DEF";
1060         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
1061         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
1062         final DetailAST expectedMethodDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
1063                 TokenTypes.COMPILATION_UNIT)
1064                 .findFirstToken(TokenTypes.CLASS_DEF)
1065                 .findFirstToken(TokenTypes.OBJBLOCK)
1066                 .findFirstToken(TokenTypes.METHOD_DEF);
1067         final DetailAST[] expected = {expectedMethodDefNode,
1068             expectedMethodDefNode.getNextSibling()};
1069         assertWithMessage("Result nodes differ from expected")
1070                 .that(actual)
1071                 .isEqualTo(expected);
1072         assertWithMessage("Invalid token type")
1073                 .that(actual[0].getType())
1074                 .isEqualTo(TokenTypes.METHOD_DEF);
1075         assertWithMessage("Invalid token type")
1076                 .that(actual[1].getType())
1077                 .isEqualTo(TokenTypes.METHOD_DEF);
1078     }
1079 
1080     @Test
1081     public void testQueryElementNoFollowing() throws Exception {
1082         final String xpath = "//CLASS_DEF/following::*";
1083         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
1084         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
1085         assertWithMessage("Invalid number of nodes")
1086                 .that(actual)
1087                 .isEmpty();
1088     }
1089 
1090     @Test
1091     public void testQueryElementPrecedingSibling() throws Exception {
1092         final String xpath = "//VARIABLE_DEF[./IDENT[@text='array']]/preceding-sibling::*";
1093         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
1094         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
1095         final DetailAST expectedVariableDefNode1 = getSiblingByType(rootNode.getUnderlyingNode(),
1096                 TokenTypes.COMPILATION_UNIT)
1097                 .findFirstToken(TokenTypes.CLASS_DEF)
1098                 .findFirstToken(TokenTypes.OBJBLOCK)
1099                 .findFirstToken(TokenTypes.METHOD_DEF)
1100                 .findFirstToken(TokenTypes.SLIST)
1101                 .findFirstToken(TokenTypes.VARIABLE_DEF);
1102         final DetailAST expectedSemiNode1 = expectedVariableDefNode1.getNextSibling();
1103         final DetailAST expectedVariableDefNode2 = expectedSemiNode1.getNextSibling();
1104         final DetailAST expectedSemiNode2 = expectedVariableDefNode2.getNextSibling();
1105         final DetailAST[] expected = {expectedVariableDefNode1, expectedSemiNode1,
1106             expectedVariableDefNode2, expectedSemiNode2};
1107         assertWithMessage("Result nodes differ from expected")
1108                 .that(actual)
1109                 .isEqualTo(expected);
1110     }
1111 
1112     @Test
1113     public void testQueryElementPrecedingSiblingVariableDef() throws Exception {
1114         final String xpath = "//VARIABLE_DEF[./IDENT[@text='array']]/preceding-sibling::"
1115                 + "VARIABLE_DEF";
1116         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
1117         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
1118         final DetailAST expectedVariableDefNode1 = getSiblingByType(rootNode.getUnderlyingNode(),
1119                 TokenTypes.COMPILATION_UNIT)
1120                 .findFirstToken(TokenTypes.CLASS_DEF)
1121                 .findFirstToken(TokenTypes.OBJBLOCK)
1122                 .findFirstToken(TokenTypes.METHOD_DEF)
1123                 .findFirstToken(TokenTypes.SLIST)
1124                 .findFirstToken(TokenTypes.VARIABLE_DEF);
1125         final DetailAST expectedVariableDefNode2 = expectedVariableDefNode1.getNextSibling()
1126                 .getNextSibling();
1127         final DetailAST[] expected = {expectedVariableDefNode1, expectedVariableDefNode2};
1128         assertWithMessage("Result nodes differ from expected")
1129                 .that(actual)
1130                 .isEqualTo(expected);
1131     }
1132 
1133     @Test
1134     public void testQueryElementPrecedingSiblingArray() throws Exception {
1135         final String xpath = "//VARIABLE_DEF[./IDENT[@text='array']]/preceding-sibling::*[1]";
1136         final RootNode rootNode = getRootNode("InputXpathMapperAst.java");
1137         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
1138         final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
1139                 TokenTypes.COMPILATION_UNIT)
1140                 .findFirstToken(TokenTypes.CLASS_DEF)
1141                 .findFirstToken(TokenTypes.OBJBLOCK)
1142                 .findFirstToken(TokenTypes.METHOD_DEF)
1143                 .findFirstToken(TokenTypes.SLIST)
1144                 .findFirstToken(TokenTypes.SEMI)
1145                 .getNextSibling().getNextSibling();
1146         final DetailAST[] expected = {expectedVariableDefNode};
1147         assertWithMessage("Result nodes differ from expected")
1148                 .that(actual)
1149                 .isEqualTo(expected);
1150     }
1151 
1152     @Test
1153     public void testQueryElementPrecedingOne() throws Exception {
1154         final String xpath = "//LITERAL_CLASS/preceding::*";
1155         final RootNode rootNode = getRootNode("InputXpathMapperSingleTopClass.java");
1156         final DetailAST[] actual = convertToArray(getXpathItems(xpath,
1157                 rootNode.createChildren().getFirst()));
1158         assertWithMessage("Invalid number of nodes")
1159                 .that(actual)
1160                 .hasLength(18);
1161     }
1162 
1163     @Test
1164     public void testQueryElementPrecedingTwo() throws Exception {
1165         final String xpath = "//PACKAGE_DEF/DOT/preceding::*";
1166         final RootNode rootNode = getRootNode("InputXpathMapperSingleTopClass.java");
1167         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
1168         final DetailAST expectedPackageDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
1169                 TokenTypes.COMPILATION_UNIT)
1170                 .findFirstToken(TokenTypes.PACKAGE_DEF);
1171         final DetailAST expectedAnnotationsNode = expectedPackageDefNode.getFirstChild();
1172         final DetailAST[] expected = {
1173             rootNode.getUnderlyingNode(),
1174             expectedPackageDefNode,
1175             expectedAnnotationsNode,
1176         };
1177         assertWithMessage("Result nodes differ from expected")
1178                 .that(actual)
1179                 .isEqualTo(expected);
1180     }
1181 
1182     @Test
1183     public void testQueryElementPrecedingLiteralPublic() throws Exception {
1184         final String xpath = "//LITERAL_CLASS/preceding::LITERAL_PUBLIC";
1185         final RootNode rootNode = getRootNode("InputXpathMapperSingleTopClass.java");
1186         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
1187         final DetailAST expectedLiteralPublicNode = getSiblingByType(rootNode.getUnderlyingNode(),
1188                 TokenTypes.COMPILATION_UNIT)
1189                 .findFirstToken(TokenTypes.CLASS_DEF)
1190                 .getFirstChild()
1191                 .getFirstChild();
1192         final DetailAST[] expected = {expectedLiteralPublicNode};
1193         assertWithMessage("Result nodes differ from expected")
1194                 .that(actual)
1195                 .isEqualTo(expected);
1196     }
1197 
1198     @Test
1199     public void testTextBlockByItsValue() throws Exception {
1200         final String xpath = "//TEXT_BLOCK_LITERAL_BEGIN[./TEXT_BLOCK_CONTENT"
1201                 + "[@text='\\n        &1line\\n        >2line\\n        <3line\\n        ']]";
1202         final RootNode rootNode = getRootNode("InputXpathMapperTextBlock.java");
1203         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
1204         final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
1205                 TokenTypes.COMPILATION_UNIT)
1206                 .findFirstToken(TokenTypes.CLASS_DEF)
1207                 .findFirstToken(TokenTypes.OBJBLOCK)
1208                 .findFirstToken(TokenTypes.VARIABLE_DEF)
1209                 .findFirstToken(TokenTypes.ASSIGN)
1210                 .findFirstToken(TokenTypes.EXPR)
1211                 .findFirstToken(TokenTypes.TEXT_BLOCK_LITERAL_BEGIN);
1212         final DetailAST[] expected = {expectedVariableDefNode};
1213         assertWithMessage("Result nodes differ from expected")
1214                 .that(actual)
1215                 .isEqualTo(expected);
1216     }
1217 
1218     @Test
1219     public void testQuerySingleLineCommentByCommentContent() throws Exception {
1220         final String xpath = "//SINGLE_LINE_COMMENT[./COMMENT_CONTENT[@text=' some comment\\n']]";
1221         final RootNode rootNode = getRootNodeWithComments("InputXpathMapperSingleLineComment.java");
1222         final DetailAST[] actual = convertToArray(getXpathItems(xpath, rootNode));
1223         final DetailAST expectedVariableDefNode = getSiblingByType(rootNode.getUnderlyingNode(),
1224                 TokenTypes.COMPILATION_UNIT)
1225                 .findFirstToken(TokenTypes.CLASS_DEF)
1226                 .findFirstToken(TokenTypes.OBJBLOCK)
1227                 .findFirstToken(TokenTypes.SINGLE_LINE_COMMENT);
1228         final DetailAST[] expected = {expectedVariableDefNode};
1229         assertWithMessage("Result nodes differ from expected")
1230                 .that(actual)
1231                 .isEqualTo(expected);
1232     }
1233 
1234     @Test
1235     public void testManyNestedNodes() throws Exception {
1236         final String xpath = "//STRING_LITERAL";
1237         final RootNode rootNode = getRootNode("InputXpathMapperStringConcat.java");
1238         final List<NodeInfo> actual = getXpathItems(xpath, rootNode);
1239         assertWithMessage("Number of result nodes differ from expected")
1240                 .that(actual.size())
1241                 .isEqualTo(39299);
1242     }
1243 
1244     private RootNode getRootNode(String fileName) throws Exception {
1245         final File file = new File(getPath(fileName));
1246         final DetailAST rootAst = JavaParser.parseFile(file, JavaParser.Options.WITHOUT_COMMENTS);
1247         return new RootNode(rootAst);
1248     }
1249 
1250     private RootNode getRootNodeWithComments(String fileName) throws Exception {
1251         final File file = new File(getPath(fileName));
1252         final DetailAST rootAst = JavaParser.parseFile(file, JavaParser.Options.WITH_COMMENTS);
1253         return new RootNode(rootAst);
1254     }
1255 
1256     private static DetailAST[] convertToArray(List<NodeInfo> nodes) {
1257         final DetailAST[] result = new DetailAST[nodes.size()];
1258         for (int i = 0; i < nodes.size(); i++) {
1259             final ElementNode abstractNode = (ElementNode) nodes.get(i);
1260             result[i] = abstractNode.getUnderlyingNode();
1261         }
1262         return result;
1263     }
1264 
1265     private static DetailAST getSiblingByType(DetailAST node, int type) {
1266         DetailAST returnValue = null;
1267         for (DetailAST ast = node; ast != null; ast = ast.getNextSibling()) {
1268             if (ast.getType() == type) {
1269                 returnValue = ast;
1270                 break;
1271             }
1272         }
1273         return returnValue;
1274     }
1275 
1276 }