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 1374 final DetailAstImpl patternDef; 1375 if (patternOrType instanceof JavaLanguageParser.ParenPatternContext) { 1376 // Parenthesized pattern has a `PATTERN_DEF` parent 1377 patternDef = createImaginary(TokenTypes.PATTERN_DEF); 1378 patternDef.addChild(visit(patternOrType)); 1379 } 1380 else { 1381 patternDef = visit(patternOrType); 1382 } 1383 literalInstanceOf.addChild(patternDef); 1384 return literalInstanceOf; 1385 } 1386 1387 @Override 1388 public DetailAstImpl visitBitShift(JavaLanguageParser.BitShiftContext ctx) { 1389 final DetailAstImpl shiftOperation; 1390 1391 // We determine the type of shift operation in the parser, instead of the 1392 // lexer as in older grammars. This makes it easier to parse type parameters 1393 // and less than/ greater than operators in general. 1394 if (ctx.LT().size() == LEFT_SHIFT.length()) { 1395 shiftOperation = create(TokenTypes.SL, (Token) ctx.LT(0).getPayload()); 1396 shiftOperation.setText(LEFT_SHIFT); 1397 } 1398 else if (ctx.GT().size() == UNSIGNED_RIGHT_SHIFT.length()) { 1399 shiftOperation = create(TokenTypes.BSR, (Token) ctx.GT(0).getPayload()); 1400 shiftOperation.setText(UNSIGNED_RIGHT_SHIFT); 1401 } 1402 else { 1403 shiftOperation = create(TokenTypes.SR, (Token) ctx.GT(0).getPayload()); 1404 shiftOperation.setText(RIGHT_SHIFT); 1405 } 1406 1407 shiftOperation.addChild(visit(ctx.expr(0))); 1408 shiftOperation.addChild(visit(ctx.expr(1))); 1409 return shiftOperation; 1410 } 1411 1412 @Override 1413 public DetailAstImpl visitNewExp(JavaLanguageParser.NewExpContext ctx) { 1414 final DetailAstImpl newExp = create(ctx.LITERAL_NEW()); 1415 // child [0] is LITERAL_NEW 1416 processChildren(newExp, ctx.children.subList(1, ctx.children.size())); 1417 return newExp; 1418 } 1419 1420 @Override 1421 public DetailAstImpl visitPrefix(JavaLanguageParser.PrefixContext ctx) { 1422 final int tokenType = switch (ctx.prefix.getType()) { 1423 case JavaLanguageLexer.PLUS -> TokenTypes.UNARY_PLUS; 1424 case JavaLanguageLexer.MINUS -> TokenTypes.UNARY_MINUS; 1425 default -> ctx.prefix.getType(); 1426 }; 1427 final DetailAstImpl prefix = create(tokenType, ctx.prefix); 1428 prefix.addChild(visit(ctx.expr())); 1429 return prefix; 1430 } 1431 1432 @Override 1433 public DetailAstImpl visitCastExp(JavaLanguageParser.CastExpContext ctx) { 1434 final DetailAstImpl cast = create(TokenTypes.TYPECAST, (Token) ctx.LPAREN().getPayload()); 1435 // child [0] is LPAREN 1436 processChildren(cast, ctx.children.subList(1, ctx.children.size())); 1437 return cast; 1438 } 1439 1440 @Override 1441 public DetailAstImpl visitIndexOp(JavaLanguageParser.IndexOpContext ctx) { 1442 // LBRACK -> INDEX_OP is root of this AST 1443 final DetailAstImpl indexOp = create(TokenTypes.INDEX_OP, 1444 (Token) ctx.LBRACK().getPayload()); 1445 1446 // add expression(IDENT) on LHS 1447 indexOp.addChild(visit(ctx.expr(0))); 1448 1449 // create imaginary node for expression on RHS 1450 final DetailAstImpl expr = visit(ctx.expr(1)); 1451 final DetailAstImpl imaginaryExpr = createImaginary(TokenTypes.EXPR); 1452 imaginaryExpr.addChild(expr); 1453 indexOp.addChild(imaginaryExpr); 1454 1455 // complete AST by adding RBRACK 1456 indexOp.addChild(create(ctx.RBRACK())); 1457 return indexOp; 1458 } 1459 1460 @Override 1461 public DetailAstImpl visitInvOp(JavaLanguageParser.InvOpContext ctx) { 1462 final DetailAstPair currentAst = new DetailAstPair(); 1463 1464 final DetailAstImpl returnAst = visit(ctx.expr()); 1465 DetailAstPair.addAstChild(currentAst, returnAst); 1466 DetailAstPair.makeAstRoot(currentAst, create(ctx.bop)); 1467 1468 DetailAstPair.addAstChild(currentAst, 1469 visit(ctx.nonWildcardTypeArguments())); 1470 DetailAstPair.addAstChild(currentAst, visit(ctx.id())); 1471 final DetailAstImpl lparen = create(TokenTypes.METHOD_CALL, 1472 (Token) ctx.LPAREN().getPayload()); 1473 DetailAstPair.makeAstRoot(currentAst, lparen); 1474 1475 // We always add an 'ELIST' node 1476 final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList())) 1477 .orElseGet(() -> createImaginary(TokenTypes.ELIST)); 1478 1479 DetailAstPair.addAstChild(currentAst, expressionList); 1480 DetailAstPair.addAstChild(currentAst, create(ctx.RPAREN())); 1481 1482 return currentAst.root; 1483 } 1484 1485 @Override 1486 public DetailAstImpl visitInitExp(JavaLanguageParser.InitExpContext ctx) { 1487 final DetailAstImpl dot = create(ctx.bop); 1488 dot.addChild(visit(ctx.expr())); 1489 final DetailAstImpl literalNew = create(ctx.LITERAL_NEW()); 1490 literalNew.addChild(visit(ctx.nonWildcardTypeArguments())); 1491 literalNew.addChild(visit(ctx.innerCreator())); 1492 dot.addChild(literalNew); 1493 return dot; 1494 } 1495 1496 @Override 1497 public DetailAstImpl visitSimpleMethodCall(JavaLanguageParser.SimpleMethodCallContext ctx) { 1498 final DetailAstImpl methodCall = create(TokenTypes.METHOD_CALL, 1499 (Token) ctx.LPAREN().getPayload()); 1500 methodCall.addChild(visit(ctx.id())); 1501 // We always add an 'ELIST' node 1502 final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList())) 1503 .orElseGet(() -> createImaginary(TokenTypes.ELIST)); 1504 1505 methodCall.addChild(expressionList); 1506 methodCall.addChild(create((Token) ctx.RPAREN().getPayload())); 1507 return methodCall; 1508 } 1509 1510 @Override 1511 public DetailAstImpl visitLambdaExp(JavaLanguageParser.LambdaExpContext ctx) { 1512 final DetailAstImpl lambda = create(ctx.LAMBDA()); 1513 lambda.addChild(visit(ctx.lambdaParameters())); 1514 1515 final JavaLanguageParser.BlockContext blockContext = ctx.block(); 1516 final DetailAstImpl rightHandLambdaChild; 1517 if (blockContext != null) { 1518 rightHandLambdaChild = visit(blockContext); 1519 } 1520 else { 1521 // Lambda expression child is built the same way that we build 1522 // the initial expression node in visitExpression, i.e. with 1523 // an imaginary EXPR node. This results in nested EXPR nodes 1524 // in the AST. 1525 rightHandLambdaChild = buildExpressionNode(ctx.expr()); 1526 } 1527 lambda.addChild(rightHandLambdaChild); 1528 return lambda; 1529 } 1530 1531 @Override 1532 public DetailAstImpl visitThisExp(JavaLanguageParser.ThisExpContext ctx) { 1533 final DetailAstImpl bop = create(ctx.bop); 1534 bop.addChild(visit(ctx.expr())); 1535 bop.addChild(create(ctx.LITERAL_THIS())); 1536 return bop; 1537 } 1538 1539 @Override 1540 public DetailAstImpl visitPrimaryExp(JavaLanguageParser.PrimaryExpContext ctx) { 1541 return flattenedTree(ctx); 1542 } 1543 1544 @Override 1545 public DetailAstImpl visitPostfix(JavaLanguageParser.PostfixContext ctx) { 1546 final DetailAstImpl postfix; 1547 if (ctx.postfix.getType() == JavaLanguageLexer.INC) { 1548 postfix = create(TokenTypes.POST_INC, ctx.postfix); 1549 } 1550 else { 1551 postfix = create(TokenTypes.POST_DEC, ctx.postfix); 1552 } 1553 postfix.addChild(visit(ctx.expr())); 1554 return postfix; 1555 } 1556 1557 @Override 1558 public DetailAstImpl visitMethodRef(JavaLanguageParser.MethodRefContext ctx) { 1559 final DetailAstImpl doubleColon = create(TokenTypes.METHOD_REF, 1560 (Token) ctx.DOUBLE_COLON().getPayload()); 1561 final List<ParseTree> children = ctx.children.stream() 1562 .filter(child -> !child.equals(ctx.DOUBLE_COLON())) 1563 .toList(); 1564 processChildren(doubleColon, children); 1565 return doubleColon; 1566 } 1567 1568 @Override 1569 public DetailAstImpl visitTernaryOp(JavaLanguageParser.TernaryOpContext ctx) { 1570 final DetailAstImpl root = create(ctx.QUESTION()); 1571 processChildren(root, ctx.children.stream() 1572 .filter(child -> !child.equals(ctx.QUESTION())) 1573 .toList()); 1574 return root; 1575 } 1576 1577 @Override 1578 public DetailAstImpl visitBinOp(JavaLanguageParser.BinOpContext ctx) { 1579 final DetailAstImpl bop = create(ctx.bop); 1580 1581 // To improve performance, we iterate through binary operations 1582 // since they are frequently deeply nested. 1583 final List<JavaLanguageParser.BinOpContext> binOpList = new ArrayList<>(); 1584 ParseTree firstExpression = ctx.expr(0); 1585 while (firstExpression instanceof JavaLanguageParser.BinOpContext) { 1586 // Get all nested binOps 1587 binOpList.add((JavaLanguageParser.BinOpContext) firstExpression); 1588 firstExpression = ((JavaLanguageParser.BinOpContext) firstExpression).expr(0); 1589 } 1590 1591 if (binOpList.isEmpty()) { 1592 final DetailAstImpl leftChild = visit(ctx.children.getFirst()); 1593 bop.addChild(leftChild); 1594 } 1595 else { 1596 // Map all descendants to individual AST's since we can parallelize this 1597 // operation 1598 final Queue<DetailAstImpl> descendantList = binOpList.parallelStream() 1599 .map(this::getInnerBopAst) 1600 .collect(Collectors.toCollection(ArrayDeque::new)); 1601 1602 bop.addChild(descendantList.poll()); 1603 DetailAstImpl pointer = bop.getFirstChild(); 1604 // Build tree 1605 for (DetailAstImpl descendant : descendantList) { 1606 pointer.getFirstChild().addPreviousSibling(descendant); 1607 pointer = descendant; 1608 } 1609 } 1610 1611 bop.addChild(visit(ctx.children.get(2))); 1612 return bop; 1613 } 1614 1615 /** 1616 * Builds the binary operation (binOp) AST. 1617 * 1618 * @param descendant the BinOpContext to build AST from 1619 * @return binOp AST 1620 */ 1621 private DetailAstImpl getInnerBopAst(JavaLanguageParser.BinOpContext descendant) { 1622 final DetailAstImpl innerBop = create(descendant.bop); 1623 final JavaLanguageParser.ExprContext expr = descendant.expr(0); 1624 if (!(expr instanceof JavaLanguageParser.BinOpContext)) { 1625 innerBop.addChild(visit(expr)); 1626 } 1627 innerBop.addChild(visit(descendant.expr(1))); 1628 return innerBop; 1629 } 1630 1631 @Override 1632 public DetailAstImpl visitMethodCall(JavaLanguageParser.MethodCallContext ctx) { 1633 final DetailAstImpl methodCall = create(TokenTypes.METHOD_CALL, 1634 (Token) ctx.LPAREN().getPayload()); 1635 // We always add an 'ELIST' node 1636 final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList())) 1637 .orElseGet(() -> createImaginary(TokenTypes.ELIST)); 1638 1639 final DetailAstImpl dot = create(ctx.DOT()); 1640 dot.addChild(visit(ctx.expr())); 1641 dot.addChild(visit(ctx.id())); 1642 methodCall.addChild(dot); 1643 methodCall.addChild(expressionList); 1644 methodCall.addChild(create((Token) ctx.RPAREN().getPayload())); 1645 return methodCall; 1646 } 1647 1648 @Override 1649 public DetailAstImpl visitTypeCastParameters( 1650 JavaLanguageParser.TypeCastParametersContext ctx) { 1651 final DetailAstImpl typeType = visit(ctx.typeType(0)); 1652 for (int i = 0; i < ctx.BAND().size(); i++) { 1653 addLastSibling(typeType, create(TokenTypes.TYPE_EXTENSION_AND, 1654 (Token) ctx.BAND(i).getPayload())); 1655 addLastSibling(typeType, visit(ctx.typeType(i + 1))); 1656 } 1657 return typeType; 1658 } 1659 1660 @Override 1661 public DetailAstImpl visitSingleLambdaParam(JavaLanguageParser.SingleLambdaParamContext ctx) { 1662 return flattenedTree(ctx); 1663 } 1664 1665 @Override 1666 public DetailAstImpl visitFormalLambdaParam(JavaLanguageParser.FormalLambdaParamContext ctx) { 1667 final DetailAstImpl lparen = create(ctx.LPAREN()); 1668 1669 // We add an 'PARAMETERS' node here whether it exists or not 1670 final DetailAstImpl parameters = Optional.ofNullable(visit(ctx.formalParameterList())) 1671 .orElseGet(() -> createImaginary(TokenTypes.PARAMETERS)); 1672 addLastSibling(lparen, parameters); 1673 addLastSibling(lparen, create(ctx.RPAREN())); 1674 return lparen; 1675 } 1676 1677 @Override 1678 public DetailAstImpl visitMultiLambdaParam(JavaLanguageParser.MultiLambdaParamContext ctx) { 1679 final DetailAstImpl lparen = create(ctx.LPAREN()); 1680 addLastSibling(lparen, visit(ctx.multiLambdaParams())); 1681 addLastSibling(lparen, create(ctx.RPAREN())); 1682 return lparen; 1683 } 1684 1685 @Override 1686 public DetailAstImpl visitMultiLambdaParams(JavaLanguageParser.MultiLambdaParamsContext ctx) { 1687 final DetailAstImpl parameters = createImaginary(TokenTypes.PARAMETERS); 1688 parameters.addChild(createLambdaParameter(ctx.id(0))); 1689 1690 for (int i = 0; i < ctx.COMMA().size(); i++) { 1691 parameters.addChild(create(ctx.COMMA(i))); 1692 parameters.addChild(createLambdaParameter(ctx.id(i + 1))); 1693 } 1694 return parameters; 1695 } 1696 1697 /** 1698 * Creates a 'PARAMETER_DEF' node for a lambda expression, with 1699 * imaginary modifier and type nodes. 1700 * 1701 * @param ctx the IdContext to create imaginary nodes for 1702 * @return DetailAstImpl of lambda parameter 1703 */ 1704 private DetailAstImpl createLambdaParameter(JavaLanguageParser.IdContext ctx) { 1705 final DetailAstImpl ident = visitId(ctx); 1706 final DetailAstImpl parameter = createImaginary(TokenTypes.PARAMETER_DEF); 1707 final DetailAstImpl modifiers = createImaginary(TokenTypes.MODIFIERS); 1708 final DetailAstImpl type = createImaginary(TokenTypes.TYPE); 1709 parameter.addChild(modifiers); 1710 parameter.addChild(type); 1711 parameter.addChild(ident); 1712 return parameter; 1713 } 1714 1715 @Override 1716 public DetailAstImpl visitParenPrimary(JavaLanguageParser.ParenPrimaryContext ctx) { 1717 return flattenedTree(ctx); 1718 } 1719 1720 @Override 1721 public DetailAstImpl visitTokenPrimary(JavaLanguageParser.TokenPrimaryContext ctx) { 1722 return flattenedTree(ctx); 1723 } 1724 1725 @Override 1726 public DetailAstImpl visitClassRefPrimary(JavaLanguageParser.ClassRefPrimaryContext ctx) { 1727 final DetailAstImpl dot = create(ctx.DOT()); 1728 final DetailAstImpl primaryTypeNoArray = visit(ctx.type); 1729 dot.addChild(primaryTypeNoArray); 1730 if (TokenUtil.isOfType(primaryTypeNoArray, TokenTypes.DOT)) { 1731 // We append '[]' to the qualified name 'TYPE' `ast 1732 ctx.arrayDeclarator() 1733 .forEach(child -> primaryTypeNoArray.addChild(visit(child))); 1734 } 1735 else { 1736 ctx.arrayDeclarator() 1737 .forEach(child -> addLastSibling(primaryTypeNoArray, visit(child))); 1738 } 1739 dot.addChild(create(ctx.LITERAL_CLASS())); 1740 return dot; 1741 } 1742 1743 @Override 1744 public DetailAstImpl visitPrimitivePrimary(JavaLanguageParser.PrimitivePrimaryContext ctx) { 1745 final DetailAstImpl dot = create(ctx.DOT()); 1746 final DetailAstImpl primaryTypeNoArray = visit(ctx.type); 1747 dot.addChild(primaryTypeNoArray); 1748 ctx.arrayDeclarator().forEach(child -> dot.addChild(visit(child))); 1749 dot.addChild(create(ctx.LITERAL_CLASS())); 1750 return dot; 1751 } 1752 1753 @Override 1754 public DetailAstImpl visitCreator(JavaLanguageParser.CreatorContext ctx) { 1755 return flattenedTree(ctx); 1756 } 1757 1758 @Override 1759 public DetailAstImpl visitCreatedNameObject(JavaLanguageParser.CreatedNameObjectContext ctx) { 1760 final DetailAstPair currentAST = new DetailAstPair(); 1761 DetailAstPair.addAstChild(currentAST, visit(ctx.annotations())); 1762 DetailAstPair.addAstChild(currentAST, visit(ctx.id())); 1763 DetailAstPair.addAstChild(currentAST, visit(ctx.typeArgumentsOrDiamond())); 1764 1765 // This is how we build the type arguments/ qualified name tree 1766 for (ParserRuleContext extendedContext : ctx.extended) { 1767 final DetailAstImpl dot = create(extendedContext.start); 1768 DetailAstPair.makeAstRoot(currentAST, dot); 1769 final List<ParseTree> childList = extendedContext 1770 .children.subList(1, extendedContext.children.size()); 1771 processChildren(dot, childList); 1772 } 1773 1774 return currentAST.root; 1775 } 1776 1777 @Override 1778 public DetailAstImpl visitCreatedNamePrimitive( 1779 JavaLanguageParser.CreatedNamePrimitiveContext ctx) { 1780 return flattenedTree(ctx); 1781 } 1782 1783 @Override 1784 public DetailAstImpl visitInnerCreator(JavaLanguageParser.InnerCreatorContext ctx) { 1785 return flattenedTree(ctx); 1786 } 1787 1788 @Override 1789 public DetailAstImpl visitArrayCreatorRest(JavaLanguageParser.ArrayCreatorRestContext ctx) { 1790 final DetailAstImpl arrayDeclarator = create(TokenTypes.ARRAY_DECLARATOR, 1791 (Token) ctx.LBRACK().getPayload()); 1792 final JavaLanguageParser.ExpressionContext expression = ctx.expression(); 1793 final TerminalNode rbrack = ctx.RBRACK(); 1794 // child[0] is LBRACK 1795 for (int i = 1; i < ctx.children.size(); i++) { 1796 if (ctx.children.get(i) == rbrack) { 1797 arrayDeclarator.addChild(create(rbrack)); 1798 } 1799 else if (ctx.children.get(i) == expression) { 1800 // Handle '[8]', etc. 1801 arrayDeclarator.addChild(visit(expression)); 1802 } 1803 else { 1804 addLastSibling(arrayDeclarator, visit(ctx.children.get(i))); 1805 } 1806 } 1807 return arrayDeclarator; 1808 } 1809 1810 @Override 1811 public DetailAstImpl visitBracketsWithExp(JavaLanguageParser.BracketsWithExpContext ctx) { 1812 final DetailAstImpl dummyRoot = new DetailAstImpl(); 1813 dummyRoot.addChild(visit(ctx.annotations())); 1814 final DetailAstImpl arrayDeclarator = 1815 create(TokenTypes.ARRAY_DECLARATOR, (Token) ctx.LBRACK().getPayload()); 1816 arrayDeclarator.addChild(visit(ctx.expression())); 1817 arrayDeclarator.addChild(create(ctx.stop)); 1818 dummyRoot.addChild(arrayDeclarator); 1819 return dummyRoot.getFirstChild(); 1820 } 1821 1822 @Override 1823 public DetailAstImpl visitClassCreatorRest(JavaLanguageParser.ClassCreatorRestContext ctx) { 1824 return flattenedTree(ctx); 1825 } 1826 1827 @Override 1828 public DetailAstImpl visitDiamond(JavaLanguageParser.DiamondContext ctx) { 1829 final DetailAstImpl typeArguments = 1830 createImaginary(TokenTypes.TYPE_ARGUMENTS); 1831 typeArguments.addChild(create(TokenTypes.GENERIC_START, 1832 (Token) ctx.LT().getPayload())); 1833 typeArguments.addChild(create(TokenTypes.GENERIC_END, 1834 (Token) ctx.GT().getPayload())); 1835 return typeArguments; 1836 } 1837 1838 @Override 1839 public DetailAstImpl visitTypeArgs(JavaLanguageParser.TypeArgsContext ctx) { 1840 return flattenedTree(ctx); 1841 } 1842 1843 @Override 1844 public DetailAstImpl visitNonWildcardDiamond( 1845 JavaLanguageParser.NonWildcardDiamondContext ctx) { 1846 final DetailAstImpl typeArguments = 1847 createImaginary(TokenTypes.TYPE_ARGUMENTS); 1848 typeArguments.addChild(create(TokenTypes.GENERIC_START, 1849 (Token) ctx.LT().getPayload())); 1850 typeArguments.addChild(create(TokenTypes.GENERIC_END, 1851 (Token) ctx.GT().getPayload())); 1852 return typeArguments; 1853 } 1854 1855 @Override 1856 public DetailAstImpl visitNonWildcardTypeArguments( 1857 JavaLanguageParser.NonWildcardTypeArgumentsContext ctx) { 1858 final DetailAstImpl typeArguments = createImaginary(TokenTypes.TYPE_ARGUMENTS); 1859 typeArguments.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload())); 1860 typeArguments.addChild(visit(ctx.typeArgumentsTypeList())); 1861 typeArguments.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload())); 1862 return typeArguments; 1863 } 1864 1865 @Override 1866 public DetailAstImpl visitTypeArgumentsTypeList( 1867 JavaLanguageParser.TypeArgumentsTypeListContext ctx) { 1868 final DetailAstImpl firstIdent = visit(ctx.typeType(0)); 1869 final DetailAstImpl firstTypeArgument = createImaginary(TokenTypes.TYPE_ARGUMENT); 1870 firstTypeArgument.addChild(firstIdent); 1871 1872 for (int i = 0; i < ctx.COMMA().size(); i++) { 1873 addLastSibling(firstTypeArgument, create(ctx.COMMA(i))); 1874 final DetailAstImpl ident = visit(ctx.typeType(i + 1)); 1875 final DetailAstImpl typeArgument = createImaginary(TokenTypes.TYPE_ARGUMENT); 1876 typeArgument.addChild(ident); 1877 addLastSibling(firstTypeArgument, typeArgument); 1878 } 1879 return firstTypeArgument; 1880 } 1881 1882 @Override 1883 public DetailAstImpl visitTypeList(JavaLanguageParser.TypeListContext ctx) { 1884 return flattenedTree(ctx); 1885 } 1886 1887 @Override 1888 public DetailAstImpl visitTypeType(JavaLanguageParser.TypeTypeContext ctx) { 1889 final DetailAstImpl type = createImaginary(TokenTypes.TYPE); 1890 processChildren(type, ctx.children); 1891 1892 final DetailAstImpl returnTree; 1893 if (ctx.createImaginaryNode) { 1894 returnTree = type; 1895 } 1896 else { 1897 returnTree = type.getFirstChild(); 1898 } 1899 return returnTree; 1900 } 1901 1902 @Override 1903 public DetailAstImpl visitArrayDeclarator(JavaLanguageParser.ArrayDeclaratorContext ctx) { 1904 final DetailAstImpl arrayDeclarator = create(TokenTypes.ARRAY_DECLARATOR, 1905 (Token) ctx.LBRACK().getPayload()); 1906 arrayDeclarator.addChild(create(ctx.RBRACK())); 1907 1908 final DetailAstImpl returnTree; 1909 final DetailAstImpl annotations = visit(ctx.anno); 1910 if (annotations == null) { 1911 returnTree = arrayDeclarator; 1912 } 1913 else { 1914 returnTree = annotations; 1915 addLastSibling(returnTree, arrayDeclarator); 1916 } 1917 return returnTree; 1918 } 1919 1920 @Override 1921 public DetailAstImpl visitPrimitiveType(JavaLanguageParser.PrimitiveTypeContext ctx) { 1922 return create(ctx.start); 1923 } 1924 1925 @Override 1926 public DetailAstImpl visitTypeArguments(JavaLanguageParser.TypeArgumentsContext ctx) { 1927 final DetailAstImpl typeArguments = createImaginary(TokenTypes.TYPE_ARGUMENTS); 1928 typeArguments.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload())); 1929 // Exclude '<' and '>' 1930 processChildren(typeArguments, ctx.children.subList(1, ctx.children.size() - 1)); 1931 typeArguments.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload())); 1932 return typeArguments; 1933 } 1934 1935 @Override 1936 public DetailAstImpl visitSuperSuffixDot(JavaLanguageParser.SuperSuffixDotContext ctx) { 1937 final DetailAstImpl root; 1938 if (ctx.LPAREN() == null) { 1939 root = create(ctx.DOT()); 1940 root.addChild(visit(ctx.id())); 1941 } 1942 else { 1943 root = create(TokenTypes.METHOD_CALL, (Token) ctx.LPAREN().getPayload()); 1944 1945 final DetailAstImpl dot = create(ctx.DOT()); 1946 dot.addChild(visit(ctx.id())); 1947 root.addChild(dot); 1948 1949 final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList())) 1950 .orElseGet(() -> createImaginary(TokenTypes.ELIST)); 1951 root.addChild(expressionList); 1952 1953 root.addChild(create(ctx.RPAREN())); 1954 } 1955 1956 return root; 1957 } 1958 1959 @Override 1960 public DetailAstImpl visitArguments(JavaLanguageParser.ArgumentsContext ctx) { 1961 final DetailAstImpl lparen = create(ctx.LPAREN()); 1962 1963 // We always add an 'ELIST' node 1964 final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList())) 1965 .orElseGet(() -> createImaginary(TokenTypes.ELIST)); 1966 addLastSibling(lparen, expressionList); 1967 addLastSibling(lparen, create(ctx.RPAREN())); 1968 return lparen; 1969 } 1970 1971 @Override 1972 public DetailAstImpl visitPattern(JavaLanguageParser.PatternContext ctx) { 1973 final JavaLanguageParser.InnerPatternContext innerPattern = ctx.innerPattern(); 1974 final ParserRuleContext primaryPattern = innerPattern.primaryPattern(); 1975 final ParserRuleContext recordPattern = innerPattern.recordPattern(); 1976 final boolean isSimpleTypePattern = primaryPattern != null 1977 && primaryPattern.getChild(0) instanceof JavaLanguageParser.TypePatternContext; 1978 1979 final DetailAstImpl pattern; 1980 1981 if (recordPattern != null) { 1982 pattern = visit(recordPattern); 1983 } 1984 else if (isSimpleTypePattern) { 1985 // For simple type pattern like 'Integer i`, we do not add `PATTERN_DEF` parent 1986 pattern = visit(primaryPattern); 1987 } 1988 else { 1989 pattern = createImaginary(TokenTypes.PATTERN_DEF); 1990 pattern.addChild(visit(ctx.getChild(0))); 1991 } 1992 return pattern; 1993 } 1994 1995 @Override 1996 public DetailAstImpl visitInnerPattern(JavaLanguageParser.InnerPatternContext ctx) { 1997 return flattenedTree(ctx); 1998 } 1999 2000 @Override 2001 public DetailAstImpl visitGuardedPattern(JavaLanguageParser.GuardedPatternContext ctx) { 2002 final DetailAstImpl guardAstNode = flattenedTree(ctx.guard()); 2003 guardAstNode.addChild(visit(ctx.primaryPattern())); 2004 guardAstNode.addChild(visit(ctx.expression())); 2005 return guardAstNode; 2006 } 2007 2008 @Override 2009 public DetailAstImpl visitParenPattern(JavaLanguageParser.ParenPatternContext ctx) { 2010 final DetailAstImpl lparen = create(ctx.LPAREN()); 2011 final ParseTree innerPattern = ctx.getChild(1); 2012 lparen.addChild(visit(innerPattern)); 2013 lparen.addChild(create(ctx.RPAREN())); 2014 return lparen; 2015 } 2016 2017 @Override 2018 public DetailAstImpl visitRecordPatternDef(JavaLanguageParser.RecordPatternDefContext ctx) { 2019 return flattenedTree(ctx); 2020 } 2021 2022 @Override 2023 public DetailAstImpl visitTypePatternDef( 2024 JavaLanguageParser.TypePatternDefContext ctx) { 2025 final DetailAstImpl type = visit(ctx.type); 2026 final DetailAstImpl patternVariableDef = createImaginary(TokenTypes.PATTERN_VARIABLE_DEF); 2027 patternVariableDef.addChild(createModifiers(ctx.mods)); 2028 patternVariableDef.addChild(type); 2029 patternVariableDef.addChild(visit(ctx.id())); 2030 return patternVariableDef; 2031 } 2032 2033 @Override 2034 public DetailAstImpl visitUnnamedPatternDef(JavaLanguageParser.UnnamedPatternDefContext ctx) { 2035 return create(TokenTypes.UNNAMED_PATTERN_DEF, ctx.start); 2036 } 2037 2038 @Override 2039 public DetailAstImpl visitRecordPattern(JavaLanguageParser.RecordPatternContext ctx) { 2040 final DetailAstImpl recordPattern = createImaginary(TokenTypes.RECORD_PATTERN_DEF); 2041 recordPattern.addChild(createModifiers(ctx.mods)); 2042 processChildren(recordPattern, 2043 ctx.children.subList(ctx.mods.size(), ctx.children.size())); 2044 return recordPattern; 2045 } 2046 2047 @Override 2048 public DetailAstImpl visitRecordComponentPatternList( 2049 JavaLanguageParser.RecordComponentPatternListContext ctx) { 2050 final DetailAstImpl recordComponents = 2051 createImaginary(TokenTypes.RECORD_PATTERN_COMPONENTS); 2052 processChildren(recordComponents, ctx.children); 2053 return recordComponents; 2054 } 2055 2056 @Override 2057 public DetailAstImpl visitPermittedSubclassesAndInterfaces( 2058 JavaLanguageParser.PermittedSubclassesAndInterfacesContext ctx) { 2059 final DetailAstImpl literalPermits = 2060 create(TokenTypes.PERMITS_CLAUSE, (Token) ctx.LITERAL_PERMITS().getPayload()); 2061 // 'LITERAL_PERMITS' is child[0] 2062 processChildren(literalPermits, ctx.children.subList(1, ctx.children.size())); 2063 return literalPermits; 2064 } 2065 2066 @Override 2067 public DetailAstImpl visitId(JavaLanguageParser.IdContext ctx) { 2068 return create(TokenTypes.IDENT, ctx.start); 2069 } 2070 2071 /** 2072 * Builds the AST for a particular node, then returns a "flattened" tree 2073 * of siblings. This method should be used in rule contexts such as 2074 * {@code variableDeclarators}, where we have both terminals and non-terminals. 2075 * 2076 * @param ctx the ParserRuleContext to base tree on 2077 * @return flattened DetailAstImpl 2078 */ 2079 private DetailAstImpl flattenedTree(ParserRuleContext ctx) { 2080 final DetailAstImpl dummyNode = new DetailAstImpl(); 2081 processChildren(dummyNode, ctx.children); 2082 return dummyNode.getFirstChild(); 2083 } 2084 2085 /** 2086 * Adds all the children from the given ParseTree or JavaParserContext 2087 * list to the parent DetailAstImpl. 2088 * 2089 * @param parent the DetailAstImpl to add children to 2090 * @param children the list of children to add 2091 */ 2092 private void processChildren(DetailAstImpl parent, List<? extends ParseTree> children) { 2093 children.forEach(child -> { 2094 if (child instanceof TerminalNode node) { 2095 // Child is a token, create a new DetailAstImpl and add it to parent 2096 parent.addChild(create(node)); 2097 } 2098 else { 2099 // Child is another rule context; visit it, create token, and add to parent 2100 parent.addChild(visit(child)); 2101 } 2102 }); 2103 } 2104 2105 /** 2106 * Create a DetailAstImpl from a given token and token type. This method 2107 * should be used for imaginary nodes only, i.e. 'OBJBLOCK -> OBJBLOCK', 2108 * where the text on the RHS matches the text on the LHS. 2109 * 2110 * @param tokenType the token type of this DetailAstImpl 2111 * @return new DetailAstImpl of given type 2112 */ 2113 private static DetailAstImpl createImaginary(int tokenType) { 2114 final DetailAstImpl detailAst = new DetailAstImpl(); 2115 detailAst.setType(tokenType); 2116 detailAst.setText(TokenUtil.getTokenName(tokenType)); 2117 return detailAst; 2118 } 2119 2120 /** 2121 * Create a DetailAstImpl from a given token and token type. This method 2122 * should be used for literal nodes only, i.e. 'PACKAGE_DEF -> package'. 2123 * 2124 * @param tokenType the token type of this DetailAstImpl 2125 * @param startToken the first token that appears in this DetailAstImpl. 2126 * @return new DetailAstImpl of given type 2127 */ 2128 private DetailAstImpl create(int tokenType, Token startToken) { 2129 final DetailAstImpl ast = create(startToken); 2130 ast.setType(tokenType); 2131 return ast; 2132 } 2133 2134 /** 2135 * Create a DetailAstImpl from a given token. This method should be 2136 * used for terminal nodes, i.e. {@code LCURLY}, when we are building 2137 * an AST for a specific token, regardless of position. 2138 * 2139 * @param token the token to build the DetailAstImpl from 2140 * @return new DetailAstImpl of given type 2141 */ 2142 private DetailAstImpl create(Token token) { 2143 final int tokenIndex = token.getTokenIndex(); 2144 final List<Token> tokensToLeft = 2145 tokens.getHiddenTokensToLeft(tokenIndex, JavaLanguageLexer.COMMENTS); 2146 final List<Token> tokensToRight = 2147 tokens.getHiddenTokensToRight(tokenIndex, JavaLanguageLexer.COMMENTS); 2148 2149 final DetailAstImpl detailAst = new DetailAstImpl(); 2150 detailAst.initialize(token); 2151 if (tokensToLeft != null) { 2152 detailAst.setHiddenBefore(tokensToLeft); 2153 } 2154 if (tokensToRight != null) { 2155 detailAst.setHiddenAfter(tokensToRight); 2156 } 2157 return detailAst; 2158 } 2159 2160 /** 2161 * Create a DetailAstImpl from a given TerminalNode. This method should be 2162 * used for terminal nodes, i.e. {@code @}. 2163 * 2164 * @param node the TerminalNode to build the DetailAstImpl from 2165 * @return new DetailAstImpl of given type 2166 */ 2167 private DetailAstImpl create(TerminalNode node) { 2168 return create((Token) node.getPayload()); 2169 } 2170 2171 /** 2172 * Creates a type declaration DetailAstImpl from a given rule context. 2173 * 2174 * @param ctx ParserRuleContext we are in 2175 * @param type the type declaration to create 2176 * @param modifierList respective modifiers 2177 * @return type declaration DetailAstImpl 2178 */ 2179 private DetailAstImpl createTypeDeclaration(ParserRuleContext ctx, int type, 2180 List<? extends ParseTree> modifierList) { 2181 final DetailAstImpl typeDeclaration = createImaginary(type); 2182 typeDeclaration.addChild(createModifiers(modifierList)); 2183 processChildren(typeDeclaration, ctx.children); 2184 return typeDeclaration; 2185 } 2186 2187 /** 2188 * Builds the modifiers AST. 2189 * 2190 * @param modifierList the list of modifier contexts 2191 * @return "MODIFIERS" ast 2192 */ 2193 private DetailAstImpl createModifiers(List<? extends ParseTree> modifierList) { 2194 final DetailAstImpl mods = createImaginary(TokenTypes.MODIFIERS); 2195 processChildren(mods, modifierList); 2196 return mods; 2197 } 2198 2199 /** 2200 * Add new sibling to the end of existing siblings. 2201 * 2202 * @param self DetailAstImpl to add last sibling to 2203 * @param sibling DetailAstImpl sibling to add 2204 */ 2205 private static void addLastSibling(DetailAstImpl self, DetailAstImpl sibling) { 2206 DetailAstImpl nextSibling = self; 2207 if (nextSibling != null) { 2208 while (nextSibling.getNextSibling() != null) { 2209 nextSibling = nextSibling.getNextSibling(); 2210 } 2211 nextSibling.setNextSibling(sibling); 2212 } 2213 } 2214 2215 @Override 2216 public DetailAstImpl visit(ParseTree tree) { 2217 DetailAstImpl ast = null; 2218 if (tree != null) { 2219 ast = tree.accept(this); 2220 } 2221 return ast; 2222 } 2223 2224 /** 2225 * Builds an expression node. This is used to build the root of an expression with 2226 * an imaginary {@code EXPR} node. 2227 * 2228 * @param exprNode expression to build node for 2229 * @return expression DetailAstImpl node 2230 */ 2231 private DetailAstImpl buildExpressionNode(ParseTree exprNode) { 2232 final DetailAstImpl expression = visit(exprNode); 2233 2234 final DetailAstImpl exprRoot; 2235 if (TokenUtil.isOfType(expression, EXPRESSIONS_WITH_NO_EXPR_ROOT)) { 2236 exprRoot = expression; 2237 } 2238 else { 2239 // create imaginary 'EXPR' node as root of expression 2240 exprRoot = createImaginary(TokenTypes.EXPR); 2241 exprRoot.addChild(expression); 2242 } 2243 return exprRoot; 2244 } 2245 2246 /** 2247 * Used to swap and organize DetailAstImpl subtrees. 2248 */ 2249 private static final class DetailAstPair { 2250 2251 /** The root DetailAstImpl of this pair. */ 2252 private DetailAstImpl root; 2253 2254 /** The child (potentially with siblings) of this pair. */ 2255 private DetailAstImpl child; 2256 2257 /** 2258 * Moves child reference to the last child. 2259 */ 2260 private void advanceChildToEnd() { 2261 while (child.getNextSibling() != null) { 2262 child = child.getNextSibling(); 2263 } 2264 } 2265 2266 /** 2267 * Returns the root node. 2268 * 2269 * @return the root node 2270 */ 2271 private DetailAstImpl getRoot() { 2272 return root; 2273 } 2274 2275 /** 2276 * This method is used to replace the {@code ^} (set as root node) ANTLR2 2277 * operator. 2278 * 2279 * @param pair the DetailAstPair to use for swapping nodes 2280 * @param ast the new root 2281 */ 2282 private static void makeAstRoot(DetailAstPair pair, DetailAstImpl ast) { 2283 ast.addChild(pair.root); 2284 pair.child = pair.root; 2285 pair.advanceChildToEnd(); 2286 pair.root = ast; 2287 } 2288 2289 /** 2290 * Adds a child (or new root) to the given DetailAstPair. 2291 * 2292 * @param pair the DetailAstPair to add child to 2293 * @param ast the child to add 2294 */ 2295 private static void addAstChild(DetailAstPair pair, DetailAstImpl ast) { 2296 if (ast != null) { 2297 if (pair.root == null) { 2298 pair.root = ast; 2299 } 2300 else { 2301 pair.child.setNextSibling(ast); 2302 } 2303 pair.child = ast; 2304 } 2305 } 2306 } 2307}