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