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