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