All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.puppycrawl.tools.checkstyle.JavaAstVisitor Maven / Gradle / Ivy

Go to download

Checkstyle is a development tool to help programmers write Java code that adheres to a coding standard

There is a newer version: 10.20.1
Show newest version
///////////////////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
// Copyright (C) 2001-2024 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
///////////////////////////////////////////////////////////////////////////////////////////////

package com.puppycrawl.tools.checkstyle;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.stream.Collectors;

import org.antlr.v4.runtime.BufferedTokenStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;

import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageLexer;
import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser;
import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParserBaseVisitor;
import com.puppycrawl.tools.checkstyle.utils.TokenUtil;

/**
 * Visitor class used to build Checkstyle's Java AST from the parse tree produced by
 * {@link JavaLanguageParser}. In each {@code visit...} method, we visit the children of a node
 * (which correspond to subrules) or create terminal nodes (tokens), and return a subtree as a
 * result.
 *
 * 

Example:

* *

The following package declaration:

*
 * package com.puppycrawl.tools.checkstyle;
 * 
* *

* Will be parsed by the {@code packageDeclaration} rule from {@code JavaLanguageParser.g4}: *

*
 * packageDeclaration
 *     : annotations[true] LITERAL_PACKAGE qualifiedName SEMI
 *     ;
 * 
* *

* We override the {@code visitPackageDeclaration} method generated by ANTLR in * {@link JavaLanguageParser} at * {@link JavaAstVisitor#visitPackageDeclaration(JavaLanguageParser.PackageDeclarationContext)} * to create a subtree based on the subrules and tokens found in the {@code packageDeclaration} * subrule accordingly, thus producing the following AST: *

*
 * PACKAGE_DEF -> package
 * |--ANNOTATIONS -> ANNOTATIONS
 * |--DOT -> .
 * |   |--DOT -> .
 * |   |   |--DOT -> .
 * |   |   |   |--IDENT -> com
 * |   |   |   `--IDENT -> puppycrawl
 * |   |   `--IDENT -> tools
 * |   `--IDENT -> checkstyle
 * `--SEMI -> ;
 * 
*

* See #10434 * for a good example of how * to make changes to Checkstyle's grammar and AST. *

*

* The order of {@code visit...} methods in {@code JavaAstVisitor.java} and production rules in * {@code JavaLanguageParser.g4} should be consistent to ease maintenance. *

*/ public final class JavaAstVisitor extends JavaLanguageParserBaseVisitor { /** String representation of the left shift operator. */ private static final String LEFT_SHIFT = "<<"; /** String representation of the unsigned right shift operator. */ private static final String UNSIGNED_RIGHT_SHIFT = ">>>"; /** String representation of the right shift operator. */ private static final String RIGHT_SHIFT = ">>"; /** * The tokens here are technically expressions, but should * not return an EXPR token as their root. */ private static final int[] EXPRESSIONS_WITH_NO_EXPR_ROOT = { TokenTypes.CTOR_CALL, TokenTypes.SUPER_CTOR_CALL, TokenTypes.LAMBDA, }; /** Token stream to check for hidden tokens. */ private final BufferedTokenStream tokens; /** * Constructs a JavaAstVisitor with given token stream. * * @param tokenStream the token stream to check for hidden tokens */ public JavaAstVisitor(CommonTokenStream tokenStream) { tokens = tokenStream; } @Override public DetailAstImpl visitCompilationUnit(JavaLanguageParser.CompilationUnitContext ctx) { final DetailAstImpl compilationUnit; // 'EOF' token is always present; therefore if we only have one child, we have an empty file final boolean isEmptyFile = ctx.children.size() == 1; if (isEmptyFile) { compilationUnit = null; } else { compilationUnit = createImaginary(TokenTypes.COMPILATION_UNIT); // last child is 'EOF', we do not include this token in AST processChildren(compilationUnit, ctx.children.subList(0, ctx.children.size() - 1)); } return compilationUnit; } @Override public DetailAstImpl visitPackageDeclaration( JavaLanguageParser.PackageDeclarationContext ctx) { final DetailAstImpl packageDeclaration = create(TokenTypes.PACKAGE_DEF, (Token) ctx.LITERAL_PACKAGE().getPayload()); packageDeclaration.addChild(visit(ctx.annotations())); packageDeclaration.addChild(visit(ctx.qualifiedName())); packageDeclaration.addChild(create(ctx.SEMI())); return packageDeclaration; } @Override public DetailAstImpl visitImportDec(JavaLanguageParser.ImportDecContext ctx) { final DetailAstImpl importRoot = create(ctx.start); // Static import final TerminalNode literalStaticNode = ctx.LITERAL_STATIC(); if (literalStaticNode != null) { importRoot.setType(TokenTypes.STATIC_IMPORT); importRoot.addChild(create(literalStaticNode)); } // Handle star imports final boolean isStarImport = ctx.STAR() != null; if (isStarImport) { final DetailAstImpl dot = create(ctx.DOT()); dot.addChild(visit(ctx.qualifiedName())); dot.addChild(create(ctx.STAR())); importRoot.addChild(dot); } else { importRoot.addChild(visit(ctx.qualifiedName())); } importRoot.addChild(create(ctx.SEMI())); return importRoot; } @Override public DetailAstImpl visitSingleSemiImport(JavaLanguageParser.SingleSemiImportContext ctx) { return create(ctx.SEMI()); } @Override public DetailAstImpl visitTypeDeclaration(JavaLanguageParser.TypeDeclarationContext ctx) { final DetailAstImpl typeDeclaration; if (ctx.type == null) { typeDeclaration = create(ctx.semi.get(0)); ctx.semi.subList(1, ctx.semi.size()) .forEach(semi -> addLastSibling(typeDeclaration, create(semi))); } else { typeDeclaration = visit(ctx.type); } return typeDeclaration; } @Override public DetailAstImpl visitModifier(JavaLanguageParser.ModifierContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitVariableModifier(JavaLanguageParser.VariableModifierContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitClassDeclaration(JavaLanguageParser.ClassDeclarationContext ctx) { return createTypeDeclaration(ctx, TokenTypes.CLASS_DEF, ctx.mods); } @Override public DetailAstImpl visitRecordDeclaration(JavaLanguageParser.RecordDeclarationContext ctx) { return createTypeDeclaration(ctx, TokenTypes.RECORD_DEF, ctx.mods); } @Override public DetailAstImpl visitRecordComponentsList( JavaLanguageParser.RecordComponentsListContext ctx) { final DetailAstImpl lparen = create(ctx.LPAREN()); // We make a "RECORD_COMPONENTS" node whether components exist or not if (ctx.recordComponents() == null) { addLastSibling(lparen, createImaginary(TokenTypes.RECORD_COMPONENTS)); } else { addLastSibling(lparen, visit(ctx.recordComponents())); } addLastSibling(lparen, create(ctx.RPAREN())); return lparen; } @Override public DetailAstImpl visitRecordComponents(JavaLanguageParser.RecordComponentsContext ctx) { final DetailAstImpl recordComponents = createImaginary(TokenTypes.RECORD_COMPONENTS); processChildren(recordComponents, ctx.children); return recordComponents; } @Override public DetailAstImpl visitRecordComponent(JavaLanguageParser.RecordComponentContext ctx) { final DetailAstImpl recordComponent = createImaginary(TokenTypes.RECORD_COMPONENT_DEF); processChildren(recordComponent, ctx.children); return recordComponent; } @Override public DetailAstImpl visitLastRecordComponent( JavaLanguageParser.LastRecordComponentContext ctx) { final DetailAstImpl recordComponent = createImaginary(TokenTypes.RECORD_COMPONENT_DEF); processChildren(recordComponent, ctx.children); return recordComponent; } @Override public DetailAstImpl visitRecordBody(JavaLanguageParser.RecordBodyContext ctx) { final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK); processChildren(objBlock, ctx.children); return objBlock; } @Override public DetailAstImpl visitCompactConstructorDeclaration( JavaLanguageParser.CompactConstructorDeclarationContext ctx) { final DetailAstImpl compactConstructor = createImaginary(TokenTypes.COMPACT_CTOR_DEF); compactConstructor.addChild(createModifiers(ctx.mods)); compactConstructor.addChild(visit(ctx.id())); compactConstructor.addChild(visit(ctx.constructorBlock())); return compactConstructor; } @Override public DetailAstImpl visitClassExtends(JavaLanguageParser.ClassExtendsContext ctx) { final DetailAstImpl classExtends = create(ctx.EXTENDS_CLAUSE()); classExtends.addChild(visit(ctx.type)); return classExtends; } @Override public DetailAstImpl visitImplementsClause(JavaLanguageParser.ImplementsClauseContext ctx) { final DetailAstImpl classImplements = create(TokenTypes.IMPLEMENTS_CLAUSE, (Token) ctx.LITERAL_IMPLEMENTS().getPayload()); classImplements.addChild(visit(ctx.typeList())); return classImplements; } @Override public DetailAstImpl visitTypeParameters(JavaLanguageParser.TypeParametersContext ctx) { final DetailAstImpl typeParameters = createImaginary(TokenTypes.TYPE_PARAMETERS); typeParameters.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload())); // Exclude '<' and '>' processChildren(typeParameters, ctx.children.subList(1, ctx.children.size() - 1)); typeParameters.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload())); return typeParameters; } @Override public DetailAstImpl visitTypeParameter(JavaLanguageParser.TypeParameterContext ctx) { final DetailAstImpl typeParameter = createImaginary(TokenTypes.TYPE_PARAMETER); processChildren(typeParameter, ctx.children); return typeParameter; } @Override public DetailAstImpl visitTypeUpperBounds(JavaLanguageParser.TypeUpperBoundsContext ctx) { // In this case, we call 'extends` TYPE_UPPER_BOUNDS final DetailAstImpl typeUpperBounds = create(TokenTypes.TYPE_UPPER_BOUNDS, (Token) ctx.EXTENDS_CLAUSE().getPayload()); // 'extends' is child[0] processChildren(typeUpperBounds, ctx.children.subList(1, ctx.children.size())); return typeUpperBounds; } @Override public DetailAstImpl visitTypeBound(JavaLanguageParser.TypeBoundContext ctx) { final DetailAstImpl typeBoundType = visit(ctx.typeBoundType(0)); final Iterator typeBoundTypeIterator = ctx.typeBoundType().listIterator(1); ctx.BAND().forEach(band -> { addLastSibling(typeBoundType, create(TokenTypes.TYPE_EXTENSION_AND, (Token) band.getPayload())); addLastSibling(typeBoundType, visit(typeBoundTypeIterator.next())); }); return typeBoundType; } @Override public DetailAstImpl visitTypeBoundType(JavaLanguageParser.TypeBoundTypeContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitEnumDeclaration(JavaLanguageParser.EnumDeclarationContext ctx) { return createTypeDeclaration(ctx, TokenTypes.ENUM_DEF, ctx.mods); } @Override public DetailAstImpl visitEnumBody(JavaLanguageParser.EnumBodyContext ctx) { final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK); processChildren(objBlock, ctx.children); return objBlock; } @Override public DetailAstImpl visitEnumConstants(JavaLanguageParser.EnumConstantsContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitEnumConstant(JavaLanguageParser.EnumConstantContext ctx) { final DetailAstImpl enumConstant = createImaginary(TokenTypes.ENUM_CONSTANT_DEF); processChildren(enumConstant, ctx.children); return enumConstant; } @Override public DetailAstImpl visitEnumBodyDeclarations( JavaLanguageParser.EnumBodyDeclarationsContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitInterfaceDeclaration( JavaLanguageParser.InterfaceDeclarationContext ctx) { return createTypeDeclaration(ctx, TokenTypes.INTERFACE_DEF, ctx.mods); } @Override public DetailAstImpl visitInterfaceExtends(JavaLanguageParser.InterfaceExtendsContext ctx) { final DetailAstImpl interfaceExtends = create(ctx.EXTENDS_CLAUSE()); interfaceExtends.addChild(visit(ctx.typeList())); return interfaceExtends; } @Override public DetailAstImpl visitClassBody(JavaLanguageParser.ClassBodyContext ctx) { final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK); processChildren(objBlock, ctx.children); return objBlock; } @Override public DetailAstImpl visitInterfaceBody(JavaLanguageParser.InterfaceBodyContext ctx) { final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK); processChildren(objBlock, ctx.children); return objBlock; } @Override public DetailAstImpl visitEmptyClass(JavaLanguageParser.EmptyClassContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitClassBlock(JavaLanguageParser.ClassBlockContext ctx) { final DetailAstImpl classBlock; if (ctx.LITERAL_STATIC() == null) { // We call it an INSTANCE_INIT classBlock = createImaginary(TokenTypes.INSTANCE_INIT); } else { classBlock = create(TokenTypes.STATIC_INIT, (Token) ctx.LITERAL_STATIC().getPayload()); classBlock.setText(TokenUtil.getTokenName(TokenTypes.STATIC_INIT)); } classBlock.addChild(visit(ctx.block())); return classBlock; } @Override public DetailAstImpl visitMethodDeclaration(JavaLanguageParser.MethodDeclarationContext ctx) { final DetailAstImpl methodDef = createImaginary(TokenTypes.METHOD_DEF); methodDef.addChild(createModifiers(ctx.mods)); // Process all children except C style array declarators processChildren(methodDef, ctx.children.stream() .filter(child -> !(child instanceof JavaLanguageParser.ArrayDeclaratorContext)) .collect(Collectors.toUnmodifiableList())); // We add C style array declarator brackets to TYPE ast final DetailAstImpl typeAst = (DetailAstImpl) methodDef.findFirstToken(TokenTypes.TYPE); ctx.cStyleArrDec.forEach(child -> typeAst.addChild(visit(child))); return methodDef; } @Override public DetailAstImpl visitMethodBody(JavaLanguageParser.MethodBodyContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitThrowsList(JavaLanguageParser.ThrowsListContext ctx) { final DetailAstImpl throwsRoot = create(ctx.LITERAL_THROWS()); throwsRoot.addChild(visit(ctx.qualifiedNameList())); return throwsRoot; } @Override public DetailAstImpl visitConstructorDeclaration( JavaLanguageParser.ConstructorDeclarationContext ctx) { final DetailAstImpl constructorDeclaration = createImaginary(TokenTypes.CTOR_DEF); constructorDeclaration.addChild(createModifiers(ctx.mods)); processChildren(constructorDeclaration, ctx.children); return constructorDeclaration; } @Override public DetailAstImpl visitFieldDeclaration(JavaLanguageParser.FieldDeclarationContext ctx) { final DetailAstImpl dummyNode = new DetailAstImpl(); // Since the TYPE AST is built by visitVariableDeclarator(), we skip it here (child [0]) // We also append the SEMI token to the first child [size() - 1], // until https://github.com/checkstyle/checkstyle/issues/3151 processChildren(dummyNode, ctx.children.subList(1, ctx.children.size() - 1)); dummyNode.getFirstChild().addChild(create(ctx.SEMI())); return dummyNode.getFirstChild(); } @Override public DetailAstImpl visitInterfaceBodyDeclaration( JavaLanguageParser.InterfaceBodyDeclarationContext ctx) { final DetailAstImpl returnTree; if (ctx.SEMI() == null) { returnTree = visit(ctx.interfaceMemberDeclaration()); } else { returnTree = create(ctx.SEMI()); } return returnTree; } @Override public DetailAstImpl visitInterfaceMethodDeclaration( JavaLanguageParser.InterfaceMethodDeclarationContext ctx) { final DetailAstImpl methodDef = createImaginary(TokenTypes.METHOD_DEF); methodDef.addChild(createModifiers(ctx.mods)); // Process all children except C style array declarators and modifiers final List children = ctx.children .stream() .filter(child -> !(child instanceof JavaLanguageParser.ArrayDeclaratorContext)) .collect(Collectors.toUnmodifiableList()); processChildren(methodDef, children); // We add C style array declarator brackets to TYPE ast final DetailAstImpl typeAst = (DetailAstImpl) methodDef.findFirstToken(TokenTypes.TYPE); ctx.cStyleArrDec.forEach(child -> typeAst.addChild(visit(child))); return methodDef; } @Override public DetailAstImpl visitVariableDeclarators( JavaLanguageParser.VariableDeclaratorsContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitVariableDeclarator( JavaLanguageParser.VariableDeclaratorContext ctx) { final DetailAstImpl variableDef = createImaginary(TokenTypes.VARIABLE_DEF); variableDef.addChild(createModifiers(ctx.mods)); final DetailAstImpl type = visit(ctx.type); variableDef.addChild(type); variableDef.addChild(visit(ctx.id())); // Add C style array declarator brackets to TYPE ast ctx.arrayDeclarator().forEach(child -> type.addChild(visit(child))); // If this is an assignment statement, ASSIGN becomes the parent of EXPR final TerminalNode assignNode = ctx.ASSIGN(); if (assignNode != null) { final DetailAstImpl assign = create(assignNode); variableDef.addChild(assign); assign.addChild(visit(ctx.variableInitializer())); } return variableDef; } @Override public DetailAstImpl visitVariableDeclaratorId( JavaLanguageParser.VariableDeclaratorIdContext ctx) { final DetailAstImpl root = new DetailAstImpl(); root.addChild(createModifiers(ctx.mods)); final DetailAstImpl type = visit(ctx.type); root.addChild(type); final DetailAstImpl declaratorId; if (ctx.LITERAL_THIS() == null) { declaratorId = visit(ctx.qualifiedName()); } else if (ctx.DOT() == null) { declaratorId = create(ctx.LITERAL_THIS()); } else { declaratorId = create(ctx.DOT()); declaratorId.addChild(visit(ctx.qualifiedName())); declaratorId.addChild(create(ctx.LITERAL_THIS())); } root.addChild(declaratorId); ctx.arrayDeclarator().forEach(child -> type.addChild(visit(child))); return root.getFirstChild(); } @Override public DetailAstImpl visitArrayInitializer(JavaLanguageParser.ArrayInitializerContext ctx) { final DetailAstImpl arrayInitializer = create(TokenTypes.ARRAY_INIT, ctx.start); // ARRAY_INIT was child[0] processChildren(arrayInitializer, ctx.children.subList(1, ctx.children.size())); return arrayInitializer; } @Override public DetailAstImpl visitClassOrInterfaceType( JavaLanguageParser.ClassOrInterfaceTypeContext ctx) { final DetailAstPair currentAST = new DetailAstPair(); DetailAstPair.addAstChild(currentAST, visit(ctx.id())); DetailAstPair.addAstChild(currentAST, visit(ctx.typeArguments())); // This is how we build the annotations/ qualified name/ type parameters tree for (ParserRuleContext extendedContext : ctx.extended) { final DetailAstImpl dot = create(extendedContext.start); DetailAstPair.makeAstRoot(currentAST, dot); extendedContext.children .forEach(child -> DetailAstPair.addAstChild(currentAST, visit(child))); } // Create imaginary 'TYPE' parent if specified final DetailAstImpl returnTree; if (ctx.createImaginaryNode) { returnTree = createImaginary(TokenTypes.TYPE); returnTree.addChild(currentAST.root); } else { returnTree = currentAST.root; } return returnTree; } @Override public DetailAstImpl visitSimpleTypeArgument( JavaLanguageParser.SimpleTypeArgumentContext ctx) { final DetailAstImpl typeArgument = createImaginary(TokenTypes.TYPE_ARGUMENT); typeArgument.addChild(visit(ctx.typeType())); return typeArgument; } @Override public DetailAstImpl visitWildCardTypeArgument( JavaLanguageParser.WildCardTypeArgumentContext ctx) { final DetailAstImpl typeArgument = createImaginary(TokenTypes.TYPE_ARGUMENT); typeArgument.addChild(visit(ctx.annotations())); typeArgument.addChild(create(TokenTypes.WILDCARD_TYPE, (Token) ctx.QUESTION().getPayload())); if (ctx.upperBound != null) { final DetailAstImpl upperBound = create(TokenTypes.TYPE_UPPER_BOUNDS, ctx.upperBound); upperBound.addChild(visit(ctx.typeType())); typeArgument.addChild(upperBound); } else if (ctx.lowerBound != null) { final DetailAstImpl lowerBound = create(TokenTypes.TYPE_LOWER_BOUNDS, ctx.lowerBound); lowerBound.addChild(visit(ctx.typeType())); typeArgument.addChild(lowerBound); } return typeArgument; } @Override public DetailAstImpl visitQualifiedNameList(JavaLanguageParser.QualifiedNameListContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitFormalParameters(JavaLanguageParser.FormalParametersContext ctx) { final DetailAstImpl lparen = create(ctx.LPAREN()); // We make a "PARAMETERS" node whether parameters exist or not if (ctx.formalParameterList() == null) { addLastSibling(lparen, createImaginary(TokenTypes.PARAMETERS)); } else { addLastSibling(lparen, visit(ctx.formalParameterList())); } addLastSibling(lparen, create(ctx.RPAREN())); return lparen; } @Override public DetailAstImpl visitFormalParameterList( JavaLanguageParser.FormalParameterListContext ctx) { final DetailAstImpl parameters = createImaginary(TokenTypes.PARAMETERS); processChildren(parameters, ctx.children); return parameters; } @Override public DetailAstImpl visitFormalParameter(JavaLanguageParser.FormalParameterContext ctx) { final DetailAstImpl variableDeclaratorId = visitVariableDeclaratorId(ctx.variableDeclaratorId()); final DetailAstImpl parameterDef = createImaginary(TokenTypes.PARAMETER_DEF); parameterDef.addChild(variableDeclaratorId); return parameterDef; } @Override public DetailAstImpl visitLastFormalParameter( JavaLanguageParser.LastFormalParameterContext ctx) { final DetailAstImpl parameterDef = createImaginary(TokenTypes.PARAMETER_DEF); parameterDef.addChild(visit(ctx.variableDeclaratorId())); final DetailAstImpl ident = (DetailAstImpl) parameterDef.findFirstToken(TokenTypes.IDENT); ident.addPreviousSibling(create(ctx.ELLIPSIS())); // We attach annotations on ellipses in varargs to the 'TYPE' ast final DetailAstImpl type = (DetailAstImpl) parameterDef.findFirstToken(TokenTypes.TYPE); type.addChild(visit(ctx.annotations())); return parameterDef; } @Override public DetailAstImpl visitQualifiedName(JavaLanguageParser.QualifiedNameContext ctx) { final DetailAstImpl ast = visit(ctx.id()); final DetailAstPair currentAst = new DetailAstPair(); DetailAstPair.addAstChild(currentAst, ast); for (ParserRuleContext extendedContext : ctx.extended) { final DetailAstImpl dot = create(extendedContext.start); DetailAstPair.makeAstRoot(currentAst, dot); final List childList = extendedContext .children.subList(1, extendedContext.children.size()); processChildren(dot, childList); } return currentAst.getRoot(); } @Override public DetailAstImpl visitLiteral(JavaLanguageParser.LiteralContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitIntegerLiteral(JavaLanguageParser.IntegerLiteralContext ctx) { final int[] longTypes = { JavaLanguageLexer.DECIMAL_LITERAL_LONG, JavaLanguageLexer.HEX_LITERAL_LONG, JavaLanguageLexer.OCT_LITERAL_LONG, JavaLanguageLexer.BINARY_LITERAL_LONG, }; final int tokenType; if (TokenUtil.isOfType(ctx.start.getType(), longTypes)) { tokenType = TokenTypes.NUM_LONG; } else { tokenType = TokenTypes.NUM_INT; } return create(tokenType, ctx.start); } @Override public DetailAstImpl visitFloatLiteral(JavaLanguageParser.FloatLiteralContext ctx) { final DetailAstImpl floatLiteral; if (TokenUtil.isOfType(ctx.start.getType(), JavaLanguageLexer.DOUBLE_LITERAL, JavaLanguageLexer.HEX_DOUBLE_LITERAL)) { floatLiteral = create(TokenTypes.NUM_DOUBLE, ctx.start); } else { floatLiteral = create(TokenTypes.NUM_FLOAT, ctx.start); } return floatLiteral; } @Override public DetailAstImpl visitTextBlockLiteral(JavaLanguageParser.TextBlockLiteralContext ctx) { final DetailAstImpl textBlockLiteralBegin = create(ctx.TEXT_BLOCK_LITERAL_BEGIN()); textBlockLiteralBegin.addChild(create(ctx.TEXT_BLOCK_CONTENT())); textBlockLiteralBegin.addChild(create(ctx.TEXT_BLOCK_LITERAL_END())); return textBlockLiteralBegin; } @Override public DetailAstImpl visitAnnotations(JavaLanguageParser.AnnotationsContext ctx) { final DetailAstImpl annotations; if (!ctx.createImaginaryNode && ctx.anno.isEmpty()) { // There are no annotations, and we don't want to create the empty node annotations = null; } else { // There are annotations, or we just want the empty node annotations = createImaginary(TokenTypes.ANNOTATIONS); processChildren(annotations, ctx.anno); } return annotations; } @Override public DetailAstImpl visitAnnotation(JavaLanguageParser.AnnotationContext ctx) { final DetailAstImpl annotation = createImaginary(TokenTypes.ANNOTATION); processChildren(annotation, ctx.children); return annotation; } @Override public DetailAstImpl visitElementValuePairs(JavaLanguageParser.ElementValuePairsContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitElementValuePair(JavaLanguageParser.ElementValuePairContext ctx) { final DetailAstImpl elementValuePair = createImaginary(TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR); processChildren(elementValuePair, ctx.children); return elementValuePair; } @Override public DetailAstImpl visitElementValue(JavaLanguageParser.ElementValueContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitElementValueArrayInitializer( JavaLanguageParser.ElementValueArrayInitializerContext ctx) { final DetailAstImpl arrayInit = create(TokenTypes.ANNOTATION_ARRAY_INIT, (Token) ctx.LCURLY().getPayload()); processChildren(arrayInit, ctx.children.subList(1, ctx.children.size())); return arrayInit; } @Override public DetailAstImpl visitAnnotationTypeDeclaration( JavaLanguageParser.AnnotationTypeDeclarationContext ctx) { return createTypeDeclaration(ctx, TokenTypes.ANNOTATION_DEF, ctx.mods); } @Override public DetailAstImpl visitAnnotationTypeBody( JavaLanguageParser.AnnotationTypeBodyContext ctx) { final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK); processChildren(objBlock, ctx.children); return objBlock; } @Override public DetailAstImpl visitAnnotationTypeElementDeclaration( JavaLanguageParser.AnnotationTypeElementDeclarationContext ctx) { final DetailAstImpl returnTree; if (ctx.SEMI() == null) { returnTree = visit(ctx.annotationTypeElementRest()); } else { returnTree = create(ctx.SEMI()); } return returnTree; } @Override public DetailAstImpl visitAnnotationField(JavaLanguageParser.AnnotationFieldContext ctx) { final DetailAstImpl dummyNode = new DetailAstImpl(); // Since the TYPE AST is built by visitAnnotationMethodOrConstantRest(), we skip it // here (child [0]) processChildren(dummyNode, Collections.singletonList(ctx.children.get(1))); // We also append the SEMI token to the first child [size() - 1], // until https://github.com/checkstyle/checkstyle/issues/3151 dummyNode.getFirstChild().addChild(create(ctx.SEMI())); return dummyNode.getFirstChild(); } @Override public DetailAstImpl visitAnnotationType(JavaLanguageParser.AnnotationTypeContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitAnnotationMethodRest( JavaLanguageParser.AnnotationMethodRestContext ctx) { final DetailAstImpl annotationFieldDef = createImaginary(TokenTypes.ANNOTATION_FIELD_DEF); annotationFieldDef.addChild(createModifiers(ctx.mods)); annotationFieldDef.addChild(visit(ctx.type)); // Process all children except C style array declarators processChildren(annotationFieldDef, ctx.children.stream() .filter(child -> !(child instanceof JavaLanguageParser.ArrayDeclaratorContext)) .collect(Collectors.toUnmodifiableList())); // We add C style array declarator brackets to TYPE ast final DetailAstImpl typeAst = (DetailAstImpl) annotationFieldDef.findFirstToken(TokenTypes.TYPE); ctx.cStyleArrDec.forEach(child -> typeAst.addChild(visit(child))); return annotationFieldDef; } @Override public DetailAstImpl visitDefaultValue(JavaLanguageParser.DefaultValueContext ctx) { final DetailAstImpl defaultValue = create(ctx.LITERAL_DEFAULT()); defaultValue.addChild(visit(ctx.elementValue())); return defaultValue; } @Override public DetailAstImpl visitConstructorBlock(JavaLanguageParser.ConstructorBlockContext ctx) { final DetailAstImpl slist = create(TokenTypes.SLIST, ctx.start); // SLIST was child [0] processChildren(slist, ctx.children.subList(1, ctx.children.size())); return slist; } @Override public DetailAstImpl visitExplicitCtorCall(JavaLanguageParser.ExplicitCtorCallContext ctx) { final DetailAstImpl root; if (ctx.LITERAL_THIS() == null) { root = create(TokenTypes.SUPER_CTOR_CALL, (Token) ctx.LITERAL_SUPER().getPayload()); } else { root = create(TokenTypes.CTOR_CALL, (Token) ctx.LITERAL_THIS().getPayload()); } root.addChild(visit(ctx.typeArguments())); root.addChild(visit(ctx.arguments())); root.addChild(create(ctx.SEMI())); return root; } @Override public DetailAstImpl visitPrimaryCtorCall(JavaLanguageParser.PrimaryCtorCallContext ctx) { final DetailAstImpl primaryCtorCall = create(TokenTypes.SUPER_CTOR_CALL, (Token) ctx.LITERAL_SUPER().getPayload()); // filter 'LITERAL_SUPER' processChildren(primaryCtorCall, ctx.children.stream() .filter(child -> !child.equals(ctx.LITERAL_SUPER())) .collect(Collectors.toUnmodifiableList())); return primaryCtorCall; } @Override public DetailAstImpl visitBlock(JavaLanguageParser.BlockContext ctx) { final DetailAstImpl slist = create(TokenTypes.SLIST, ctx.start); // SLIST was child [0] processChildren(slist, ctx.children.subList(1, ctx.children.size())); return slist; } @Override public DetailAstImpl visitLocalVar(JavaLanguageParser.LocalVarContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitBlockStat(JavaLanguageParser.BlockStatContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitAssertExp(JavaLanguageParser.AssertExpContext ctx) { final DetailAstImpl assertExp = create(ctx.ASSERT()); // child[0] is 'ASSERT' processChildren(assertExp, ctx.children.subList(1, ctx.children.size())); return assertExp; } @Override public DetailAstImpl visitIfStat(JavaLanguageParser.IfStatContext ctx) { final DetailAstImpl ifStat = create(ctx.LITERAL_IF()); // child[0] is 'LITERAL_IF' processChildren(ifStat, ctx.children.subList(1, ctx.children.size())); return ifStat; } @Override public DetailAstImpl visitForStat(JavaLanguageParser.ForStatContext ctx) { final DetailAstImpl forInit = create(ctx.start); // child[0] is LITERAL_FOR processChildren(forInit, ctx.children.subList(1, ctx.children.size())); return forInit; } @Override public DetailAstImpl visitWhileStat(JavaLanguageParser.WhileStatContext ctx) { final DetailAstImpl whileStatement = create(ctx.start); // 'LITERAL_WHILE' is child[0] processChildren(whileStatement, ctx.children.subList(1, ctx.children.size())); return whileStatement; } @Override public DetailAstImpl visitDoStat(JavaLanguageParser.DoStatContext ctx) { final DetailAstImpl doStatement = create(ctx.start); // 'LITERAL_DO' is child[0] doStatement.addChild(visit(ctx.statement())); // We make 'LITERAL_WHILE' into 'DO_WHILE' doStatement.addChild(create(TokenTypes.DO_WHILE, (Token) ctx.LITERAL_WHILE().getPayload())); doStatement.addChild(visit(ctx.parExpression())); doStatement.addChild(create(ctx.SEMI())); return doStatement; } @Override public DetailAstImpl visitTryStat(JavaLanguageParser.TryStatContext ctx) { final DetailAstImpl tryStat = create(ctx.start); // child[0] is 'LITERAL_TRY' processChildren(tryStat, ctx.children.subList(1, ctx.children.size())); return tryStat; } @Override public DetailAstImpl visitTryWithResourceStat( JavaLanguageParser.TryWithResourceStatContext ctx) { final DetailAstImpl tryWithResources = create(ctx.LITERAL_TRY()); // child[0] is 'LITERAL_TRY' processChildren(tryWithResources, ctx.children.subList(1, ctx.children.size())); return tryWithResources; } @Override public DetailAstImpl visitYieldStat(JavaLanguageParser.YieldStatContext ctx) { final DetailAstImpl yieldParent = create(ctx.LITERAL_YIELD()); // LITERAL_YIELD is child[0] processChildren(yieldParent, ctx.children.subList(1, ctx.children.size())); return yieldParent; } @Override public DetailAstImpl visitSyncStat(JavaLanguageParser.SyncStatContext ctx) { final DetailAstImpl syncStatement = create(ctx.start); // child[0] is 'LITERAL_SYNCHRONIZED' processChildren(syncStatement, ctx.children.subList(1, ctx.children.size())); return syncStatement; } @Override public DetailAstImpl visitReturnStat(JavaLanguageParser.ReturnStatContext ctx) { final DetailAstImpl returnStat = create(ctx.LITERAL_RETURN()); // child[0] is 'LITERAL_RETURN' processChildren(returnStat, ctx.children.subList(1, ctx.children.size())); return returnStat; } @Override public DetailAstImpl visitThrowStat(JavaLanguageParser.ThrowStatContext ctx) { final DetailAstImpl throwStat = create(ctx.LITERAL_THROW()); // child[0] is 'LITERAL_THROW' processChildren(throwStat, ctx.children.subList(1, ctx.children.size())); return throwStat; } @Override public DetailAstImpl visitBreakStat(JavaLanguageParser.BreakStatContext ctx) { final DetailAstImpl literalBreak = create(ctx.LITERAL_BREAK()); // child[0] is 'LITERAL_BREAK' processChildren(literalBreak, ctx.children.subList(1, ctx.children.size())); return literalBreak; } @Override public DetailAstImpl visitContinueStat(JavaLanguageParser.ContinueStatContext ctx) { final DetailAstImpl continueStat = create(ctx.LITERAL_CONTINUE()); // child[0] is 'LITERAL_CONTINUE' processChildren(continueStat, ctx.children.subList(1, ctx.children.size())); return continueStat; } @Override public DetailAstImpl visitEmptyStat(JavaLanguageParser.EmptyStatContext ctx) { return create(TokenTypes.EMPTY_STAT, ctx.start); } @Override public DetailAstImpl visitExpStat(JavaLanguageParser.ExpStatContext ctx) { final DetailAstImpl expStatRoot = visit(ctx.statementExpression); addLastSibling(expStatRoot, create(ctx.SEMI())); return expStatRoot; } @Override public DetailAstImpl visitLabelStat(JavaLanguageParser.LabelStatContext ctx) { final DetailAstImpl labelStat = create(TokenTypes.LABELED_STAT, (Token) ctx.COLON().getPayload()); labelStat.addChild(visit(ctx.id())); labelStat.addChild(visit(ctx.statement())); return labelStat; } @Override public DetailAstImpl visitSwitchExpressionOrStatement( JavaLanguageParser.SwitchExpressionOrStatementContext ctx) { final DetailAstImpl switchStat = create(ctx.LITERAL_SWITCH()); switchStat.addChild(visit(ctx.parExpression())); switchStat.addChild(create(ctx.LCURLY())); switchStat.addChild(visit(ctx.switchBlock())); switchStat.addChild(create(ctx.RCURLY())); return switchStat; } @Override public DetailAstImpl visitSwitchRules(JavaLanguageParser.SwitchRulesContext ctx) { final DetailAstImpl dummyRoot = new DetailAstImpl(); ctx.switchLabeledRule().forEach(switchLabeledRuleContext -> { final DetailAstImpl switchRule = visit(switchLabeledRuleContext); final DetailAstImpl switchRuleParent = createImaginary(TokenTypes.SWITCH_RULE); switchRuleParent.addChild(switchRule); dummyRoot.addChild(switchRuleParent); }); return dummyRoot.getFirstChild(); } @Override public DetailAstImpl visitSwitchBlocks(JavaLanguageParser.SwitchBlocksContext ctx) { final DetailAstImpl dummyRoot = new DetailAstImpl(); ctx.groups.forEach(group -> dummyRoot.addChild(visit(group))); // Add any empty switch labels to end of statement in one 'CASE_GROUP' if (!ctx.emptyLabels.isEmpty()) { final DetailAstImpl emptyLabelParent = createImaginary(TokenTypes.CASE_GROUP); ctx.emptyLabels.forEach(label -> emptyLabelParent.addChild(visit(label))); dummyRoot.addChild(emptyLabelParent); } return dummyRoot.getFirstChild(); } @Override public DetailAstImpl visitSwitchLabeledExpression( JavaLanguageParser.SwitchLabeledExpressionContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitSwitchLabeledBlock( JavaLanguageParser.SwitchLabeledBlockContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitSwitchLabeledThrow( JavaLanguageParser.SwitchLabeledThrowContext ctx) { final DetailAstImpl switchLabel = visit(ctx.switchLabel()); addLastSibling(switchLabel, create(ctx.LAMBDA())); final DetailAstImpl literalThrow = create(ctx.LITERAL_THROW()); literalThrow.addChild(visit(ctx.expression())); literalThrow.addChild(create(ctx.SEMI())); addLastSibling(switchLabel, literalThrow); return switchLabel; } @Override public DetailAstImpl visitElseStat(JavaLanguageParser.ElseStatContext ctx) { final DetailAstImpl elseStat = create(ctx.LITERAL_ELSE()); // child[0] is 'LITERAL_ELSE' processChildren(elseStat, ctx.children.subList(1, ctx.children.size())); return elseStat; } @Override public DetailAstImpl visitCatchClause(JavaLanguageParser.CatchClauseContext ctx) { final DetailAstImpl catchClause = create(TokenTypes.LITERAL_CATCH, (Token) ctx.LITERAL_CATCH().getPayload()); // 'LITERAL_CATCH' is child[0] processChildren(catchClause, ctx.children.subList(1, ctx.children.size())); return catchClause; } @Override public DetailAstImpl visitCatchParameter(JavaLanguageParser.CatchParameterContext ctx) { final DetailAstImpl catchParameterDef = createImaginary(TokenTypes.PARAMETER_DEF); catchParameterDef.addChild(createModifiers(ctx.mods)); // filter mods processChildren(catchParameterDef, ctx.children.stream() .filter(child -> !(child instanceof JavaLanguageParser.VariableModifierContext)) .collect(Collectors.toUnmodifiableList())); return catchParameterDef; } @Override public DetailAstImpl visitCatchType(JavaLanguageParser.CatchTypeContext ctx) { final DetailAstImpl type = createImaginary(TokenTypes.TYPE); processChildren(type, ctx.children); return type; } @Override public DetailAstImpl visitFinallyBlock(JavaLanguageParser.FinallyBlockContext ctx) { final DetailAstImpl finallyBlock = create(ctx.LITERAL_FINALLY()); // child[0] is 'LITERAL_FINALLY' processChildren(finallyBlock, ctx.children.subList(1, ctx.children.size())); return finallyBlock; } @Override public DetailAstImpl visitResourceSpecification( JavaLanguageParser.ResourceSpecificationContext ctx) { final DetailAstImpl resourceSpecification = createImaginary(TokenTypes.RESOURCE_SPECIFICATION); processChildren(resourceSpecification, ctx.children); return resourceSpecification; } @Override public DetailAstImpl visitResources(JavaLanguageParser.ResourcesContext ctx) { final DetailAstImpl firstResource = visit(ctx.resource(0)); final DetailAstImpl resources = createImaginary(TokenTypes.RESOURCES); resources.addChild(firstResource); processChildren(resources, ctx.children.subList(1, ctx.children.size())); return resources; } @Override public DetailAstImpl visitResourceDeclaration( JavaLanguageParser.ResourceDeclarationContext ctx) { final DetailAstImpl resource = createImaginary(TokenTypes.RESOURCE); resource.addChild(visit(ctx.variableDeclaratorId())); final DetailAstImpl assign = create(ctx.ASSIGN()); resource.addChild(assign); assign.addChild(visit(ctx.expression())); return resource; } @Override public DetailAstImpl visitVariableAccess(JavaLanguageParser.VariableAccessContext ctx) { final DetailAstImpl resource; if (ctx.accessList.isEmpty()) { resource = createImaginary(TokenTypes.RESOURCE); resource.addChild(visit(ctx.id())); } else { final DetailAstPair currentAst = new DetailAstPair(); ctx.accessList.forEach(fieldAccess -> { DetailAstPair.addAstChild(currentAst, visit(fieldAccess.expr())); DetailAstPair.makeAstRoot(currentAst, create(fieldAccess.DOT())); }); resource = createImaginary(TokenTypes.RESOURCE); resource.addChild(currentAst.root); if (ctx.LITERAL_THIS() == null) { resource.getFirstChild().addChild(visit(ctx.id())); } else { resource.getFirstChild().addChild(create(ctx.LITERAL_THIS())); } } return resource; } @Override public DetailAstImpl visitSwitchBlockStatementGroup( JavaLanguageParser.SwitchBlockStatementGroupContext ctx) { final DetailAstImpl caseGroup = createImaginary(TokenTypes.CASE_GROUP); processChildren(caseGroup, ctx.switchLabel()); final DetailAstImpl sList = createImaginary(TokenTypes.SLIST); processChildren(sList, ctx.slists); caseGroup.addChild(sList); return caseGroup; } @Override public DetailAstImpl visitCaseLabel(JavaLanguageParser.CaseLabelContext ctx) { final DetailAstImpl caseLabel = create(ctx.LITERAL_CASE()); // child [0] is 'LITERAL_CASE' processChildren(caseLabel, ctx.children.subList(1, ctx.children.size())); return caseLabel; } @Override public DetailAstImpl visitDefaultLabel(JavaLanguageParser.DefaultLabelContext ctx) { final DetailAstImpl defaultLabel = create(ctx.LITERAL_DEFAULT()); if (ctx.COLON() != null) { defaultLabel.addChild(create(ctx.COLON())); } return defaultLabel; } @Override public DetailAstImpl visitCaseConstants(JavaLanguageParser.CaseConstantsContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitCaseConstant(JavaLanguageParser.CaseConstantContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitEnhancedFor(JavaLanguageParser.EnhancedForContext ctx) { final DetailAstImpl leftParen = create(ctx.LPAREN()); final DetailAstImpl enhancedForControl = visit(ctx.getChild(1)); final DetailAstImpl forEachClause = createImaginary(TokenTypes.FOR_EACH_CLAUSE); forEachClause.addChild(enhancedForControl); addLastSibling(leftParen, forEachClause); addLastSibling(leftParen, create(ctx.RPAREN())); return leftParen; } @Override public DetailAstImpl visitForFor(JavaLanguageParser.ForForContext ctx) { final DetailAstImpl dummyRoot = new DetailAstImpl(); dummyRoot.addChild(create(ctx.LPAREN())); if (ctx.forInit() == null) { final DetailAstImpl imaginaryForInitParent = createImaginary(TokenTypes.FOR_INIT); dummyRoot.addChild(imaginaryForInitParent); } else { dummyRoot.addChild(visit(ctx.forInit())); } dummyRoot.addChild(create(ctx.SEMI(0))); final DetailAstImpl forCondParent = createImaginary(TokenTypes.FOR_CONDITION); forCondParent.addChild(visit(ctx.forCond)); dummyRoot.addChild(forCondParent); dummyRoot.addChild(create(ctx.SEMI(1))); final DetailAstImpl forItParent = createImaginary(TokenTypes.FOR_ITERATOR); forItParent.addChild(visit(ctx.forUpdate)); dummyRoot.addChild(forItParent); dummyRoot.addChild(create(ctx.RPAREN())); return dummyRoot.getFirstChild(); } @Override public DetailAstImpl visitForInit(JavaLanguageParser.ForInitContext ctx) { final DetailAstImpl forInit = createImaginary(TokenTypes.FOR_INIT); processChildren(forInit, ctx.children); return forInit; } @Override public DetailAstImpl visitEnhancedForControl( JavaLanguageParser.EnhancedForControlContext ctx) { final DetailAstImpl variableDeclaratorId = visit(ctx.variableDeclaratorId()); final DetailAstImpl variableDef = createImaginary(TokenTypes.VARIABLE_DEF); variableDef.addChild(variableDeclaratorId); addLastSibling(variableDef, create(ctx.COLON())); addLastSibling(variableDef, visit(ctx.expression())); return variableDef; } @Override public DetailAstImpl visitEnhancedForControlWithRecordPattern( JavaLanguageParser.EnhancedForControlWithRecordPatternContext ctx) { final DetailAstImpl recordPattern = visit(ctx.pattern()); addLastSibling(recordPattern, create(ctx.COLON())); addLastSibling(recordPattern, visit(ctx.expression())); return recordPattern; } @Override public DetailAstImpl visitParExpression(JavaLanguageParser.ParExpressionContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitExpressionList(JavaLanguageParser.ExpressionListContext ctx) { final DetailAstImpl elist = createImaginary(TokenTypes.ELIST); processChildren(elist, ctx.children); return elist; } @Override public DetailAstImpl visitExpression(JavaLanguageParser.ExpressionContext ctx) { return buildExpressionNode(ctx.expr()); } @Override public DetailAstImpl visitRefOp(JavaLanguageParser.RefOpContext ctx) { final DetailAstImpl bop = create(ctx.bop); final DetailAstImpl leftChild = visit(ctx.expr()); final DetailAstImpl rightChild = create(TokenTypes.IDENT, ctx.stop); bop.addChild(leftChild); bop.addChild(rightChild); return bop; } @Override public DetailAstImpl visitSuperExp(JavaLanguageParser.SuperExpContext ctx) { final DetailAstImpl bop = create(ctx.bop); bop.addChild(visit(ctx.expr())); bop.addChild(create(ctx.LITERAL_SUPER())); DetailAstImpl superSuffixParent = visit(ctx.superSuffix()); if (superSuffixParent == null) { superSuffixParent = bop; } else { DetailAstImpl firstChild = superSuffixParent; while (firstChild.getFirstChild() != null) { firstChild = firstChild.getFirstChild(); } firstChild.addPreviousSibling(bop); } return superSuffixParent; } @Override public DetailAstImpl visitInstanceOfExp(JavaLanguageParser.InstanceOfExpContext ctx) { final DetailAstImpl literalInstanceOf = create(ctx.LITERAL_INSTANCEOF()); literalInstanceOf.addChild(visit(ctx.expr())); final ParseTree patternOrType = ctx.getChild(2); final DetailAstImpl patternDef; if (patternOrType instanceof JavaLanguageParser.ParenPatternContext) { // Parenthesized pattern has a `PATTERN_DEF` parent patternDef = createImaginary(TokenTypes.PATTERN_DEF); patternDef.addChild(visit(patternOrType)); } else { patternDef = visit(patternOrType); } literalInstanceOf.addChild(patternDef); return literalInstanceOf; } @Override public DetailAstImpl visitBitShift(JavaLanguageParser.BitShiftContext ctx) { final DetailAstImpl shiftOperation; // We determine the type of shift operation in the parser, instead of the // lexer as in older grammars. This makes it easier to parse type parameters // and less than/ greater than operators in general. if (ctx.LT().size() == LEFT_SHIFT.length()) { shiftOperation = create(TokenTypes.SL, (Token) ctx.LT(0).getPayload()); shiftOperation.setText(LEFT_SHIFT); } else if (ctx.GT().size() == UNSIGNED_RIGHT_SHIFT.length()) { shiftOperation = create(TokenTypes.BSR, (Token) ctx.GT(0).getPayload()); shiftOperation.setText(UNSIGNED_RIGHT_SHIFT); } else { shiftOperation = create(TokenTypes.SR, (Token) ctx.GT(0).getPayload()); shiftOperation.setText(RIGHT_SHIFT); } shiftOperation.addChild(visit(ctx.expr(0))); shiftOperation.addChild(visit(ctx.expr(1))); return shiftOperation; } @Override public DetailAstImpl visitNewExp(JavaLanguageParser.NewExpContext ctx) { final DetailAstImpl newExp = create(ctx.LITERAL_NEW()); // child [0] is LITERAL_NEW processChildren(newExp, ctx.children.subList(1, ctx.children.size())); return newExp; } @Override public DetailAstImpl visitPrefix(JavaLanguageParser.PrefixContext ctx) { final int tokenType; switch (ctx.prefix.getType()) { case JavaLanguageLexer.PLUS: tokenType = TokenTypes.UNARY_PLUS; break; case JavaLanguageLexer.MINUS: tokenType = TokenTypes.UNARY_MINUS; break; default: tokenType = ctx.prefix.getType(); } final DetailAstImpl prefix = create(tokenType, ctx.prefix); prefix.addChild(visit(ctx.expr())); return prefix; } @Override public DetailAstImpl visitCastExp(JavaLanguageParser.CastExpContext ctx) { final DetailAstImpl cast = create(TokenTypes.TYPECAST, (Token) ctx.LPAREN().getPayload()); // child [0] is LPAREN processChildren(cast, ctx.children.subList(1, ctx.children.size())); return cast; } @Override public DetailAstImpl visitIndexOp(JavaLanguageParser.IndexOpContext ctx) { // LBRACK -> INDEX_OP is root of this AST final DetailAstImpl indexOp = create(TokenTypes.INDEX_OP, (Token) ctx.LBRACK().getPayload()); // add expression(IDENT) on LHS indexOp.addChild(visit(ctx.expr(0))); // create imaginary node for expression on RHS final DetailAstImpl expr = visit(ctx.expr(1)); final DetailAstImpl imaginaryExpr = createImaginary(TokenTypes.EXPR); imaginaryExpr.addChild(expr); indexOp.addChild(imaginaryExpr); // complete AST by adding RBRACK indexOp.addChild(create(ctx.RBRACK())); return indexOp; } @Override public DetailAstImpl visitInvOp(JavaLanguageParser.InvOpContext ctx) { final DetailAstPair currentAst = new DetailAstPair(); final DetailAstImpl returnAst = visit(ctx.expr()); DetailAstPair.addAstChild(currentAst, returnAst); DetailAstPair.makeAstRoot(currentAst, create(ctx.bop)); DetailAstPair.addAstChild(currentAst, visit(ctx.nonWildcardTypeArguments())); DetailAstPair.addAstChild(currentAst, visit(ctx.id())); final DetailAstImpl lparen = create(TokenTypes.METHOD_CALL, (Token) ctx.LPAREN().getPayload()); DetailAstPair.makeAstRoot(currentAst, lparen); // We always add an 'ELIST' node final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList())) .orElseGet(() -> createImaginary(TokenTypes.ELIST)); DetailAstPair.addAstChild(currentAst, expressionList); DetailAstPair.addAstChild(currentAst, create(ctx.RPAREN())); return currentAst.root; } @Override public DetailAstImpl visitInitExp(JavaLanguageParser.InitExpContext ctx) { final DetailAstImpl dot = create(ctx.bop); dot.addChild(visit(ctx.expr())); final DetailAstImpl literalNew = create(ctx.LITERAL_NEW()); literalNew.addChild(visit(ctx.nonWildcardTypeArguments())); literalNew.addChild(visit(ctx.innerCreator())); dot.addChild(literalNew); return dot; } @Override public DetailAstImpl visitSimpleMethodCall(JavaLanguageParser.SimpleMethodCallContext ctx) { final DetailAstImpl methodCall = create(TokenTypes.METHOD_CALL, (Token) ctx.LPAREN().getPayload()); methodCall.addChild(visit(ctx.id())); // We always add an 'ELIST' node final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList())) .orElseGet(() -> createImaginary(TokenTypes.ELIST)); methodCall.addChild(expressionList); methodCall.addChild(create((Token) ctx.RPAREN().getPayload())); return methodCall; } @Override public DetailAstImpl visitLambdaExp(JavaLanguageParser.LambdaExpContext ctx) { final DetailAstImpl lambda = create(ctx.LAMBDA()); lambda.addChild(visit(ctx.lambdaParameters())); final JavaLanguageParser.BlockContext blockContext = ctx.block(); final DetailAstImpl rightHandLambdaChild; if (blockContext != null) { rightHandLambdaChild = visit(blockContext); } else { // Lambda expression child is built the same way that we build // the initial expression node in visitExpression, i.e. with // an imaginary EXPR node. This results in nested EXPR nodes // in the AST. rightHandLambdaChild = buildExpressionNode(ctx.expr()); } lambda.addChild(rightHandLambdaChild); return lambda; } @Override public DetailAstImpl visitThisExp(JavaLanguageParser.ThisExpContext ctx) { final DetailAstImpl bop = create(ctx.bop); bop.addChild(visit(ctx.expr())); bop.addChild(create(ctx.LITERAL_THIS())); return bop; } @Override public DetailAstImpl visitPrimaryExp(JavaLanguageParser.PrimaryExpContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitPostfix(JavaLanguageParser.PostfixContext ctx) { final DetailAstImpl postfix; if (ctx.postfix.getType() == JavaLanguageLexer.INC) { postfix = create(TokenTypes.POST_INC, ctx.postfix); } else { postfix = create(TokenTypes.POST_DEC, ctx.postfix); } postfix.addChild(visit(ctx.expr())); return postfix; } @Override public DetailAstImpl visitMethodRef(JavaLanguageParser.MethodRefContext ctx) { final DetailAstImpl doubleColon = create(TokenTypes.METHOD_REF, (Token) ctx.DOUBLE_COLON().getPayload()); final List children = ctx.children.stream() .filter(child -> !child.equals(ctx.DOUBLE_COLON())) .collect(Collectors.toUnmodifiableList()); processChildren(doubleColon, children); return doubleColon; } @Override public DetailAstImpl visitTernaryOp(JavaLanguageParser.TernaryOpContext ctx) { final DetailAstImpl root = create(ctx.QUESTION()); processChildren(root, ctx.children.stream() .filter(child -> !child.equals(ctx.QUESTION())) .collect(Collectors.toUnmodifiableList())); return root; } @Override public DetailAstImpl visitBinOp(JavaLanguageParser.BinOpContext ctx) { final DetailAstImpl bop = create(ctx.bop); // To improve performance, we iterate through binary operations // since they are frequently deeply nested. final List binOpList = new ArrayList<>(); ParseTree firstExpression = ctx.expr(0); while (firstExpression instanceof JavaLanguageParser.BinOpContext) { // Get all nested binOps binOpList.add((JavaLanguageParser.BinOpContext) firstExpression); firstExpression = ((JavaLanguageParser.BinOpContext) firstExpression).expr(0); } if (binOpList.isEmpty()) { final DetailAstImpl leftChild = visit(ctx.children.get(0)); bop.addChild(leftChild); } else { // Map all descendants to individual AST's since we can parallelize this // operation final Queue descendantList = binOpList.parallelStream() .map(this::getInnerBopAst) .collect(Collectors.toCollection(ConcurrentLinkedQueue::new)); bop.addChild(descendantList.poll()); DetailAstImpl pointer = bop.getFirstChild(); // Build tree for (DetailAstImpl descendant : descendantList) { pointer.getFirstChild().addPreviousSibling(descendant); pointer = descendant; } } bop.addChild(visit(ctx.children.get(2))); return bop; } /** * Builds the binary operation (binOp) AST. * * @param descendant the BinOpContext to build AST from * @return binOp AST */ private DetailAstImpl getInnerBopAst(JavaLanguageParser.BinOpContext descendant) { final DetailAstImpl innerBop = create(descendant.bop); final JavaLanguageParser.ExprContext expr = descendant.expr(0); if (!(expr instanceof JavaLanguageParser.BinOpContext)) { innerBop.addChild(visit(expr)); } innerBop.addChild(visit(descendant.expr(1))); return innerBop; } @Override public DetailAstImpl visitMethodCall(JavaLanguageParser.MethodCallContext ctx) { final DetailAstImpl methodCall = create(TokenTypes.METHOD_CALL, (Token) ctx.LPAREN().getPayload()); // We always add an 'ELIST' node final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList())) .orElseGet(() -> createImaginary(TokenTypes.ELIST)); final DetailAstImpl dot = create(ctx.DOT()); dot.addChild(visit(ctx.expr())); dot.addChild(visit(ctx.id())); methodCall.addChild(dot); methodCall.addChild(expressionList); methodCall.addChild(create((Token) ctx.RPAREN().getPayload())); return methodCall; } @Override public DetailAstImpl visitTypeCastParameters( JavaLanguageParser.TypeCastParametersContext ctx) { final DetailAstImpl typeType = visit(ctx.typeType(0)); for (int i = 0; i < ctx.BAND().size(); i++) { addLastSibling(typeType, create(TokenTypes.TYPE_EXTENSION_AND, (Token) ctx.BAND(i).getPayload())); addLastSibling(typeType, visit(ctx.typeType(i + 1))); } return typeType; } @Override public DetailAstImpl visitSingleLambdaParam(JavaLanguageParser.SingleLambdaParamContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitFormalLambdaParam(JavaLanguageParser.FormalLambdaParamContext ctx) { final DetailAstImpl lparen = create(ctx.LPAREN()); // We add an 'PARAMETERS' node here whether it exists or not final DetailAstImpl parameters = Optional.ofNullable(visit(ctx.formalParameterList())) .orElseGet(() -> createImaginary(TokenTypes.PARAMETERS)); addLastSibling(lparen, parameters); addLastSibling(lparen, create(ctx.RPAREN())); return lparen; } @Override public DetailAstImpl visitMultiLambdaParam(JavaLanguageParser.MultiLambdaParamContext ctx) { final DetailAstImpl lparen = create(ctx.LPAREN()); addLastSibling(lparen, visit(ctx.multiLambdaParams())); addLastSibling(lparen, create(ctx.RPAREN())); return lparen; } @Override public DetailAstImpl visitMultiLambdaParams(JavaLanguageParser.MultiLambdaParamsContext ctx) { final DetailAstImpl parameters = createImaginary(TokenTypes.PARAMETERS); parameters.addChild(createLambdaParameter(ctx.id(0))); for (int i = 0; i < ctx.COMMA().size(); i++) { parameters.addChild(create(ctx.COMMA(i))); parameters.addChild(createLambdaParameter(ctx.id(i + 1))); } return parameters; } /** * Creates a 'PARAMETER_DEF' node for a lambda expression, with * imaginary modifier and type nodes. * * @param ctx the IdContext to create imaginary nodes for * @return DetailAstImpl of lambda parameter */ private DetailAstImpl createLambdaParameter(JavaLanguageParser.IdContext ctx) { final DetailAstImpl ident = visitId(ctx); final DetailAstImpl parameter = createImaginary(TokenTypes.PARAMETER_DEF); final DetailAstImpl modifiers = createImaginary(TokenTypes.MODIFIERS); final DetailAstImpl type = createImaginary(TokenTypes.TYPE); parameter.addChild(modifiers); parameter.addChild(type); parameter.addChild(ident); return parameter; } @Override public DetailAstImpl visitParenPrimary(JavaLanguageParser.ParenPrimaryContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitTokenPrimary(JavaLanguageParser.TokenPrimaryContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitClassRefPrimary(JavaLanguageParser.ClassRefPrimaryContext ctx) { final DetailAstImpl dot = create(ctx.DOT()); final DetailAstImpl primaryTypeNoArray = visit(ctx.type); dot.addChild(primaryTypeNoArray); if (TokenUtil.isOfType(primaryTypeNoArray, TokenTypes.DOT)) { // We append '[]' to the qualified name 'TYPE' `ast ctx.arrayDeclarator() .forEach(child -> primaryTypeNoArray.addChild(visit(child))); } else { ctx.arrayDeclarator() .forEach(child -> addLastSibling(primaryTypeNoArray, visit(child))); } dot.addChild(create(ctx.LITERAL_CLASS())); return dot; } @Override public DetailAstImpl visitPrimitivePrimary(JavaLanguageParser.PrimitivePrimaryContext ctx) { final DetailAstImpl dot = create(ctx.DOT()); final DetailAstImpl primaryTypeNoArray = visit(ctx.type); dot.addChild(primaryTypeNoArray); ctx.arrayDeclarator().forEach(child -> dot.addChild(visit(child))); dot.addChild(create(ctx.LITERAL_CLASS())); return dot; } @Override public DetailAstImpl visitCreator(JavaLanguageParser.CreatorContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitCreatedNameObject(JavaLanguageParser.CreatedNameObjectContext ctx) { final DetailAstPair currentAST = new DetailAstPair(); DetailAstPair.addAstChild(currentAST, visit(ctx.annotations())); DetailAstPair.addAstChild(currentAST, visit(ctx.id())); DetailAstPair.addAstChild(currentAST, visit(ctx.typeArgumentsOrDiamond())); // This is how we build the type arguments/ qualified name tree for (ParserRuleContext extendedContext : ctx.extended) { final DetailAstImpl dot = create(extendedContext.start); DetailAstPair.makeAstRoot(currentAST, dot); final List childList = extendedContext .children.subList(1, extendedContext.children.size()); processChildren(dot, childList); } return currentAST.root; } @Override public DetailAstImpl visitCreatedNamePrimitive( JavaLanguageParser.CreatedNamePrimitiveContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitInnerCreator(JavaLanguageParser.InnerCreatorContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitArrayCreatorRest(JavaLanguageParser.ArrayCreatorRestContext ctx) { final DetailAstImpl arrayDeclarator = create(TokenTypes.ARRAY_DECLARATOR, (Token) ctx.LBRACK().getPayload()); final JavaLanguageParser.ExpressionContext expression = ctx.expression(); final TerminalNode rbrack = ctx.RBRACK(); // child[0] is LBRACK for (int i = 1; i < ctx.children.size(); i++) { if (ctx.children.get(i) == rbrack) { arrayDeclarator.addChild(create(rbrack)); } else if (ctx.children.get(i) == expression) { // Handle '[8]', etc. arrayDeclarator.addChild(visit(expression)); } else { addLastSibling(arrayDeclarator, visit(ctx.children.get(i))); } } return arrayDeclarator; } @Override public DetailAstImpl visitBracketsWithExp(JavaLanguageParser.BracketsWithExpContext ctx) { final DetailAstImpl dummyRoot = new DetailAstImpl(); dummyRoot.addChild(visit(ctx.annotations())); final DetailAstImpl arrayDeclarator = create(TokenTypes.ARRAY_DECLARATOR, (Token) ctx.LBRACK().getPayload()); arrayDeclarator.addChild(visit(ctx.expression())); arrayDeclarator.addChild(create(ctx.stop)); dummyRoot.addChild(arrayDeclarator); return dummyRoot.getFirstChild(); } @Override public DetailAstImpl visitClassCreatorRest(JavaLanguageParser.ClassCreatorRestContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitDiamond(JavaLanguageParser.DiamondContext ctx) { final DetailAstImpl typeArguments = createImaginary(TokenTypes.TYPE_ARGUMENTS); typeArguments.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload())); typeArguments.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload())); return typeArguments; } @Override public DetailAstImpl visitTypeArgs(JavaLanguageParser.TypeArgsContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitNonWildcardDiamond( JavaLanguageParser.NonWildcardDiamondContext ctx) { final DetailAstImpl typeArguments = createImaginary(TokenTypes.TYPE_ARGUMENTS); typeArguments.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload())); typeArguments.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload())); return typeArguments; } @Override public DetailAstImpl visitNonWildcardTypeArguments( JavaLanguageParser.NonWildcardTypeArgumentsContext ctx) { final DetailAstImpl typeArguments = createImaginary(TokenTypes.TYPE_ARGUMENTS); typeArguments.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload())); typeArguments.addChild(visit(ctx.typeArgumentsTypeList())); typeArguments.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload())); return typeArguments; } @Override public DetailAstImpl visitTypeArgumentsTypeList( JavaLanguageParser.TypeArgumentsTypeListContext ctx) { final DetailAstImpl firstIdent = visit(ctx.typeType(0)); final DetailAstImpl firstTypeArgument = createImaginary(TokenTypes.TYPE_ARGUMENT); firstTypeArgument.addChild(firstIdent); for (int i = 0; i < ctx.COMMA().size(); i++) { addLastSibling(firstTypeArgument, create(ctx.COMMA(i))); final DetailAstImpl ident = visit(ctx.typeType(i + 1)); final DetailAstImpl typeArgument = createImaginary(TokenTypes.TYPE_ARGUMENT); typeArgument.addChild(ident); addLastSibling(firstTypeArgument, typeArgument); } return firstTypeArgument; } @Override public DetailAstImpl visitTypeList(JavaLanguageParser.TypeListContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitTypeType(JavaLanguageParser.TypeTypeContext ctx) { final DetailAstImpl type = createImaginary(TokenTypes.TYPE); processChildren(type, ctx.children); final DetailAstImpl returnTree; if (ctx.createImaginaryNode) { returnTree = type; } else { returnTree = type.getFirstChild(); } return returnTree; } @Override public DetailAstImpl visitArrayDeclarator(JavaLanguageParser.ArrayDeclaratorContext ctx) { final DetailAstImpl arrayDeclarator = create(TokenTypes.ARRAY_DECLARATOR, (Token) ctx.LBRACK().getPayload()); arrayDeclarator.addChild(create(ctx.RBRACK())); final DetailAstImpl returnTree; final DetailAstImpl annotations = visit(ctx.anno); if (annotations == null) { returnTree = arrayDeclarator; } else { returnTree = annotations; addLastSibling(returnTree, arrayDeclarator); } return returnTree; } @Override public DetailAstImpl visitPrimitiveType(JavaLanguageParser.PrimitiveTypeContext ctx) { return create(ctx.start); } @Override public DetailAstImpl visitTypeArguments(JavaLanguageParser.TypeArgumentsContext ctx) { final DetailAstImpl typeArguments = createImaginary(TokenTypes.TYPE_ARGUMENTS); typeArguments.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload())); // Exclude '<' and '>' processChildren(typeArguments, ctx.children.subList(1, ctx.children.size() - 1)); typeArguments.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload())); return typeArguments; } @Override public DetailAstImpl visitSuperSuffixDot(JavaLanguageParser.SuperSuffixDotContext ctx) { final DetailAstImpl root; if (ctx.LPAREN() == null) { root = create(ctx.DOT()); root.addChild(visit(ctx.id())); } else { root = create(TokenTypes.METHOD_CALL, (Token) ctx.LPAREN().getPayload()); final DetailAstImpl dot = create(ctx.DOT()); dot.addChild(visit(ctx.id())); root.addChild(dot); final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList())) .orElseGet(() -> createImaginary(TokenTypes.ELIST)); root.addChild(expressionList); root.addChild(create(ctx.RPAREN())); } return root; } @Override public DetailAstImpl visitArguments(JavaLanguageParser.ArgumentsContext ctx) { final DetailAstImpl lparen = create(ctx.LPAREN()); // We always add an 'ELIST' node final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList())) .orElseGet(() -> createImaginary(TokenTypes.ELIST)); addLastSibling(lparen, expressionList); addLastSibling(lparen, create(ctx.RPAREN())); return lparen; } @Override public DetailAstImpl visitPattern(JavaLanguageParser.PatternContext ctx) { final JavaLanguageParser.InnerPatternContext innerPattern = ctx.innerPattern(); final ParserRuleContext primaryPattern = innerPattern.primaryPattern(); final ParserRuleContext recordPattern = innerPattern.recordPattern(); final boolean isSimpleTypePattern = primaryPattern != null && primaryPattern.getChild(0) instanceof JavaLanguageParser.TypePatternContext; final DetailAstImpl pattern; if (recordPattern != null) { pattern = visit(recordPattern); } else if (isSimpleTypePattern) { // For simple type pattern like 'Integer i`, we do not add `PATTERN_DEF` parent pattern = visit(primaryPattern); } else { pattern = createImaginary(TokenTypes.PATTERN_DEF); pattern.addChild(visit(ctx.getChild(0))); } return pattern; } @Override public DetailAstImpl visitInnerPattern(JavaLanguageParser.InnerPatternContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitGuardedPattern(JavaLanguageParser.GuardedPatternContext ctx) { final DetailAstImpl guardAstNode = flattenedTree(ctx.guard()); guardAstNode.addChild(visit(ctx.primaryPattern())); guardAstNode.addChild(visit(ctx.expression())); return guardAstNode; } @Override public DetailAstImpl visitParenPattern(JavaLanguageParser.ParenPatternContext ctx) { final DetailAstImpl lparen = create(ctx.LPAREN()); final ParseTree innerPattern = ctx.getChild(1); lparen.addChild(visit(innerPattern)); lparen.addChild(create(ctx.RPAREN())); return lparen; } @Override public DetailAstImpl visitRecordPatternDef(JavaLanguageParser.RecordPatternDefContext ctx) { return flattenedTree(ctx); } @Override public DetailAstImpl visitTypePatternDef( JavaLanguageParser.TypePatternDefContext ctx) { final DetailAstImpl type = visit(ctx.type); final DetailAstImpl patternVariableDef = createImaginary(TokenTypes.PATTERN_VARIABLE_DEF); patternVariableDef.addChild(createModifiers(ctx.mods)); patternVariableDef.addChild(type); patternVariableDef.addChild(visit(ctx.id())); return patternVariableDef; } @Override public DetailAstImpl visitUnnamedPatternDef(JavaLanguageParser.UnnamedPatternDefContext ctx) { return create(TokenTypes.UNNAMED_PATTERN_DEF, ctx.start); } @Override public DetailAstImpl visitRecordPattern(JavaLanguageParser.RecordPatternContext ctx) { final DetailAstImpl recordPattern = createImaginary(TokenTypes.RECORD_PATTERN_DEF); recordPattern.addChild(createModifiers(ctx.mods)); processChildren(recordPattern, ctx.children.subList(ctx.mods.size(), ctx.children.size())); return recordPattern; } @Override public DetailAstImpl visitRecordComponentPatternList( JavaLanguageParser.RecordComponentPatternListContext ctx) { final DetailAstImpl recordComponents = createImaginary(TokenTypes.RECORD_PATTERN_COMPONENTS); processChildren(recordComponents, ctx.children); return recordComponents; } @Override public DetailAstImpl visitPermittedSubclassesAndInterfaces( JavaLanguageParser.PermittedSubclassesAndInterfacesContext ctx) { final DetailAstImpl literalPermits = create(TokenTypes.PERMITS_CLAUSE, (Token) ctx.LITERAL_PERMITS().getPayload()); // 'LITERAL_PERMITS' is child[0] processChildren(literalPermits, ctx.children.subList(1, ctx.children.size())); return literalPermits; } @Override public DetailAstImpl visitId(JavaLanguageParser.IdContext ctx) { return create(TokenTypes.IDENT, ctx.start); } /** * Builds the AST for a particular node, then returns a "flattened" tree * of siblings. This method should be used in rule contexts such as * {@code variableDeclarators}, where we have both terminals and non-terminals. * * @param ctx the ParserRuleContext to base tree on * @return flattened DetailAstImpl */ private DetailAstImpl flattenedTree(ParserRuleContext ctx) { final DetailAstImpl dummyNode = new DetailAstImpl(); processChildren(dummyNode, ctx.children); return dummyNode.getFirstChild(); } /** * Adds all the children from the given ParseTree or JavaParserContext * list to the parent DetailAstImpl. * * @param parent the DetailAstImpl to add children to * @param children the list of children to add */ private void processChildren(DetailAstImpl parent, List children) { children.forEach(child -> { if (child instanceof TerminalNode) { // Child is a token, create a new DetailAstImpl and add it to parent parent.addChild(create((TerminalNode) child)); } else { // Child is another rule context; visit it, create token, and add to parent parent.addChild(visit(child)); } }); } /** * Create a DetailAstImpl from a given token and token type. This method * should be used for imaginary nodes only, i.e. 'OBJBLOCK -> OBJBLOCK', * where the text on the RHS matches the text on the LHS. * * @param tokenType the token type of this DetailAstImpl * @return new DetailAstImpl of given type */ private static DetailAstImpl createImaginary(int tokenType) { final DetailAstImpl detailAst = new DetailAstImpl(); detailAst.setType(tokenType); detailAst.setText(TokenUtil.getTokenName(tokenType)); return detailAst; } /** * Create a DetailAstImpl from a given token and token type. This method * should be used for literal nodes only, i.e. 'PACKAGE_DEF -> package'. * * @param tokenType the token type of this DetailAstImpl * @param startToken the first token that appears in this DetailAstImpl. * @return new DetailAstImpl of given type */ private DetailAstImpl create(int tokenType, Token startToken) { final DetailAstImpl ast = create(startToken); ast.setType(tokenType); return ast; } /** * Create a DetailAstImpl from a given token. This method should be * used for terminal nodes, i.e. {@code LCURLY}, when we are building * an AST for a specific token, regardless of position. * * @param token the token to build the DetailAstImpl from * @return new DetailAstImpl of given type */ private DetailAstImpl create(Token token) { final int tokenIndex = token.getTokenIndex(); final List tokensToLeft = tokens.getHiddenTokensToLeft(tokenIndex, JavaLanguageLexer.COMMENTS); final List tokensToRight = tokens.getHiddenTokensToRight(tokenIndex, JavaLanguageLexer.COMMENTS); final DetailAstImpl detailAst = new DetailAstImpl(); detailAst.initialize(token); if (tokensToLeft != null) { detailAst.setHiddenBefore(tokensToLeft); } if (tokensToRight != null) { detailAst.setHiddenAfter(tokensToRight); } return detailAst; } /** * Create a DetailAstImpl from a given TerminalNode. This method should be * used for terminal nodes, i.e. {@code @}. * * @param node the TerminalNode to build the DetailAstImpl from * @return new DetailAstImpl of given type */ private DetailAstImpl create(TerminalNode node) { return create((Token) node.getPayload()); } /** * Creates a type declaration DetailAstImpl from a given rule context. * * @param ctx ParserRuleContext we are in * @param type the type declaration to create * @param modifierList respective modifiers * @return type declaration DetailAstImpl */ private DetailAstImpl createTypeDeclaration(ParserRuleContext ctx, int type, List modifierList) { final DetailAstImpl typeDeclaration = createImaginary(type); typeDeclaration.addChild(createModifiers(modifierList)); processChildren(typeDeclaration, ctx.children); return typeDeclaration; } /** * Builds the modifiers AST. * * @param modifierList the list of modifier contexts * @return "MODIFIERS" ast */ private DetailAstImpl createModifiers(List modifierList) { final DetailAstImpl mods = createImaginary(TokenTypes.MODIFIERS); processChildren(mods, modifierList); return mods; } /** * Add new sibling to the end of existing siblings. * * @param self DetailAstImpl to add last sibling to * @param sibling DetailAstImpl sibling to add */ private static void addLastSibling(DetailAstImpl self, DetailAstImpl sibling) { DetailAstImpl nextSibling = self; if (nextSibling != null) { while (nextSibling.getNextSibling() != null) { nextSibling = nextSibling.getNextSibling(); } nextSibling.setNextSibling(sibling); } } @Override public DetailAstImpl visit(ParseTree tree) { DetailAstImpl ast = null; if (tree != null) { ast = tree.accept(this); } return ast; } /** * Builds an expression node. This is used to build the root of an expression with * an imaginary {@code EXPR} node. * * @param exprNode expression to build node for * @return expression DetailAstImpl node */ private DetailAstImpl buildExpressionNode(ParseTree exprNode) { final DetailAstImpl expression = visit(exprNode); final DetailAstImpl exprRoot; if (TokenUtil.isOfType(expression, EXPRESSIONS_WITH_NO_EXPR_ROOT)) { exprRoot = expression; } else { // create imaginary 'EXPR' node as root of expression exprRoot = createImaginary(TokenTypes.EXPR); exprRoot.addChild(expression); } return exprRoot; } /** * Used to swap and organize DetailAstImpl subtrees. */ private static final class DetailAstPair { /** The root DetailAstImpl of this pair. */ private DetailAstImpl root; /** The child (potentially with siblings) of this pair. */ private DetailAstImpl child; /** * Moves child reference to the last child. */ private void advanceChildToEnd() { while (child.getNextSibling() != null) { child = child.getNextSibling(); } } /** * Returns the root node. * * @return the root node */ private DetailAstImpl getRoot() { return root; } /** * This method is used to replace the {@code ^} (set as root node) ANTLR2 * operator. * * @param pair the DetailAstPair to use for swapping nodes * @param ast the new root */ private static void makeAstRoot(DetailAstPair pair, DetailAstImpl ast) { ast.addChild(pair.root); pair.child = pair.root; pair.advanceChildToEnd(); pair.root = ast; } /** * Adds a child (or new root) to the given DetailAstPair. * * @param pair the DetailAstPair to add child to * @param ast the child to add */ private static void addAstChild(DetailAstPair pair, DetailAstImpl ast) { if (ast != null) { if (pair.root == null) { pair.root = ast; } else { pair.child.setNextSibling(ast); } pair.child = ast; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy