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