1 // Copyright 2022 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package com.google.api.generator.engine.writer; 16 17 import com.google.api.generator.engine.ast.AnnotationNode; 18 import com.google.api.generator.engine.ast.AnonymousClassExpr; 19 import com.google.api.generator.engine.ast.ArithmeticOperationExpr; 20 import com.google.api.generator.engine.ast.ArrayExpr; 21 import com.google.api.generator.engine.ast.AssignmentExpr; 22 import com.google.api.generator.engine.ast.AssignmentOperationExpr; 23 import com.google.api.generator.engine.ast.AstNodeVisitor; 24 import com.google.api.generator.engine.ast.BlockComment; 25 import com.google.api.generator.engine.ast.BlockStatement; 26 import com.google.api.generator.engine.ast.BreakStatement; 27 import com.google.api.generator.engine.ast.CastExpr; 28 import com.google.api.generator.engine.ast.ClassDefinition; 29 import com.google.api.generator.engine.ast.CommentStatement; 30 import com.google.api.generator.engine.ast.ConcreteReference; 31 import com.google.api.generator.engine.ast.EmptyLineStatement; 32 import com.google.api.generator.engine.ast.EnumRefExpr; 33 import com.google.api.generator.engine.ast.Expr; 34 import com.google.api.generator.engine.ast.ExprStatement; 35 import com.google.api.generator.engine.ast.ForStatement; 36 import com.google.api.generator.engine.ast.GeneralForStatement; 37 import com.google.api.generator.engine.ast.IdentifierNode; 38 import com.google.api.generator.engine.ast.IfStatement; 39 import com.google.api.generator.engine.ast.InstanceofExpr; 40 import com.google.api.generator.engine.ast.JavaDocComment; 41 import com.google.api.generator.engine.ast.LambdaExpr; 42 import com.google.api.generator.engine.ast.LineComment; 43 import com.google.api.generator.engine.ast.LogicalOperationExpr; 44 import com.google.api.generator.engine.ast.MethodDefinition; 45 import com.google.api.generator.engine.ast.MethodInvocationExpr; 46 import com.google.api.generator.engine.ast.NewObjectExpr; 47 import com.google.api.generator.engine.ast.OperatorKind; 48 import com.google.api.generator.engine.ast.PackageInfoDefinition; 49 import com.google.api.generator.engine.ast.Reference; 50 import com.google.api.generator.engine.ast.ReferenceConstructorExpr; 51 import com.google.api.generator.engine.ast.RelationalOperationExpr; 52 import com.google.api.generator.engine.ast.ReturnExpr; 53 import com.google.api.generator.engine.ast.ScopeNode; 54 import com.google.api.generator.engine.ast.Statement; 55 import com.google.api.generator.engine.ast.SynchronizedStatement; 56 import com.google.api.generator.engine.ast.TernaryExpr; 57 import com.google.api.generator.engine.ast.ThrowExpr; 58 import com.google.api.generator.engine.ast.TryCatchStatement; 59 import com.google.api.generator.engine.ast.TypeNode; 60 import com.google.api.generator.engine.ast.TypeNode.TypeKind; 61 import com.google.api.generator.engine.ast.UnaryOperationExpr; 62 import com.google.api.generator.engine.ast.ValueExpr; 63 import com.google.api.generator.engine.ast.VaporReference; 64 import com.google.api.generator.engine.ast.Variable; 65 import com.google.api.generator.engine.ast.VariableExpr; 66 import com.google.api.generator.engine.ast.WhileStatement; 67 import com.google.api.generator.gapic.model.RegionTag; 68 import java.util.Arrays; 69 import java.util.Iterator; 70 import java.util.List; 71 import java.util.Map; 72 import java.util.stream.Collectors; 73 import java.util.stream.IntStream; 74 75 public class JavaWriterVisitor implements AstNodeVisitor { 76 private static final String SPACE = " "; 77 private static final String NEWLINE = "\n"; 78 79 private static final String AT = "@"; 80 81 private static final String COLON = ":"; 82 private static final String COMMA = ","; 83 private static final String BLOCK_COMMENT_START = "/*"; 84 private static final String BLOCK_COMMENT_END = "*/"; 85 private static final String DOT = "."; 86 private static final String EQUALS = "="; 87 private static final String LEFT_ANGLE = "<"; 88 private static final String LEFT_BRACE = "{"; 89 private static final String LEFT_PAREN = "("; 90 private static final String JAVADOC_COMMENT_START = "/**"; 91 private static final String QUESTION_MARK = "?"; 92 private static final String RIGHT_ANGLE = ">"; 93 private static final String RIGHT_BRACE = "}"; 94 private static final String RIGHT_PAREN = ")"; 95 private static final String SEMICOLON = ";"; 96 private static final String ASTERISK = "*"; 97 98 private static final String ABSTRACT = "abstract"; 99 private static final String CATCH = "catch"; 100 private static final String CLASS = "class"; 101 private static final String ELSE = "else"; 102 private static final String EXTENDS = "extends"; 103 private static final String FINAL = "final"; 104 private static final String FOR = "for"; 105 private static final String IF = "if"; 106 private static final String INSTANCEOF = "instanceof"; 107 private static final String IMPLEMENTS = "implements"; 108 private static final String NEW = "new"; 109 private static final String RETURN = "return"; 110 private static final String SYNCHRONIZED = "synchronized"; 111 private static final String STATIC = "static"; 112 private static final String THROW = "throw"; 113 private static final String THROWS = "throws"; 114 private static final String TRY = "try"; 115 private static final String VOLATILE = "volatile"; 116 private static final String WHILE = "while"; 117 private static final String BREAK = "break"; 118 119 // Operators 120 private static final String OPERATOR_ADDITION = "+"; 121 private static final String OPERATOR_EQUAL_TO = "=="; 122 private static final String OPERATOR_NOT_EQUAL_TO = "!="; 123 private static final String OPERATOR_LESS_THAN = "<"; 124 private static final String OPERATOR_INCREMENT = "++"; 125 private static final String OPERATOR_LOGICAL_NOT = "!"; 126 private static final String OPERATOR_LOGICAL_AND = "&&"; 127 private static final String OPERATOR_LOGICAL_OR = "||"; 128 private static final String OPERATOR_XOR = "^="; 129 private static final String OPERATOR_MULTIPLE_AND_ASSIGNMENT = "*="; 130 131 private final StringBuffer buffer = new StringBuffer(); 132 private final ImportWriterVisitor importWriterVisitor = new ImportWriterVisitor(); 133 JavaWriterVisitor()134 public JavaWriterVisitor() {} 135 clear()136 public void clear() { 137 buffer.setLength(0); 138 importWriterVisitor.clear(); 139 } 140 write()141 public String write() { 142 return buffer.toString(); 143 } 144 145 @Override visit(IdentifierNode identifier)146 public void visit(IdentifierNode identifier) { 147 buffer.append(identifier.name()); 148 } 149 150 @Override visit(TypeNode type)151 public void visit(TypeNode type) { 152 TypeKind typeKind = type.typeKind(); 153 if (type.isPrimitiveType()) { 154 buffer.append(typeKind.toString().toLowerCase()); 155 } else { 156 type.reference().accept(this); 157 } 158 159 if (type.isArray()) { 160 buffer.append("[]"); 161 } 162 } 163 164 @Override visit(ScopeNode scope)165 public void visit(ScopeNode scope) { 166 buffer.append(scope.toString()); 167 } 168 169 @Override visit(ArrayExpr expr)170 public void visit(ArrayExpr expr) { 171 buffer.append(LEFT_BRACE); 172 for (int i = 0; i < expr.exprs().size(); i++) { 173 expr.exprs().get(i).accept(this); 174 if (i < expr.exprs().size() - 1) { 175 buffer.append(COMMA); 176 buffer.append(SPACE); 177 } 178 } 179 buffer.append(RIGHT_BRACE); 180 } 181 182 @Override visit(AnnotationNode annotation)183 public void visit(AnnotationNode annotation) { 184 buffer.append(AT); 185 annotation.type().accept(this); 186 if (annotation.descriptionExprs() != null) { 187 leftParen(); 188 for (int i = 0; i < annotation.descriptionExprs().size(); i++) { 189 annotation.descriptionExprs().get(i).accept(this); 190 if (i < annotation.descriptionExprs().size() - 1) { 191 buffer.append(COMMA); 192 buffer.append(SPACE); 193 } 194 } 195 rightParen(); 196 } 197 newline(); 198 } 199 200 @Override visit(ConcreteReference reference)201 public void visit(ConcreteReference reference) { 202 if (reference.isWildcard()) { 203 buffer.append(QUESTION_MARK); 204 if (reference.wildcardUpperBound() != null) { 205 // Handle the upper bound. 206 buffer.append(SPACE); 207 buffer.append(EXTENDS); 208 buffer.append(SPACE); 209 reference.wildcardUpperBound().accept(this); 210 } 211 return; 212 } 213 String pakkage = reference.pakkage(); 214 String shortName = reference.name(); 215 if (reference.useFullName() || importWriterVisitor.collidesWithImport(pakkage, shortName)) { 216 buffer.append(pakkage); 217 buffer.append(DOT); 218 } 219 220 if (reference.hasEnclosingClass() && !reference.isStaticImport()) { 221 buffer.append(String.join(DOT, reference.enclosingClassNames())); 222 buffer.append(DOT); 223 } 224 225 buffer.append(reference.simpleName()); 226 227 if (!reference.generics().isEmpty()) { 228 buffer.append(LEFT_ANGLE); 229 for (int i = 0; i < reference.generics().size(); i++) { 230 Reference r = reference.generics().get(i); 231 r.accept(this); 232 if (i < reference.generics().size() - 1) { 233 buffer.append(COMMA); 234 buffer.append(SPACE); 235 } 236 } 237 buffer.append(RIGHT_ANGLE); 238 } 239 } 240 241 @Override visit(VaporReference reference)242 public void visit(VaporReference reference) { 243 // This implementation should be more extensive, but there are no existing use cases that 244 // exercise the edge cases. 245 // TODO(miraleung): Give this behavioral parity with ConcreteReference. 246 String pakkage = reference.pakkage(); 247 String shortName = reference.name(); 248 249 if (reference.useFullName() || importWriterVisitor.collidesWithImport(pakkage, shortName)) { 250 buffer.append(pakkage); 251 buffer.append(DOT); 252 if (reference.hasEnclosingClass()) { 253 buffer.append(String.join(DOT, reference.enclosingClassNames())); 254 buffer.append(DOT); 255 } 256 } 257 258 // A null pointer exception will be thrown if reference is null, which is WAI. 259 buffer.append(shortName); 260 } 261 262 /** =============================== EXPRESSIONS =============================== */ 263 @Override visit(ValueExpr valueExpr)264 public void visit(ValueExpr valueExpr) { 265 buffer.append(valueExpr.value().value()); 266 } 267 268 @Override visit(VariableExpr variableExpr)269 public void visit(VariableExpr variableExpr) { 270 Variable variable = variableExpr.variable(); 271 TypeNode type = variable.type(); 272 ScopeNode scope = variableExpr.scope(); 273 274 // VariableExpr will handle isDecl and exprReferenceExpr edge cases. 275 if (variableExpr.isDecl()) { 276 // Annotations, if any. 277 annotations(variableExpr.annotations()); 278 279 if (!scope.equals(ScopeNode.LOCAL)) { 280 scope.accept(this); 281 space(); 282 } 283 284 if (variableExpr.isStatic()) { 285 buffer.append(STATIC); 286 space(); 287 } 288 289 if (variableExpr.isFinal()) { 290 buffer.append(FINAL); 291 space(); 292 } 293 294 if (variableExpr.isVolatile()) { 295 buffer.append(VOLATILE); 296 space(); 297 } 298 299 type.accept(this); 300 if (!variableExpr.templateNodes().isEmpty()) { 301 leftAngle(); 302 IntStream.range(0, variableExpr.templateNodes().size()) 303 .forEach( 304 i -> { 305 variableExpr.templateNodes().get(i).accept(this); 306 if (i < variableExpr.templateNodes().size() - 1) { 307 buffer.append(COMMA); 308 space(); 309 } 310 }); 311 rightAngle(); 312 } 313 space(); 314 } else { 315 // Expression or static reference. 316 if (variableExpr.exprReferenceExpr() != null) { 317 variableExpr.exprReferenceExpr().accept(this); 318 buffer.append(DOT); 319 } else if (variableExpr.staticReferenceType() != null) { 320 variableExpr.staticReferenceType().accept(this); 321 buffer.append(DOT); 322 } 323 } 324 325 variable.identifier().accept(this); 326 } 327 328 @Override visit(TernaryExpr ternaryExpr)329 public void visit(TernaryExpr ternaryExpr) { 330 ternaryExpr.conditionExpr().accept(this); 331 space(); 332 buffer.append(QUESTION_MARK); 333 space(); 334 ternaryExpr.thenExpr().accept(this); 335 space(); 336 buffer.append(COLON); 337 space(); 338 ternaryExpr.elseExpr().accept(this); 339 } 340 341 @Override visit(AssignmentExpr assignmentExpr)342 public void visit(AssignmentExpr assignmentExpr) { 343 assignmentExpr.variableExpr().accept(this); 344 space(); 345 buffer.append(EQUALS); 346 space(); 347 assignmentExpr.valueExpr().accept(this); 348 } 349 350 @Override visit(MethodInvocationExpr methodInvocationExpr)351 public void visit(MethodInvocationExpr methodInvocationExpr) { 352 // Expression or static reference. 353 if (methodInvocationExpr.exprReferenceExpr() != null) { 354 methodInvocationExpr.exprReferenceExpr().accept(this); 355 buffer.append(DOT); 356 } else if (methodInvocationExpr.staticReferenceType() != null) { 357 methodInvocationExpr.staticReferenceType().accept(this); 358 buffer.append(DOT); 359 } 360 361 if (methodInvocationExpr.isGeneric()) { 362 leftAngle(); 363 int numGenerics = methodInvocationExpr.generics().size(); 364 for (int i = 0; i < numGenerics; i++) { 365 buffer.append(methodInvocationExpr.generics().get(i).name()); 366 if (i < numGenerics - 1) { 367 buffer.append(COMMA); 368 space(); 369 } 370 } 371 rightAngle(); 372 } 373 374 methodInvocationExpr.methodIdentifier().accept(this); 375 leftParen(); 376 int numArguments = methodInvocationExpr.arguments().size(); 377 for (int i = 0; i < numArguments; i++) { 378 Expr argExpr = methodInvocationExpr.arguments().get(i); 379 argExpr.accept(this); 380 if (i < numArguments - 1) { 381 buffer.append(COMMA); 382 space(); 383 } 384 } 385 rightParen(); 386 } 387 388 @Override visit(CastExpr castExpr)389 public void visit(CastExpr castExpr) { 390 leftParen(); 391 leftParen(); 392 castExpr.type().accept(this); 393 rightParen(); 394 space(); 395 castExpr.expr().accept(this); 396 rightParen(); 397 } 398 399 @Override visit(AnonymousClassExpr anonymousClassExpr)400 public void visit(AnonymousClassExpr anonymousClassExpr) { 401 buffer.append(NEW); 402 space(); 403 anonymousClassExpr.type().accept(this); 404 leftParen(); 405 rightParen(); 406 space(); 407 leftBrace(); 408 newline(); 409 statements(anonymousClassExpr.statements()); 410 methods(anonymousClassExpr.methods()); 411 rightBrace(); 412 } 413 414 @Override visit(ThrowExpr throwExpr)415 public void visit(ThrowExpr throwExpr) { 416 buffer.append(THROW); 417 space(); 418 // If throwExpr is present, then messageExpr and causeExpr will not be present. Relies on AST 419 // build-time checks. 420 if (throwExpr.throwExpr() != null) { 421 throwExpr.throwExpr().accept(this); 422 return; 423 } 424 425 buffer.append(NEW); 426 space(); 427 throwExpr.type().accept(this); 428 leftParen(); 429 if (throwExpr.messageExpr() != null) { 430 throwExpr.messageExpr().accept(this); 431 } 432 if (throwExpr.causeExpr() != null) { 433 if (throwExpr.messageExpr() != null) { 434 buffer.append(COMMA); 435 space(); 436 } 437 throwExpr.causeExpr().accept(this); 438 } 439 rightParen(); 440 } 441 442 @Override visit(InstanceofExpr instanceofExpr)443 public void visit(InstanceofExpr instanceofExpr) { 444 instanceofExpr.expr().accept(this); 445 space(); 446 buffer.append(INSTANCEOF); 447 space(); 448 instanceofExpr.checkType().accept(this); 449 } 450 451 @Override visit(NewObjectExpr newObjectExpr)452 public void visit(NewObjectExpr newObjectExpr) { 453 buffer.append(NEW); 454 space(); 455 newObjectExpr.type().accept(this); 456 // If isGeneric() is true, but generic list is empty, we will append `<>` to the buffer. 457 if (newObjectExpr.isGeneric() && newObjectExpr.type().reference().generics().isEmpty()) { 458 leftAngle(); 459 rightAngle(); 460 } 461 leftParen(); 462 int numArguments = newObjectExpr.arguments().size(); 463 for (int i = 0; i < numArguments; i++) { 464 newObjectExpr.arguments().get(i).accept(this); 465 if (i < numArguments - 1) { 466 buffer.append(COMMA); 467 space(); 468 } 469 } 470 rightParen(); 471 } 472 473 @Override visit(EnumRefExpr enumRefExpr)474 public void visit(EnumRefExpr enumRefExpr) { 475 enumRefExpr.type().accept(this); 476 buffer.append(DOT); 477 enumRefExpr.identifier().accept(this); 478 } 479 480 @Override visit(ReturnExpr returnExpr)481 public void visit(ReturnExpr returnExpr) { 482 buffer.append(RETURN); 483 space(); 484 returnExpr.expr().accept(this); 485 } 486 487 @Override visit(ReferenceConstructorExpr referenceConstructorExpr)488 public void visit(ReferenceConstructorExpr referenceConstructorExpr) { 489 buffer.append(referenceConstructorExpr.keywordKind().name().toLowerCase()); 490 leftParen(); 491 IntStream.range(0, referenceConstructorExpr.arguments().size()) 492 .forEach( 493 i -> { 494 referenceConstructorExpr.arguments().get(i).accept(this); 495 if (i < referenceConstructorExpr.arguments().size() - 1) { 496 buffer.append(COMMA); 497 space(); 498 } 499 }); 500 rightParen(); 501 } 502 503 @Override visit(ArithmeticOperationExpr arithmeticOperationExpr)504 public void visit(ArithmeticOperationExpr arithmeticOperationExpr) { 505 arithmeticOperationExpr.lhsExpr().accept(this); 506 space(); 507 operator(arithmeticOperationExpr.operatorKind()); 508 space(); 509 arithmeticOperationExpr.rhsExpr().accept(this); 510 } 511 512 @Override visit(UnaryOperationExpr unaryOperationExpr)513 public void visit(UnaryOperationExpr unaryOperationExpr) { 514 if (unaryOperationExpr.operatorKind().isPrefixOperator()) { 515 operator(unaryOperationExpr.operatorKind()); 516 unaryOperationExpr.expr().accept(this); 517 } else { 518 unaryOperationExpr.expr().accept(this); 519 operator(unaryOperationExpr.operatorKind()); 520 } 521 } 522 523 @Override visit(RelationalOperationExpr relationalOperationExpr)524 public void visit(RelationalOperationExpr relationalOperationExpr) { 525 relationalOperationExpr.lhsExpr().accept(this); 526 space(); 527 operator(relationalOperationExpr.operatorKind()); 528 space(); 529 relationalOperationExpr.rhsExpr().accept(this); 530 } 531 532 @Override visit(LogicalOperationExpr logicalOperationExpr)533 public void visit(LogicalOperationExpr logicalOperationExpr) { 534 logicalOperationExpr.lhsExpr().accept(this); 535 space(); 536 operator(logicalOperationExpr.operatorKind()); 537 space(); 538 logicalOperationExpr.rhsExpr().accept(this); 539 } 540 541 @Override visit(AssignmentOperationExpr assignmentOperationExpr)542 public void visit(AssignmentOperationExpr assignmentOperationExpr) { 543 assignmentOperationExpr.variableExpr().accept(this); 544 space(); 545 operator(assignmentOperationExpr.operatorKind()); 546 space(); 547 assignmentOperationExpr.valueExpr().accept(this); 548 } 549 550 @Override visit(LambdaExpr lambdaExpr)551 public void visit(LambdaExpr lambdaExpr) { 552 if (lambdaExpr.arguments().isEmpty()) { 553 leftParen(); 554 rightParen(); 555 } else if (lambdaExpr.arguments().size() == 1) { 556 // Print just the variable. 557 lambdaExpr.arguments().get(0).variable().identifier().accept(this); 558 } else { 559 // Stylistic choice - print the types and variable names for clarity. 560 leftParen(); 561 int numArguments = lambdaExpr.arguments().size(); 562 for (int i = 0; i < numArguments; i++) { 563 lambdaExpr.arguments().get(i).accept(this); 564 if (i < numArguments - 1) { 565 buffer.append(COMMA); 566 space(); 567 } 568 } 569 rightParen(); 570 } 571 572 space(); 573 buffer.append("->"); 574 space(); 575 576 if (lambdaExpr.body().isEmpty()) { 577 // Just the return expression - don't render "return". 578 lambdaExpr.returnExpr().expr().accept(this); 579 return; 580 } 581 582 leftBrace(); 583 newline(); 584 statements(lambdaExpr.body()); 585 ExprStatement.withExpr(lambdaExpr.returnExpr()).accept(this); 586 rightBrace(); 587 } 588 589 /** =============================== STATEMENTS =============================== */ 590 @Override visit(ExprStatement exprStatement)591 public void visit(ExprStatement exprStatement) { 592 exprStatement.expression().accept(this); 593 semicolon(); 594 newline(); 595 } 596 597 @Override visit(BlockStatement blockStatement)598 public void visit(BlockStatement blockStatement) { 599 if (blockStatement.isStatic()) { 600 buffer.append(STATIC); 601 space(); 602 } 603 leftBrace(); 604 newline(); 605 statements(blockStatement.body()); 606 rightBrace(); 607 newline(); 608 } 609 610 @Override visit(IfStatement ifStatement)611 public void visit(IfStatement ifStatement) { 612 buffer.append(IF); 613 space(); 614 leftParen(); 615 616 ifStatement.conditionExpr().accept(this); 617 rightParen(); 618 space(); 619 leftBrace(); 620 newline(); 621 622 statements(ifStatement.body()); 623 buffer.append(RIGHT_BRACE); 624 if (!ifStatement.elseIfs().isEmpty()) { 625 for (Map.Entry<Expr, List<Statement>> elseIfEntry : ifStatement.elseIfs().entrySet()) { 626 Expr elseIfConditionExpr = elseIfEntry.getKey(); 627 List<Statement> elseIfBody = elseIfEntry.getValue(); 628 space(); 629 buffer.append(ELSE); 630 space(); 631 buffer.append(IF); 632 space(); 633 leftParen(); 634 elseIfConditionExpr.accept(this); 635 rightParen(); 636 space(); 637 leftBrace(); 638 newline(); 639 statements(elseIfBody); 640 rightBrace(); 641 } 642 } 643 if (!ifStatement.elseBody().isEmpty()) { 644 space(); 645 buffer.append(ELSE); 646 space(); 647 leftBrace(); 648 newline(); 649 statements(ifStatement.elseBody()); 650 rightBrace(); 651 } 652 newline(); 653 } 654 655 @Override visit(ForStatement forStatement)656 public void visit(ForStatement forStatement) { 657 buffer.append(FOR); 658 space(); 659 leftParen(); 660 forStatement.localVariableExpr().accept(this); 661 space(); 662 buffer.append(COLON); 663 space(); 664 forStatement.collectionExpr().accept(this); 665 rightParen(); 666 space(); 667 leftBrace(); 668 newline(); 669 statements(forStatement.body()); 670 rightBrace(); 671 newline(); 672 } 673 674 @Override visit(GeneralForStatement generalForStatement)675 public void visit(GeneralForStatement generalForStatement) { 676 buffer.append(FOR); 677 space(); 678 leftParen(); 679 generalForStatement.initializationExpr().accept(this); 680 semicolon(); 681 space(); 682 683 generalForStatement.terminationExpr().accept(this); 684 semicolon(); 685 space(); 686 687 generalForStatement.updateExpr().accept(this); 688 rightParen(); 689 space(); 690 leftBrace(); 691 newline(); 692 693 statements(generalForStatement.body()); 694 rightBrace(); 695 newline(); 696 } 697 698 @Override visit(WhileStatement whileStatement)699 public void visit(WhileStatement whileStatement) { 700 buffer.append(WHILE); 701 space(); 702 leftParen(); 703 whileStatement.conditionExpr().accept(this); 704 rightParen(); 705 space(); 706 leftBrace(); 707 newline(); 708 statements(whileStatement.body()); 709 rightBrace(); 710 newline(); 711 } 712 713 @Override visit(TryCatchStatement tryCatchStatement)714 public void visit(TryCatchStatement tryCatchStatement) { 715 buffer.append(TRY); 716 space(); 717 if (tryCatchStatement.tryResourceExpr() != null) { 718 leftParen(); 719 tryCatchStatement.tryResourceExpr().accept(this); 720 rightParen(); 721 space(); 722 } 723 leftBrace(); 724 newline(); 725 726 statements(tryCatchStatement.tryBody()); 727 rightBrace(); 728 729 for (int i = 0; i < tryCatchStatement.catchVariableExprs().size(); i++) { 730 space(); 731 buffer.append(CATCH); 732 space(); 733 leftParen(); 734 tryCatchStatement.catchVariableExprs().get(i).accept(this); 735 rightParen(); 736 space(); 737 leftBrace(); 738 newline(); 739 statements(tryCatchStatement.catchBlocks().get(i)); 740 rightBrace(); 741 } 742 newline(); 743 } 744 745 @Override visit(SynchronizedStatement synchronizedStatement)746 public void visit(SynchronizedStatement synchronizedStatement) { 747 buffer.append(SYNCHRONIZED); 748 space(); 749 leftParen(); 750 synchronizedStatement.lock().accept(this); 751 rightParen(); 752 space(); 753 leftBrace(); 754 newline(); 755 statements(synchronizedStatement.body()); 756 rightBrace(); 757 newline(); 758 } 759 760 @Override visit(CommentStatement commentStatement)761 public void visit(CommentStatement commentStatement) { 762 commentStatement.comment().accept(this); 763 } 764 765 @Override visit(EmptyLineStatement emptyLineStatement)766 public void visit(EmptyLineStatement emptyLineStatement) { 767 newline(); 768 } 769 770 @Override visit(BreakStatement breakStatement)771 public void visit(BreakStatement breakStatement) { 772 buffer.append(BREAK); 773 semicolon(); 774 } 775 776 /** =============================== COMMENT =============================== */ 777 @Override visit(LineComment lineComment)778 public void visit(LineComment lineComment) { 779 // Split comments by new line and add `//` to each line. 780 String formattedSource = 781 JavaFormatter.format( 782 String.format("// %s", String.join("\n//", lineComment.comment().split("\\r?\\n")))); 783 buffer.append(formattedSource); 784 } 785 786 @Override visit(BlockComment blockComment)787 public void visit(BlockComment blockComment) { 788 // Split comments by new line and embrace the comment block with `/* */`. 789 StringBuilder sourceComment = new StringBuilder(); 790 sourceComment.append(BLOCK_COMMENT_START).append(NEWLINE); 791 Arrays.stream(blockComment.comment().split("\\r?\\n")) 792 .forEach( 793 comment -> { 794 sourceComment.append(String.format("%s %s%s", ASTERISK, comment, NEWLINE)); 795 }); 796 sourceComment.append(BLOCK_COMMENT_END); 797 buffer.append(JavaFormatter.format(sourceComment.toString())); 798 } 799 800 @Override visit(JavaDocComment javaDocComment)801 public void visit(JavaDocComment javaDocComment) { 802 StringBuilder sourceComment = new StringBuilder(); 803 sourceComment.append(JAVADOC_COMMENT_START).append(NEWLINE); 804 Arrays.stream(javaDocComment.comment().split("\\r?\\n")) 805 .forEach( 806 comment -> { 807 sourceComment.append(String.format("%s %s%s", ASTERISK, comment, NEWLINE)); 808 }); 809 sourceComment.append(BLOCK_COMMENT_END); 810 buffer.append(JavaFormatter.format(sourceComment.toString())); 811 } 812 813 /** =============================== OTHER =============================== */ 814 @Override visit(MethodDefinition methodDefinition)815 public void visit(MethodDefinition methodDefinition) { 816 // Header comments, if any. 817 statements(methodDefinition.headerCommentStatements().stream().collect(Collectors.toList())); 818 // Annotations, if any. 819 annotations(methodDefinition.annotations()); 820 821 // Method scope. 822 methodDefinition.scope().accept(this); 823 space(); 824 825 // Templates, if any. 826 if (!methodDefinition.templateIdentifiers().isEmpty()) { 827 leftAngle(); 828 IntStream.range(0, methodDefinition.templateIdentifiers().size()) 829 .forEach( 830 i -> { 831 methodDefinition.templateIdentifiers().get(i).accept(this); 832 if (i < methodDefinition.templateIdentifiers().size() - 1) { 833 buffer.append(COMMA); 834 space(); 835 } 836 }); 837 rightAngle(); 838 space(); 839 } 840 841 // Modifiers. 842 if (methodDefinition.isAbstract()) { 843 buffer.append(ABSTRACT); 844 space(); 845 } 846 if (methodDefinition.isStatic()) { 847 buffer.append(STATIC); 848 space(); 849 } 850 if (methodDefinition.isFinal()) { 851 buffer.append(FINAL); 852 space(); 853 } 854 855 if (!methodDefinition.isConstructor()) { 856 methodDefinition.returnType().accept(this); 857 if (!methodDefinition.returnTemplateIdentifiers().isEmpty()) { 858 leftAngle(); 859 IntStream.range(0, methodDefinition.returnTemplateIdentifiers().size()) 860 .forEach( 861 i -> { 862 methodDefinition.returnTemplateIdentifiers().get(i).accept(this); 863 if (i < methodDefinition.returnTemplateIdentifiers().size() - 1) { 864 buffer.append(COMMA); 865 space(); 866 } 867 }); 868 rightAngle(); 869 } 870 space(); 871 } 872 873 // Method name. 874 methodDefinition.methodIdentifier().accept(this); 875 leftParen(); 876 877 // Arguments, if any. 878 int numArguments = methodDefinition.arguments().size(); 879 for (int i = 0; i < numArguments; i++) { 880 methodDefinition.arguments().get(i).accept(this); 881 if (i < numArguments - 1) { 882 buffer.append(COMMA); 883 space(); 884 } 885 } 886 rightParen(); 887 888 // Thrown exceptions. 889 if (!methodDefinition.throwsExceptions().isEmpty()) { 890 space(); 891 buffer.append(THROWS); 892 space(); 893 894 Iterator<TypeNode> exceptionIter = methodDefinition.throwsExceptions().iterator(); 895 while (exceptionIter.hasNext()) { 896 TypeNode exceptionType = exceptionIter.next(); 897 exceptionType.accept(this); 898 if (exceptionIter.hasNext()) { 899 buffer.append(COMMA); 900 space(); 901 } 902 } 903 } 904 905 if (methodDefinition.isAbstract() && methodDefinition.body().isEmpty()) { 906 semicolon(); 907 newline(); 908 return; 909 } 910 911 // Method body. 912 space(); 913 leftBrace(); 914 newline(); 915 statements(methodDefinition.body()); 916 if (methodDefinition.returnExpr() != null) { 917 ExprStatement.withExpr(methodDefinition.returnExpr()).accept(this); 918 } 919 920 rightBrace(); 921 newline(); 922 newline(); 923 } 924 925 @Override visit(ClassDefinition classDefinition)926 public void visit(ClassDefinition classDefinition) { 927 if (!classDefinition.isNested()) { 928 statements(classDefinition.fileHeader().stream().collect(Collectors.toList())); 929 newline(); 930 importWriterVisitor.initialize( 931 classDefinition.packageString(), classDefinition.classIdentifier().name()); 932 buffer.append(String.format("package %s;", classDefinition.packageString())); 933 newline(); 934 newline(); 935 } 936 937 String regionTagReplace = "REPLACE_REGION_TAG"; 938 if (classDefinition.regionTag() != null) { 939 statements( 940 Arrays.asList( 941 classDefinition 942 .regionTag() 943 .generateTag(RegionTag.RegionTagRegion.START, regionTagReplace))); 944 } 945 946 // This must go first, so that we can check for type collisions. 947 classDefinition.accept(importWriterVisitor); 948 if (!classDefinition.isNested()) { 949 buffer.append(importWriterVisitor.write()); 950 } 951 // Header comments, if any. 952 statements(classDefinition.headerCommentStatements().stream().collect(Collectors.toList())); 953 // Annotations, if any. 954 annotations(classDefinition.annotations()); 955 956 classDefinition.scope().accept(this); 957 space(); 958 959 // Modifiers. 960 if (classDefinition.isStatic()) { 961 buffer.append(STATIC); 962 space(); 963 } 964 if (classDefinition.isFinal()) { 965 buffer.append(FINAL); 966 space(); 967 } 968 if (classDefinition.isAbstract()) { 969 buffer.append(ABSTRACT); 970 space(); 971 } 972 973 // Name, extends, implements. 974 buffer.append(CLASS); 975 space(); 976 classDefinition.classIdentifier().accept(this); 977 space(); 978 if (classDefinition.extendsType() != null) { 979 buffer.append(EXTENDS); 980 space(); 981 classDefinition.extendsType().accept(this); 982 space(); 983 } 984 985 if (!classDefinition.implementsTypes().isEmpty()) { 986 buffer.append(IMPLEMENTS); 987 space(); 988 989 int numImplementsTypes = classDefinition.implementsTypes().size(); 990 for (int i = 0; i < numImplementsTypes; i++) { 991 classDefinition.implementsTypes().get(i).accept(this); 992 if (i < numImplementsTypes - 1) { 993 buffer.append(COMMA); 994 } 995 space(); 996 } 997 } 998 999 // Class body. 1000 leftBrace(); 1001 newline(); 1002 1003 statements(classDefinition.statements()); 1004 newline(); 1005 methods(classDefinition.methods()); 1006 newline(); 1007 classes(classDefinition.nestedClasses()); 1008 1009 rightBrace(); 1010 if (classDefinition.regionTag() != null) { 1011 statements( 1012 Arrays.asList( 1013 classDefinition 1014 .regionTag() 1015 .generateTag(RegionTag.RegionTagRegion.END, regionTagReplace))); 1016 } 1017 1018 // We should have valid Java by now, so format it. 1019 if (!classDefinition.isNested()) { 1020 String formattedClazz = JavaFormatter.format(buffer.toString()); 1021 1022 // fixing region tag after formatting 1023 // formatter splits long region tags on multiple lines and moves the end tag up - doesn't meet 1024 // tag requirements. See https://github.com/google/google-java-format/issues/137 1025 if (classDefinition.regionTag() != null) { 1026 formattedClazz = 1027 formattedClazz.replaceAll(regionTagReplace, classDefinition.regionTag().generate()); 1028 formattedClazz = formattedClazz.replaceAll("} // \\[END", "}\n// \\[END"); 1029 } 1030 buffer.replace(0, buffer.length(), formattedClazz); 1031 } 1032 } 1033 1034 @Override visit(PackageInfoDefinition packageInfoDefinition)1035 public void visit(PackageInfoDefinition packageInfoDefinition) { 1036 statements(packageInfoDefinition.fileHeader().stream().collect(Collectors.toList())); 1037 newline(); 1038 statements( 1039 packageInfoDefinition.headerCommentStatements().stream().collect(Collectors.toList())); 1040 newline(); 1041 1042 annotations(packageInfoDefinition.annotations()); 1043 buffer.append(String.format("package %s;", packageInfoDefinition.pakkage())); 1044 newline(); 1045 1046 packageInfoDefinition.accept(importWriterVisitor); 1047 importWriterVisitor.initialize(packageInfoDefinition.pakkage()); 1048 buffer.append(importWriterVisitor.write()); 1049 1050 // Format code. 1051 buffer.replace(0, buffer.length(), JavaFormatter.format(buffer.toString())); 1052 } 1053 1054 /** =============================== PRIVATE HELPERS =============================== */ annotations(List<AnnotationNode> annotations)1055 private void annotations(List<AnnotationNode> annotations) { 1056 for (AnnotationNode annotation : annotations) { 1057 annotation.accept(this); 1058 } 1059 } 1060 statements(List<Statement> statements)1061 private void statements(List<Statement> statements) { 1062 for (Statement statement : statements) { 1063 statement.accept(this); 1064 } 1065 } 1066 methods(List<MethodDefinition> methods)1067 private void methods(List<MethodDefinition> methods) { 1068 for (MethodDefinition method : methods) { 1069 method.accept(this); 1070 } 1071 } 1072 classes(List<ClassDefinition> classes)1073 private void classes(List<ClassDefinition> classes) { 1074 if (!classes.isEmpty()) { 1075 newline(); 1076 } 1077 for (ClassDefinition classDef : classes) { 1078 classDef.accept(this); 1079 newline(); 1080 newline(); 1081 } 1082 } 1083 space()1084 private void space() { 1085 buffer.append(SPACE); 1086 } 1087 newline()1088 private void newline() { 1089 buffer.append(NEWLINE); 1090 } 1091 leftParen()1092 private void leftParen() { 1093 buffer.append(LEFT_PAREN); 1094 } 1095 rightParen()1096 private void rightParen() { 1097 buffer.append(RIGHT_PAREN); 1098 } 1099 leftAngle()1100 private void leftAngle() { 1101 buffer.append(LEFT_ANGLE); 1102 } 1103 rightAngle()1104 private void rightAngle() { 1105 buffer.append(RIGHT_ANGLE); 1106 } 1107 leftBrace()1108 private void leftBrace() { 1109 buffer.append(LEFT_BRACE); 1110 } 1111 rightBrace()1112 private void rightBrace() { 1113 buffer.append(RIGHT_BRACE); 1114 } 1115 semicolon()1116 private void semicolon() { 1117 buffer.append(SEMICOLON); 1118 } 1119 operator(OperatorKind kind)1120 private void operator(OperatorKind kind) { 1121 switch (kind) { 1122 case ARITHMETIC_ADDITION: 1123 buffer.append(OPERATOR_ADDITION); 1124 break; 1125 case ASSIGNMENT_XOR: 1126 buffer.append(OPERATOR_XOR); 1127 break; 1128 case ASSIGNMENT_MULTIPLY: 1129 buffer.append(OPERATOR_MULTIPLE_AND_ASSIGNMENT); 1130 break; 1131 case RELATIONAL_EQUAL_TO: 1132 buffer.append(OPERATOR_EQUAL_TO); 1133 break; 1134 case RELATIONAL_NOT_EQUAL_TO: 1135 buffer.append(OPERATOR_NOT_EQUAL_TO); 1136 break; 1137 case RELATIONAL_LESS_THAN: 1138 buffer.append(OPERATOR_LESS_THAN); 1139 break; 1140 case UNARY_POST_INCREMENT: 1141 buffer.append(OPERATOR_INCREMENT); 1142 break; 1143 case UNARY_LOGICAL_NOT: 1144 buffer.append(OPERATOR_LOGICAL_NOT); 1145 break; 1146 case LOGICAL_AND: 1147 buffer.append(OPERATOR_LOGICAL_AND); 1148 break; 1149 case LOGICAL_OR: 1150 buffer.append(OPERATOR_LOGICAL_OR); 1151 break; 1152 } 1153 } 1154 } 1155