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