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