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