001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2026 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018///////////////////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle;
021
022import java.util.ArrayDeque;
023import java.util.ArrayList;
024import java.util.Collections;
025import java.util.Iterator;
026import java.util.List;
027import java.util.Optional;
028import java.util.Queue;
029import java.util.stream.Collectors;
030
031import org.antlr.v4.runtime.BufferedTokenStream;
032import org.antlr.v4.runtime.CommonTokenStream;
033import org.antlr.v4.runtime.ParserRuleContext;
034import org.antlr.v4.runtime.Token;
035import org.antlr.v4.runtime.tree.ParseTree;
036import org.antlr.v4.runtime.tree.TerminalNode;
037
038import com.puppycrawl.tools.checkstyle.api.TokenTypes;
039import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageLexer;
040import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser;
041import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParserBaseVisitor;
042import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
043
044/**
045 * Visitor class used to build Checkstyle's Java AST from the parse tree produced by
046 * {@link JavaLanguageParser}. In each {@code visit...} method, we visit the children of a node
047 * (which correspond to subrules) or create terminal nodes (tokens), and return a subtree as a
048 * result.
049 *
050 * <p>Example:</p>
051 *
052 * <p>The following package declaration:</p>
053 * <pre>
054 * package com.puppycrawl.tools.checkstyle;
055 * </pre>
056 *
057 * <p>
058 * Will be parsed by the {@code packageDeclaration} rule from {@code JavaLanguageParser.g4}:
059 * </p>
060 * <pre>
061 * packageDeclaration
062 *     : annotations[true] LITERAL_PACKAGE qualifiedName SEMI
063 *     ;
064 * </pre>
065 *
066 * <p>
067 * We override the {@code visitPackageDeclaration} method generated by ANTLR in
068 * {@link JavaLanguageParser} at
069 * {@link JavaAstVisitor#visitPackageDeclaration(JavaLanguageParser.PackageDeclarationContext)}
070 * to create a subtree based on the subrules and tokens found in the {@code packageDeclaration}
071 * subrule accordingly, thus producing the following AST:
072 * </p>
073 * <pre>
074 * PACKAGE_DEF -&gt; package
075 * |--ANNOTATIONS -&gt; ANNOTATIONS
076 * |--DOT -&gt; .
077 * |   |--DOT -&gt; .
078 * |   |   |--DOT -&gt; .
079 * |   |   |   |--IDENT -&gt; com
080 * |   |   |   `--IDENT -&gt; puppycrawl
081 * |   |   `--IDENT -&gt; tools
082 * |   `--IDENT -&gt; checkstyle
083 * `--SEMI -&gt; ;
084 * </pre>
085 *
086 * <p>
087 * See <a href="https://github.com/checkstyle/checkstyle/pull/10434">#10434</a>
088 * for a good example of how
089 * to make changes to Checkstyle's grammar and AST.
090 * </p>
091 *
092 * <p>
093 * The order of {@code visit...} methods in {@code JavaAstVisitor.java} and production rules in
094 * {@code JavaLanguageParser.g4} should be consistent to ease maintenance.
095 * </p>
096 *
097 * @noinspection JavadocReference
098 * @noinspectionreason JavadocReference - References are valid
099 */
100public 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}