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