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