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