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