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