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;
21  
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Optional;
27  import java.util.Queue;
28  import java.util.concurrent.ConcurrentLinkedQueue;
29  import java.util.stream.Collectors;
30  
31  import org.antlr.v4.runtime.BufferedTokenStream;
32  import org.antlr.v4.runtime.CommonTokenStream;
33  import org.antlr.v4.runtime.ParserRuleContext;
34  import org.antlr.v4.runtime.Token;
35  import org.antlr.v4.runtime.tree.ParseTree;
36  import org.antlr.v4.runtime.tree.TerminalNode;
37  
38  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
39  import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageLexer;
40  import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser;
41  import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParserBaseVisitor;
42  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
43  
44  /**
45   * Visitor class used to build Checkstyle's Java AST from the parse tree produced by
46   * {@link JavaLanguageParser}. In each {@code visit...} method, we visit the children of a node
47   * (which correspond to subrules) or create terminal nodes (tokens), and return a subtree as a
48   * result.
49   *
50   * <p>Example:</p>
51   *
52   * <p>The following package declaration:</p>
53   * <pre>
54   * package com.puppycrawl.tools.checkstyle;
55   * </pre>
56   *
57   * <p>
58   * Will be parsed by the {@code packageDeclaration} rule from {@code JavaLanguageParser.g4}:
59   * </p>
60   * <pre>
61   * packageDeclaration
62   *     : annotations[true] LITERAL_PACKAGE qualifiedName SEMI
63   *     ;
64   * </pre>
65   *
66   * <p>
67   * We override the {@code visitPackageDeclaration} method generated by ANTLR in
68   * {@link JavaLanguageParser} at
69   * {@link JavaAstVisitor#visitPackageDeclaration(JavaLanguageParser.PackageDeclarationContext)}
70   * to create a subtree based on the subrules and tokens found in the {@code packageDeclaration}
71   * subrule accordingly, thus producing the following AST:
72   * </p>
73   * <pre>
74   * PACKAGE_DEF -&gt; package
75   * |--ANNOTATIONS -&gt; ANNOTATIONS
76   * |--DOT -&gt; .
77   * |   |--DOT -&gt; .
78   * |   |   |--DOT -&gt; .
79   * |   |   |   |--IDENT -&gt; com
80   * |   |   |   `--IDENT -&gt; puppycrawl
81   * |   |   `--IDENT -&gt; tools
82   * |   `--IDENT -&gt; checkstyle
83   * `--SEMI -&gt; ;
84   * </pre>
85   * <p>
86   * See <a href="https://github.com/checkstyle/checkstyle/pull/10434">#10434</a>
87   * for a good example of how
88   * to make changes to Checkstyle's grammar and AST.
89   * </p>
90   * <p>
91   * The order of {@code visit...} methods in {@code JavaAstVisitor.java} and production rules in
92   * {@code JavaLanguageParser.g4} should be consistent to ease maintenance.
93   * </p>
94   */
95  public final class JavaAstVisitor extends JavaLanguageParserBaseVisitor<DetailAstImpl> {
96  
97      /** String representation of the left shift operator. */
98      private static final String LEFT_SHIFT = "<<";
99  
100     /** String representation of the unsigned right shift operator. */
101     private static final String UNSIGNED_RIGHT_SHIFT = ">>>";
102 
103     /** String representation of the right shift operator. */
104     private static final String RIGHT_SHIFT = ">>";
105 
106     /**
107      * The tokens here are technically expressions, but should
108      * not return an EXPR token as their root.
109      */
110     private static final int[] EXPRESSIONS_WITH_NO_EXPR_ROOT = {
111         TokenTypes.CTOR_CALL,
112         TokenTypes.SUPER_CTOR_CALL,
113         TokenTypes.LAMBDA,
114     };
115 
116     /** Token stream to check for hidden tokens. */
117     private final BufferedTokenStream tokens;
118 
119     /**
120      * Constructs a JavaAstVisitor with given token stream.
121      *
122      * @param tokenStream the token stream to check for hidden tokens
123      */
124     public JavaAstVisitor(CommonTokenStream tokenStream) {
125         tokens = tokenStream;
126     }
127 
128     @Override
129     public DetailAstImpl visitCompilationUnit(JavaLanguageParser.CompilationUnitContext ctx) {
130         final DetailAstImpl compilationUnit;
131         // 'EOF' token is always present; therefore if we only have one child, we have an empty file
132         final boolean isEmptyFile = ctx.children.size() == 1;
133         if (isEmptyFile) {
134             compilationUnit = null;
135         }
136         else {
137             compilationUnit = createImaginary(TokenTypes.COMPILATION_UNIT);
138             // last child is 'EOF', we do not include this token in AST
139             processChildren(compilationUnit, ctx.children.subList(0, ctx.children.size() - 1));
140         }
141         return compilationUnit;
142     }
143 
144     @Override
145     public DetailAstImpl visitPackageDeclaration(
146             JavaLanguageParser.PackageDeclarationContext ctx) {
147         final DetailAstImpl packageDeclaration =
148                 create(TokenTypes.PACKAGE_DEF, (Token) ctx.LITERAL_PACKAGE().getPayload());
149         packageDeclaration.addChild(visit(ctx.annotations()));
150         packageDeclaration.addChild(visit(ctx.qualifiedName()));
151         packageDeclaration.addChild(create(ctx.SEMI()));
152         return packageDeclaration;
153     }
154 
155     @Override
156     public DetailAstImpl visitImportDec(JavaLanguageParser.ImportDecContext ctx) {
157         final DetailAstImpl importRoot = create(ctx.start);
158 
159         // Static import
160         final TerminalNode literalStaticNode = ctx.LITERAL_STATIC();
161         if (literalStaticNode != null) {
162             importRoot.setType(TokenTypes.STATIC_IMPORT);
163             importRoot.addChild(create(literalStaticNode));
164         }
165 
166         // Handle star imports
167         final boolean isStarImport = ctx.STAR() != null;
168         if (isStarImport) {
169             final DetailAstImpl dot = create(ctx.DOT());
170             dot.addChild(visit(ctx.qualifiedName()));
171             dot.addChild(create(ctx.STAR()));
172             importRoot.addChild(dot);
173         }
174         else {
175             importRoot.addChild(visit(ctx.qualifiedName()));
176         }
177 
178         importRoot.addChild(create(ctx.SEMI()));
179         return importRoot;
180     }
181 
182     @Override
183     public DetailAstImpl visitSingleSemiImport(JavaLanguageParser.SingleSemiImportContext ctx) {
184         return create(ctx.SEMI());
185     }
186 
187     @Override
188     public DetailAstImpl visitTypeDeclaration(JavaLanguageParser.TypeDeclarationContext ctx) {
189         final DetailAstImpl typeDeclaration;
190         if (ctx.type == null) {
191             typeDeclaration = create(ctx.semi.get(0));
192             ctx.semi.subList(1, ctx.semi.size())
193                     .forEach(semi -> addLastSibling(typeDeclaration, create(semi)));
194         }
195         else {
196             typeDeclaration = visit(ctx.type);
197         }
198         return typeDeclaration;
199     }
200 
201     @Override
202     public DetailAstImpl visitModifier(JavaLanguageParser.ModifierContext ctx) {
203         return flattenedTree(ctx);
204     }
205 
206     @Override
207     public DetailAstImpl visitVariableModifier(JavaLanguageParser.VariableModifierContext ctx) {
208         return flattenedTree(ctx);
209     }
210 
211     @Override
212     public DetailAstImpl visitClassDeclaration(JavaLanguageParser.ClassDeclarationContext ctx) {
213         return createTypeDeclaration(ctx, TokenTypes.CLASS_DEF, ctx.mods);
214     }
215 
216     @Override
217     public DetailAstImpl visitRecordDeclaration(JavaLanguageParser.RecordDeclarationContext ctx) {
218         return createTypeDeclaration(ctx, TokenTypes.RECORD_DEF, ctx.mods);
219     }
220 
221     @Override
222     public DetailAstImpl visitRecordComponentsList(
223             JavaLanguageParser.RecordComponentsListContext ctx) {
224         final DetailAstImpl lparen = create(ctx.LPAREN());
225 
226         // We make a "RECORD_COMPONENTS" node whether components exist or not
227         if (ctx.recordComponents() == null) {
228             addLastSibling(lparen, createImaginary(TokenTypes.RECORD_COMPONENTS));
229         }
230         else {
231             addLastSibling(lparen, visit(ctx.recordComponents()));
232         }
233         addLastSibling(lparen, create(ctx.RPAREN()));
234         return lparen;
235     }
236 
237     @Override
238     public DetailAstImpl visitRecordComponents(JavaLanguageParser.RecordComponentsContext ctx) {
239         final DetailAstImpl recordComponents = createImaginary(TokenTypes.RECORD_COMPONENTS);
240         processChildren(recordComponents, ctx.children);
241         return recordComponents;
242     }
243 
244     @Override
245     public DetailAstImpl visitRecordComponent(JavaLanguageParser.RecordComponentContext ctx) {
246         final DetailAstImpl recordComponent = createImaginary(TokenTypes.RECORD_COMPONENT_DEF);
247         processChildren(recordComponent, ctx.children);
248         return recordComponent;
249     }
250 
251     @Override
252     public DetailAstImpl visitLastRecordComponent(
253             JavaLanguageParser.LastRecordComponentContext ctx) {
254         final DetailAstImpl recordComponent = createImaginary(TokenTypes.RECORD_COMPONENT_DEF);
255         processChildren(recordComponent, ctx.children);
256         return recordComponent;
257     }
258 
259     @Override
260     public DetailAstImpl visitRecordBody(JavaLanguageParser.RecordBodyContext ctx) {
261         final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK);
262         processChildren(objBlock, ctx.children);
263         return objBlock;
264     }
265 
266     @Override
267     public DetailAstImpl visitCompactConstructorDeclaration(
268             JavaLanguageParser.CompactConstructorDeclarationContext ctx) {
269         final DetailAstImpl compactConstructor = createImaginary(TokenTypes.COMPACT_CTOR_DEF);
270         compactConstructor.addChild(createModifiers(ctx.mods));
271         compactConstructor.addChild(visit(ctx.id()));
272         compactConstructor.addChild(visit(ctx.constructorBlock()));
273         return compactConstructor;
274     }
275 
276     @Override
277     public DetailAstImpl visitClassExtends(JavaLanguageParser.ClassExtendsContext ctx) {
278         final DetailAstImpl classExtends = create(ctx.EXTENDS_CLAUSE());
279         classExtends.addChild(visit(ctx.type));
280         return classExtends;
281     }
282 
283     @Override
284     public DetailAstImpl visitImplementsClause(JavaLanguageParser.ImplementsClauseContext ctx) {
285         final DetailAstImpl classImplements = create(TokenTypes.IMPLEMENTS_CLAUSE,
286                 (Token) ctx.LITERAL_IMPLEMENTS().getPayload());
287         classImplements.addChild(visit(ctx.typeList()));
288         return classImplements;
289     }
290 
291     @Override
292     public DetailAstImpl visitTypeParameters(JavaLanguageParser.TypeParametersContext ctx) {
293         final DetailAstImpl typeParameters = createImaginary(TokenTypes.TYPE_PARAMETERS);
294         typeParameters.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload()));
295         // Exclude '<' and '>'
296         processChildren(typeParameters, ctx.children.subList(1, ctx.children.size() - 1));
297         typeParameters.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload()));
298         return typeParameters;
299     }
300 
301     @Override
302     public DetailAstImpl visitTypeParameter(JavaLanguageParser.TypeParameterContext ctx) {
303         final DetailAstImpl typeParameter = createImaginary(TokenTypes.TYPE_PARAMETER);
304         processChildren(typeParameter, ctx.children);
305         return typeParameter;
306     }
307 
308     @Override
309     public DetailAstImpl visitTypeUpperBounds(JavaLanguageParser.TypeUpperBoundsContext ctx) {
310         // In this case, we call 'extends` TYPE_UPPER_BOUNDS
311         final DetailAstImpl typeUpperBounds = create(TokenTypes.TYPE_UPPER_BOUNDS,
312                 (Token) ctx.EXTENDS_CLAUSE().getPayload());
313         // 'extends' is child[0]
314         processChildren(typeUpperBounds, ctx.children.subList(1, ctx.children.size()));
315         return typeUpperBounds;
316     }
317 
318     @Override
319     public DetailAstImpl visitTypeBound(JavaLanguageParser.TypeBoundContext ctx) {
320         final DetailAstImpl typeBoundType = visit(ctx.typeBoundType(0));
321         final Iterator<JavaLanguageParser.TypeBoundTypeContext> typeBoundTypeIterator =
322                 ctx.typeBoundType().listIterator(1);
323         ctx.BAND().forEach(band -> {
324             addLastSibling(typeBoundType, create(TokenTypes.TYPE_EXTENSION_AND,
325                                 (Token) band.getPayload()));
326             addLastSibling(typeBoundType, visit(typeBoundTypeIterator.next()));
327         });
328         return typeBoundType;
329     }
330 
331     @Override
332     public DetailAstImpl visitTypeBoundType(JavaLanguageParser.TypeBoundTypeContext ctx) {
333         return flattenedTree(ctx);
334     }
335 
336     @Override
337     public DetailAstImpl visitEnumDeclaration(JavaLanguageParser.EnumDeclarationContext ctx) {
338         return createTypeDeclaration(ctx, TokenTypes.ENUM_DEF, ctx.mods);
339     }
340 
341     @Override
342     public DetailAstImpl visitEnumBody(JavaLanguageParser.EnumBodyContext ctx) {
343         final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK);
344         processChildren(objBlock, ctx.children);
345         return objBlock;
346     }
347 
348     @Override
349     public DetailAstImpl visitEnumConstants(JavaLanguageParser.EnumConstantsContext ctx) {
350         return flattenedTree(ctx);
351     }
352 
353     @Override
354     public DetailAstImpl visitEnumConstant(JavaLanguageParser.EnumConstantContext ctx) {
355         final DetailAstImpl enumConstant =
356                 createImaginary(TokenTypes.ENUM_CONSTANT_DEF);
357         processChildren(enumConstant, ctx.children);
358         return enumConstant;
359     }
360 
361     @Override
362     public DetailAstImpl visitEnumBodyDeclarations(
363             JavaLanguageParser.EnumBodyDeclarationsContext ctx) {
364         return flattenedTree(ctx);
365     }
366 
367     @Override
368     public DetailAstImpl visitInterfaceDeclaration(
369             JavaLanguageParser.InterfaceDeclarationContext ctx) {
370         return createTypeDeclaration(ctx, TokenTypes.INTERFACE_DEF, ctx.mods);
371     }
372 
373     @Override
374     public DetailAstImpl visitInterfaceExtends(JavaLanguageParser.InterfaceExtendsContext ctx) {
375         final DetailAstImpl interfaceExtends = create(ctx.EXTENDS_CLAUSE());
376         interfaceExtends.addChild(visit(ctx.typeList()));
377         return interfaceExtends;
378     }
379 
380     @Override
381     public DetailAstImpl visitClassBody(JavaLanguageParser.ClassBodyContext ctx) {
382         final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK);
383         processChildren(objBlock, ctx.children);
384         return objBlock;
385     }
386 
387     @Override
388     public DetailAstImpl visitInterfaceBody(JavaLanguageParser.InterfaceBodyContext ctx) {
389         final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK);
390         processChildren(objBlock, ctx.children);
391         return objBlock;
392     }
393 
394     @Override
395     public DetailAstImpl visitEmptyClass(JavaLanguageParser.EmptyClassContext ctx) {
396         return flattenedTree(ctx);
397     }
398 
399     @Override
400     public DetailAstImpl visitClassBlock(JavaLanguageParser.ClassBlockContext ctx) {
401         final DetailAstImpl classBlock;
402         if (ctx.LITERAL_STATIC() == null) {
403             // We call it an INSTANCE_INIT
404             classBlock = createImaginary(TokenTypes.INSTANCE_INIT);
405         }
406         else {
407             classBlock = create(TokenTypes.STATIC_INIT, (Token) ctx.LITERAL_STATIC().getPayload());
408             classBlock.setText(TokenUtil.getTokenName(TokenTypes.STATIC_INIT));
409         }
410         classBlock.addChild(visit(ctx.block()));
411         return classBlock;
412     }
413 
414     @Override
415     public DetailAstImpl visitMethodDeclaration(JavaLanguageParser.MethodDeclarationContext ctx) {
416         final DetailAstImpl methodDef = createImaginary(TokenTypes.METHOD_DEF);
417         methodDef.addChild(createModifiers(ctx.mods));
418 
419         // Process all children except C style array declarators
420         processChildren(methodDef, ctx.children.stream()
421                 .filter(child -> !(child instanceof JavaLanguageParser.ArrayDeclaratorContext))
422                 .collect(Collectors.toUnmodifiableList()));
423 
424         // We add C style array declarator brackets to TYPE ast
425         final DetailAstImpl typeAst = (DetailAstImpl) methodDef.findFirstToken(TokenTypes.TYPE);
426         ctx.cStyleArrDec.forEach(child -> typeAst.addChild(visit(child)));
427 
428         return methodDef;
429     }
430 
431     @Override
432     public DetailAstImpl visitMethodBody(JavaLanguageParser.MethodBodyContext ctx) {
433         return flattenedTree(ctx);
434     }
435 
436     @Override
437     public DetailAstImpl visitThrowsList(JavaLanguageParser.ThrowsListContext ctx) {
438         final DetailAstImpl throwsRoot = create(ctx.LITERAL_THROWS());
439         throwsRoot.addChild(visit(ctx.qualifiedNameList()));
440         return throwsRoot;
441     }
442 
443     @Override
444     public DetailAstImpl visitConstructorDeclaration(
445             JavaLanguageParser.ConstructorDeclarationContext ctx) {
446         final DetailAstImpl constructorDeclaration = createImaginary(TokenTypes.CTOR_DEF);
447         constructorDeclaration.addChild(createModifiers(ctx.mods));
448         processChildren(constructorDeclaration, ctx.children);
449         return constructorDeclaration;
450     }
451 
452     @Override
453     public DetailAstImpl visitFieldDeclaration(JavaLanguageParser.FieldDeclarationContext ctx) {
454         final DetailAstImpl dummyNode = new DetailAstImpl();
455         // Since the TYPE AST is built by visitVariableDeclarator(), we skip it here (child [0])
456         // We also append the SEMI token to the first child [size() - 1],
457         // until https://github.com/checkstyle/checkstyle/issues/3151
458         processChildren(dummyNode, ctx.children.subList(1, ctx.children.size() - 1));
459         dummyNode.getFirstChild().addChild(create(ctx.SEMI()));
460         return dummyNode.getFirstChild();
461     }
462 
463     @Override
464     public DetailAstImpl visitInterfaceBodyDeclaration(
465             JavaLanguageParser.InterfaceBodyDeclarationContext ctx) {
466         final DetailAstImpl returnTree;
467         if (ctx.SEMI() == null) {
468             returnTree = visit(ctx.interfaceMemberDeclaration());
469         }
470         else {
471             returnTree = create(ctx.SEMI());
472         }
473         return returnTree;
474     }
475 
476     @Override
477     public DetailAstImpl visitInterfaceMethodDeclaration(
478             JavaLanguageParser.InterfaceMethodDeclarationContext ctx) {
479         final DetailAstImpl methodDef = createImaginary(TokenTypes.METHOD_DEF);
480         methodDef.addChild(createModifiers(ctx.mods));
481 
482         // Process all children except C style array declarators and modifiers
483         final List<ParseTree> children = ctx.children
484                 .stream()
485                 .filter(child -> !(child instanceof JavaLanguageParser.ArrayDeclaratorContext))
486                 .collect(Collectors.toUnmodifiableList());
487         processChildren(methodDef, children);
488 
489         // We add C style array declarator brackets to TYPE ast
490         final DetailAstImpl typeAst = (DetailAstImpl) methodDef.findFirstToken(TokenTypes.TYPE);
491         ctx.cStyleArrDec.forEach(child -> typeAst.addChild(visit(child)));
492 
493         return methodDef;
494     }
495 
496     @Override
497     public DetailAstImpl visitVariableDeclarators(
498             JavaLanguageParser.VariableDeclaratorsContext ctx) {
499         return flattenedTree(ctx);
500     }
501 
502     @Override
503     public DetailAstImpl visitVariableDeclarator(
504             JavaLanguageParser.VariableDeclaratorContext ctx) {
505         final DetailAstImpl variableDef = createImaginary(TokenTypes.VARIABLE_DEF);
506         variableDef.addChild(createModifiers(ctx.mods));
507 
508         final DetailAstImpl type = visit(ctx.type);
509         variableDef.addChild(type);
510         variableDef.addChild(visit(ctx.id()));
511 
512         // Add C style array declarator brackets to TYPE ast
513         ctx.arrayDeclarator().forEach(child -> type.addChild(visit(child)));
514 
515         // If this is an assignment statement, ASSIGN becomes the parent of EXPR
516         final TerminalNode assignNode = ctx.ASSIGN();
517         if (assignNode != null) {
518             final DetailAstImpl assign = create(assignNode);
519             variableDef.addChild(assign);
520             assign.addChild(visit(ctx.variableInitializer()));
521         }
522         return variableDef;
523     }
524 
525     @Override
526     public DetailAstImpl visitVariableDeclaratorId(
527             JavaLanguageParser.VariableDeclaratorIdContext ctx) {
528         final DetailAstImpl root = new DetailAstImpl();
529         root.addChild(createModifiers(ctx.mods));
530         final DetailAstImpl type = visit(ctx.type);
531         root.addChild(type);
532 
533         final DetailAstImpl declaratorId;
534         if (ctx.LITERAL_THIS() == null) {
535             declaratorId = visit(ctx.qualifiedName());
536         }
537         else if (ctx.DOT() == null) {
538             declaratorId = create(ctx.LITERAL_THIS());
539         }
540         else {
541             declaratorId = create(ctx.DOT());
542             declaratorId.addChild(visit(ctx.qualifiedName()));
543             declaratorId.addChild(create(ctx.LITERAL_THIS()));
544         }
545 
546         root.addChild(declaratorId);
547         ctx.arrayDeclarator().forEach(child -> type.addChild(visit(child)));
548 
549         return root.getFirstChild();
550     }
551 
552     @Override
553     public DetailAstImpl visitArrayInitializer(JavaLanguageParser.ArrayInitializerContext ctx) {
554         final DetailAstImpl arrayInitializer = create(TokenTypes.ARRAY_INIT, ctx.start);
555         // ARRAY_INIT was child[0]
556         processChildren(arrayInitializer, ctx.children.subList(1, ctx.children.size()));
557         return arrayInitializer;
558     }
559 
560     @Override
561     public DetailAstImpl visitClassOrInterfaceType(
562             JavaLanguageParser.ClassOrInterfaceTypeContext ctx) {
563         final DetailAstPair currentAST = new DetailAstPair();
564         DetailAstPair.addAstChild(currentAST, visit(ctx.id()));
565         DetailAstPair.addAstChild(currentAST, visit(ctx.typeArguments()));
566 
567         // This is how we build the annotations/ qualified name/ type parameters tree
568         for (ParserRuleContext extendedContext : ctx.extended) {
569             final DetailAstImpl dot = create(extendedContext.start);
570             DetailAstPair.makeAstRoot(currentAST, dot);
571             extendedContext.children
572                 .forEach(child -> DetailAstPair.addAstChild(currentAST, visit(child)));
573         }
574 
575         // Create imaginary 'TYPE' parent if specified
576         final DetailAstImpl returnTree;
577         if (ctx.createImaginaryNode) {
578             returnTree = createImaginary(TokenTypes.TYPE);
579             returnTree.addChild(currentAST.root);
580         }
581         else {
582             returnTree = currentAST.root;
583         }
584         return returnTree;
585     }
586 
587     @Override
588     public DetailAstImpl visitSimpleTypeArgument(
589             JavaLanguageParser.SimpleTypeArgumentContext ctx) {
590         final DetailAstImpl typeArgument =
591                 createImaginary(TokenTypes.TYPE_ARGUMENT);
592         typeArgument.addChild(visit(ctx.typeType()));
593         return typeArgument;
594     }
595 
596     @Override
597     public DetailAstImpl visitWildCardTypeArgument(
598             JavaLanguageParser.WildCardTypeArgumentContext ctx) {
599         final DetailAstImpl typeArgument = createImaginary(TokenTypes.TYPE_ARGUMENT);
600         typeArgument.addChild(visit(ctx.annotations()));
601         typeArgument.addChild(create(TokenTypes.WILDCARD_TYPE,
602                 (Token) ctx.QUESTION().getPayload()));
603 
604         if (ctx.upperBound != null) {
605             final DetailAstImpl upperBound = create(TokenTypes.TYPE_UPPER_BOUNDS, ctx.upperBound);
606             upperBound.addChild(visit(ctx.typeType()));
607             typeArgument.addChild(upperBound);
608         }
609         else if (ctx.lowerBound != null) {
610             final DetailAstImpl lowerBound = create(TokenTypes.TYPE_LOWER_BOUNDS, ctx.lowerBound);
611             lowerBound.addChild(visit(ctx.typeType()));
612             typeArgument.addChild(lowerBound);
613         }
614 
615         return typeArgument;
616     }
617 
618     @Override
619     public DetailAstImpl visitQualifiedNameList(JavaLanguageParser.QualifiedNameListContext ctx) {
620         return flattenedTree(ctx);
621     }
622 
623     @Override
624     public DetailAstImpl visitFormalParameters(JavaLanguageParser.FormalParametersContext ctx) {
625         final DetailAstImpl lparen = create(ctx.LPAREN());
626 
627         // We make a "PARAMETERS" node whether parameters exist or not
628         if (ctx.formalParameterList() == null) {
629             addLastSibling(lparen, createImaginary(TokenTypes.PARAMETERS));
630         }
631         else {
632             addLastSibling(lparen, visit(ctx.formalParameterList()));
633         }
634         addLastSibling(lparen, create(ctx.RPAREN()));
635         return lparen;
636     }
637 
638     @Override
639     public DetailAstImpl visitFormalParameterList(
640             JavaLanguageParser.FormalParameterListContext ctx) {
641         final DetailAstImpl parameters = createImaginary(TokenTypes.PARAMETERS);
642         processChildren(parameters, ctx.children);
643         return parameters;
644     }
645 
646     @Override
647     public DetailAstImpl visitFormalParameter(JavaLanguageParser.FormalParameterContext ctx) {
648         final DetailAstImpl variableDeclaratorId =
649                 visitVariableDeclaratorId(ctx.variableDeclaratorId());
650         final DetailAstImpl parameterDef = createImaginary(TokenTypes.PARAMETER_DEF);
651         parameterDef.addChild(variableDeclaratorId);
652         return parameterDef;
653     }
654 
655     @Override
656     public DetailAstImpl visitLastFormalParameter(
657             JavaLanguageParser.LastFormalParameterContext ctx) {
658         final DetailAstImpl parameterDef =
659                 createImaginary(TokenTypes.PARAMETER_DEF);
660         parameterDef.addChild(visit(ctx.variableDeclaratorId()));
661         final DetailAstImpl ident = (DetailAstImpl) parameterDef.findFirstToken(TokenTypes.IDENT);
662         ident.addPreviousSibling(create(ctx.ELLIPSIS()));
663         // We attach annotations on ellipses in varargs to the 'TYPE' ast
664         final DetailAstImpl type = (DetailAstImpl) parameterDef.findFirstToken(TokenTypes.TYPE);
665         type.addChild(visit(ctx.annotations()));
666         return parameterDef;
667     }
668 
669     @Override
670     public DetailAstImpl visitQualifiedName(JavaLanguageParser.QualifiedNameContext ctx) {
671         final DetailAstImpl ast = visit(ctx.id());
672         final DetailAstPair currentAst = new DetailAstPair();
673         DetailAstPair.addAstChild(currentAst, ast);
674 
675         for (ParserRuleContext extendedContext : ctx.extended) {
676             final DetailAstImpl dot = create(extendedContext.start);
677             DetailAstPair.makeAstRoot(currentAst, dot);
678             final List<ParseTree> childList = extendedContext
679                     .children.subList(1, extendedContext.children.size());
680             processChildren(dot, childList);
681         }
682         return currentAst.getRoot();
683     }
684 
685     @Override
686     public DetailAstImpl visitLiteral(JavaLanguageParser.LiteralContext ctx) {
687         return flattenedTree(ctx);
688     }
689 
690     @Override
691     public DetailAstImpl visitIntegerLiteral(JavaLanguageParser.IntegerLiteralContext ctx) {
692         final int[] longTypes = {
693             JavaLanguageLexer.DECIMAL_LITERAL_LONG,
694             JavaLanguageLexer.HEX_LITERAL_LONG,
695             JavaLanguageLexer.OCT_LITERAL_LONG,
696             JavaLanguageLexer.BINARY_LITERAL_LONG,
697         };
698 
699         final int tokenType;
700         if (TokenUtil.isOfType(ctx.start.getType(), longTypes)) {
701             tokenType = TokenTypes.NUM_LONG;
702         }
703         else {
704             tokenType = TokenTypes.NUM_INT;
705         }
706 
707         return create(tokenType, ctx.start);
708     }
709 
710     @Override
711     public DetailAstImpl visitFloatLiteral(JavaLanguageParser.FloatLiteralContext ctx) {
712         final DetailAstImpl floatLiteral;
713         if (TokenUtil.isOfType(ctx.start.getType(),
714                 JavaLanguageLexer.DOUBLE_LITERAL, JavaLanguageLexer.HEX_DOUBLE_LITERAL)) {
715             floatLiteral = create(TokenTypes.NUM_DOUBLE, ctx.start);
716         }
717         else {
718             floatLiteral = create(TokenTypes.NUM_FLOAT, ctx.start);
719         }
720         return floatLiteral;
721     }
722 
723     @Override
724     public DetailAstImpl visitTextBlockLiteral(JavaLanguageParser.TextBlockLiteralContext ctx) {
725         final DetailAstImpl textBlockLiteralBegin = create(ctx.TEXT_BLOCK_LITERAL_BEGIN());
726         textBlockLiteralBegin.addChild(create(ctx.TEXT_BLOCK_CONTENT()));
727         textBlockLiteralBegin.addChild(create(ctx.TEXT_BLOCK_LITERAL_END()));
728         return textBlockLiteralBegin;
729     }
730 
731     @Override
732     public DetailAstImpl visitAnnotations(JavaLanguageParser.AnnotationsContext ctx) {
733         final DetailAstImpl annotations;
734 
735         if (!ctx.createImaginaryNode && ctx.anno.isEmpty()) {
736             // There are no annotations, and we don't want to create the empty node
737             annotations = null;
738         }
739         else {
740             // There are annotations, or we just want the empty node
741             annotations = createImaginary(TokenTypes.ANNOTATIONS);
742             processChildren(annotations, ctx.anno);
743         }
744 
745         return annotations;
746     }
747 
748     @Override
749     public DetailAstImpl visitAnnotation(JavaLanguageParser.AnnotationContext ctx) {
750         final DetailAstImpl annotation = createImaginary(TokenTypes.ANNOTATION);
751         processChildren(annotation, ctx.children);
752         return annotation;
753     }
754 
755     @Override
756     public DetailAstImpl visitElementValuePairs(JavaLanguageParser.ElementValuePairsContext ctx) {
757         return flattenedTree(ctx);
758     }
759 
760     @Override
761     public DetailAstImpl visitElementValuePair(JavaLanguageParser.ElementValuePairContext ctx) {
762         final DetailAstImpl elementValuePair =
763                 createImaginary(TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR);
764         processChildren(elementValuePair, ctx.children);
765         return elementValuePair;
766     }
767 
768     @Override
769     public DetailAstImpl visitElementValue(JavaLanguageParser.ElementValueContext ctx) {
770         return flattenedTree(ctx);
771     }
772 
773     @Override
774     public DetailAstImpl visitElementValueArrayInitializer(
775             JavaLanguageParser.ElementValueArrayInitializerContext ctx) {
776         final DetailAstImpl arrayInit =
777                 create(TokenTypes.ANNOTATION_ARRAY_INIT, (Token) ctx.LCURLY().getPayload());
778         processChildren(arrayInit, ctx.children.subList(1, ctx.children.size()));
779         return arrayInit;
780     }
781 
782     @Override
783     public DetailAstImpl visitAnnotationTypeDeclaration(
784             JavaLanguageParser.AnnotationTypeDeclarationContext ctx) {
785         return createTypeDeclaration(ctx, TokenTypes.ANNOTATION_DEF, ctx.mods);
786     }
787 
788     @Override
789     public DetailAstImpl visitAnnotationTypeBody(
790             JavaLanguageParser.AnnotationTypeBodyContext ctx) {
791         final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK);
792         processChildren(objBlock, ctx.children);
793         return objBlock;
794     }
795 
796     @Override
797     public DetailAstImpl visitAnnotationTypeElementDeclaration(
798             JavaLanguageParser.AnnotationTypeElementDeclarationContext ctx) {
799         final DetailAstImpl returnTree;
800         if (ctx.SEMI() == null) {
801             returnTree = visit(ctx.annotationTypeElementRest());
802         }
803         else {
804             returnTree = create(ctx.SEMI());
805         }
806         return returnTree;
807     }
808 
809     @Override
810     public DetailAstImpl visitAnnotationField(JavaLanguageParser.AnnotationFieldContext ctx) {
811         final DetailAstImpl dummyNode = new DetailAstImpl();
812         // Since the TYPE AST is built by visitAnnotationMethodOrConstantRest(), we skip it
813         // here (child [0])
814         processChildren(dummyNode, Collections.singletonList(ctx.children.get(1)));
815         // We also append the SEMI token to the first child [size() - 1],
816         // until https://github.com/checkstyle/checkstyle/issues/3151
817         dummyNode.getFirstChild().addChild(create(ctx.SEMI()));
818         return dummyNode.getFirstChild();
819     }
820 
821     @Override
822     public DetailAstImpl visitAnnotationType(JavaLanguageParser.AnnotationTypeContext ctx) {
823         return flattenedTree(ctx);
824     }
825 
826     @Override
827     public DetailAstImpl visitAnnotationMethodRest(
828             JavaLanguageParser.AnnotationMethodRestContext ctx) {
829         final DetailAstImpl annotationFieldDef =
830                 createImaginary(TokenTypes.ANNOTATION_FIELD_DEF);
831         annotationFieldDef.addChild(createModifiers(ctx.mods));
832         annotationFieldDef.addChild(visit(ctx.type));
833 
834         // Process all children except C style array declarators
835         processChildren(annotationFieldDef, ctx.children.stream()
836                 .filter(child -> !(child instanceof JavaLanguageParser.ArrayDeclaratorContext))
837                 .collect(Collectors.toUnmodifiableList()));
838 
839         // We add C style array declarator brackets to TYPE ast
840         final DetailAstImpl typeAst =
841                 (DetailAstImpl) annotationFieldDef.findFirstToken(TokenTypes.TYPE);
842         ctx.cStyleArrDec.forEach(child -> typeAst.addChild(visit(child)));
843 
844         return annotationFieldDef;
845     }
846 
847     @Override
848     public DetailAstImpl visitDefaultValue(JavaLanguageParser.DefaultValueContext ctx) {
849         final DetailAstImpl defaultValue = create(ctx.LITERAL_DEFAULT());
850         defaultValue.addChild(visit(ctx.elementValue()));
851         return defaultValue;
852     }
853 
854     @Override
855     public DetailAstImpl visitConstructorBlock(JavaLanguageParser.ConstructorBlockContext ctx) {
856         final DetailAstImpl slist = create(TokenTypes.SLIST, ctx.start);
857         // SLIST was child [0]
858         processChildren(slist, ctx.children.subList(1, ctx.children.size()));
859         return slist;
860     }
861 
862     @Override
863     public DetailAstImpl visitExplicitCtorCall(JavaLanguageParser.ExplicitCtorCallContext ctx) {
864         final DetailAstImpl root;
865         if (ctx.LITERAL_THIS() == null) {
866             root = create(TokenTypes.SUPER_CTOR_CALL, (Token) ctx.LITERAL_SUPER().getPayload());
867         }
868         else {
869             root = create(TokenTypes.CTOR_CALL, (Token) ctx.LITERAL_THIS().getPayload());
870         }
871         root.addChild(visit(ctx.typeArguments()));
872         root.addChild(visit(ctx.arguments()));
873         root.addChild(create(ctx.SEMI()));
874         return root;
875     }
876 
877     @Override
878     public DetailAstImpl visitPrimaryCtorCall(JavaLanguageParser.PrimaryCtorCallContext ctx) {
879         final DetailAstImpl primaryCtorCall = create(TokenTypes.SUPER_CTOR_CALL,
880                 (Token) ctx.LITERAL_SUPER().getPayload());
881         // filter 'LITERAL_SUPER'
882         processChildren(primaryCtorCall, ctx.children.stream()
883                    .filter(child -> !child.equals(ctx.LITERAL_SUPER()))
884                    .collect(Collectors.toUnmodifiableList()));
885         return primaryCtorCall;
886     }
887 
888     @Override
889     public DetailAstImpl visitBlock(JavaLanguageParser.BlockContext ctx) {
890         final DetailAstImpl slist = create(TokenTypes.SLIST, ctx.start);
891         // SLIST was child [0]
892         processChildren(slist, ctx.children.subList(1, ctx.children.size()));
893         return slist;
894     }
895 
896     @Override
897     public DetailAstImpl visitLocalVar(JavaLanguageParser.LocalVarContext ctx) {
898         return flattenedTree(ctx);
899     }
900 
901     @Override
902     public DetailAstImpl visitBlockStat(JavaLanguageParser.BlockStatContext ctx) {
903         return flattenedTree(ctx);
904     }
905 
906     @Override
907     public DetailAstImpl visitAssertExp(JavaLanguageParser.AssertExpContext ctx) {
908         final DetailAstImpl assertExp = create(ctx.ASSERT());
909         // child[0] is 'ASSERT'
910         processChildren(assertExp, ctx.children.subList(1, ctx.children.size()));
911         return assertExp;
912     }
913 
914     @Override
915     public DetailAstImpl visitIfStat(JavaLanguageParser.IfStatContext ctx) {
916         final DetailAstImpl ifStat = create(ctx.LITERAL_IF());
917         // child[0] is 'LITERAL_IF'
918         processChildren(ifStat, ctx.children.subList(1, ctx.children.size()));
919         return ifStat;
920     }
921 
922     @Override
923     public DetailAstImpl visitForStat(JavaLanguageParser.ForStatContext ctx) {
924         final DetailAstImpl forInit = create(ctx.start);
925         // child[0] is LITERAL_FOR
926         processChildren(forInit, ctx.children.subList(1, ctx.children.size()));
927         return forInit;
928     }
929 
930     @Override
931     public DetailAstImpl visitWhileStat(JavaLanguageParser.WhileStatContext ctx) {
932         final DetailAstImpl whileStatement = create(ctx.start);
933         // 'LITERAL_WHILE' is child[0]
934         processChildren(whileStatement, ctx.children.subList(1, ctx.children.size()));
935         return whileStatement;
936     }
937 
938     @Override
939     public DetailAstImpl visitDoStat(JavaLanguageParser.DoStatContext ctx) {
940         final DetailAstImpl doStatement = create(ctx.start);
941         // 'LITERAL_DO' is child[0]
942         doStatement.addChild(visit(ctx.statement()));
943         // We make 'LITERAL_WHILE' into 'DO_WHILE'
944         doStatement.addChild(create(TokenTypes.DO_WHILE, (Token) ctx.LITERAL_WHILE().getPayload()));
945         doStatement.addChild(visit(ctx.parExpression()));
946         doStatement.addChild(create(ctx.SEMI()));
947         return doStatement;
948     }
949 
950     @Override
951     public DetailAstImpl visitTryStat(JavaLanguageParser.TryStatContext ctx) {
952         final DetailAstImpl tryStat = create(ctx.start);
953         // child[0] is 'LITERAL_TRY'
954         processChildren(tryStat, ctx.children.subList(1, ctx.children.size()));
955         return tryStat;
956     }
957 
958     @Override
959     public DetailAstImpl visitTryWithResourceStat(
960             JavaLanguageParser.TryWithResourceStatContext ctx) {
961         final DetailAstImpl tryWithResources = create(ctx.LITERAL_TRY());
962         // child[0] is 'LITERAL_TRY'
963         processChildren(tryWithResources, ctx.children.subList(1, ctx.children.size()));
964         return tryWithResources;
965     }
966 
967     @Override
968     public DetailAstImpl visitYieldStat(JavaLanguageParser.YieldStatContext ctx) {
969         final DetailAstImpl yieldParent = create(ctx.LITERAL_YIELD());
970         // LITERAL_YIELD is child[0]
971         processChildren(yieldParent, ctx.children.subList(1, ctx.children.size()));
972         return yieldParent;
973     }
974 
975     @Override
976     public DetailAstImpl visitSyncStat(JavaLanguageParser.SyncStatContext ctx) {
977         final DetailAstImpl syncStatement = create(ctx.start);
978         // child[0] is 'LITERAL_SYNCHRONIZED'
979         processChildren(syncStatement, ctx.children.subList(1, ctx.children.size()));
980         return syncStatement;
981     }
982 
983     @Override
984     public DetailAstImpl visitReturnStat(JavaLanguageParser.ReturnStatContext ctx) {
985         final DetailAstImpl returnStat = create(ctx.LITERAL_RETURN());
986         // child[0] is 'LITERAL_RETURN'
987         processChildren(returnStat, ctx.children.subList(1, ctx.children.size()));
988         return returnStat;
989     }
990 
991     @Override
992     public DetailAstImpl visitThrowStat(JavaLanguageParser.ThrowStatContext ctx) {
993         final DetailAstImpl throwStat = create(ctx.LITERAL_THROW());
994         // child[0] is 'LITERAL_THROW'
995         processChildren(throwStat, ctx.children.subList(1, ctx.children.size()));
996         return throwStat;
997     }
998 
999     @Override
1000     public DetailAstImpl visitBreakStat(JavaLanguageParser.BreakStatContext ctx) {
1001         final DetailAstImpl literalBreak = create(ctx.LITERAL_BREAK());
1002         // child[0] is 'LITERAL_BREAK'
1003         processChildren(literalBreak, ctx.children.subList(1, ctx.children.size()));
1004         return literalBreak;
1005     }
1006 
1007     @Override
1008     public DetailAstImpl visitContinueStat(JavaLanguageParser.ContinueStatContext ctx) {
1009         final DetailAstImpl continueStat = create(ctx.LITERAL_CONTINUE());
1010         // child[0] is 'LITERAL_CONTINUE'
1011         processChildren(continueStat, ctx.children.subList(1, ctx.children.size()));
1012         return continueStat;
1013     }
1014 
1015     @Override
1016     public DetailAstImpl visitEmptyStat(JavaLanguageParser.EmptyStatContext ctx) {
1017         return create(TokenTypes.EMPTY_STAT, ctx.start);
1018     }
1019 
1020     @Override
1021     public DetailAstImpl visitExpStat(JavaLanguageParser.ExpStatContext ctx) {
1022         final DetailAstImpl expStatRoot = visit(ctx.statementExpression);
1023         addLastSibling(expStatRoot, create(ctx.SEMI()));
1024         return expStatRoot;
1025     }
1026 
1027     @Override
1028     public DetailAstImpl visitLabelStat(JavaLanguageParser.LabelStatContext ctx) {
1029         final DetailAstImpl labelStat = create(TokenTypes.LABELED_STAT,
1030                 (Token) ctx.COLON().getPayload());
1031         labelStat.addChild(visit(ctx.id()));
1032         labelStat.addChild(visit(ctx.statement()));
1033         return labelStat;
1034     }
1035 
1036     @Override
1037     public DetailAstImpl visitSwitchExpressionOrStatement(
1038             JavaLanguageParser.SwitchExpressionOrStatementContext ctx) {
1039         final DetailAstImpl switchStat = create(ctx.LITERAL_SWITCH());
1040         switchStat.addChild(visit(ctx.parExpression()));
1041         switchStat.addChild(create(ctx.LCURLY()));
1042         switchStat.addChild(visit(ctx.switchBlock()));
1043         switchStat.addChild(create(ctx.RCURLY()));
1044         return switchStat;
1045     }
1046 
1047     @Override
1048     public DetailAstImpl visitSwitchRules(JavaLanguageParser.SwitchRulesContext ctx) {
1049         final DetailAstImpl dummyRoot = new DetailAstImpl();
1050         ctx.switchLabeledRule().forEach(switchLabeledRuleContext -> {
1051             final DetailAstImpl switchRule = visit(switchLabeledRuleContext);
1052             final DetailAstImpl switchRuleParent = createImaginary(TokenTypes.SWITCH_RULE);
1053             switchRuleParent.addChild(switchRule);
1054             dummyRoot.addChild(switchRuleParent);
1055         });
1056         return dummyRoot.getFirstChild();
1057     }
1058 
1059     @Override
1060     public DetailAstImpl visitSwitchBlocks(JavaLanguageParser.SwitchBlocksContext ctx) {
1061         final DetailAstImpl dummyRoot = new DetailAstImpl();
1062         ctx.groups.forEach(group -> dummyRoot.addChild(visit(group)));
1063 
1064         // Add any empty switch labels to end of statement in one 'CASE_GROUP'
1065         if (!ctx.emptyLabels.isEmpty()) {
1066             final DetailAstImpl emptyLabelParent =
1067                     createImaginary(TokenTypes.CASE_GROUP);
1068             ctx.emptyLabels.forEach(label -> emptyLabelParent.addChild(visit(label)));
1069             dummyRoot.addChild(emptyLabelParent);
1070         }
1071         return dummyRoot.getFirstChild();
1072     }
1073 
1074     @Override
1075     public DetailAstImpl visitSwitchLabeledExpression(
1076             JavaLanguageParser.SwitchLabeledExpressionContext ctx) {
1077         return flattenedTree(ctx);
1078     }
1079 
1080     @Override
1081     public DetailAstImpl visitSwitchLabeledBlock(
1082             JavaLanguageParser.SwitchLabeledBlockContext ctx) {
1083         return flattenedTree(ctx);
1084     }
1085 
1086     @Override
1087     public DetailAstImpl visitSwitchLabeledThrow(
1088             JavaLanguageParser.SwitchLabeledThrowContext ctx) {
1089         final DetailAstImpl switchLabel = visit(ctx.switchLabel());
1090         addLastSibling(switchLabel, create(ctx.LAMBDA()));
1091         final DetailAstImpl literalThrow = create(ctx.LITERAL_THROW());
1092         literalThrow.addChild(visit(ctx.expression()));
1093         literalThrow.addChild(create(ctx.SEMI()));
1094         addLastSibling(switchLabel, literalThrow);
1095         return switchLabel;
1096     }
1097 
1098     @Override
1099     public DetailAstImpl visitElseStat(JavaLanguageParser.ElseStatContext ctx) {
1100         final DetailAstImpl elseStat = create(ctx.LITERAL_ELSE());
1101         // child[0] is 'LITERAL_ELSE'
1102         processChildren(elseStat, ctx.children.subList(1, ctx.children.size()));
1103         return elseStat;
1104     }
1105 
1106     @Override
1107     public DetailAstImpl visitCatchClause(JavaLanguageParser.CatchClauseContext ctx) {
1108         final DetailAstImpl catchClause = create(TokenTypes.LITERAL_CATCH,
1109                 (Token) ctx.LITERAL_CATCH().getPayload());
1110         // 'LITERAL_CATCH' is child[0]
1111         processChildren(catchClause, ctx.children.subList(1, ctx.children.size()));
1112         return catchClause;
1113     }
1114 
1115     @Override
1116     public DetailAstImpl visitCatchParameter(JavaLanguageParser.CatchParameterContext ctx) {
1117         final DetailAstImpl catchParameterDef = createImaginary(TokenTypes.PARAMETER_DEF);
1118         catchParameterDef.addChild(createModifiers(ctx.mods));
1119         // filter mods
1120         processChildren(catchParameterDef, ctx.children.stream()
1121                 .filter(child -> !(child instanceof JavaLanguageParser.VariableModifierContext))
1122                 .collect(Collectors.toUnmodifiableList()));
1123         return catchParameterDef;
1124     }
1125 
1126     @Override
1127     public DetailAstImpl visitCatchType(JavaLanguageParser.CatchTypeContext ctx) {
1128         final DetailAstImpl type = createImaginary(TokenTypes.TYPE);
1129         processChildren(type, ctx.children);
1130         return type;
1131     }
1132 
1133     @Override
1134     public DetailAstImpl visitFinallyBlock(JavaLanguageParser.FinallyBlockContext ctx) {
1135         final DetailAstImpl finallyBlock = create(ctx.LITERAL_FINALLY());
1136         // child[0] is 'LITERAL_FINALLY'
1137         processChildren(finallyBlock, ctx.children.subList(1, ctx.children.size()));
1138         return finallyBlock;
1139     }
1140 
1141     @Override
1142     public DetailAstImpl visitResourceSpecification(
1143             JavaLanguageParser.ResourceSpecificationContext ctx) {
1144         final DetailAstImpl resourceSpecification =
1145                 createImaginary(TokenTypes.RESOURCE_SPECIFICATION);
1146         processChildren(resourceSpecification, ctx.children);
1147         return resourceSpecification;
1148     }
1149 
1150     @Override
1151     public DetailAstImpl visitResources(JavaLanguageParser.ResourcesContext ctx) {
1152         final DetailAstImpl firstResource = visit(ctx.resource(0));
1153         final DetailAstImpl resources = createImaginary(TokenTypes.RESOURCES);
1154         resources.addChild(firstResource);
1155         processChildren(resources, ctx.children.subList(1, ctx.children.size()));
1156         return resources;
1157     }
1158 
1159     @Override
1160     public DetailAstImpl visitResourceDeclaration(
1161             JavaLanguageParser.ResourceDeclarationContext ctx) {
1162         final DetailAstImpl resource = createImaginary(TokenTypes.RESOURCE);
1163         resource.addChild(visit(ctx.variableDeclaratorId()));
1164 
1165         final DetailAstImpl assign = create(ctx.ASSIGN());
1166         resource.addChild(assign);
1167         assign.addChild(visit(ctx.expression()));
1168         return resource;
1169     }
1170 
1171     @Override
1172     public DetailAstImpl visitVariableAccess(JavaLanguageParser.VariableAccessContext ctx) {
1173         final DetailAstImpl resource;
1174         if (ctx.accessList.isEmpty()) {
1175             resource = createImaginary(TokenTypes.RESOURCE);
1176             resource.addChild(visit(ctx.id()));
1177         }
1178         else {
1179             final DetailAstPair currentAst = new DetailAstPair();
1180             ctx.accessList.forEach(fieldAccess -> {
1181                 DetailAstPair.addAstChild(currentAst, visit(fieldAccess.expr()));
1182                 DetailAstPair.makeAstRoot(currentAst, create(fieldAccess.DOT()));
1183             });
1184             resource = createImaginary(TokenTypes.RESOURCE);
1185             resource.addChild(currentAst.root);
1186             if (ctx.LITERAL_THIS() == null) {
1187                 resource.getFirstChild().addChild(visit(ctx.id()));
1188             }
1189             else {
1190                 resource.getFirstChild().addChild(create(ctx.LITERAL_THIS()));
1191             }
1192         }
1193         return resource;
1194     }
1195 
1196     @Override
1197     public DetailAstImpl visitSwitchBlockStatementGroup(
1198             JavaLanguageParser.SwitchBlockStatementGroupContext ctx) {
1199         final DetailAstImpl caseGroup = createImaginary(TokenTypes.CASE_GROUP);
1200         processChildren(caseGroup, ctx.switchLabel());
1201         final DetailAstImpl sList = createImaginary(TokenTypes.SLIST);
1202         processChildren(sList, ctx.slists);
1203         caseGroup.addChild(sList);
1204         return caseGroup;
1205     }
1206 
1207     @Override
1208     public DetailAstImpl visitCaseLabel(JavaLanguageParser.CaseLabelContext ctx) {
1209         final DetailAstImpl caseLabel = create(ctx.LITERAL_CASE());
1210         // child [0] is 'LITERAL_CASE'
1211         processChildren(caseLabel, ctx.children.subList(1, ctx.children.size()));
1212         return caseLabel;
1213     }
1214 
1215     @Override
1216     public DetailAstImpl visitDefaultLabel(JavaLanguageParser.DefaultLabelContext ctx) {
1217         final DetailAstImpl defaultLabel = create(ctx.LITERAL_DEFAULT());
1218         if (ctx.COLON() != null) {
1219             defaultLabel.addChild(create(ctx.COLON()));
1220         }
1221         return defaultLabel;
1222     }
1223 
1224     @Override
1225     public DetailAstImpl visitCaseConstants(JavaLanguageParser.CaseConstantsContext ctx) {
1226         return flattenedTree(ctx);
1227     }
1228 
1229     @Override
1230     public DetailAstImpl visitCaseConstant(JavaLanguageParser.CaseConstantContext ctx) {
1231         return flattenedTree(ctx);
1232     }
1233 
1234     @Override
1235     public DetailAstImpl visitEnhancedFor(JavaLanguageParser.EnhancedForContext ctx) {
1236         final DetailAstImpl leftParen = create(ctx.LPAREN());
1237         final DetailAstImpl enhancedForControl =
1238                  visit(ctx.getChild(1));
1239         final DetailAstImpl forEachClause = createImaginary(TokenTypes.FOR_EACH_CLAUSE);
1240         forEachClause.addChild(enhancedForControl);
1241         addLastSibling(leftParen, forEachClause);
1242         addLastSibling(leftParen, create(ctx.RPAREN()));
1243         return leftParen;
1244     }
1245 
1246     @Override
1247     public DetailAstImpl visitForFor(JavaLanguageParser.ForForContext ctx) {
1248         final DetailAstImpl dummyRoot = new DetailAstImpl();
1249         dummyRoot.addChild(create(ctx.LPAREN()));
1250 
1251         if (ctx.forInit() == null) {
1252             final DetailAstImpl imaginaryForInitParent =
1253                     createImaginary(TokenTypes.FOR_INIT);
1254             dummyRoot.addChild(imaginaryForInitParent);
1255         }
1256         else {
1257             dummyRoot.addChild(visit(ctx.forInit()));
1258         }
1259 
1260         dummyRoot.addChild(create(ctx.SEMI(0)));
1261 
1262         final DetailAstImpl forCondParent = createImaginary(TokenTypes.FOR_CONDITION);
1263         forCondParent.addChild(visit(ctx.forCond));
1264         dummyRoot.addChild(forCondParent);
1265         dummyRoot.addChild(create(ctx.SEMI(1)));
1266 
1267         final DetailAstImpl forItParent = createImaginary(TokenTypes.FOR_ITERATOR);
1268         forItParent.addChild(visit(ctx.forUpdate));
1269         dummyRoot.addChild(forItParent);
1270 
1271         dummyRoot.addChild(create(ctx.RPAREN()));
1272 
1273         return dummyRoot.getFirstChild();
1274     }
1275 
1276     @Override
1277     public DetailAstImpl visitForInit(JavaLanguageParser.ForInitContext ctx) {
1278         final DetailAstImpl forInit = createImaginary(TokenTypes.FOR_INIT);
1279         processChildren(forInit, ctx.children);
1280         return forInit;
1281     }
1282 
1283     @Override
1284     public DetailAstImpl visitEnhancedForControl(
1285             JavaLanguageParser.EnhancedForControlContext ctx) {
1286         final DetailAstImpl variableDeclaratorId =
1287                  visit(ctx.variableDeclaratorId());
1288         final DetailAstImpl variableDef = createImaginary(TokenTypes.VARIABLE_DEF);
1289         variableDef.addChild(variableDeclaratorId);
1290 
1291         addLastSibling(variableDef, create(ctx.COLON()));
1292         addLastSibling(variableDef, visit(ctx.expression()));
1293         return variableDef;
1294     }
1295 
1296     @Override
1297     public DetailAstImpl visitEnhancedForControlWithRecordPattern(
1298             JavaLanguageParser.EnhancedForControlWithRecordPatternContext ctx) {
1299         final DetailAstImpl recordPattern =
1300                  visit(ctx.pattern());
1301         addLastSibling(recordPattern, create(ctx.COLON()));
1302         addLastSibling(recordPattern, visit(ctx.expression()));
1303         return recordPattern;
1304     }
1305 
1306     @Override
1307     public DetailAstImpl visitParExpression(JavaLanguageParser.ParExpressionContext ctx) {
1308         return flattenedTree(ctx);
1309     }
1310 
1311     @Override
1312     public DetailAstImpl visitExpressionList(JavaLanguageParser.ExpressionListContext ctx) {
1313         final DetailAstImpl elist = createImaginary(TokenTypes.ELIST);
1314         processChildren(elist, ctx.children);
1315         return elist;
1316     }
1317 
1318     @Override
1319     public DetailAstImpl visitExpression(JavaLanguageParser.ExpressionContext ctx) {
1320         return buildExpressionNode(ctx.expr());
1321     }
1322 
1323     @Override
1324     public DetailAstImpl visitRefOp(JavaLanguageParser.RefOpContext ctx) {
1325         final DetailAstImpl bop = create(ctx.bop);
1326         final DetailAstImpl leftChild = visit(ctx.expr());
1327         final DetailAstImpl rightChild = create(TokenTypes.IDENT, ctx.stop);
1328         bop.addChild(leftChild);
1329         bop.addChild(rightChild);
1330         return bop;
1331     }
1332 
1333     @Override
1334     public DetailAstImpl visitSuperExp(JavaLanguageParser.SuperExpContext ctx) {
1335         final DetailAstImpl bop = create(ctx.bop);
1336         bop.addChild(visit(ctx.expr()));
1337         bop.addChild(create(ctx.LITERAL_SUPER()));
1338         DetailAstImpl superSuffixParent = visit(ctx.superSuffix());
1339 
1340         if (superSuffixParent == null) {
1341             superSuffixParent = bop;
1342         }
1343         else {
1344             DetailAstImpl firstChild = superSuffixParent;
1345             while (firstChild.getFirstChild() != null) {
1346                 firstChild = firstChild.getFirstChild();
1347             }
1348             firstChild.addPreviousSibling(bop);
1349         }
1350 
1351         return superSuffixParent;
1352     }
1353 
1354     @Override
1355     public DetailAstImpl visitInstanceOfExp(JavaLanguageParser.InstanceOfExpContext ctx) {
1356         final DetailAstImpl literalInstanceOf = create(ctx.LITERAL_INSTANCEOF());
1357         literalInstanceOf.addChild(visit(ctx.expr()));
1358         final ParseTree patternOrType = ctx.getChild(2);
1359 
1360         final DetailAstImpl patternDef;
1361         if (patternOrType instanceof JavaLanguageParser.ParenPatternContext) {
1362             // Parenthesized pattern has a `PATTERN_DEF` parent
1363             patternDef = createImaginary(TokenTypes.PATTERN_DEF);
1364             patternDef.addChild(visit(patternOrType));
1365         }
1366         else {
1367             patternDef = visit(patternOrType);
1368         }
1369         literalInstanceOf.addChild(patternDef);
1370         return literalInstanceOf;
1371     }
1372 
1373     @Override
1374     public DetailAstImpl visitBitShift(JavaLanguageParser.BitShiftContext ctx) {
1375         final DetailAstImpl shiftOperation;
1376 
1377         // We determine the type of shift operation in the parser, instead of the
1378         // lexer as in older grammars. This makes it easier to parse type parameters
1379         // and less than/ greater than operators in general.
1380         if (ctx.LT().size() == LEFT_SHIFT.length()) {
1381             shiftOperation = create(TokenTypes.SL, (Token) ctx.LT(0).getPayload());
1382             shiftOperation.setText(LEFT_SHIFT);
1383         }
1384         else if (ctx.GT().size() == UNSIGNED_RIGHT_SHIFT.length()) {
1385             shiftOperation = create(TokenTypes.BSR, (Token) ctx.GT(0).getPayload());
1386             shiftOperation.setText(UNSIGNED_RIGHT_SHIFT);
1387         }
1388         else {
1389             shiftOperation = create(TokenTypes.SR, (Token) ctx.GT(0).getPayload());
1390             shiftOperation.setText(RIGHT_SHIFT);
1391         }
1392 
1393         shiftOperation.addChild(visit(ctx.expr(0)));
1394         shiftOperation.addChild(visit(ctx.expr(1)));
1395         return shiftOperation;
1396     }
1397 
1398     @Override
1399     public DetailAstImpl visitNewExp(JavaLanguageParser.NewExpContext ctx) {
1400         final DetailAstImpl newExp = create(ctx.LITERAL_NEW());
1401         // child [0] is LITERAL_NEW
1402         processChildren(newExp, ctx.children.subList(1, ctx.children.size()));
1403         return newExp;
1404     }
1405 
1406     @Override
1407     public DetailAstImpl visitPrefix(JavaLanguageParser.PrefixContext ctx) {
1408         final int tokenType;
1409         switch (ctx.prefix.getType()) {
1410             case JavaLanguageLexer.PLUS:
1411                 tokenType = TokenTypes.UNARY_PLUS;
1412                 break;
1413             case JavaLanguageLexer.MINUS:
1414                 tokenType = TokenTypes.UNARY_MINUS;
1415                 break;
1416             default:
1417                 tokenType = ctx.prefix.getType();
1418         }
1419         final DetailAstImpl prefix = create(tokenType, ctx.prefix);
1420         prefix.addChild(visit(ctx.expr()));
1421         return prefix;
1422     }
1423 
1424     @Override
1425     public DetailAstImpl visitCastExp(JavaLanguageParser.CastExpContext ctx) {
1426         final DetailAstImpl cast = create(TokenTypes.TYPECAST, (Token) ctx.LPAREN().getPayload());
1427         // child [0] is LPAREN
1428         processChildren(cast, ctx.children.subList(1, ctx.children.size()));
1429         return cast;
1430     }
1431 
1432     @Override
1433     public DetailAstImpl visitIndexOp(JavaLanguageParser.IndexOpContext ctx) {
1434         // LBRACK -> INDEX_OP is root of this AST
1435         final DetailAstImpl indexOp = create(TokenTypes.INDEX_OP,
1436                 (Token) ctx.LBRACK().getPayload());
1437 
1438         // add expression(IDENT) on LHS
1439         indexOp.addChild(visit(ctx.expr(0)));
1440 
1441         // create imaginary node for expression on RHS
1442         final DetailAstImpl expr = visit(ctx.expr(1));
1443         final DetailAstImpl imaginaryExpr = createImaginary(TokenTypes.EXPR);
1444         imaginaryExpr.addChild(expr);
1445         indexOp.addChild(imaginaryExpr);
1446 
1447         // complete AST by adding RBRACK
1448         indexOp.addChild(create(ctx.RBRACK()));
1449         return indexOp;
1450     }
1451 
1452     @Override
1453     public DetailAstImpl visitInvOp(JavaLanguageParser.InvOpContext ctx) {
1454         final DetailAstPair currentAst = new DetailAstPair();
1455 
1456         final DetailAstImpl returnAst = visit(ctx.expr());
1457         DetailAstPair.addAstChild(currentAst, returnAst);
1458         DetailAstPair.makeAstRoot(currentAst, create(ctx.bop));
1459 
1460         DetailAstPair.addAstChild(currentAst,
1461                  visit(ctx.nonWildcardTypeArguments()));
1462         DetailAstPair.addAstChild(currentAst, visit(ctx.id()));
1463         final DetailAstImpl lparen = create(TokenTypes.METHOD_CALL,
1464                 (Token) ctx.LPAREN().getPayload());
1465         DetailAstPair.makeAstRoot(currentAst, lparen);
1466 
1467         // We always add an 'ELIST' node
1468         final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList()))
1469                 .orElseGet(() -> createImaginary(TokenTypes.ELIST));
1470 
1471         DetailAstPair.addAstChild(currentAst, expressionList);
1472         DetailAstPair.addAstChild(currentAst, create(ctx.RPAREN()));
1473 
1474         return currentAst.root;
1475     }
1476 
1477     @Override
1478     public DetailAstImpl visitInitExp(JavaLanguageParser.InitExpContext ctx) {
1479         final DetailAstImpl dot = create(ctx.bop);
1480         dot.addChild(visit(ctx.expr()));
1481         final DetailAstImpl literalNew = create(ctx.LITERAL_NEW());
1482         literalNew.addChild(visit(ctx.nonWildcardTypeArguments()));
1483         literalNew.addChild(visit(ctx.innerCreator()));
1484         dot.addChild(literalNew);
1485         return dot;
1486     }
1487 
1488     @Override
1489     public DetailAstImpl visitSimpleMethodCall(JavaLanguageParser.SimpleMethodCallContext ctx) {
1490         final DetailAstImpl methodCall = create(TokenTypes.METHOD_CALL,
1491                 (Token) ctx.LPAREN().getPayload());
1492         methodCall.addChild(visit(ctx.id()));
1493         // We always add an 'ELIST' node
1494         final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList()))
1495                 .orElseGet(() -> createImaginary(TokenTypes.ELIST));
1496 
1497         methodCall.addChild(expressionList);
1498         methodCall.addChild(create((Token) ctx.RPAREN().getPayload()));
1499         return methodCall;
1500     }
1501 
1502     @Override
1503     public DetailAstImpl visitLambdaExp(JavaLanguageParser.LambdaExpContext ctx) {
1504         final DetailAstImpl lambda = create(ctx.LAMBDA());
1505         lambda.addChild(visit(ctx.lambdaParameters()));
1506 
1507         final JavaLanguageParser.BlockContext blockContext = ctx.block();
1508         final DetailAstImpl rightHandLambdaChild;
1509         if (blockContext != null) {
1510             rightHandLambdaChild = visit(blockContext);
1511         }
1512         else {
1513             // Lambda expression child is built the same way that we build
1514             // the initial expression node in visitExpression, i.e. with
1515             // an imaginary EXPR node. This results in nested EXPR nodes
1516             // in the AST.
1517             rightHandLambdaChild = buildExpressionNode(ctx.expr());
1518         }
1519         lambda.addChild(rightHandLambdaChild);
1520         return lambda;
1521     }
1522 
1523     @Override
1524     public DetailAstImpl visitThisExp(JavaLanguageParser.ThisExpContext ctx) {
1525         final DetailAstImpl bop = create(ctx.bop);
1526         bop.addChild(visit(ctx.expr()));
1527         bop.addChild(create(ctx.LITERAL_THIS()));
1528         return bop;
1529     }
1530 
1531     @Override
1532     public DetailAstImpl visitPrimaryExp(JavaLanguageParser.PrimaryExpContext ctx) {
1533         return flattenedTree(ctx);
1534     }
1535 
1536     @Override
1537     public DetailAstImpl visitPostfix(JavaLanguageParser.PostfixContext ctx) {
1538         final DetailAstImpl postfix;
1539         if (ctx.postfix.getType() == JavaLanguageLexer.INC) {
1540             postfix = create(TokenTypes.POST_INC, ctx.postfix);
1541         }
1542         else {
1543             postfix = create(TokenTypes.POST_DEC, ctx.postfix);
1544         }
1545         postfix.addChild(visit(ctx.expr()));
1546         return postfix;
1547     }
1548 
1549     @Override
1550     public DetailAstImpl visitMethodRef(JavaLanguageParser.MethodRefContext ctx) {
1551         final DetailAstImpl doubleColon = create(TokenTypes.METHOD_REF,
1552                 (Token) ctx.DOUBLE_COLON().getPayload());
1553         final List<ParseTree> children = ctx.children.stream()
1554                 .filter(child -> !child.equals(ctx.DOUBLE_COLON()))
1555                 .collect(Collectors.toUnmodifiableList());
1556         processChildren(doubleColon, children);
1557         return doubleColon;
1558     }
1559 
1560     @Override
1561     public DetailAstImpl visitTernaryOp(JavaLanguageParser.TernaryOpContext ctx) {
1562         final DetailAstImpl root = create(ctx.QUESTION());
1563         processChildren(root, ctx.children.stream()
1564                 .filter(child -> !child.equals(ctx.QUESTION()))
1565                 .collect(Collectors.toUnmodifiableList()));
1566         return root;
1567     }
1568 
1569     @Override
1570     public DetailAstImpl visitBinOp(JavaLanguageParser.BinOpContext ctx) {
1571         final DetailAstImpl bop = create(ctx.bop);
1572 
1573         // To improve performance, we iterate through binary operations
1574         // since they are frequently deeply nested.
1575         final List<JavaLanguageParser.BinOpContext> binOpList = new ArrayList<>();
1576         ParseTree firstExpression = ctx.expr(0);
1577         while (firstExpression instanceof JavaLanguageParser.BinOpContext) {
1578             // Get all nested binOps
1579             binOpList.add((JavaLanguageParser.BinOpContext) firstExpression);
1580             firstExpression = ((JavaLanguageParser.BinOpContext) firstExpression).expr(0);
1581         }
1582 
1583         if (binOpList.isEmpty()) {
1584             final DetailAstImpl leftChild = visit(ctx.children.get(0));
1585             bop.addChild(leftChild);
1586         }
1587         else {
1588             // Map all descendants to individual AST's since we can parallelize this
1589             // operation
1590             final Queue<DetailAstImpl> descendantList = binOpList.parallelStream()
1591                     .map(this::getInnerBopAst)
1592                     .collect(Collectors.toCollection(ConcurrentLinkedQueue::new));
1593 
1594             bop.addChild(descendantList.poll());
1595             DetailAstImpl pointer = bop.getFirstChild();
1596             // Build tree
1597             for (DetailAstImpl descendant : descendantList) {
1598                 pointer.getFirstChild().addPreviousSibling(descendant);
1599                 pointer = descendant;
1600             }
1601         }
1602 
1603         bop.addChild(visit(ctx.children.get(2)));
1604         return bop;
1605     }
1606 
1607     /**
1608      * Builds the binary operation (binOp) AST.
1609      *
1610      * @param descendant the BinOpContext to build AST from
1611      * @return binOp AST
1612      */
1613     private DetailAstImpl getInnerBopAst(JavaLanguageParser.BinOpContext descendant) {
1614         final DetailAstImpl innerBop = create(descendant.bop);
1615         final JavaLanguageParser.ExprContext expr = descendant.expr(0);
1616         if (!(expr instanceof JavaLanguageParser.BinOpContext)) {
1617             innerBop.addChild(visit(expr));
1618         }
1619         innerBop.addChild(visit(descendant.expr(1)));
1620         return innerBop;
1621     }
1622 
1623     @Override
1624     public DetailAstImpl visitMethodCall(JavaLanguageParser.MethodCallContext ctx) {
1625         final DetailAstImpl methodCall = create(TokenTypes.METHOD_CALL,
1626                 (Token) ctx.LPAREN().getPayload());
1627         // We always add an 'ELIST' node
1628         final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList()))
1629                 .orElseGet(() -> createImaginary(TokenTypes.ELIST));
1630 
1631         final DetailAstImpl dot = create(ctx.DOT());
1632         dot.addChild(visit(ctx.expr()));
1633         dot.addChild(visit(ctx.id()));
1634         methodCall.addChild(dot);
1635         methodCall.addChild(expressionList);
1636         methodCall.addChild(create((Token) ctx.RPAREN().getPayload()));
1637         return methodCall;
1638     }
1639 
1640     @Override
1641     public DetailAstImpl visitTypeCastParameters(
1642             JavaLanguageParser.TypeCastParametersContext ctx) {
1643         final DetailAstImpl typeType = visit(ctx.typeType(0));
1644         for (int i = 0; i < ctx.BAND().size(); i++) {
1645             addLastSibling(typeType, create(TokenTypes.TYPE_EXTENSION_AND,
1646                                 (Token) ctx.BAND(i).getPayload()));
1647             addLastSibling(typeType, visit(ctx.typeType(i + 1)));
1648         }
1649         return typeType;
1650     }
1651 
1652     @Override
1653     public DetailAstImpl visitSingleLambdaParam(JavaLanguageParser.SingleLambdaParamContext ctx) {
1654         return flattenedTree(ctx);
1655     }
1656 
1657     @Override
1658     public DetailAstImpl visitFormalLambdaParam(JavaLanguageParser.FormalLambdaParamContext ctx) {
1659         final DetailAstImpl lparen = create(ctx.LPAREN());
1660 
1661         // We add an 'PARAMETERS' node here whether it exists or not
1662         final DetailAstImpl parameters = Optional.ofNullable(visit(ctx.formalParameterList()))
1663                 .orElseGet(() -> createImaginary(TokenTypes.PARAMETERS));
1664         addLastSibling(lparen, parameters);
1665         addLastSibling(lparen, create(ctx.RPAREN()));
1666         return lparen;
1667     }
1668 
1669     @Override
1670     public DetailAstImpl visitMultiLambdaParam(JavaLanguageParser.MultiLambdaParamContext ctx) {
1671         final DetailAstImpl lparen = create(ctx.LPAREN());
1672         addLastSibling(lparen, visit(ctx.multiLambdaParams()));
1673         addLastSibling(lparen, create(ctx.RPAREN()));
1674         return lparen;
1675     }
1676 
1677     @Override
1678     public DetailAstImpl visitMultiLambdaParams(JavaLanguageParser.MultiLambdaParamsContext ctx) {
1679         final DetailAstImpl parameters = createImaginary(TokenTypes.PARAMETERS);
1680         parameters.addChild(createLambdaParameter(ctx.id(0)));
1681 
1682         for (int i = 0; i < ctx.COMMA().size(); i++) {
1683             parameters.addChild(create(ctx.COMMA(i)));
1684             parameters.addChild(createLambdaParameter(ctx.id(i + 1)));
1685         }
1686         return parameters;
1687     }
1688 
1689     /**
1690      * Creates a 'PARAMETER_DEF' node for a lambda expression, with
1691      * imaginary modifier and type nodes.
1692      *
1693      * @param ctx the IdContext to create imaginary nodes for
1694      * @return DetailAstImpl of lambda parameter
1695      */
1696     private DetailAstImpl createLambdaParameter(JavaLanguageParser.IdContext ctx) {
1697         final DetailAstImpl ident = visitId(ctx);
1698         final DetailAstImpl parameter = createImaginary(TokenTypes.PARAMETER_DEF);
1699         final DetailAstImpl modifiers = createImaginary(TokenTypes.MODIFIERS);
1700         final DetailAstImpl type = createImaginary(TokenTypes.TYPE);
1701         parameter.addChild(modifiers);
1702         parameter.addChild(type);
1703         parameter.addChild(ident);
1704         return parameter;
1705     }
1706 
1707     @Override
1708     public DetailAstImpl visitParenPrimary(JavaLanguageParser.ParenPrimaryContext ctx) {
1709         return flattenedTree(ctx);
1710     }
1711 
1712     @Override
1713     public DetailAstImpl visitTokenPrimary(JavaLanguageParser.TokenPrimaryContext ctx) {
1714         return flattenedTree(ctx);
1715     }
1716 
1717     @Override
1718     public DetailAstImpl visitClassRefPrimary(JavaLanguageParser.ClassRefPrimaryContext ctx) {
1719         final DetailAstImpl dot = create(ctx.DOT());
1720         final DetailAstImpl primaryTypeNoArray = visit(ctx.type);
1721         dot.addChild(primaryTypeNoArray);
1722         if (TokenUtil.isOfType(primaryTypeNoArray, TokenTypes.DOT)) {
1723             // We append '[]' to the qualified name 'TYPE' `ast
1724             ctx.arrayDeclarator()
1725                     .forEach(child -> primaryTypeNoArray.addChild(visit(child)));
1726         }
1727         else {
1728             ctx.arrayDeclarator()
1729                     .forEach(child -> addLastSibling(primaryTypeNoArray, visit(child)));
1730         }
1731         dot.addChild(create(ctx.LITERAL_CLASS()));
1732         return dot;
1733     }
1734 
1735     @Override
1736     public DetailAstImpl visitPrimitivePrimary(JavaLanguageParser.PrimitivePrimaryContext ctx) {
1737         final DetailAstImpl dot = create(ctx.DOT());
1738         final DetailAstImpl primaryTypeNoArray = visit(ctx.type);
1739         dot.addChild(primaryTypeNoArray);
1740         ctx.arrayDeclarator().forEach(child -> dot.addChild(visit(child)));
1741         dot.addChild(create(ctx.LITERAL_CLASS()));
1742         return dot;
1743     }
1744 
1745     @Override
1746     public DetailAstImpl visitCreator(JavaLanguageParser.CreatorContext ctx) {
1747         return flattenedTree(ctx);
1748     }
1749 
1750     @Override
1751     public DetailAstImpl visitCreatedNameObject(JavaLanguageParser.CreatedNameObjectContext ctx) {
1752         final DetailAstPair currentAST = new DetailAstPair();
1753         DetailAstPair.addAstChild(currentAST, visit(ctx.annotations()));
1754         DetailAstPair.addAstChild(currentAST, visit(ctx.id()));
1755         DetailAstPair.addAstChild(currentAST, visit(ctx.typeArgumentsOrDiamond()));
1756 
1757         // This is how we build the type arguments/ qualified name tree
1758         for (ParserRuleContext extendedContext : ctx.extended) {
1759             final DetailAstImpl dot = create(extendedContext.start);
1760             DetailAstPair.makeAstRoot(currentAST, dot);
1761             final List<ParseTree> childList = extendedContext
1762                     .children.subList(1, extendedContext.children.size());
1763             processChildren(dot, childList);
1764         }
1765 
1766         return currentAST.root;
1767     }
1768 
1769     @Override
1770     public DetailAstImpl visitCreatedNamePrimitive(
1771             JavaLanguageParser.CreatedNamePrimitiveContext ctx) {
1772         return flattenedTree(ctx);
1773     }
1774 
1775     @Override
1776     public DetailAstImpl visitInnerCreator(JavaLanguageParser.InnerCreatorContext ctx) {
1777         return flattenedTree(ctx);
1778     }
1779 
1780     @Override
1781     public DetailAstImpl visitArrayCreatorRest(JavaLanguageParser.ArrayCreatorRestContext ctx) {
1782         final DetailAstImpl arrayDeclarator = create(TokenTypes.ARRAY_DECLARATOR,
1783                 (Token) ctx.LBRACK().getPayload());
1784         final JavaLanguageParser.ExpressionContext expression = ctx.expression();
1785         final TerminalNode rbrack = ctx.RBRACK();
1786         // child[0] is LBRACK
1787         for (int i = 1; i < ctx.children.size(); i++) {
1788             if (ctx.children.get(i) == rbrack) {
1789                 arrayDeclarator.addChild(create(rbrack));
1790             }
1791             else if (ctx.children.get(i) == expression) {
1792                 // Handle '[8]', etc.
1793                 arrayDeclarator.addChild(visit(expression));
1794             }
1795             else {
1796                 addLastSibling(arrayDeclarator, visit(ctx.children.get(i)));
1797             }
1798         }
1799         return arrayDeclarator;
1800     }
1801 
1802     @Override
1803     public DetailAstImpl visitBracketsWithExp(JavaLanguageParser.BracketsWithExpContext ctx) {
1804         final DetailAstImpl dummyRoot = new DetailAstImpl();
1805         dummyRoot.addChild(visit(ctx.annotations()));
1806         final DetailAstImpl arrayDeclarator =
1807                 create(TokenTypes.ARRAY_DECLARATOR, (Token) ctx.LBRACK().getPayload());
1808         arrayDeclarator.addChild(visit(ctx.expression()));
1809         arrayDeclarator.addChild(create(ctx.stop));
1810         dummyRoot.addChild(arrayDeclarator);
1811         return dummyRoot.getFirstChild();
1812     }
1813 
1814     @Override
1815     public DetailAstImpl visitClassCreatorRest(JavaLanguageParser.ClassCreatorRestContext ctx) {
1816         return flattenedTree(ctx);
1817     }
1818 
1819     @Override
1820     public DetailAstImpl visitDiamond(JavaLanguageParser.DiamondContext ctx) {
1821         final DetailAstImpl typeArguments =
1822                 createImaginary(TokenTypes.TYPE_ARGUMENTS);
1823         typeArguments.addChild(create(TokenTypes.GENERIC_START,
1824                 (Token) ctx.LT().getPayload()));
1825         typeArguments.addChild(create(TokenTypes.GENERIC_END,
1826                 (Token) ctx.GT().getPayload()));
1827         return typeArguments;
1828     }
1829 
1830     @Override
1831     public DetailAstImpl visitTypeArgs(JavaLanguageParser.TypeArgsContext ctx) {
1832         return flattenedTree(ctx);
1833     }
1834 
1835     @Override
1836     public DetailAstImpl visitNonWildcardDiamond(
1837             JavaLanguageParser.NonWildcardDiamondContext ctx) {
1838         final DetailAstImpl typeArguments =
1839                 createImaginary(TokenTypes.TYPE_ARGUMENTS);
1840         typeArguments.addChild(create(TokenTypes.GENERIC_START,
1841                 (Token) ctx.LT().getPayload()));
1842         typeArguments.addChild(create(TokenTypes.GENERIC_END,
1843                 (Token) ctx.GT().getPayload()));
1844         return typeArguments;
1845     }
1846 
1847     @Override
1848     public DetailAstImpl visitNonWildcardTypeArguments(
1849             JavaLanguageParser.NonWildcardTypeArgumentsContext ctx) {
1850         final DetailAstImpl typeArguments = createImaginary(TokenTypes.TYPE_ARGUMENTS);
1851         typeArguments.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload()));
1852         typeArguments.addChild(visit(ctx.typeArgumentsTypeList()));
1853         typeArguments.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload()));
1854         return typeArguments;
1855     }
1856 
1857     @Override
1858     public DetailAstImpl visitTypeArgumentsTypeList(
1859             JavaLanguageParser.TypeArgumentsTypeListContext ctx) {
1860         final DetailAstImpl firstIdent = visit(ctx.typeType(0));
1861         final DetailAstImpl firstTypeArgument = createImaginary(TokenTypes.TYPE_ARGUMENT);
1862         firstTypeArgument.addChild(firstIdent);
1863 
1864         for (int i = 0; i < ctx.COMMA().size(); i++) {
1865             addLastSibling(firstTypeArgument, create(ctx.COMMA(i)));
1866             final DetailAstImpl ident = visit(ctx.typeType(i + 1));
1867             final DetailAstImpl typeArgument = createImaginary(TokenTypes.TYPE_ARGUMENT);
1868             typeArgument.addChild(ident);
1869             addLastSibling(firstTypeArgument, typeArgument);
1870         }
1871         return firstTypeArgument;
1872     }
1873 
1874     @Override
1875     public DetailAstImpl visitTypeList(JavaLanguageParser.TypeListContext ctx) {
1876         return flattenedTree(ctx);
1877     }
1878 
1879     @Override
1880     public DetailAstImpl visitTypeType(JavaLanguageParser.TypeTypeContext ctx) {
1881         final DetailAstImpl type = createImaginary(TokenTypes.TYPE);
1882         processChildren(type, ctx.children);
1883 
1884         final DetailAstImpl returnTree;
1885         if (ctx.createImaginaryNode) {
1886             returnTree = type;
1887         }
1888         else {
1889             returnTree = type.getFirstChild();
1890         }
1891         return returnTree;
1892     }
1893 
1894     @Override
1895     public DetailAstImpl visitArrayDeclarator(JavaLanguageParser.ArrayDeclaratorContext ctx) {
1896         final DetailAstImpl arrayDeclarator = create(TokenTypes.ARRAY_DECLARATOR,
1897                 (Token) ctx.LBRACK().getPayload());
1898         arrayDeclarator.addChild(create(ctx.RBRACK()));
1899 
1900         final DetailAstImpl returnTree;
1901         final DetailAstImpl annotations = visit(ctx.anno);
1902         if (annotations == null) {
1903             returnTree = arrayDeclarator;
1904         }
1905         else {
1906             returnTree = annotations;
1907             addLastSibling(returnTree, arrayDeclarator);
1908         }
1909         return returnTree;
1910     }
1911 
1912     @Override
1913     public DetailAstImpl visitPrimitiveType(JavaLanguageParser.PrimitiveTypeContext ctx) {
1914         return create(ctx.start);
1915     }
1916 
1917     @Override
1918     public DetailAstImpl visitTypeArguments(JavaLanguageParser.TypeArgumentsContext ctx) {
1919         final DetailAstImpl typeArguments = createImaginary(TokenTypes.TYPE_ARGUMENTS);
1920         typeArguments.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload()));
1921         // Exclude '<' and '>'
1922         processChildren(typeArguments, ctx.children.subList(1, ctx.children.size() - 1));
1923         typeArguments.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload()));
1924         return typeArguments;
1925     }
1926 
1927     @Override
1928     public DetailAstImpl visitSuperSuffixDot(JavaLanguageParser.SuperSuffixDotContext ctx) {
1929         final DetailAstImpl root;
1930         if (ctx.LPAREN() == null) {
1931             root = create(ctx.DOT());
1932             root.addChild(visit(ctx.id()));
1933         }
1934         else {
1935             root = create(TokenTypes.METHOD_CALL, (Token) ctx.LPAREN().getPayload());
1936 
1937             final DetailAstImpl dot = create(ctx.DOT());
1938             dot.addChild(visit(ctx.id()));
1939             root.addChild(dot);
1940 
1941             final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList()))
1942                     .orElseGet(() -> createImaginary(TokenTypes.ELIST));
1943             root.addChild(expressionList);
1944 
1945             root.addChild(create(ctx.RPAREN()));
1946         }
1947 
1948         return root;
1949     }
1950 
1951     @Override
1952     public DetailAstImpl visitArguments(JavaLanguageParser.ArgumentsContext ctx) {
1953         final DetailAstImpl lparen = create(ctx.LPAREN());
1954 
1955         // We always add an 'ELIST' node
1956         final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList()))
1957                 .orElseGet(() -> createImaginary(TokenTypes.ELIST));
1958         addLastSibling(lparen, expressionList);
1959         addLastSibling(lparen, create(ctx.RPAREN()));
1960         return lparen;
1961     }
1962 
1963     @Override
1964     public DetailAstImpl visitPattern(JavaLanguageParser.PatternContext ctx) {
1965         final JavaLanguageParser.InnerPatternContext innerPattern = ctx.innerPattern();
1966         final ParserRuleContext primaryPattern = innerPattern.primaryPattern();
1967         final ParserRuleContext recordPattern = innerPattern.recordPattern();
1968         final boolean isSimpleTypePattern = primaryPattern != null
1969                 && primaryPattern.getChild(0) instanceof JavaLanguageParser.TypePatternContext;
1970 
1971         final DetailAstImpl pattern;
1972 
1973         if (recordPattern != null) {
1974             pattern = visit(recordPattern);
1975         }
1976         else if (isSimpleTypePattern) {
1977             // For simple type pattern like 'Integer i`, we do not add `PATTERN_DEF` parent
1978             pattern = visit(primaryPattern);
1979         }
1980         else {
1981             pattern = createImaginary(TokenTypes.PATTERN_DEF);
1982             pattern.addChild(visit(ctx.getChild(0)));
1983         }
1984         return pattern;
1985     }
1986 
1987     @Override
1988     public DetailAstImpl visitInnerPattern(JavaLanguageParser.InnerPatternContext ctx) {
1989         return flattenedTree(ctx);
1990     }
1991 
1992     @Override
1993     public DetailAstImpl visitGuardedPattern(JavaLanguageParser.GuardedPatternContext ctx) {
1994         final DetailAstImpl guardAstNode = flattenedTree(ctx.guard());
1995         guardAstNode.addChild(visit(ctx.primaryPattern()));
1996         guardAstNode.addChild(visit(ctx.expression()));
1997         return guardAstNode;
1998     }
1999 
2000     @Override
2001     public DetailAstImpl visitParenPattern(JavaLanguageParser.ParenPatternContext ctx) {
2002         final DetailAstImpl lparen = create(ctx.LPAREN());
2003         final ParseTree innerPattern = ctx.getChild(1);
2004         lparen.addChild(visit(innerPattern));
2005         lparen.addChild(create(ctx.RPAREN()));
2006         return lparen;
2007     }
2008 
2009     @Override
2010     public DetailAstImpl visitRecordPatternDef(JavaLanguageParser.RecordPatternDefContext ctx) {
2011         return flattenedTree(ctx);
2012     }
2013 
2014     @Override
2015     public DetailAstImpl visitTypePatternDef(
2016             JavaLanguageParser.TypePatternDefContext ctx) {
2017         final DetailAstImpl type = visit(ctx.type);
2018         final DetailAstImpl patternVariableDef = createImaginary(TokenTypes.PATTERN_VARIABLE_DEF);
2019         patternVariableDef.addChild(createModifiers(ctx.mods));
2020         patternVariableDef.addChild(type);
2021         patternVariableDef.addChild(visit(ctx.id()));
2022         return patternVariableDef;
2023     }
2024 
2025     @Override
2026     public DetailAstImpl visitUnnamedPatternDef(JavaLanguageParser.UnnamedPatternDefContext ctx) {
2027         return create(TokenTypes.UNNAMED_PATTERN_DEF, ctx.start);
2028     }
2029 
2030     @Override
2031     public DetailAstImpl visitRecordPattern(JavaLanguageParser.RecordPatternContext ctx) {
2032         final DetailAstImpl recordPattern = createImaginary(TokenTypes.RECORD_PATTERN_DEF);
2033         recordPattern.addChild(createModifiers(ctx.mods));
2034         processChildren(recordPattern,
2035                 ctx.children.subList(ctx.mods.size(), ctx.children.size()));
2036         return recordPattern;
2037     }
2038 
2039     @Override
2040     public DetailAstImpl visitRecordComponentPatternList(
2041             JavaLanguageParser.RecordComponentPatternListContext ctx) {
2042         final DetailAstImpl recordComponents =
2043                 createImaginary(TokenTypes.RECORD_PATTERN_COMPONENTS);
2044         processChildren(recordComponents, ctx.children);
2045         return recordComponents;
2046     }
2047 
2048     @Override
2049     public DetailAstImpl visitPermittedSubclassesAndInterfaces(
2050             JavaLanguageParser.PermittedSubclassesAndInterfacesContext ctx) {
2051         final DetailAstImpl literalPermits =
2052                 create(TokenTypes.PERMITS_CLAUSE, (Token) ctx.LITERAL_PERMITS().getPayload());
2053         // 'LITERAL_PERMITS' is child[0]
2054         processChildren(literalPermits, ctx.children.subList(1, ctx.children.size()));
2055         return literalPermits;
2056     }
2057 
2058     @Override
2059     public DetailAstImpl visitId(JavaLanguageParser.IdContext ctx) {
2060         return create(TokenTypes.IDENT, ctx.start);
2061     }
2062 
2063     /**
2064      * Builds the AST for a particular node, then returns a "flattened" tree
2065      * of siblings. This method should be used in rule contexts such as
2066      * {@code variableDeclarators}, where we have both terminals and non-terminals.
2067      *
2068      * @param ctx the ParserRuleContext to base tree on
2069      * @return flattened DetailAstImpl
2070      */
2071     private DetailAstImpl flattenedTree(ParserRuleContext ctx) {
2072         final DetailAstImpl dummyNode = new DetailAstImpl();
2073         processChildren(dummyNode, ctx.children);
2074         return dummyNode.getFirstChild();
2075     }
2076 
2077     /**
2078      * Adds all the children from the given ParseTree or JavaParserContext
2079      * list to the parent DetailAstImpl.
2080      *
2081      * @param parent the DetailAstImpl to add children to
2082      * @param children the list of children to add
2083      */
2084     private void processChildren(DetailAstImpl parent, List<? extends ParseTree> children) {
2085         children.forEach(child -> {
2086             if (child instanceof TerminalNode) {
2087                 // Child is a token, create a new DetailAstImpl and add it to parent
2088                 parent.addChild(create((TerminalNode) child));
2089             }
2090             else {
2091                 // Child is another rule context; visit it, create token, and add to parent
2092                 parent.addChild(visit(child));
2093             }
2094         });
2095     }
2096 
2097     /**
2098      * Create a DetailAstImpl from a given token and token type. This method
2099      * should be used for imaginary nodes only, i.e. 'OBJBLOCK -&gt; OBJBLOCK',
2100      * where the text on the RHS matches the text on the LHS.
2101      *
2102      * @param tokenType the token type of this DetailAstImpl
2103      * @return new DetailAstImpl of given type
2104      */
2105     private static DetailAstImpl createImaginary(int tokenType) {
2106         final DetailAstImpl detailAst = new DetailAstImpl();
2107         detailAst.setType(tokenType);
2108         detailAst.setText(TokenUtil.getTokenName(tokenType));
2109         return detailAst;
2110     }
2111 
2112     /**
2113      * Create a DetailAstImpl from a given token and token type. This method
2114      * should be used for literal nodes only, i.e. 'PACKAGE_DEF -&gt; package'.
2115      *
2116      * @param tokenType the token type of this DetailAstImpl
2117      * @param startToken the first token that appears in this DetailAstImpl.
2118      * @return new DetailAstImpl of given type
2119      */
2120     private DetailAstImpl create(int tokenType, Token startToken) {
2121         final DetailAstImpl ast = create(startToken);
2122         ast.setType(tokenType);
2123         return ast;
2124     }
2125 
2126     /**
2127      * Create a DetailAstImpl from a given token. This method should be
2128      * used for terminal nodes, i.e. {@code LCURLY}, when we are building
2129      * an AST for a specific token, regardless of position.
2130      *
2131      * @param token the token to build the DetailAstImpl from
2132      * @return new DetailAstImpl of given type
2133      */
2134     private DetailAstImpl create(Token token) {
2135         final int tokenIndex = token.getTokenIndex();
2136         final List<Token> tokensToLeft =
2137                 tokens.getHiddenTokensToLeft(tokenIndex, JavaLanguageLexer.COMMENTS);
2138         final List<Token> tokensToRight =
2139                 tokens.getHiddenTokensToRight(tokenIndex, JavaLanguageLexer.COMMENTS);
2140 
2141         final DetailAstImpl detailAst = new DetailAstImpl();
2142         detailAst.initialize(token);
2143         if (tokensToLeft != null) {
2144             detailAst.setHiddenBefore(tokensToLeft);
2145         }
2146         if (tokensToRight != null) {
2147             detailAst.setHiddenAfter(tokensToRight);
2148         }
2149         return detailAst;
2150     }
2151 
2152     /**
2153      * Create a DetailAstImpl from a given TerminalNode. This method should be
2154      * used for terminal nodes, i.e. {@code @}.
2155      *
2156      * @param node the TerminalNode to build the DetailAstImpl from
2157      * @return new DetailAstImpl of given type
2158      */
2159     private DetailAstImpl create(TerminalNode node) {
2160         return create((Token) node.getPayload());
2161     }
2162 
2163     /**
2164      * Creates a type declaration DetailAstImpl from a given rule context.
2165      *
2166      * @param ctx ParserRuleContext we are in
2167      * @param type the type declaration to create
2168      * @param modifierList respective modifiers
2169      * @return type declaration DetailAstImpl
2170      */
2171     private DetailAstImpl createTypeDeclaration(ParserRuleContext ctx, int type,
2172                                                 List<? extends ParseTree> modifierList) {
2173         final DetailAstImpl typeDeclaration = createImaginary(type);
2174         typeDeclaration.addChild(createModifiers(modifierList));
2175         processChildren(typeDeclaration, ctx.children);
2176         return typeDeclaration;
2177     }
2178 
2179     /**
2180      * Builds the modifiers AST.
2181      *
2182      * @param modifierList the list of modifier contexts
2183      * @return "MODIFIERS" ast
2184      */
2185     private DetailAstImpl createModifiers(List<? extends ParseTree> modifierList) {
2186         final DetailAstImpl mods = createImaginary(TokenTypes.MODIFIERS);
2187         processChildren(mods, modifierList);
2188         return mods;
2189     }
2190 
2191     /**
2192      * Add new sibling to the end of existing siblings.
2193      *
2194      * @param self DetailAstImpl to add last sibling to
2195      * @param sibling DetailAstImpl sibling to add
2196      */
2197     private static void addLastSibling(DetailAstImpl self, DetailAstImpl sibling) {
2198         DetailAstImpl nextSibling = self;
2199         if (nextSibling != null) {
2200             while (nextSibling.getNextSibling() != null) {
2201                 nextSibling = nextSibling.getNextSibling();
2202             }
2203             nextSibling.setNextSibling(sibling);
2204         }
2205     }
2206 
2207     @Override
2208     public DetailAstImpl visit(ParseTree tree) {
2209         DetailAstImpl ast = null;
2210         if (tree != null) {
2211             ast = tree.accept(this);
2212         }
2213         return ast;
2214     }
2215 
2216     /**
2217      * Builds an expression node. This is used to build the root of an expression with
2218      * an imaginary {@code EXPR} node.
2219      *
2220      * @param exprNode expression to build node for
2221      * @return expression DetailAstImpl node
2222      */
2223     private DetailAstImpl buildExpressionNode(ParseTree exprNode) {
2224         final DetailAstImpl expression = visit(exprNode);
2225 
2226         final DetailAstImpl exprRoot;
2227         if (TokenUtil.isOfType(expression, EXPRESSIONS_WITH_NO_EXPR_ROOT)) {
2228             exprRoot = expression;
2229         }
2230         else {
2231             // create imaginary 'EXPR' node as root of expression
2232             exprRoot = createImaginary(TokenTypes.EXPR);
2233             exprRoot.addChild(expression);
2234         }
2235         return exprRoot;
2236     }
2237 
2238     /**
2239      * Used to swap and organize DetailAstImpl subtrees.
2240      */
2241     private static final class DetailAstPair {
2242 
2243         /** The root DetailAstImpl of this pair. */
2244         private DetailAstImpl root;
2245 
2246         /** The child (potentially with siblings) of this pair. */
2247         private DetailAstImpl child;
2248 
2249         /**
2250          * Moves child reference to the last child.
2251          */
2252         private void advanceChildToEnd() {
2253             while (child.getNextSibling() != null) {
2254                 child = child.getNextSibling();
2255             }
2256         }
2257 
2258         /**
2259          * Returns the root node.
2260          *
2261          * @return the root node
2262          */
2263         private DetailAstImpl getRoot() {
2264             return root;
2265         }
2266 
2267         /**
2268          * This method is used to replace the {@code ^} (set as root node) ANTLR2
2269          * operator.
2270          *
2271          * @param pair the DetailAstPair to use for swapping nodes
2272          * @param ast the new root
2273          */
2274         private static void makeAstRoot(DetailAstPair pair, DetailAstImpl ast) {
2275             ast.addChild(pair.root);
2276             pair.child = pair.root;
2277             pair.advanceChildToEnd();
2278             pair.root = ast;
2279         }
2280 
2281         /**
2282          * Adds a child (or new root) to the given DetailAstPair.
2283          *
2284          * @param pair the DetailAstPair to add child to
2285          * @param ast the child to add
2286          */
2287         private static void addAstChild(DetailAstPair pair, DetailAstImpl ast) {
2288             if (ast != null) {
2289                 if (pair.root == null) {
2290                     pair.root = ast;
2291                 }
2292                 else {
2293                     pair.child.setNextSibling(ast);
2294                 }
2295                 pair.child = ast;
2296             }
2297         }
2298     }
2299 }