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