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