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