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