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