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.Objects;
27  import java.util.Optional;
28  import java.util.Queue;
29  import java.util.concurrent.ConcurrentLinkedQueue;
30  import java.util.stream.Collectors;
31  
32  import org.antlr.v4.runtime.BufferedTokenStream;
33  import org.antlr.v4.runtime.CommonTokenStream;
34  import org.antlr.v4.runtime.ParserRuleContext;
35  import org.antlr.v4.runtime.Token;
36  import org.antlr.v4.runtime.tree.ParseTree;
37  import org.antlr.v4.runtime.tree.TerminalNode;
38  
39  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
40  import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageLexer;
41  import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser;
42  import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParserBaseVisitor;
43  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
44  
45  /**
46   * Visitor class used to build Checkstyle's Java AST from the parse tree produced by
47   * {@link JavaLanguageParser}. In each {@code visit...} method, we visit the children of a node
48   * (which correspond to subrules) or create terminal nodes (tokens), and return a subtree as a
49   * result.
50   *
51   * <p>Example:</p>
52   *
53   * <p>The following package declaration:</p>
54   * <pre>
55   * package com.puppycrawl.tools.checkstyle;
56   * </pre>
57   *
58   * <p>
59   * Will be parsed by the {@code packageDeclaration} rule from {@code JavaLanguageParser.g4}:
60   * </p>
61   * <pre>
62   * packageDeclaration
63   *     : annotations[true] LITERAL_PACKAGE qualifiedName SEMI
64   *     ;
65   * </pre>
66   *
67   * <p>
68   * We override the {@code visitPackageDeclaration} method generated by ANTLR in
69   * {@link JavaLanguageParser} at
70   * {@link JavaAstVisitor#visitPackageDeclaration(JavaLanguageParser.PackageDeclarationContext)}
71   * to create a subtree based on the subrules and tokens found in the {@code packageDeclaration}
72   * subrule accordingly, thus producing the following AST:
73   * </p>
74   * <pre>
75   * PACKAGE_DEF -&gt; package
76   * |--ANNOTATIONS -&gt; ANNOTATIONS
77   * |--DOT -&gt; .
78   * |   |--DOT -&gt; .
79   * |   |   |--DOT -&gt; .
80   * |   |   |   |--IDENT -&gt; com
81   * |   |   |   `--IDENT -&gt; puppycrawl
82   * |   |   `--IDENT -&gt; tools
83   * |   `--IDENT -&gt; checkstyle
84   * `--SEMI -&gt; ;
85   * </pre>
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   * <p>
92   * The order of {@code visit...} methods in {@code JavaAstVisitor.java} and production rules in
93   * {@code JavaLanguageParser.g4} should be consistent to ease maintenance.
94   * </p>
95   */
96  public final class JavaAstVisitor extends JavaLanguageParserBaseVisitor<DetailAstImpl> {
97  
98      /** String representation of the left shift operator. */
99      private static final String LEFT_SHIFT = "<<";
100 
101     /** String representation of the unsigned right shift operator. */
102     private static final String UNSIGNED_RIGHT_SHIFT = ">>>";
103 
104     /** String representation of the right shift operator. */
105     private static final String RIGHT_SHIFT = ">>";
106 
107     /** String representation of the double quote character. */
108     private static final String QUOTE = "\"";
109 
110     /**
111      * The tokens here are technically expressions, but should
112      * not return an EXPR token as their root.
113      */
114     private static final int[] EXPRESSIONS_WITH_NO_EXPR_ROOT = {
115         TokenTypes.CTOR_CALL,
116         TokenTypes.SUPER_CTOR_CALL,
117         TokenTypes.LAMBDA,
118     };
119 
120     /** Token stream to check for hidden tokens. */
121     private final BufferedTokenStream tokens;
122 
123     /**
124      * Constructs a JavaAstVisitor with given token stream.
125      *
126      * @param tokenStream the token stream to check for hidden tokens
127      */
128     public JavaAstVisitor(CommonTokenStream tokenStream) {
129         tokens = tokenStream;
130     }
131 
132     @Override
133     public DetailAstImpl visitCompilationUnit(JavaLanguageParser.CompilationUnitContext ctx) {
134         final DetailAstImpl compilationUnit;
135         // 'EOF' token is always present; therefore if we only have one child, we have an empty file
136         final boolean isEmptyFile = ctx.children.size() == 1;
137         if (isEmptyFile) {
138             compilationUnit = null;
139         }
140         else {
141             compilationUnit = createImaginary(TokenTypes.COMPILATION_UNIT);
142             // last child is 'EOF', we do not include this token in AST
143             processChildren(compilationUnit, ctx.children.subList(0, ctx.children.size() - 1));
144         }
145         return compilationUnit;
146     }
147 
148     @Override
149     public DetailAstImpl visitPackageDeclaration(
150             JavaLanguageParser.PackageDeclarationContext ctx) {
151         final DetailAstImpl packageDeclaration =
152                 create(TokenTypes.PACKAGE_DEF, (Token) ctx.LITERAL_PACKAGE().getPayload());
153         packageDeclaration.addChild(visit(ctx.annotations()));
154         packageDeclaration.addChild(visit(ctx.qualifiedName()));
155         packageDeclaration.addChild(create(ctx.SEMI()));
156         return packageDeclaration;
157     }
158 
159     @Override
160     public DetailAstImpl visitImportDec(JavaLanguageParser.ImportDecContext ctx) {
161         final DetailAstImpl importRoot = create(ctx.start);
162 
163         // Static import
164         final TerminalNode literalStaticNode = ctx.LITERAL_STATIC();
165         if (literalStaticNode != null) {
166             importRoot.setType(TokenTypes.STATIC_IMPORT);
167             importRoot.addChild(create(literalStaticNode));
168         }
169 
170         // Handle star imports
171         final boolean isStarImport = ctx.STAR() != null;
172         if (isStarImport) {
173             final DetailAstImpl dot = create(ctx.DOT());
174             dot.addChild(visit(ctx.qualifiedName()));
175             dot.addChild(create(ctx.STAR()));
176             importRoot.addChild(dot);
177         }
178         else {
179             importRoot.addChild(visit(ctx.qualifiedName()));
180         }
181 
182         importRoot.addChild(create(ctx.SEMI()));
183         return importRoot;
184     }
185 
186     @Override
187     public DetailAstImpl visitSingleSemiImport(JavaLanguageParser.SingleSemiImportContext ctx) {
188         return create(ctx.SEMI());
189     }
190 
191     @Override
192     public DetailAstImpl visitTypeDeclaration(JavaLanguageParser.TypeDeclarationContext ctx) {
193         final DetailAstImpl typeDeclaration;
194         if (ctx.type == null) {
195             typeDeclaration = create(ctx.semi.get(0));
196             ctx.semi.subList(1, ctx.semi.size())
197                     .forEach(semi -> addLastSibling(typeDeclaration, create(semi)));
198         }
199         else {
200             typeDeclaration = visit(ctx.type);
201         }
202         return typeDeclaration;
203     }
204 
205     @Override
206     public DetailAstImpl visitModifier(JavaLanguageParser.ModifierContext ctx) {
207         return flattenedTree(ctx);
208     }
209 
210     @Override
211     public DetailAstImpl visitVariableModifier(JavaLanguageParser.VariableModifierContext ctx) {
212         return flattenedTree(ctx);
213     }
214 
215     @Override
216     public DetailAstImpl visitClassDeclaration(JavaLanguageParser.ClassDeclarationContext ctx) {
217         return createTypeDeclaration(ctx, TokenTypes.CLASS_DEF, ctx.mods);
218     }
219 
220     @Override
221     public DetailAstImpl visitRecordDeclaration(JavaLanguageParser.RecordDeclarationContext ctx) {
222         return createTypeDeclaration(ctx, TokenTypes.RECORD_DEF, ctx.mods);
223     }
224 
225     @Override
226     public DetailAstImpl visitRecordComponentsList(
227             JavaLanguageParser.RecordComponentsListContext ctx) {
228         final DetailAstImpl lparen = create(ctx.LPAREN());
229 
230         // We make a "RECORD_COMPONENTS" node whether components exist or not
231         if (ctx.recordComponents() == null) {
232             addLastSibling(lparen, createImaginary(TokenTypes.RECORD_COMPONENTS));
233         }
234         else {
235             addLastSibling(lparen, visit(ctx.recordComponents()));
236         }
237         addLastSibling(lparen, create(ctx.RPAREN()));
238         return lparen;
239     }
240 
241     @Override
242     public DetailAstImpl visitRecordComponents(JavaLanguageParser.RecordComponentsContext ctx) {
243         final DetailAstImpl recordComponents = createImaginary(TokenTypes.RECORD_COMPONENTS);
244         processChildren(recordComponents, ctx.children);
245         return recordComponents;
246     }
247 
248     @Override
249     public DetailAstImpl visitRecordComponent(JavaLanguageParser.RecordComponentContext ctx) {
250         final DetailAstImpl recordComponent = createImaginary(TokenTypes.RECORD_COMPONENT_DEF);
251         processChildren(recordComponent, ctx.children);
252         return recordComponent;
253     }
254 
255     @Override
256     public DetailAstImpl visitLastRecordComponent(
257             JavaLanguageParser.LastRecordComponentContext ctx) {
258         final DetailAstImpl recordComponent = createImaginary(TokenTypes.RECORD_COMPONENT_DEF);
259         processChildren(recordComponent, ctx.children);
260         return recordComponent;
261     }
262 
263     @Override
264     public DetailAstImpl visitRecordBody(JavaLanguageParser.RecordBodyContext ctx) {
265         final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK);
266         processChildren(objBlock, ctx.children);
267         return objBlock;
268     }
269 
270     @Override
271     public DetailAstImpl visitCompactConstructorDeclaration(
272             JavaLanguageParser.CompactConstructorDeclarationContext ctx) {
273         final DetailAstImpl compactConstructor = createImaginary(TokenTypes.COMPACT_CTOR_DEF);
274         compactConstructor.addChild(createModifiers(ctx.mods));
275         compactConstructor.addChild(visit(ctx.id()));
276         compactConstructor.addChild(visit(ctx.constructorBlock()));
277         return compactConstructor;
278     }
279 
280     @Override
281     public DetailAstImpl visitClassExtends(JavaLanguageParser.ClassExtendsContext ctx) {
282         final DetailAstImpl classExtends = create(ctx.EXTENDS_CLAUSE());
283         classExtends.addChild(visit(ctx.type));
284         return classExtends;
285     }
286 
287     @Override
288     public DetailAstImpl visitImplementsClause(JavaLanguageParser.ImplementsClauseContext ctx) {
289         final DetailAstImpl classImplements = create(TokenTypes.IMPLEMENTS_CLAUSE,
290                 (Token) ctx.LITERAL_IMPLEMENTS().getPayload());
291         classImplements.addChild(visit(ctx.typeList()));
292         return classImplements;
293     }
294 
295     @Override
296     public DetailAstImpl visitTypeParameters(JavaLanguageParser.TypeParametersContext ctx) {
297         final DetailAstImpl typeParameters = createImaginary(TokenTypes.TYPE_PARAMETERS);
298         typeParameters.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload()));
299         // Exclude '<' and '>'
300         processChildren(typeParameters, ctx.children.subList(1, ctx.children.size() - 1));
301         typeParameters.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload()));
302         return typeParameters;
303     }
304 
305     @Override
306     public DetailAstImpl visitTypeParameter(JavaLanguageParser.TypeParameterContext ctx) {
307         final DetailAstImpl typeParameter = createImaginary(TokenTypes.TYPE_PARAMETER);
308         processChildren(typeParameter, ctx.children);
309         return typeParameter;
310     }
311 
312     @Override
313     public DetailAstImpl visitTypeUpperBounds(JavaLanguageParser.TypeUpperBoundsContext ctx) {
314         // In this case, we call 'extends` TYPE_UPPER_BOUNDS
315         final DetailAstImpl typeUpperBounds = create(TokenTypes.TYPE_UPPER_BOUNDS,
316                 (Token) ctx.EXTENDS_CLAUSE().getPayload());
317         // 'extends' is child[0]
318         processChildren(typeUpperBounds, ctx.children.subList(1, ctx.children.size()));
319         return typeUpperBounds;
320     }
321 
322     @Override
323     public DetailAstImpl visitTypeBound(JavaLanguageParser.TypeBoundContext ctx) {
324         final DetailAstImpl typeBoundType = visit(ctx.typeBoundType(0));
325         final Iterator<JavaLanguageParser.TypeBoundTypeContext> typeBoundTypeIterator =
326                 ctx.typeBoundType().listIterator(1);
327         ctx.BAND().forEach(band -> {
328             addLastSibling(typeBoundType, create(TokenTypes.TYPE_EXTENSION_AND,
329                                 (Token) band.getPayload()));
330             addLastSibling(typeBoundType, visit(typeBoundTypeIterator.next()));
331         });
332         return typeBoundType;
333     }
334 
335     @Override
336     public DetailAstImpl visitTypeBoundType(JavaLanguageParser.TypeBoundTypeContext ctx) {
337         return flattenedTree(ctx);
338     }
339 
340     @Override
341     public DetailAstImpl visitEnumDeclaration(JavaLanguageParser.EnumDeclarationContext ctx) {
342         return createTypeDeclaration(ctx, TokenTypes.ENUM_DEF, ctx.mods);
343     }
344 
345     @Override
346     public DetailAstImpl visitEnumBody(JavaLanguageParser.EnumBodyContext ctx) {
347         final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK);
348         processChildren(objBlock, ctx.children);
349         return objBlock;
350     }
351 
352     @Override
353     public DetailAstImpl visitEnumConstants(JavaLanguageParser.EnumConstantsContext ctx) {
354         return flattenedTree(ctx);
355     }
356 
357     @Override
358     public DetailAstImpl visitEnumConstant(JavaLanguageParser.EnumConstantContext ctx) {
359         final DetailAstImpl enumConstant =
360                 createImaginary(TokenTypes.ENUM_CONSTANT_DEF);
361         processChildren(enumConstant, ctx.children);
362         return enumConstant;
363     }
364 
365     @Override
366     public DetailAstImpl visitEnumBodyDeclarations(
367             JavaLanguageParser.EnumBodyDeclarationsContext ctx) {
368         return flattenedTree(ctx);
369     }
370 
371     @Override
372     public DetailAstImpl visitInterfaceDeclaration(
373             JavaLanguageParser.InterfaceDeclarationContext ctx) {
374         return createTypeDeclaration(ctx, TokenTypes.INTERFACE_DEF, ctx.mods);
375     }
376 
377     @Override
378     public DetailAstImpl visitInterfaceExtends(JavaLanguageParser.InterfaceExtendsContext ctx) {
379         final DetailAstImpl interfaceExtends = create(ctx.EXTENDS_CLAUSE());
380         interfaceExtends.addChild(visit(ctx.typeList()));
381         return interfaceExtends;
382     }
383 
384     @Override
385     public DetailAstImpl visitClassBody(JavaLanguageParser.ClassBodyContext ctx) {
386         final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK);
387         processChildren(objBlock, ctx.children);
388         return objBlock;
389     }
390 
391     @Override
392     public DetailAstImpl visitInterfaceBody(JavaLanguageParser.InterfaceBodyContext ctx) {
393         final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK);
394         processChildren(objBlock, ctx.children);
395         return objBlock;
396     }
397 
398     @Override
399     public DetailAstImpl visitEmptyClass(JavaLanguageParser.EmptyClassContext ctx) {
400         return flattenedTree(ctx);
401     }
402 
403     @Override
404     public DetailAstImpl visitClassBlock(JavaLanguageParser.ClassBlockContext ctx) {
405         final DetailAstImpl classBlock;
406         if (ctx.LITERAL_STATIC() == null) {
407             // We call it an INSTANCE_INIT
408             classBlock = createImaginary(TokenTypes.INSTANCE_INIT);
409         }
410         else {
411             classBlock = create(TokenTypes.STATIC_INIT, (Token) ctx.LITERAL_STATIC().getPayload());
412             classBlock.setText(TokenUtil.getTokenName(TokenTypes.STATIC_INIT));
413         }
414         classBlock.addChild(visit(ctx.block()));
415         return classBlock;
416     }
417 
418     @Override
419     public DetailAstImpl visitMethodDeclaration(JavaLanguageParser.MethodDeclarationContext ctx) {
420         final DetailAstImpl methodDef = createImaginary(TokenTypes.METHOD_DEF);
421         methodDef.addChild(createModifiers(ctx.mods));
422 
423         // Process all children except C style array declarators
424         processChildren(methodDef, ctx.children.stream()
425                 .filter(child -> !(child instanceof JavaLanguageParser.ArrayDeclaratorContext))
426                 .collect(Collectors.toUnmodifiableList()));
427 
428         // We add C style array declarator brackets to TYPE ast
429         final DetailAstImpl typeAst = (DetailAstImpl) methodDef.findFirstToken(TokenTypes.TYPE);
430         ctx.cStyleArrDec.forEach(child -> typeAst.addChild(visit(child)));
431 
432         return methodDef;
433     }
434 
435     @Override
436     public DetailAstImpl visitMethodBody(JavaLanguageParser.MethodBodyContext ctx) {
437         return flattenedTree(ctx);
438     }
439 
440     @Override
441     public DetailAstImpl visitThrowsList(JavaLanguageParser.ThrowsListContext ctx) {
442         final DetailAstImpl throwsRoot = create(ctx.LITERAL_THROWS());
443         throwsRoot.addChild(visit(ctx.qualifiedNameList()));
444         return throwsRoot;
445     }
446 
447     @Override
448     public DetailAstImpl visitConstructorDeclaration(
449             JavaLanguageParser.ConstructorDeclarationContext ctx) {
450         final DetailAstImpl constructorDeclaration = createImaginary(TokenTypes.CTOR_DEF);
451         constructorDeclaration.addChild(createModifiers(ctx.mods));
452         processChildren(constructorDeclaration, ctx.children);
453         return constructorDeclaration;
454     }
455 
456     @Override
457     public DetailAstImpl visitFieldDeclaration(JavaLanguageParser.FieldDeclarationContext ctx) {
458         final DetailAstImpl dummyNode = new DetailAstImpl();
459         // Since the TYPE AST is built by visitVariableDeclarator(), we skip it here (child [0])
460         // We also append the SEMI token to the first child [size() - 1],
461         // until https://github.com/checkstyle/checkstyle/issues/3151
462         processChildren(dummyNode, ctx.children.subList(1, ctx.children.size() - 1));
463         dummyNode.getFirstChild().addChild(create(ctx.SEMI()));
464         return dummyNode.getFirstChild();
465     }
466 
467     @Override
468     public DetailAstImpl visitInterfaceBodyDeclaration(
469             JavaLanguageParser.InterfaceBodyDeclarationContext ctx) {
470         final DetailAstImpl returnTree;
471         if (ctx.SEMI() == null) {
472             returnTree = visit(ctx.interfaceMemberDeclaration());
473         }
474         else {
475             returnTree = create(ctx.SEMI());
476         }
477         return returnTree;
478     }
479 
480     @Override
481     public DetailAstImpl visitInterfaceMethodDeclaration(
482             JavaLanguageParser.InterfaceMethodDeclarationContext ctx) {
483         final DetailAstImpl methodDef = createImaginary(TokenTypes.METHOD_DEF);
484         methodDef.addChild(createModifiers(ctx.mods));
485 
486         // Process all children except C style array declarators and modifiers
487         final List<ParseTree> children = ctx.children
488                 .stream()
489                 .filter(child -> !(child instanceof JavaLanguageParser.ArrayDeclaratorContext))
490                 .collect(Collectors.toUnmodifiableList());
491         processChildren(methodDef, children);
492 
493         // We add C style array declarator brackets to TYPE ast
494         final DetailAstImpl typeAst = (DetailAstImpl) methodDef.findFirstToken(TokenTypes.TYPE);
495         ctx.cStyleArrDec.forEach(child -> typeAst.addChild(visit(child)));
496 
497         return methodDef;
498     }
499 
500     @Override
501     public DetailAstImpl visitVariableDeclarators(
502             JavaLanguageParser.VariableDeclaratorsContext ctx) {
503         return flattenedTree(ctx);
504     }
505 
506     @Override
507     public DetailAstImpl visitVariableDeclarator(
508             JavaLanguageParser.VariableDeclaratorContext ctx) {
509         final DetailAstImpl variableDef = createImaginary(TokenTypes.VARIABLE_DEF);
510         variableDef.addChild(createModifiers(ctx.mods));
511 
512         final DetailAstImpl type = visit(ctx.type);
513         variableDef.addChild(type);
514         variableDef.addChild(visit(ctx.id()));
515 
516         // Add C style array declarator brackets to TYPE ast
517         ctx.arrayDeclarator().forEach(child -> type.addChild(visit(child)));
518 
519         // If this is an assignment statement, ASSIGN becomes the parent of EXPR
520         final TerminalNode assignNode = ctx.ASSIGN();
521         if (assignNode != null) {
522             final DetailAstImpl assign = create(assignNode);
523             variableDef.addChild(assign);
524             assign.addChild(visit(ctx.variableInitializer()));
525         }
526         return variableDef;
527     }
528 
529     @Override
530     public DetailAstImpl visitVariableDeclaratorId(
531             JavaLanguageParser.VariableDeclaratorIdContext ctx) {
532         final DetailAstImpl root = new DetailAstImpl();
533         root.addChild(createModifiers(ctx.mods));
534         final DetailAstImpl type = visit(ctx.type);
535         root.addChild(type);
536 
537         final DetailAstImpl declaratorId;
538         if (ctx.LITERAL_THIS() == null) {
539             declaratorId = visit(ctx.qualifiedName());
540         }
541         else if (ctx.DOT() == null) {
542             declaratorId = create(ctx.LITERAL_THIS());
543         }
544         else {
545             declaratorId = create(ctx.DOT());
546             declaratorId.addChild(visit(ctx.qualifiedName()));
547             declaratorId.addChild(create(ctx.LITERAL_THIS()));
548         }
549 
550         root.addChild(declaratorId);
551         ctx.arrayDeclarator().forEach(child -> type.addChild(visit(child)));
552 
553         return root.getFirstChild();
554     }
555 
556     @Override
557     public DetailAstImpl visitArrayInitializer(JavaLanguageParser.ArrayInitializerContext ctx) {
558         final DetailAstImpl arrayInitializer = create(TokenTypes.ARRAY_INIT, ctx.start);
559         // ARRAY_INIT was child[0]
560         processChildren(arrayInitializer, ctx.children.subList(1, ctx.children.size()));
561         return arrayInitializer;
562     }
563 
564     @Override
565     public DetailAstImpl visitClassOrInterfaceType(
566             JavaLanguageParser.ClassOrInterfaceTypeContext ctx) {
567         final DetailAstPair currentAST = new DetailAstPair();
568         DetailAstPair.addAstChild(currentAST, visit(ctx.id()));
569         DetailAstPair.addAstChild(currentAST, visit(ctx.typeArguments()));
570 
571         // This is how we build the annotations/ qualified name/ type parameters tree
572         for (ParserRuleContext extendedContext : ctx.extended) {
573             final DetailAstImpl dot = create(extendedContext.start);
574             DetailAstPair.makeAstRoot(currentAST, dot);
575             extendedContext.children
576                 .forEach(child -> DetailAstPair.addAstChild(currentAST, visit(child)));
577         }
578 
579         // Create imaginary 'TYPE' parent if specified
580         final DetailAstImpl returnTree;
581         if (ctx.createImaginaryNode) {
582             returnTree = createImaginary(TokenTypes.TYPE);
583             returnTree.addChild(currentAST.root);
584         }
585         else {
586             returnTree = currentAST.root;
587         }
588         return returnTree;
589     }
590 
591     @Override
592     public DetailAstImpl visitSimpleTypeArgument(
593             JavaLanguageParser.SimpleTypeArgumentContext ctx) {
594         final DetailAstImpl typeArgument =
595                 createImaginary(TokenTypes.TYPE_ARGUMENT);
596         typeArgument.addChild(visit(ctx.typeType()));
597         return typeArgument;
598     }
599 
600     @Override
601     public DetailAstImpl visitWildCardTypeArgument(
602             JavaLanguageParser.WildCardTypeArgumentContext ctx) {
603         final DetailAstImpl typeArgument = createImaginary(TokenTypes.TYPE_ARGUMENT);
604         typeArgument.addChild(visit(ctx.annotations()));
605         typeArgument.addChild(create(TokenTypes.WILDCARD_TYPE,
606                 (Token) ctx.QUESTION().getPayload()));
607 
608         if (ctx.upperBound != null) {
609             final DetailAstImpl upperBound = create(TokenTypes.TYPE_UPPER_BOUNDS, ctx.upperBound);
610             upperBound.addChild(visit(ctx.typeType()));
611             typeArgument.addChild(upperBound);
612         }
613         else if (ctx.lowerBound != null) {
614             final DetailAstImpl lowerBound = create(TokenTypes.TYPE_LOWER_BOUNDS, ctx.lowerBound);
615             lowerBound.addChild(visit(ctx.typeType()));
616             typeArgument.addChild(lowerBound);
617         }
618 
619         return typeArgument;
620     }
621 
622     @Override
623     public DetailAstImpl visitQualifiedNameList(JavaLanguageParser.QualifiedNameListContext ctx) {
624         return flattenedTree(ctx);
625     }
626 
627     @Override
628     public DetailAstImpl visitFormalParameters(JavaLanguageParser.FormalParametersContext ctx) {
629         final DetailAstImpl lparen = create(ctx.LPAREN());
630 
631         // We make a "PARAMETERS" node whether parameters exist or not
632         if (ctx.formalParameterList() == null) {
633             addLastSibling(lparen, createImaginary(TokenTypes.PARAMETERS));
634         }
635         else {
636             addLastSibling(lparen, visit(ctx.formalParameterList()));
637         }
638         addLastSibling(lparen, create(ctx.RPAREN()));
639         return lparen;
640     }
641 
642     @Override
643     public DetailAstImpl visitFormalParameterList(
644             JavaLanguageParser.FormalParameterListContext ctx) {
645         final DetailAstImpl parameters = createImaginary(TokenTypes.PARAMETERS);
646         processChildren(parameters, ctx.children);
647         return parameters;
648     }
649 
650     @Override
651     public DetailAstImpl visitFormalParameter(JavaLanguageParser.FormalParameterContext ctx) {
652         final DetailAstImpl variableDeclaratorId =
653                 visitVariableDeclaratorId(ctx.variableDeclaratorId());
654         final DetailAstImpl parameterDef = createImaginary(TokenTypes.PARAMETER_DEF);
655         parameterDef.addChild(variableDeclaratorId);
656         return parameterDef;
657     }
658 
659     @Override
660     public DetailAstImpl visitLastFormalParameter(
661             JavaLanguageParser.LastFormalParameterContext ctx) {
662         final DetailAstImpl parameterDef =
663                 createImaginary(TokenTypes.PARAMETER_DEF);
664         parameterDef.addChild(visit(ctx.variableDeclaratorId()));
665         final DetailAstImpl ident = (DetailAstImpl) parameterDef.findFirstToken(TokenTypes.IDENT);
666         ident.addPreviousSibling(create(ctx.ELLIPSIS()));
667         // We attach annotations on ellipses in varargs to the 'TYPE' ast
668         final DetailAstImpl type = (DetailAstImpl) parameterDef.findFirstToken(TokenTypes.TYPE);
669         type.addChild(visit(ctx.annotations()));
670         return parameterDef;
671     }
672 
673     @Override
674     public DetailAstImpl visitQualifiedName(JavaLanguageParser.QualifiedNameContext ctx) {
675         final DetailAstImpl ast = visit(ctx.id());
676         final DetailAstPair currentAst = new DetailAstPair();
677         DetailAstPair.addAstChild(currentAst, ast);
678 
679         for (ParserRuleContext extendedContext : ctx.extended) {
680             final DetailAstImpl dot = create(extendedContext.start);
681             DetailAstPair.makeAstRoot(currentAst, dot);
682             final List<ParseTree> childList = extendedContext
683                     .children.subList(1, extendedContext.children.size());
684             processChildren(dot, childList);
685         }
686         return currentAst.getRoot();
687     }
688 
689     @Override
690     public DetailAstImpl visitLiteral(JavaLanguageParser.LiteralContext ctx) {
691         return flattenedTree(ctx);
692     }
693 
694     @Override
695     public DetailAstImpl visitIntegerLiteral(JavaLanguageParser.IntegerLiteralContext ctx) {
696         final int[] longTypes = {
697             JavaLanguageLexer.DECIMAL_LITERAL_LONG,
698             JavaLanguageLexer.HEX_LITERAL_LONG,
699             JavaLanguageLexer.OCT_LITERAL_LONG,
700             JavaLanguageLexer.BINARY_LITERAL_LONG,
701         };
702 
703         final int tokenType;
704         if (TokenUtil.isOfType(ctx.start.getType(), longTypes)) {
705             tokenType = TokenTypes.NUM_LONG;
706         }
707         else {
708             tokenType = TokenTypes.NUM_INT;
709         }
710 
711         return create(tokenType, ctx.start);
712     }
713 
714     @Override
715     public DetailAstImpl visitFloatLiteral(JavaLanguageParser.FloatLiteralContext ctx) {
716         final DetailAstImpl floatLiteral;
717         if (TokenUtil.isOfType(ctx.start.getType(),
718                 JavaLanguageLexer.DOUBLE_LITERAL, JavaLanguageLexer.HEX_DOUBLE_LITERAL)) {
719             floatLiteral = create(TokenTypes.NUM_DOUBLE, ctx.start);
720         }
721         else {
722             floatLiteral = create(TokenTypes.NUM_FLOAT, ctx.start);
723         }
724         return floatLiteral;
725     }
726 
727     @Override
728     public DetailAstImpl visitTextBlockLiteral(JavaLanguageParser.TextBlockLiteralContext ctx) {
729         final DetailAstImpl textBlockLiteralBegin = create(ctx.TEXT_BLOCK_LITERAL_BEGIN());
730         textBlockLiteralBegin.addChild(create(ctx.TEXT_BLOCK_CONTENT()));
731         textBlockLiteralBegin.addChild(create(ctx.TEXT_BLOCK_LITERAL_END()));
732         return textBlockLiteralBegin;
733     }
734 
735     @Override
736     public DetailAstImpl visitAnnotations(JavaLanguageParser.AnnotationsContext ctx) {
737         final DetailAstImpl annotations;
738 
739         if (!ctx.createImaginaryNode && ctx.anno.isEmpty()) {
740             // There are no annotations, and we don't want to create the empty node
741             annotations = null;
742         }
743         else {
744             // There are annotations, or we just want the empty node
745             annotations = createImaginary(TokenTypes.ANNOTATIONS);
746             processChildren(annotations, ctx.anno);
747         }
748 
749         return annotations;
750     }
751 
752     @Override
753     public DetailAstImpl visitAnnotation(JavaLanguageParser.AnnotationContext ctx) {
754         final DetailAstImpl annotation = createImaginary(TokenTypes.ANNOTATION);
755         processChildren(annotation, ctx.children);
756         return annotation;
757     }
758 
759     @Override
760     public DetailAstImpl visitElementValuePairs(JavaLanguageParser.ElementValuePairsContext ctx) {
761         return flattenedTree(ctx);
762     }
763 
764     @Override
765     public DetailAstImpl visitElementValuePair(JavaLanguageParser.ElementValuePairContext ctx) {
766         final DetailAstImpl elementValuePair =
767                 createImaginary(TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR);
768         processChildren(elementValuePair, ctx.children);
769         return elementValuePair;
770     }
771 
772     @Override
773     public DetailAstImpl visitElementValue(JavaLanguageParser.ElementValueContext ctx) {
774         return flattenedTree(ctx);
775     }
776 
777     @Override
778     public DetailAstImpl visitElementValueArrayInitializer(
779             JavaLanguageParser.ElementValueArrayInitializerContext ctx) {
780         final DetailAstImpl arrayInit =
781                 create(TokenTypes.ANNOTATION_ARRAY_INIT, (Token) ctx.LCURLY().getPayload());
782         processChildren(arrayInit, ctx.children.subList(1, ctx.children.size()));
783         return arrayInit;
784     }
785 
786     @Override
787     public DetailAstImpl visitAnnotationTypeDeclaration(
788             JavaLanguageParser.AnnotationTypeDeclarationContext ctx) {
789         return createTypeDeclaration(ctx, TokenTypes.ANNOTATION_DEF, ctx.mods);
790     }
791 
792     @Override
793     public DetailAstImpl visitAnnotationTypeBody(
794             JavaLanguageParser.AnnotationTypeBodyContext ctx) {
795         final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK);
796         processChildren(objBlock, ctx.children);
797         return objBlock;
798     }
799 
800     @Override
801     public DetailAstImpl visitAnnotationTypeElementDeclaration(
802             JavaLanguageParser.AnnotationTypeElementDeclarationContext ctx) {
803         final DetailAstImpl returnTree;
804         if (ctx.SEMI() == null) {
805             returnTree = visit(ctx.annotationTypeElementRest());
806         }
807         else {
808             returnTree = create(ctx.SEMI());
809         }
810         return returnTree;
811     }
812 
813     @Override
814     public DetailAstImpl visitAnnotationField(JavaLanguageParser.AnnotationFieldContext ctx) {
815         final DetailAstImpl dummyNode = new DetailAstImpl();
816         // Since the TYPE AST is built by visitAnnotationMethodOrConstantRest(), we skip it
817         // here (child [0])
818         processChildren(dummyNode, Collections.singletonList(ctx.children.get(1)));
819         // We also append the SEMI token to the first child [size() - 1],
820         // until https://github.com/checkstyle/checkstyle/issues/3151
821         dummyNode.getFirstChild().addChild(create(ctx.SEMI()));
822         return dummyNode.getFirstChild();
823     }
824 
825     @Override
826     public DetailAstImpl visitAnnotationType(JavaLanguageParser.AnnotationTypeContext ctx) {
827         return flattenedTree(ctx);
828     }
829 
830     @Override
831     public DetailAstImpl visitAnnotationMethodRest(
832             JavaLanguageParser.AnnotationMethodRestContext ctx) {
833         final DetailAstImpl annotationFieldDef =
834                 createImaginary(TokenTypes.ANNOTATION_FIELD_DEF);
835         annotationFieldDef.addChild(createModifiers(ctx.mods));
836         annotationFieldDef.addChild(visit(ctx.type));
837 
838         // Process all children except C style array declarators
839         processChildren(annotationFieldDef, ctx.children.stream()
840                 .filter(child -> !(child instanceof JavaLanguageParser.ArrayDeclaratorContext))
841                 .collect(Collectors.toUnmodifiableList()));
842 
843         // We add C style array declarator brackets to TYPE ast
844         final DetailAstImpl typeAst =
845                 (DetailAstImpl) annotationFieldDef.findFirstToken(TokenTypes.TYPE);
846         ctx.cStyleArrDec.forEach(child -> typeAst.addChild(visit(child)));
847 
848         return annotationFieldDef;
849     }
850 
851     @Override
852     public DetailAstImpl visitDefaultValue(JavaLanguageParser.DefaultValueContext ctx) {
853         final DetailAstImpl defaultValue = create(ctx.LITERAL_DEFAULT());
854         defaultValue.addChild(visit(ctx.elementValue()));
855         return defaultValue;
856     }
857 
858     @Override
859     public DetailAstImpl visitConstructorBlock(JavaLanguageParser.ConstructorBlockContext ctx) {
860         final DetailAstImpl slist = create(TokenTypes.SLIST, ctx.start);
861         // SLIST was child [0]
862         processChildren(slist, ctx.children.subList(1, ctx.children.size()));
863         return slist;
864     }
865 
866     @Override
867     public DetailAstImpl visitExplicitCtorCall(JavaLanguageParser.ExplicitCtorCallContext ctx) {
868         final DetailAstImpl root;
869         if (ctx.LITERAL_THIS() == null) {
870             root = create(TokenTypes.SUPER_CTOR_CALL, (Token) ctx.LITERAL_SUPER().getPayload());
871         }
872         else {
873             root = create(TokenTypes.CTOR_CALL, (Token) ctx.LITERAL_THIS().getPayload());
874         }
875         root.addChild(visit(ctx.typeArguments()));
876         root.addChild(visit(ctx.arguments()));
877         root.addChild(create(ctx.SEMI()));
878         return root;
879     }
880 
881     @Override
882     public DetailAstImpl visitPrimaryCtorCall(JavaLanguageParser.PrimaryCtorCallContext ctx) {
883         final DetailAstImpl primaryCtorCall = create(TokenTypes.SUPER_CTOR_CALL,
884                 (Token) ctx.LITERAL_SUPER().getPayload());
885         // filter 'LITERAL_SUPER'
886         processChildren(primaryCtorCall, ctx.children.stream()
887                    .filter(child -> !child.equals(ctx.LITERAL_SUPER()))
888                    .collect(Collectors.toUnmodifiableList()));
889         return primaryCtorCall;
890     }
891 
892     @Override
893     public DetailAstImpl visitBlock(JavaLanguageParser.BlockContext ctx) {
894         final DetailAstImpl slist = create(TokenTypes.SLIST, ctx.start);
895         // SLIST was child [0]
896         processChildren(slist, ctx.children.subList(1, ctx.children.size()));
897         return slist;
898     }
899 
900     @Override
901     public DetailAstImpl visitLocalVar(JavaLanguageParser.LocalVarContext ctx) {
902         return flattenedTree(ctx);
903     }
904 
905     @Override
906     public DetailAstImpl visitBlockStat(JavaLanguageParser.BlockStatContext ctx) {
907         return flattenedTree(ctx);
908     }
909 
910     @Override
911     public DetailAstImpl visitAssertExp(JavaLanguageParser.AssertExpContext ctx) {
912         final DetailAstImpl assertExp = create(ctx.ASSERT());
913         // child[0] is 'ASSERT'
914         processChildren(assertExp, ctx.children.subList(1, ctx.children.size()));
915         return assertExp;
916     }
917 
918     @Override
919     public DetailAstImpl visitIfStat(JavaLanguageParser.IfStatContext ctx) {
920         final DetailAstImpl ifStat = create(ctx.LITERAL_IF());
921         // child[0] is 'LITERAL_IF'
922         processChildren(ifStat, ctx.children.subList(1, ctx.children.size()));
923         return ifStat;
924     }
925 
926     @Override
927     public DetailAstImpl visitForStat(JavaLanguageParser.ForStatContext ctx) {
928         final DetailAstImpl forInit = create(ctx.start);
929         // child[0] is LITERAL_FOR
930         processChildren(forInit, ctx.children.subList(1, ctx.children.size()));
931         return forInit;
932     }
933 
934     @Override
935     public DetailAstImpl visitWhileStat(JavaLanguageParser.WhileStatContext ctx) {
936         final DetailAstImpl whileStatement = create(ctx.start);
937         // 'LITERAL_WHILE' is child[0]
938         processChildren(whileStatement, ctx.children.subList(1, ctx.children.size()));
939         return whileStatement;
940     }
941 
942     @Override
943     public DetailAstImpl visitDoStat(JavaLanguageParser.DoStatContext ctx) {
944         final DetailAstImpl doStatement = create(ctx.start);
945         // 'LITERAL_DO' is child[0]
946         doStatement.addChild(visit(ctx.statement()));
947         // We make 'LITERAL_WHILE' into 'DO_WHILE'
948         doStatement.addChild(create(TokenTypes.DO_WHILE, (Token) ctx.LITERAL_WHILE().getPayload()));
949         doStatement.addChild(visit(ctx.parExpression()));
950         doStatement.addChild(create(ctx.SEMI()));
951         return doStatement;
952     }
953 
954     @Override
955     public DetailAstImpl visitTryStat(JavaLanguageParser.TryStatContext ctx) {
956         final DetailAstImpl tryStat = create(ctx.start);
957         // child[0] is 'LITERAL_TRY'
958         processChildren(tryStat, ctx.children.subList(1, ctx.children.size()));
959         return tryStat;
960     }
961 
962     @Override
963     public DetailAstImpl visitTryWithResourceStat(
964             JavaLanguageParser.TryWithResourceStatContext ctx) {
965         final DetailAstImpl tryWithResources = create(ctx.LITERAL_TRY());
966         // child[0] is 'LITERAL_TRY'
967         processChildren(tryWithResources, ctx.children.subList(1, ctx.children.size()));
968         return tryWithResources;
969     }
970 
971     @Override
972     public DetailAstImpl visitYieldStat(JavaLanguageParser.YieldStatContext ctx) {
973         final DetailAstImpl yieldParent = create(ctx.LITERAL_YIELD());
974         // LITERAL_YIELD is child[0]
975         processChildren(yieldParent, ctx.children.subList(1, ctx.children.size()));
976         return yieldParent;
977     }
978 
979     @Override
980     public DetailAstImpl visitSyncStat(JavaLanguageParser.SyncStatContext ctx) {
981         final DetailAstImpl syncStatement = create(ctx.start);
982         // child[0] is 'LITERAL_SYNCHRONIZED'
983         processChildren(syncStatement, ctx.children.subList(1, ctx.children.size()));
984         return syncStatement;
985     }
986 
987     @Override
988     public DetailAstImpl visitReturnStat(JavaLanguageParser.ReturnStatContext ctx) {
989         final DetailAstImpl returnStat = create(ctx.LITERAL_RETURN());
990         // child[0] is 'LITERAL_RETURN'
991         processChildren(returnStat, ctx.children.subList(1, ctx.children.size()));
992         return returnStat;
993     }
994 
995     @Override
996     public DetailAstImpl visitThrowStat(JavaLanguageParser.ThrowStatContext ctx) {
997         final DetailAstImpl throwStat = create(ctx.LITERAL_THROW());
998         // child[0] is 'LITERAL_THROW'
999         processChildren(throwStat, ctx.children.subList(1, ctx.children.size()));
1000         return throwStat;
1001     }
1002 
1003     @Override
1004     public DetailAstImpl visitBreakStat(JavaLanguageParser.BreakStatContext ctx) {
1005         final DetailAstImpl literalBreak = create(ctx.LITERAL_BREAK());
1006         // child[0] is 'LITERAL_BREAK'
1007         processChildren(literalBreak, ctx.children.subList(1, ctx.children.size()));
1008         return literalBreak;
1009     }
1010 
1011     @Override
1012     public DetailAstImpl visitContinueStat(JavaLanguageParser.ContinueStatContext ctx) {
1013         final DetailAstImpl continueStat = create(ctx.LITERAL_CONTINUE());
1014         // child[0] is 'LITERAL_CONTINUE'
1015         processChildren(continueStat, ctx.children.subList(1, ctx.children.size()));
1016         return continueStat;
1017     }
1018 
1019     @Override
1020     public DetailAstImpl visitEmptyStat(JavaLanguageParser.EmptyStatContext ctx) {
1021         return create(TokenTypes.EMPTY_STAT, ctx.start);
1022     }
1023 
1024     @Override
1025     public DetailAstImpl visitExpStat(JavaLanguageParser.ExpStatContext ctx) {
1026         final DetailAstImpl expStatRoot = visit(ctx.statementExpression);
1027         addLastSibling(expStatRoot, create(ctx.SEMI()));
1028         return expStatRoot;
1029     }
1030 
1031     @Override
1032     public DetailAstImpl visitLabelStat(JavaLanguageParser.LabelStatContext ctx) {
1033         final DetailAstImpl labelStat = create(TokenTypes.LABELED_STAT,
1034                 (Token) ctx.COLON().getPayload());
1035         labelStat.addChild(visit(ctx.id()));
1036         labelStat.addChild(visit(ctx.statement()));
1037         return labelStat;
1038     }
1039 
1040     @Override
1041     public DetailAstImpl visitSwitchExpressionOrStatement(
1042             JavaLanguageParser.SwitchExpressionOrStatementContext ctx) {
1043         final DetailAstImpl switchStat = create(ctx.LITERAL_SWITCH());
1044         switchStat.addChild(visit(ctx.parExpression()));
1045         switchStat.addChild(create(ctx.LCURLY()));
1046         switchStat.addChild(visit(ctx.switchBlock()));
1047         switchStat.addChild(create(ctx.RCURLY()));
1048         return switchStat;
1049     }
1050 
1051     @Override
1052     public DetailAstImpl visitSwitchRules(JavaLanguageParser.SwitchRulesContext ctx) {
1053         final DetailAstImpl dummyRoot = new DetailAstImpl();
1054         ctx.switchLabeledRule().forEach(switchLabeledRuleContext -> {
1055             final DetailAstImpl switchRule = visit(switchLabeledRuleContext);
1056             final DetailAstImpl switchRuleParent = createImaginary(TokenTypes.SWITCH_RULE);
1057             switchRuleParent.addChild(switchRule);
1058             dummyRoot.addChild(switchRuleParent);
1059         });
1060         return dummyRoot.getFirstChild();
1061     }
1062 
1063     @Override
1064     public DetailAstImpl visitSwitchBlocks(JavaLanguageParser.SwitchBlocksContext ctx) {
1065         final DetailAstImpl dummyRoot = new DetailAstImpl();
1066         ctx.groups.forEach(group -> dummyRoot.addChild(visit(group)));
1067 
1068         // Add any empty switch labels to end of statement in one 'CASE_GROUP'
1069         if (!ctx.emptyLabels.isEmpty()) {
1070             final DetailAstImpl emptyLabelParent =
1071                     createImaginary(TokenTypes.CASE_GROUP);
1072             ctx.emptyLabels.forEach(label -> emptyLabelParent.addChild(visit(label)));
1073             dummyRoot.addChild(emptyLabelParent);
1074         }
1075         return dummyRoot.getFirstChild();
1076     }
1077 
1078     @Override
1079     public DetailAstImpl visitSwitchLabeledExpression(
1080             JavaLanguageParser.SwitchLabeledExpressionContext ctx) {
1081         return flattenedTree(ctx);
1082     }
1083 
1084     @Override
1085     public DetailAstImpl visitSwitchLabeledBlock(
1086             JavaLanguageParser.SwitchLabeledBlockContext ctx) {
1087         return flattenedTree(ctx);
1088     }
1089 
1090     @Override
1091     public DetailAstImpl visitSwitchLabeledThrow(
1092             JavaLanguageParser.SwitchLabeledThrowContext ctx) {
1093         final DetailAstImpl switchLabel = visit(ctx.switchLabel());
1094         addLastSibling(switchLabel, create(ctx.LAMBDA()));
1095         final DetailAstImpl literalThrow = create(ctx.LITERAL_THROW());
1096         literalThrow.addChild(visit(ctx.expression()));
1097         literalThrow.addChild(create(ctx.SEMI()));
1098         addLastSibling(switchLabel, literalThrow);
1099         return switchLabel;
1100     }
1101 
1102     @Override
1103     public DetailAstImpl visitElseStat(JavaLanguageParser.ElseStatContext ctx) {
1104         final DetailAstImpl elseStat = create(ctx.LITERAL_ELSE());
1105         // child[0] is 'LITERAL_ELSE'
1106         processChildren(elseStat, ctx.children.subList(1, ctx.children.size()));
1107         return elseStat;
1108     }
1109 
1110     @Override
1111     public DetailAstImpl visitCatchClause(JavaLanguageParser.CatchClauseContext ctx) {
1112         final DetailAstImpl catchClause = create(TokenTypes.LITERAL_CATCH,
1113                 (Token) ctx.LITERAL_CATCH().getPayload());
1114         // 'LITERAL_CATCH' is child[0]
1115         processChildren(catchClause, ctx.children.subList(1, ctx.children.size()));
1116         return catchClause;
1117     }
1118 
1119     @Override
1120     public DetailAstImpl visitCatchParameter(JavaLanguageParser.CatchParameterContext ctx) {
1121         final DetailAstImpl catchParameterDef = createImaginary(TokenTypes.PARAMETER_DEF);
1122         catchParameterDef.addChild(createModifiers(ctx.mods));
1123         // filter mods
1124         processChildren(catchParameterDef, ctx.children.stream()
1125                 .filter(child -> !(child instanceof JavaLanguageParser.VariableModifierContext))
1126                 .collect(Collectors.toUnmodifiableList()));
1127         return catchParameterDef;
1128     }
1129 
1130     @Override
1131     public DetailAstImpl visitCatchType(JavaLanguageParser.CatchTypeContext ctx) {
1132         final DetailAstImpl type = createImaginary(TokenTypes.TYPE);
1133         processChildren(type, ctx.children);
1134         return type;
1135     }
1136 
1137     @Override
1138     public DetailAstImpl visitFinallyBlock(JavaLanguageParser.FinallyBlockContext ctx) {
1139         final DetailAstImpl finallyBlock = create(ctx.LITERAL_FINALLY());
1140         // child[0] is 'LITERAL_FINALLY'
1141         processChildren(finallyBlock, ctx.children.subList(1, ctx.children.size()));
1142         return finallyBlock;
1143     }
1144 
1145     @Override
1146     public DetailAstImpl visitResourceSpecification(
1147             JavaLanguageParser.ResourceSpecificationContext ctx) {
1148         final DetailAstImpl resourceSpecification =
1149                 createImaginary(TokenTypes.RESOURCE_SPECIFICATION);
1150         processChildren(resourceSpecification, ctx.children);
1151         return resourceSpecification;
1152     }
1153 
1154     @Override
1155     public DetailAstImpl visitResources(JavaLanguageParser.ResourcesContext ctx) {
1156         final DetailAstImpl firstResource = visit(ctx.resource(0));
1157         final DetailAstImpl resources = createImaginary(TokenTypes.RESOURCES);
1158         resources.addChild(firstResource);
1159         processChildren(resources, ctx.children.subList(1, ctx.children.size()));
1160         return resources;
1161     }
1162 
1163     @Override
1164     public DetailAstImpl visitResourceDeclaration(
1165             JavaLanguageParser.ResourceDeclarationContext ctx) {
1166         final DetailAstImpl resource = createImaginary(TokenTypes.RESOURCE);
1167         resource.addChild(visit(ctx.variableDeclaratorId()));
1168 
1169         final DetailAstImpl assign = create(ctx.ASSIGN());
1170         resource.addChild(assign);
1171         assign.addChild(visit(ctx.expression()));
1172         return resource;
1173     }
1174 
1175     @Override
1176     public DetailAstImpl visitVariableAccess(JavaLanguageParser.VariableAccessContext ctx) {
1177         final DetailAstImpl resource;
1178         if (ctx.accessList.isEmpty()) {
1179             resource = createImaginary(TokenTypes.RESOURCE);
1180             resource.addChild(visit(ctx.id()));
1181         }
1182         else {
1183             final DetailAstPair currentAst = new DetailAstPair();
1184             ctx.accessList.forEach(fieldAccess -> {
1185                 DetailAstPair.addAstChild(currentAst, visit(fieldAccess.expr()));
1186                 DetailAstPair.makeAstRoot(currentAst, create(fieldAccess.DOT()));
1187             });
1188             resource = createImaginary(TokenTypes.RESOURCE);
1189             resource.addChild(currentAst.root);
1190             if (ctx.LITERAL_THIS() == null) {
1191                 resource.getFirstChild().addChild(visit(ctx.id()));
1192             }
1193             else {
1194                 resource.getFirstChild().addChild(create(ctx.LITERAL_THIS()));
1195             }
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 visitTemplateExp(JavaLanguageParser.TemplateExpContext ctx) {
1542         final DetailAstImpl dot = create(ctx.DOT());
1543         dot.addChild(visit(ctx.expr()));
1544         dot.addChild(visit(ctx.templateArgument()));
1545         return dot;
1546     }
1547 
1548     @Override
1549     public DetailAstImpl visitPostfix(JavaLanguageParser.PostfixContext ctx) {
1550         final DetailAstImpl postfix;
1551         if (ctx.postfix.getType() == JavaLanguageLexer.INC) {
1552             postfix = create(TokenTypes.POST_INC, ctx.postfix);
1553         }
1554         else {
1555             postfix = create(TokenTypes.POST_DEC, ctx.postfix);
1556         }
1557         postfix.addChild(visit(ctx.expr()));
1558         return postfix;
1559     }
1560 
1561     @Override
1562     public DetailAstImpl visitMethodRef(JavaLanguageParser.MethodRefContext ctx) {
1563         final DetailAstImpl doubleColon = create(TokenTypes.METHOD_REF,
1564                 (Token) ctx.DOUBLE_COLON().getPayload());
1565         final List<ParseTree> children = ctx.children.stream()
1566                 .filter(child -> !child.equals(ctx.DOUBLE_COLON()))
1567                 .collect(Collectors.toUnmodifiableList());
1568         processChildren(doubleColon, children);
1569         return doubleColon;
1570     }
1571 
1572     @Override
1573     public DetailAstImpl visitTernaryOp(JavaLanguageParser.TernaryOpContext ctx) {
1574         final DetailAstImpl root = create(ctx.QUESTION());
1575         processChildren(root, ctx.children.stream()
1576                 .filter(child -> !child.equals(ctx.QUESTION()))
1577                 .collect(Collectors.toUnmodifiableList()));
1578         return root;
1579     }
1580 
1581     @Override
1582     public DetailAstImpl visitBinOp(JavaLanguageParser.BinOpContext ctx) {
1583         final DetailAstImpl bop = create(ctx.bop);
1584 
1585         // To improve performance, we iterate through binary operations
1586         // since they are frequently deeply nested.
1587         final List<JavaLanguageParser.BinOpContext> binOpList = new ArrayList<>();
1588         ParseTree firstExpression = ctx.expr(0);
1589         while (firstExpression instanceof JavaLanguageParser.BinOpContext) {
1590             // Get all nested binOps
1591             binOpList.add((JavaLanguageParser.BinOpContext) firstExpression);
1592             firstExpression = ((JavaLanguageParser.BinOpContext) firstExpression).expr(0);
1593         }
1594 
1595         if (binOpList.isEmpty()) {
1596             final DetailAstImpl leftChild = visit(ctx.children.get(0));
1597             bop.addChild(leftChild);
1598         }
1599         else {
1600             // Map all descendants to individual AST's since we can parallelize this
1601             // operation
1602             final Queue<DetailAstImpl> descendantList = binOpList.parallelStream()
1603                     .map(this::getInnerBopAst)
1604                     .collect(Collectors.toCollection(ConcurrentLinkedQueue::new));
1605 
1606             bop.addChild(descendantList.poll());
1607             DetailAstImpl pointer = bop.getFirstChild();
1608             // Build tree
1609             for (DetailAstImpl descendant : descendantList) {
1610                 pointer.getFirstChild().addPreviousSibling(descendant);
1611                 pointer = descendant;
1612             }
1613         }
1614 
1615         bop.addChild(visit(ctx.children.get(2)));
1616         return bop;
1617     }
1618 
1619     /**
1620      * Builds the binary operation (binOp) AST.
1621      *
1622      * @param descendant the BinOpContext to build AST from
1623      * @return binOp AST
1624      */
1625     private DetailAstImpl getInnerBopAst(JavaLanguageParser.BinOpContext descendant) {
1626         final DetailAstImpl innerBop = create(descendant.bop);
1627         final JavaLanguageParser.ExprContext expr = descendant.expr(0);
1628         if (!(expr instanceof JavaLanguageParser.BinOpContext)) {
1629             innerBop.addChild(visit(expr));
1630         }
1631         innerBop.addChild(visit(descendant.expr(1)));
1632         return innerBop;
1633     }
1634 
1635     @Override
1636     public DetailAstImpl visitMethodCall(JavaLanguageParser.MethodCallContext ctx) {
1637         final DetailAstImpl methodCall = create(TokenTypes.METHOD_CALL,
1638                 (Token) ctx.LPAREN().getPayload());
1639         // We always add an 'ELIST' node
1640         final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList()))
1641                 .orElseGet(() -> createImaginary(TokenTypes.ELIST));
1642 
1643         final DetailAstImpl dot = create(ctx.DOT());
1644         dot.addChild(visit(ctx.expr()));
1645         dot.addChild(visit(ctx.id()));
1646         methodCall.addChild(dot);
1647         methodCall.addChild(expressionList);
1648         methodCall.addChild(create((Token) ctx.RPAREN().getPayload()));
1649         return methodCall;
1650     }
1651 
1652     @Override
1653     public DetailAstImpl visitTypeCastParameters(
1654             JavaLanguageParser.TypeCastParametersContext ctx) {
1655         final DetailAstImpl typeType = visit(ctx.typeType(0));
1656         for (int i = 0; i < ctx.BAND().size(); i++) {
1657             addLastSibling(typeType, create(TokenTypes.TYPE_EXTENSION_AND,
1658                                 (Token) ctx.BAND(i).getPayload()));
1659             addLastSibling(typeType, visit(ctx.typeType(i + 1)));
1660         }
1661         return typeType;
1662     }
1663 
1664     @Override
1665     public DetailAstImpl visitSingleLambdaParam(JavaLanguageParser.SingleLambdaParamContext ctx) {
1666         return flattenedTree(ctx);
1667     }
1668 
1669     @Override
1670     public DetailAstImpl visitFormalLambdaParam(JavaLanguageParser.FormalLambdaParamContext ctx) {
1671         final DetailAstImpl lparen = create(ctx.LPAREN());
1672 
1673         // We add an 'PARAMETERS' node here whether it exists or not
1674         final DetailAstImpl parameters = Optional.ofNullable(visit(ctx.formalParameterList()))
1675                 .orElseGet(() -> createImaginary(TokenTypes.PARAMETERS));
1676         addLastSibling(lparen, parameters);
1677         addLastSibling(lparen, create(ctx.RPAREN()));
1678         return lparen;
1679     }
1680 
1681     @Override
1682     public DetailAstImpl visitMultiLambdaParam(JavaLanguageParser.MultiLambdaParamContext ctx) {
1683         final DetailAstImpl lparen = create(ctx.LPAREN());
1684         addLastSibling(lparen, visit(ctx.multiLambdaParams()));
1685         addLastSibling(lparen, create(ctx.RPAREN()));
1686         return lparen;
1687     }
1688 
1689     @Override
1690     public DetailAstImpl visitMultiLambdaParams(JavaLanguageParser.MultiLambdaParamsContext ctx) {
1691         final DetailAstImpl parameters = createImaginary(TokenTypes.PARAMETERS);
1692         parameters.addChild(createLambdaParameter(ctx.id(0)));
1693 
1694         for (int i = 0; i < ctx.COMMA().size(); i++) {
1695             parameters.addChild(create(ctx.COMMA(i)));
1696             parameters.addChild(createLambdaParameter(ctx.id(i + 1)));
1697         }
1698         return parameters;
1699     }
1700 
1701     /**
1702      * Creates a 'PARAMETER_DEF' node for a lambda expression, with
1703      * imaginary modifier and type nodes.
1704      *
1705      * @param ctx the IdContext to create imaginary nodes for
1706      * @return DetailAstImpl of lambda parameter
1707      */
1708     private DetailAstImpl createLambdaParameter(JavaLanguageParser.IdContext ctx) {
1709         final DetailAstImpl ident = visitId(ctx);
1710         final DetailAstImpl parameter = createImaginary(TokenTypes.PARAMETER_DEF);
1711         final DetailAstImpl modifiers = createImaginary(TokenTypes.MODIFIERS);
1712         final DetailAstImpl type = createImaginary(TokenTypes.TYPE);
1713         parameter.addChild(modifiers);
1714         parameter.addChild(type);
1715         parameter.addChild(ident);
1716         return parameter;
1717     }
1718 
1719     @Override
1720     public DetailAstImpl visitParenPrimary(JavaLanguageParser.ParenPrimaryContext ctx) {
1721         return flattenedTree(ctx);
1722     }
1723 
1724     @Override
1725     public DetailAstImpl visitTokenPrimary(JavaLanguageParser.TokenPrimaryContext ctx) {
1726         return flattenedTree(ctx);
1727     }
1728 
1729     @Override
1730     public DetailAstImpl visitClassRefPrimary(JavaLanguageParser.ClassRefPrimaryContext ctx) {
1731         final DetailAstImpl dot = create(ctx.DOT());
1732         final DetailAstImpl primaryTypeNoArray = visit(ctx.type);
1733         dot.addChild(primaryTypeNoArray);
1734         if (TokenUtil.isOfType(primaryTypeNoArray, TokenTypes.DOT)) {
1735             // We append '[]' to the qualified name 'TYPE' `ast
1736             ctx.arrayDeclarator()
1737                     .forEach(child -> primaryTypeNoArray.addChild(visit(child)));
1738         }
1739         else {
1740             ctx.arrayDeclarator()
1741                     .forEach(child -> addLastSibling(primaryTypeNoArray, visit(child)));
1742         }
1743         dot.addChild(create(ctx.LITERAL_CLASS()));
1744         return dot;
1745     }
1746 
1747     @Override
1748     public DetailAstImpl visitPrimitivePrimary(JavaLanguageParser.PrimitivePrimaryContext ctx) {
1749         final DetailAstImpl dot = create(ctx.DOT());
1750         final DetailAstImpl primaryTypeNoArray = visit(ctx.type);
1751         dot.addChild(primaryTypeNoArray);
1752         ctx.arrayDeclarator().forEach(child -> dot.addChild(visit(child)));
1753         dot.addChild(create(ctx.LITERAL_CLASS()));
1754         return dot;
1755     }
1756 
1757     @Override
1758     public DetailAstImpl visitTemplateArgument(JavaLanguageParser.TemplateArgumentContext ctx) {
1759         return Objects.requireNonNullElseGet(visit(ctx.template()),
1760                 () -> buildSimpleStringTemplateArgument(ctx));
1761     }
1762 
1763     /**
1764      * Builds a simple string template argument AST, which basically means that we
1765      * transform a string literal into a string template AST because it is a
1766      * string template argument.
1767      *
1768      * @param ctx the TemplateArgumentContext to build AST from
1769      * @return DetailAstImpl of string template argument
1770      */
1771     private static DetailAstImpl buildSimpleStringTemplateArgument(
1772             JavaLanguageParser.TemplateArgumentContext ctx) {
1773         final int startColumn = ctx.start.getCharPositionInLine();
1774         final int endColumn = startColumn + ctx.getText().length() - 1;
1775         final int lineNumber = ctx.start.getLine();
1776 
1777         final DetailAstImpl templateArgument = createImaginary(
1778                 TokenTypes.STRING_TEMPLATE_BEGIN, QUOTE,
1779                 lineNumber, startColumn
1780         );
1781 
1782         final int quoteLength = QUOTE.length();
1783         final int tokenTextLength = ctx.getText().length();
1784 
1785         final String actualContent = ctx.getText()
1786                     .substring(quoteLength, tokenTextLength - quoteLength);
1787 
1788         final DetailAstImpl content = createImaginary(
1789                 TokenTypes.STRING_TEMPLATE_CONTENT, actualContent,
1790                 lineNumber, startColumn + quoteLength
1791         );
1792         templateArgument.addChild(content);
1793 
1794         final DetailAstImpl end = createImaginary(
1795                 TokenTypes.STRING_TEMPLATE_END, QUOTE,
1796                 lineNumber, endColumn
1797         );
1798         templateArgument.addChild(end);
1799 
1800         return templateArgument;
1801     }
1802 
1803     @Override
1804     public DetailAstImpl visitStringTemplate(JavaLanguageParser.StringTemplateContext ctx) {
1805         final DetailAstImpl stringTemplateBegin = visit(ctx.stringTemplateBegin());
1806 
1807         final Optional<DetailAstImpl> expression = Optional.ofNullable(ctx.expr())
1808                 .map(this::visit);
1809 
1810         if (expression.isPresent()) {
1811             final DetailAstImpl imaginaryExpression =
1812                     createImaginary(TokenTypes.EMBEDDED_EXPRESSION);
1813             imaginaryExpression.addChild(expression.orElseThrow());
1814             stringTemplateBegin.addChild(imaginaryExpression);
1815         }
1816 
1817         ctx.stringTemplateMiddle().stream()
1818                 .map(this::buildStringTemplateMiddle)
1819                 .forEach(stringTemplateBegin::addChild);
1820 
1821         final DetailAstImpl stringTemplateEnd = visit(ctx.stringTemplateEnd());
1822         stringTemplateBegin.addChild(stringTemplateEnd);
1823         return stringTemplateBegin;
1824     }
1825 
1826     /**
1827      * Builds a string template middle AST.
1828      *
1829      * @param ctx the StringTemplateMiddleContext to build AST from
1830      * @return DetailAstImpl of string template middle
1831      */
1832     private DetailAstImpl buildStringTemplateMiddle(
1833             JavaLanguageParser.StringTemplateMiddleContext ctx) {
1834         final DetailAstImpl stringTemplateMiddle =
1835                 visit(ctx.stringTemplateMid());
1836 
1837         final Optional<DetailAstImpl> expression = Optional.ofNullable(ctx.expr())
1838                 .map(this::visit);
1839 
1840         if (expression.isPresent()) {
1841             final DetailAstImpl imaginaryExpression =
1842                     createImaginary(TokenTypes.EMBEDDED_EXPRESSION);
1843             imaginaryExpression.addChild(expression.orElseThrow());
1844             addLastSibling(stringTemplateMiddle, imaginaryExpression);
1845         }
1846 
1847         return stringTemplateMiddle;
1848     }
1849 
1850     @Override
1851     public DetailAstImpl visitStringTemplateBegin(
1852             JavaLanguageParser.StringTemplateBeginContext ctx) {
1853         final DetailAstImpl stringTemplateBegin =
1854                 create(ctx.STRING_TEMPLATE_BEGIN());
1855         final Optional<DetailAstImpl> stringTemplateContent =
1856                 Optional.ofNullable(ctx.STRING_TEMPLATE_CONTENT())
1857                         .map(this::create);
1858         stringTemplateContent.ifPresent(stringTemplateBegin::addChild);
1859         final DetailAstImpl embeddedExpressionBegin = create(ctx.EMBEDDED_EXPRESSION_BEGIN());
1860         stringTemplateBegin.addChild(embeddedExpressionBegin);
1861         return stringTemplateBegin;
1862     }
1863 
1864     @Override
1865     public DetailAstImpl visitStringTemplateMid(JavaLanguageParser.StringTemplateMidContext ctx) {
1866         final DetailAstImpl embeddedExpressionEnd = create(ctx.EMBEDDED_EXPRESSION_END());
1867         final Optional<DetailAstImpl> stringTemplateContent =
1868                 Optional.ofNullable(ctx.STRING_TEMPLATE_CONTENT())
1869                         .map(this::create);
1870         stringTemplateContent.ifPresent(self -> addLastSibling(embeddedExpressionEnd, self));
1871         final DetailAstImpl embeddedExpressionBegin = create(ctx.EMBEDDED_EXPRESSION_BEGIN());
1872         addLastSibling(embeddedExpressionEnd, embeddedExpressionBegin);
1873         return embeddedExpressionEnd;
1874     }
1875 
1876     @Override
1877     public DetailAstImpl visitStringTemplateEnd(JavaLanguageParser.StringTemplateEndContext ctx) {
1878         final DetailAstImpl embeddedExpressionEnd = create(ctx.EMBEDDED_EXPRESSION_END());
1879         final Optional<DetailAstImpl> stringTemplateContent =
1880                 Optional.ofNullable(ctx.STRING_TEMPLATE_CONTENT())
1881                         .map(this::create);
1882         stringTemplateContent.ifPresent(self -> addLastSibling(embeddedExpressionEnd, self));
1883         final DetailAstImpl stringTemplateEnd = create(ctx.STRING_TEMPLATE_END());
1884         addLastSibling(embeddedExpressionEnd, stringTemplateEnd);
1885         return embeddedExpressionEnd;
1886     }
1887 
1888     @Override
1889     public DetailAstImpl visitCreator(JavaLanguageParser.CreatorContext ctx) {
1890         return flattenedTree(ctx);
1891     }
1892 
1893     @Override
1894     public DetailAstImpl visitCreatedNameObject(JavaLanguageParser.CreatedNameObjectContext ctx) {
1895         final DetailAstPair currentAST = new DetailAstPair();
1896         DetailAstPair.addAstChild(currentAST, visit(ctx.annotations()));
1897         DetailAstPair.addAstChild(currentAST, visit(ctx.id()));
1898         DetailAstPair.addAstChild(currentAST, visit(ctx.typeArgumentsOrDiamond()));
1899 
1900         // This is how we build the type arguments/ qualified name tree
1901         for (ParserRuleContext extendedContext : ctx.extended) {
1902             final DetailAstImpl dot = create(extendedContext.start);
1903             DetailAstPair.makeAstRoot(currentAST, dot);
1904             final List<ParseTree> childList = extendedContext
1905                     .children.subList(1, extendedContext.children.size());
1906             processChildren(dot, childList);
1907         }
1908 
1909         return currentAST.root;
1910     }
1911 
1912     @Override
1913     public DetailAstImpl visitCreatedNamePrimitive(
1914             JavaLanguageParser.CreatedNamePrimitiveContext ctx) {
1915         return flattenedTree(ctx);
1916     }
1917 
1918     @Override
1919     public DetailAstImpl visitInnerCreator(JavaLanguageParser.InnerCreatorContext ctx) {
1920         return flattenedTree(ctx);
1921     }
1922 
1923     @Override
1924     public DetailAstImpl visitArrayCreatorRest(JavaLanguageParser.ArrayCreatorRestContext ctx) {
1925         final DetailAstImpl arrayDeclarator = create(TokenTypes.ARRAY_DECLARATOR,
1926                 (Token) ctx.LBRACK().getPayload());
1927         final JavaLanguageParser.ExpressionContext expression = ctx.expression();
1928         final TerminalNode rbrack = ctx.RBRACK();
1929         // child[0] is LBRACK
1930         for (int i = 1; i < ctx.children.size(); i++) {
1931             if (ctx.children.get(i) == rbrack) {
1932                 arrayDeclarator.addChild(create(rbrack));
1933             }
1934             else if (ctx.children.get(i) == expression) {
1935                 // Handle '[8]', etc.
1936                 arrayDeclarator.addChild(visit(expression));
1937             }
1938             else {
1939                 addLastSibling(arrayDeclarator, visit(ctx.children.get(i)));
1940             }
1941         }
1942         return arrayDeclarator;
1943     }
1944 
1945     @Override
1946     public DetailAstImpl visitBracketsWithExp(JavaLanguageParser.BracketsWithExpContext ctx) {
1947         final DetailAstImpl dummyRoot = new DetailAstImpl();
1948         dummyRoot.addChild(visit(ctx.annotations()));
1949         final DetailAstImpl arrayDeclarator =
1950                 create(TokenTypes.ARRAY_DECLARATOR, (Token) ctx.LBRACK().getPayload());
1951         arrayDeclarator.addChild(visit(ctx.expression()));
1952         arrayDeclarator.addChild(create(ctx.stop));
1953         dummyRoot.addChild(arrayDeclarator);
1954         return dummyRoot.getFirstChild();
1955     }
1956 
1957     @Override
1958     public DetailAstImpl visitClassCreatorRest(JavaLanguageParser.ClassCreatorRestContext ctx) {
1959         return flattenedTree(ctx);
1960     }
1961 
1962     @Override
1963     public DetailAstImpl visitDiamond(JavaLanguageParser.DiamondContext ctx) {
1964         final DetailAstImpl typeArguments =
1965                 createImaginary(TokenTypes.TYPE_ARGUMENTS);
1966         typeArguments.addChild(create(TokenTypes.GENERIC_START,
1967                 (Token) ctx.LT().getPayload()));
1968         typeArguments.addChild(create(TokenTypes.GENERIC_END,
1969                 (Token) ctx.GT().getPayload()));
1970         return typeArguments;
1971     }
1972 
1973     @Override
1974     public DetailAstImpl visitTypeArgs(JavaLanguageParser.TypeArgsContext ctx) {
1975         return flattenedTree(ctx);
1976     }
1977 
1978     @Override
1979     public DetailAstImpl visitNonWildcardDiamond(
1980             JavaLanguageParser.NonWildcardDiamondContext ctx) {
1981         final DetailAstImpl typeArguments =
1982                 createImaginary(TokenTypes.TYPE_ARGUMENTS);
1983         typeArguments.addChild(create(TokenTypes.GENERIC_START,
1984                 (Token) ctx.LT().getPayload()));
1985         typeArguments.addChild(create(TokenTypes.GENERIC_END,
1986                 (Token) ctx.GT().getPayload()));
1987         return typeArguments;
1988     }
1989 
1990     @Override
1991     public DetailAstImpl visitNonWildcardTypeArguments(
1992             JavaLanguageParser.NonWildcardTypeArgumentsContext ctx) {
1993         final DetailAstImpl typeArguments = createImaginary(TokenTypes.TYPE_ARGUMENTS);
1994         typeArguments.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload()));
1995         typeArguments.addChild(visit(ctx.typeArgumentsTypeList()));
1996         typeArguments.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload()));
1997         return typeArguments;
1998     }
1999 
2000     @Override
2001     public DetailAstImpl visitTypeArgumentsTypeList(
2002             JavaLanguageParser.TypeArgumentsTypeListContext ctx) {
2003         final DetailAstImpl firstIdent = visit(ctx.typeType(0));
2004         final DetailAstImpl firstTypeArgument = createImaginary(TokenTypes.TYPE_ARGUMENT);
2005         firstTypeArgument.addChild(firstIdent);
2006 
2007         for (int i = 0; i < ctx.COMMA().size(); i++) {
2008             addLastSibling(firstTypeArgument, create(ctx.COMMA(i)));
2009             final DetailAstImpl ident = visit(ctx.typeType(i + 1));
2010             final DetailAstImpl typeArgument = createImaginary(TokenTypes.TYPE_ARGUMENT);
2011             typeArgument.addChild(ident);
2012             addLastSibling(firstTypeArgument, typeArgument);
2013         }
2014         return firstTypeArgument;
2015     }
2016 
2017     @Override
2018     public DetailAstImpl visitTypeList(JavaLanguageParser.TypeListContext ctx) {
2019         return flattenedTree(ctx);
2020     }
2021 
2022     @Override
2023     public DetailAstImpl visitTypeType(JavaLanguageParser.TypeTypeContext ctx) {
2024         final DetailAstImpl type = createImaginary(TokenTypes.TYPE);
2025         processChildren(type, ctx.children);
2026 
2027         final DetailAstImpl returnTree;
2028         if (ctx.createImaginaryNode) {
2029             returnTree = type;
2030         }
2031         else {
2032             returnTree = type.getFirstChild();
2033         }
2034         return returnTree;
2035     }
2036 
2037     @Override
2038     public DetailAstImpl visitArrayDeclarator(JavaLanguageParser.ArrayDeclaratorContext ctx) {
2039         final DetailAstImpl arrayDeclarator = create(TokenTypes.ARRAY_DECLARATOR,
2040                 (Token) ctx.LBRACK().getPayload());
2041         arrayDeclarator.addChild(create(ctx.RBRACK()));
2042 
2043         final DetailAstImpl returnTree;
2044         final DetailAstImpl annotations = visit(ctx.anno);
2045         if (annotations == null) {
2046             returnTree = arrayDeclarator;
2047         }
2048         else {
2049             returnTree = annotations;
2050             addLastSibling(returnTree, arrayDeclarator);
2051         }
2052         return returnTree;
2053     }
2054 
2055     @Override
2056     public DetailAstImpl visitPrimitiveType(JavaLanguageParser.PrimitiveTypeContext ctx) {
2057         return create(ctx.start);
2058     }
2059 
2060     @Override
2061     public DetailAstImpl visitTypeArguments(JavaLanguageParser.TypeArgumentsContext ctx) {
2062         final DetailAstImpl typeArguments = createImaginary(TokenTypes.TYPE_ARGUMENTS);
2063         typeArguments.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload()));
2064         // Exclude '<' and '>'
2065         processChildren(typeArguments, ctx.children.subList(1, ctx.children.size() - 1));
2066         typeArguments.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload()));
2067         return typeArguments;
2068     }
2069 
2070     @Override
2071     public DetailAstImpl visitSuperSuffixDot(JavaLanguageParser.SuperSuffixDotContext ctx) {
2072         final DetailAstImpl root;
2073         if (ctx.LPAREN() == null) {
2074             root = create(ctx.DOT());
2075             root.addChild(visit(ctx.id()));
2076         }
2077         else {
2078             root = create(TokenTypes.METHOD_CALL, (Token) ctx.LPAREN().getPayload());
2079 
2080             final DetailAstImpl dot = create(ctx.DOT());
2081             dot.addChild(visit(ctx.id()));
2082             root.addChild(dot);
2083 
2084             final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList()))
2085                     .orElseGet(() -> createImaginary(TokenTypes.ELIST));
2086             root.addChild(expressionList);
2087 
2088             root.addChild(create(ctx.RPAREN()));
2089         }
2090 
2091         return root;
2092     }
2093 
2094     @Override
2095     public DetailAstImpl visitArguments(JavaLanguageParser.ArgumentsContext ctx) {
2096         final DetailAstImpl lparen = create(ctx.LPAREN());
2097 
2098         // We always add an 'ELIST' node
2099         final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList()))
2100                 .orElseGet(() -> createImaginary(TokenTypes.ELIST));
2101         addLastSibling(lparen, expressionList);
2102         addLastSibling(lparen, create(ctx.RPAREN()));
2103         return lparen;
2104     }
2105 
2106     @Override
2107     public DetailAstImpl visitPattern(JavaLanguageParser.PatternContext ctx) {
2108         final JavaLanguageParser.InnerPatternContext innerPattern = ctx.innerPattern();
2109         final ParserRuleContext primaryPattern = innerPattern.primaryPattern();
2110         final ParserRuleContext recordPattern = innerPattern.recordPattern();
2111         final boolean isSimpleTypePattern = primaryPattern != null
2112                 && primaryPattern.getChild(0) instanceof JavaLanguageParser.TypePatternContext;
2113 
2114         final DetailAstImpl pattern;
2115 
2116         if (recordPattern != null) {
2117             pattern = visit(recordPattern);
2118         }
2119         else if (isSimpleTypePattern) {
2120             // For simple type pattern like 'Integer i`, we do not add `PATTERN_DEF` parent
2121             pattern = visit(primaryPattern);
2122         }
2123         else {
2124             pattern = createImaginary(TokenTypes.PATTERN_DEF);
2125             pattern.addChild(visit(ctx.getChild(0)));
2126         }
2127         return pattern;
2128     }
2129 
2130     @Override
2131     public DetailAstImpl visitInnerPattern(JavaLanguageParser.InnerPatternContext ctx) {
2132         return flattenedTree(ctx);
2133     }
2134 
2135     @Override
2136     public DetailAstImpl visitGuardedPattern(JavaLanguageParser.GuardedPatternContext ctx) {
2137         final DetailAstImpl guardAstNode = flattenedTree(ctx.guard());
2138         guardAstNode.addChild(visit(ctx.primaryPattern()));
2139         guardAstNode.addChild(visit(ctx.expr()));
2140         return guardAstNode;
2141     }
2142 
2143     @Override
2144     public DetailAstImpl visitParenPattern(JavaLanguageParser.ParenPatternContext ctx) {
2145         final DetailAstImpl lparen = create(ctx.LPAREN());
2146         final ParseTree innerPattern = ctx.getChild(1);
2147         lparen.addChild(visit(innerPattern));
2148         lparen.addChild(create(ctx.RPAREN()));
2149         return lparen;
2150     }
2151 
2152     @Override
2153     public DetailAstImpl visitRecordPatternDef(JavaLanguageParser.RecordPatternDefContext ctx) {
2154         return flattenedTree(ctx);
2155     }
2156 
2157     @Override
2158     public DetailAstImpl visitTypePatternDef(
2159             JavaLanguageParser.TypePatternDefContext ctx) {
2160         final DetailAstImpl type = visit(ctx.type);
2161         final DetailAstImpl patternVariableDef = createImaginary(TokenTypes.PATTERN_VARIABLE_DEF);
2162         patternVariableDef.addChild(createModifiers(ctx.mods));
2163         patternVariableDef.addChild(type);
2164         patternVariableDef.addChild(visit(ctx.id()));
2165         return patternVariableDef;
2166     }
2167 
2168     @Override
2169     public DetailAstImpl visitUnnamedPatternDef(JavaLanguageParser.UnnamedPatternDefContext ctx) {
2170         return create(TokenTypes.UNNAMED_PATTERN_DEF, ctx.start);
2171     }
2172 
2173     @Override
2174     public DetailAstImpl visitRecordPattern(JavaLanguageParser.RecordPatternContext ctx) {
2175         final DetailAstImpl recordPattern = createImaginary(TokenTypes.RECORD_PATTERN_DEF);
2176         recordPattern.addChild(createModifiers(ctx.mods));
2177         processChildren(recordPattern,
2178                 ctx.children.subList(ctx.mods.size(), ctx.children.size()));
2179         return recordPattern;
2180     }
2181 
2182     @Override
2183     public DetailAstImpl visitRecordComponentPatternList(
2184             JavaLanguageParser.RecordComponentPatternListContext ctx) {
2185         final DetailAstImpl recordComponents =
2186                 createImaginary(TokenTypes.RECORD_PATTERN_COMPONENTS);
2187         processChildren(recordComponents, ctx.children);
2188         return recordComponents;
2189     }
2190 
2191     @Override
2192     public DetailAstImpl visitPermittedSubclassesAndInterfaces(
2193             JavaLanguageParser.PermittedSubclassesAndInterfacesContext ctx) {
2194         final DetailAstImpl literalPermits =
2195                 create(TokenTypes.PERMITS_CLAUSE, (Token) ctx.LITERAL_PERMITS().getPayload());
2196         // 'LITERAL_PERMITS' is child[0]
2197         processChildren(literalPermits, ctx.children.subList(1, ctx.children.size()));
2198         return literalPermits;
2199     }
2200 
2201     @Override
2202     public DetailAstImpl visitId(JavaLanguageParser.IdContext ctx) {
2203         return create(TokenTypes.IDENT, ctx.start);
2204     }
2205 
2206     /**
2207      * Builds the AST for a particular node, then returns a "flattened" tree
2208      * of siblings. This method should be used in rule contexts such as
2209      * {@code variableDeclarators}, where we have both terminals and non-terminals.
2210      *
2211      * @param ctx the ParserRuleContext to base tree on
2212      * @return flattened DetailAstImpl
2213      */
2214     private DetailAstImpl flattenedTree(ParserRuleContext ctx) {
2215         final DetailAstImpl dummyNode = new DetailAstImpl();
2216         processChildren(dummyNode, ctx.children);
2217         return dummyNode.getFirstChild();
2218     }
2219 
2220     /**
2221      * Adds all the children from the given ParseTree or JavaParserContext
2222      * list to the parent DetailAstImpl.
2223      *
2224      * @param parent the DetailAstImpl to add children to
2225      * @param children the list of children to add
2226      */
2227     private void processChildren(DetailAstImpl parent, List<? extends ParseTree> children) {
2228         children.forEach(child -> {
2229             if (child instanceof TerminalNode) {
2230                 // Child is a token, create a new DetailAstImpl and add it to parent
2231                 parent.addChild(create((TerminalNode) child));
2232             }
2233             else {
2234                 // Child is another rule context; visit it, create token, and add to parent
2235                 parent.addChild(visit(child));
2236             }
2237         });
2238     }
2239 
2240     /**
2241      * Create a DetailAstImpl from a given token and token type. This method
2242      * should be used for imaginary nodes only, i.e. 'OBJBLOCK -&gt; OBJBLOCK',
2243      * where the text on the RHS matches the text on the LHS.
2244      *
2245      * @param tokenType the token type of this DetailAstImpl
2246      * @return new DetailAstImpl of given type
2247      */
2248     private static DetailAstImpl createImaginary(int tokenType) {
2249         final DetailAstImpl detailAst = new DetailAstImpl();
2250         detailAst.setType(tokenType);
2251         detailAst.setText(TokenUtil.getTokenName(tokenType));
2252         return detailAst;
2253     }
2254 
2255     /**
2256      * Create a DetailAstImpl from a given token type and text. This method
2257      * should be used for imaginary nodes only, i.e. 'OBJBLOCK -&gt; OBJBLOCK',
2258      * where the text on the RHS matches the text on the LHS.
2259      *
2260      * @param tokenType the token type of this DetailAstImpl
2261      * @param text the text of this DetailAstImpl
2262      * @return new DetailAstImpl of given type
2263      */
2264     private static DetailAstImpl createImaginary(int tokenType, String text) {
2265         final DetailAstImpl imaginary = new DetailAstImpl();
2266         imaginary.setType(tokenType);
2267         imaginary.setText(text);
2268         return imaginary;
2269     }
2270 
2271     /**
2272      * Creates an imaginary DetailAstImpl with the given token details.
2273      *
2274      * @param tokenType the token type of this DetailAstImpl
2275      * @param text the text of this DetailAstImpl
2276      * @param lineNumber the line number of this DetailAstImpl
2277      * @param columnNumber the column number of this DetailAstImpl
2278      * @return imaginary DetailAstImpl from given details
2279      */
2280     private static DetailAstImpl createImaginary(
2281             int tokenType, String text, int lineNumber, int columnNumber) {
2282         final DetailAstImpl imaginary = createImaginary(tokenType, text);
2283         imaginary.setLineNo(lineNumber);
2284         imaginary.setColumnNo(columnNumber);
2285         return imaginary;
2286     }
2287 
2288     /**
2289      * Create a DetailAstImpl from a given token and token type. This method
2290      * should be used for literal nodes only, i.e. 'PACKAGE_DEF -&gt; package'.
2291      *
2292      * @param tokenType the token type of this DetailAstImpl
2293      * @param startToken the first token that appears in this DetailAstImpl.
2294      * @return new DetailAstImpl of given type
2295      */
2296     private DetailAstImpl create(int tokenType, Token startToken) {
2297         final DetailAstImpl ast = create(startToken);
2298         ast.setType(tokenType);
2299         return ast;
2300     }
2301 
2302     /**
2303      * Create a DetailAstImpl from a given token. This method should be
2304      * used for terminal nodes, i.e. {@code LCURLY}, when we are building
2305      * an AST for a specific token, regardless of position.
2306      *
2307      * @param token the token to build the DetailAstImpl from
2308      * @return new DetailAstImpl of given type
2309      */
2310     private DetailAstImpl create(Token token) {
2311         final int tokenIndex = token.getTokenIndex();
2312         final List<Token> tokensToLeft =
2313                 tokens.getHiddenTokensToLeft(tokenIndex, JavaLanguageLexer.COMMENTS);
2314         final List<Token> tokensToRight =
2315                 tokens.getHiddenTokensToRight(tokenIndex, JavaLanguageLexer.COMMENTS);
2316 
2317         final DetailAstImpl detailAst = new DetailAstImpl();
2318         detailAst.initialize(token);
2319         if (tokensToLeft != null) {
2320             detailAst.setHiddenBefore(tokensToLeft);
2321         }
2322         if (tokensToRight != null) {
2323             detailAst.setHiddenAfter(tokensToRight);
2324         }
2325         return detailAst;
2326     }
2327 
2328     /**
2329      * Create a DetailAstImpl from a given TerminalNode. This method should be
2330      * used for terminal nodes, i.e. {@code @}.
2331      *
2332      * @param node the TerminalNode to build the DetailAstImpl from
2333      * @return new DetailAstImpl of given type
2334      */
2335     private DetailAstImpl create(TerminalNode node) {
2336         return create((Token) node.getPayload());
2337     }
2338 
2339     /**
2340      * Creates a type declaration DetailAstImpl from a given rule context.
2341      *
2342      * @param ctx ParserRuleContext we are in
2343      * @param type the type declaration to create
2344      * @param modifierList respective modifiers
2345      * @return type declaration DetailAstImpl
2346      */
2347     private DetailAstImpl createTypeDeclaration(ParserRuleContext ctx, int type,
2348                                                 List<? extends ParseTree> modifierList) {
2349         final DetailAstImpl typeDeclaration = createImaginary(type);
2350         typeDeclaration.addChild(createModifiers(modifierList));
2351         processChildren(typeDeclaration, ctx.children);
2352         return typeDeclaration;
2353     }
2354 
2355     /**
2356      * Builds the modifiers AST.
2357      *
2358      * @param modifierList the list of modifier contexts
2359      * @return "MODIFIERS" ast
2360      */
2361     private DetailAstImpl createModifiers(List<? extends ParseTree> modifierList) {
2362         final DetailAstImpl mods = createImaginary(TokenTypes.MODIFIERS);
2363         processChildren(mods, modifierList);
2364         return mods;
2365     }
2366 
2367     /**
2368      * Add new sibling to the end of existing siblings.
2369      *
2370      * @param self DetailAstImpl to add last sibling to
2371      * @param sibling DetailAstImpl sibling to add
2372      */
2373     private static void addLastSibling(DetailAstImpl self, DetailAstImpl sibling) {
2374         DetailAstImpl nextSibling = self;
2375         if (nextSibling != null) {
2376             while (nextSibling.getNextSibling() != null) {
2377                 nextSibling = nextSibling.getNextSibling();
2378             }
2379             nextSibling.setNextSibling(sibling);
2380         }
2381     }
2382 
2383     @Override
2384     public DetailAstImpl visit(ParseTree tree) {
2385         DetailAstImpl ast = null;
2386         if (tree != null) {
2387             ast = tree.accept(this);
2388         }
2389         return ast;
2390     }
2391 
2392     /**
2393      * Builds an expression node. This is used to build the root of an expression with
2394      * an imaginary {@code EXPR} node.
2395      *
2396      * @param exprNode expression to build node for
2397      * @return expression DetailAstImpl node
2398      */
2399     private DetailAstImpl buildExpressionNode(ParseTree exprNode) {
2400         final DetailAstImpl expression = visit(exprNode);
2401 
2402         final DetailAstImpl exprRoot;
2403         if (TokenUtil.isOfType(expression, EXPRESSIONS_WITH_NO_EXPR_ROOT)) {
2404             exprRoot = expression;
2405         }
2406         else {
2407             // create imaginary 'EXPR' node as root of expression
2408             exprRoot = createImaginary(TokenTypes.EXPR);
2409             exprRoot.addChild(expression);
2410         }
2411         return exprRoot;
2412     }
2413 
2414     /**
2415      * Used to swap and organize DetailAstImpl subtrees.
2416      */
2417     private static final class DetailAstPair {
2418 
2419         /** The root DetailAstImpl of this pair. */
2420         private DetailAstImpl root;
2421 
2422         /** The child (potentially with siblings) of this pair. */
2423         private DetailAstImpl child;
2424 
2425         /**
2426          * Moves child reference to the last child.
2427          */
2428         private void advanceChildToEnd() {
2429             while (child.getNextSibling() != null) {
2430                 child = child.getNextSibling();
2431             }
2432         }
2433 
2434         /**
2435          * Returns the root node.
2436          *
2437          * @return the root node
2438          */
2439         private DetailAstImpl getRoot() {
2440             return root;
2441         }
2442 
2443         /**
2444          * This method is used to replace the {@code ^} (set as root node) ANTLR2
2445          * operator.
2446          *
2447          * @param pair the DetailAstPair to use for swapping nodes
2448          * @param ast the new root
2449          */
2450         private static void makeAstRoot(DetailAstPair pair, DetailAstImpl ast) {
2451             ast.addChild(pair.root);
2452             pair.child = pair.root;
2453             pair.advanceChildToEnd();
2454             pair.root = ast;
2455         }
2456 
2457         /**
2458          * Adds a child (or new root) to the given DetailAstPair.
2459          *
2460          * @param pair the DetailAstPair to add child to
2461          * @param ast the child to add
2462          */
2463         private static void addAstChild(DetailAstPair pair, DetailAstImpl ast) {
2464             if (ast != null) {
2465                 if (pair.root == null) {
2466                     pair.root = ast;
2467                 }
2468                 else {
2469                     pair.child.setNextSibling(ast);
2470                 }
2471                 pair.child = ast;
2472             }
2473         }
2474     }
2475 }