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