1 /* 2 * Copyright 2015 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. 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 distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package com.google.googlejavaformat.java; 16 17 import static com.google.common.collect.Iterables.getLast; 18 import static com.google.common.collect.Iterables.getOnlyElement; 19 import static com.google.googlejavaformat.Doc.FillMode.INDEPENDENT; 20 import static com.google.googlejavaformat.Doc.FillMode.UNIFIED; 21 import static com.google.googlejavaformat.Indent.If.make; 22 import static com.google.googlejavaformat.OpsBuilder.BlankLineWanted.PRESERVE; 23 import static com.google.googlejavaformat.OpsBuilder.BlankLineWanted.YES; 24 import static com.google.googlejavaformat.java.Trees.getEndPosition; 25 import static com.google.googlejavaformat.java.Trees.getLength; 26 import static com.google.googlejavaformat.java.Trees.getMethodName; 27 import static com.google.googlejavaformat.java.Trees.getSourceForNode; 28 import static com.google.googlejavaformat.java.Trees.getStartPosition; 29 import static com.google.googlejavaformat.java.Trees.operatorName; 30 import static com.google.googlejavaformat.java.Trees.precedence; 31 import static com.google.googlejavaformat.java.Trees.skipParen; 32 import static com.sun.source.tree.Tree.Kind.ANNOTATION; 33 import static com.sun.source.tree.Tree.Kind.ARRAY_ACCESS; 34 import static com.sun.source.tree.Tree.Kind.ASSIGNMENT; 35 import static com.sun.source.tree.Tree.Kind.BLOCK; 36 import static com.sun.source.tree.Tree.Kind.EXTENDS_WILDCARD; 37 import static com.sun.source.tree.Tree.Kind.IF; 38 import static com.sun.source.tree.Tree.Kind.METHOD_INVOCATION; 39 import static com.sun.source.tree.Tree.Kind.NEW_ARRAY; 40 import static com.sun.source.tree.Tree.Kind.NEW_CLASS; 41 import static com.sun.source.tree.Tree.Kind.STRING_LITERAL; 42 import static com.sun.source.tree.Tree.Kind.UNION_TYPE; 43 import static com.sun.source.tree.Tree.Kind.VARIABLE; 44 import static java.util.stream.Collectors.toList; 45 46 import com.google.common.base.MoreObjects; 47 import com.google.common.base.Predicate; 48 import com.google.common.base.Throwables; 49 import com.google.common.base.Verify; 50 import com.google.common.collect.HashMultiset; 51 import com.google.common.collect.ImmutableList; 52 import com.google.common.collect.ImmutableSet; 53 import com.google.common.collect.ImmutableSortedSet; 54 import com.google.common.collect.Iterables; 55 import com.google.common.collect.Iterators; 56 import com.google.common.collect.Multiset; 57 import com.google.common.collect.PeekingIterator; 58 import com.google.common.collect.Streams; 59 import com.google.googlejavaformat.CloseOp; 60 import com.google.googlejavaformat.Doc; 61 import com.google.googlejavaformat.Doc.FillMode; 62 import com.google.googlejavaformat.FormattingError; 63 import com.google.googlejavaformat.Indent; 64 import com.google.googlejavaformat.Input; 65 import com.google.googlejavaformat.Op; 66 import com.google.googlejavaformat.OpenOp; 67 import com.google.googlejavaformat.OpsBuilder; 68 import com.google.googlejavaformat.OpsBuilder.BlankLineWanted; 69 import com.google.googlejavaformat.Output.BreakTag; 70 import com.google.googlejavaformat.java.DimensionHelpers.SortedDims; 71 import com.google.googlejavaformat.java.DimensionHelpers.TypeWithDims; 72 import com.sun.source.tree.AnnotatedTypeTree; 73 import com.sun.source.tree.AnnotationTree; 74 import com.sun.source.tree.ArrayAccessTree; 75 import com.sun.source.tree.ArrayTypeTree; 76 import com.sun.source.tree.AssertTree; 77 import com.sun.source.tree.AssignmentTree; 78 import com.sun.source.tree.BinaryTree; 79 import com.sun.source.tree.BlockTree; 80 import com.sun.source.tree.BreakTree; 81 import com.sun.source.tree.CaseTree; 82 import com.sun.source.tree.CatchTree; 83 import com.sun.source.tree.ClassTree; 84 import com.sun.source.tree.CompilationUnitTree; 85 import com.sun.source.tree.CompoundAssignmentTree; 86 import com.sun.source.tree.ConditionalExpressionTree; 87 import com.sun.source.tree.ContinueTree; 88 import com.sun.source.tree.DirectiveTree; 89 import com.sun.source.tree.DoWhileLoopTree; 90 import com.sun.source.tree.EmptyStatementTree; 91 import com.sun.source.tree.EnhancedForLoopTree; 92 import com.sun.source.tree.ExportsTree; 93 import com.sun.source.tree.ExpressionStatementTree; 94 import com.sun.source.tree.ExpressionTree; 95 import com.sun.source.tree.ForLoopTree; 96 import com.sun.source.tree.IdentifierTree; 97 import com.sun.source.tree.IfTree; 98 import com.sun.source.tree.ImportTree; 99 import com.sun.source.tree.InstanceOfTree; 100 import com.sun.source.tree.IntersectionTypeTree; 101 import com.sun.source.tree.LabeledStatementTree; 102 import com.sun.source.tree.LambdaExpressionTree; 103 import com.sun.source.tree.LiteralTree; 104 import com.sun.source.tree.MemberReferenceTree; 105 import com.sun.source.tree.MemberSelectTree; 106 import com.sun.source.tree.MethodInvocationTree; 107 import com.sun.source.tree.MethodTree; 108 import com.sun.source.tree.ModifiersTree; 109 import com.sun.source.tree.ModuleTree; 110 import com.sun.source.tree.NewArrayTree; 111 import com.sun.source.tree.NewClassTree; 112 import com.sun.source.tree.OpensTree; 113 import com.sun.source.tree.ParameterizedTypeTree; 114 import com.sun.source.tree.ParenthesizedTree; 115 import com.sun.source.tree.PrimitiveTypeTree; 116 import com.sun.source.tree.ProvidesTree; 117 import com.sun.source.tree.RequiresTree; 118 import com.sun.source.tree.ReturnTree; 119 import com.sun.source.tree.StatementTree; 120 import com.sun.source.tree.SwitchTree; 121 import com.sun.source.tree.SynchronizedTree; 122 import com.sun.source.tree.ThrowTree; 123 import com.sun.source.tree.Tree; 124 import com.sun.source.tree.TryTree; 125 import com.sun.source.tree.TypeCastTree; 126 import com.sun.source.tree.TypeParameterTree; 127 import com.sun.source.tree.UnaryTree; 128 import com.sun.source.tree.UnionTypeTree; 129 import com.sun.source.tree.UsesTree; 130 import com.sun.source.tree.VariableTree; 131 import com.sun.source.tree.WhileLoopTree; 132 import com.sun.source.tree.WildcardTree; 133 import com.sun.source.util.TreePath; 134 import com.sun.source.util.TreePathScanner; 135 import com.sun.tools.javac.code.Flags; 136 import com.sun.tools.javac.tree.JCTree; 137 import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 138 import com.sun.tools.javac.tree.TreeScanner; 139 import java.util.ArrayDeque; 140 import java.util.ArrayList; 141 import java.util.Collection; 142 import java.util.Collections; 143 import java.util.Deque; 144 import java.util.LinkedHashSet; 145 import java.util.List; 146 import java.util.Map; 147 import java.util.Optional; 148 import java.util.Set; 149 import java.util.regex.Pattern; 150 import java.util.stream.Stream; 151 import javax.lang.model.element.Name; 152 import org.checkerframework.checker.nullness.qual.Nullable; 153 154 /** 155 * An AST visitor that builds a stream of {@link Op}s to format from the given {@link 156 * CompilationUnitTree}. 157 */ 158 public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { 159 160 /** Direction for Annotations (usually VERTICAL). */ 161 protected enum Direction { 162 VERTICAL, 163 HORIZONTAL; 164 isVertical()165 boolean isVertical() { 166 return this == VERTICAL; 167 } 168 } 169 170 /** Whether to break or not. */ 171 enum BreakOrNot { 172 YES, 173 NO; 174 isYes()175 boolean isYes() { 176 return this == YES; 177 } 178 } 179 180 /** Whether to collapse empty blocks. */ 181 enum CollapseEmptyOrNot { 182 YES, 183 NO; 184 valueOf(boolean b)185 static CollapseEmptyOrNot valueOf(boolean b) { 186 return b ? YES : NO; 187 } 188 isYes()189 boolean isYes() { 190 return this == YES; 191 } 192 } 193 194 /** Whether to allow leading blank lines in blocks. */ 195 enum AllowLeadingBlankLine { 196 YES, 197 NO; 198 valueOf(boolean b)199 static AllowLeadingBlankLine valueOf(boolean b) { 200 return b ? YES : NO; 201 } 202 } 203 204 /** Whether to allow trailing blank lines in blocks. */ 205 enum AllowTrailingBlankLine { 206 YES, 207 NO; 208 valueOf(boolean b)209 static AllowTrailingBlankLine valueOf(boolean b) { 210 return b ? YES : NO; 211 } 212 } 213 214 /** Whether to include braces. */ 215 protected enum BracesOrNot { 216 YES, 217 NO; 218 isYes()219 boolean isYes() { 220 return this == YES; 221 } 222 } 223 224 /** Whether or not to include dimensions. */ 225 enum DimensionsOrNot { 226 YES, 227 NO; 228 isYes()229 boolean isYes() { 230 return this == YES; 231 } 232 } 233 234 /** Whether or not the declaration is Varargs. */ 235 enum VarArgsOrNot { 236 YES, 237 NO; 238 valueOf(boolean b)239 static VarArgsOrNot valueOf(boolean b) { 240 return b ? YES : NO; 241 } 242 isYes()243 boolean isYes() { 244 return this == YES; 245 } 246 fromVariable(VariableTree node)247 static VarArgsOrNot fromVariable(VariableTree node) { 248 return valueOf((((JCTree.JCVariableDecl) node).mods.flags & Flags.VARARGS) == Flags.VARARGS); 249 } 250 } 251 252 /** Whether the formal parameter declaration is a receiver. */ 253 enum ReceiverParameter { 254 YES, 255 NO; 256 isYes()257 boolean isYes() { 258 return this == YES; 259 } 260 } 261 262 /** Whether these declarations are the first in the block. */ 263 protected enum FirstDeclarationsOrNot { 264 YES, 265 NO; 266 isYes()267 boolean isYes() { 268 return this == YES; 269 } 270 } 271 272 protected final OpsBuilder builder; 273 274 protected static final Indent.Const ZERO = Indent.Const.ZERO; 275 protected final int indentMultiplier; 276 protected final Indent.Const minusTwo; 277 protected final Indent.Const minusFour; 278 protected final Indent.Const plusTwo; 279 protected final Indent.Const plusFour; 280 breakList(Optional<BreakTag> breakTag)281 private static final ImmutableList<Op> breakList(Optional<BreakTag> breakTag) { 282 return ImmutableList.of(Doc.Break.make(Doc.FillMode.UNIFIED, " ", ZERO, breakTag)); 283 } 284 breakFillList(Optional<BreakTag> breakTag)285 private static final ImmutableList<Op> breakFillList(Optional<BreakTag> breakTag) { 286 return ImmutableList.of( 287 OpenOp.make(ZERO), 288 Doc.Break.make(Doc.FillMode.INDEPENDENT, " ", ZERO, breakTag), 289 CloseOp.make()); 290 } 291 forceBreakList(Optional<BreakTag> breakTag)292 private static final ImmutableList<Op> forceBreakList(Optional<BreakTag> breakTag) { 293 return ImmutableList.of(Doc.Break.make(FillMode.FORCED, "", Indent.Const.ZERO, breakTag)); 294 } 295 296 private static final ImmutableList<Op> EMPTY_LIST = ImmutableList.of(); 297 298 /** 299 * Allow multi-line filling (of array initializers, argument lists, and boolean expressions) for 300 * items with length less than or equal to this threshold. 301 */ 302 private static final int MAX_ITEM_LENGTH_FOR_FILLING = 10; 303 304 /** 305 * The {@code Visitor} constructor. 306 * 307 * @param builder the {@link OpsBuilder} 308 */ JavaInputAstVisitor(OpsBuilder builder, int indentMultiplier)309 public JavaInputAstVisitor(OpsBuilder builder, int indentMultiplier) { 310 this.builder = builder; 311 this.indentMultiplier = indentMultiplier; 312 minusTwo = Indent.Const.make(-2, indentMultiplier); 313 minusFour = Indent.Const.make(-4, indentMultiplier); 314 plusTwo = Indent.Const.make(+2, indentMultiplier); 315 plusFour = Indent.Const.make(+4, indentMultiplier); 316 } 317 318 /** A record of whether we have visited into an expression. */ 319 private final Deque<Boolean> inExpression = new ArrayDeque<>(ImmutableList.of(false)); 320 inExpression()321 private boolean inExpression() { 322 return inExpression.peekLast(); 323 } 324 325 @Override scan(Tree tree, Void unused)326 public Void scan(Tree tree, Void unused) { 327 inExpression.addLast(tree instanceof ExpressionTree || inExpression.peekLast()); 328 int previous = builder.depth(); 329 try { 330 super.scan(tree, null); 331 } catch (FormattingError e) { 332 throw e; 333 } catch (Throwable t) { 334 throw new FormattingError(builder.diagnostic(Throwables.getStackTraceAsString(t))); 335 } finally { 336 inExpression.removeLast(); 337 } 338 builder.checkClosed(previous); 339 return null; 340 } 341 342 @Override visitCompilationUnit(CompilationUnitTree node, Void unused)343 public Void visitCompilationUnit(CompilationUnitTree node, Void unused) { 344 boolean first = true; 345 if (node.getPackageName() != null) { 346 markForPartialFormat(); 347 visitPackage(node.getPackageName(), node.getPackageAnnotations()); 348 builder.forcedBreak(); 349 first = false; 350 } 351 dropEmptyDeclarations(); 352 if (!node.getImports().isEmpty()) { 353 if (!first) { 354 builder.blankLineWanted(BlankLineWanted.YES); 355 } 356 for (ImportTree importDeclaration : node.getImports()) { 357 markForPartialFormat(); 358 builder.blankLineWanted(PRESERVE); 359 scan(importDeclaration, null); 360 builder.forcedBreak(); 361 } 362 first = false; 363 } 364 dropEmptyDeclarations(); 365 for (Tree type : node.getTypeDecls()) { 366 if (type.getKind() == Tree.Kind.IMPORT) { 367 // javac treats extra semicolons in the import list as type declarations 368 // TODO(cushon): remove this if https://bugs.openjdk.java.net/browse/JDK-8027682 is fixed 369 continue; 370 } 371 if (!first) { 372 builder.blankLineWanted(BlankLineWanted.YES); 373 } 374 markForPartialFormat(); 375 scan(type, null); 376 builder.forcedBreak(); 377 first = false; 378 dropEmptyDeclarations(); 379 } 380 // set a partial format marker at EOF to make sure we can format the entire file 381 markForPartialFormat(); 382 return null; 383 } 384 385 /** Skips over extra semi-colons at the top-level, or in a class member declaration lists. */ dropEmptyDeclarations()386 protected void dropEmptyDeclarations() { 387 if (builder.peekToken().equals(Optional.of(";"))) { 388 while (builder.peekToken().equals(Optional.of(";"))) { 389 builder.forcedBreak(); 390 markForPartialFormat(); 391 token(";"); 392 } 393 } 394 } 395 396 @Override visitClass(ClassTree tree, Void unused)397 public Void visitClass(ClassTree tree, Void unused) { 398 switch (tree.getKind()) { 399 case ANNOTATION_TYPE: 400 visitAnnotationType(tree); 401 break; 402 case CLASS: 403 case INTERFACE: 404 visitClassDeclaration(tree); 405 break; 406 case ENUM: 407 visitEnumDeclaration(tree); 408 break; 409 default: 410 throw new AssertionError(tree.getKind()); 411 } 412 return null; 413 } 414 visitAnnotationType(ClassTree node)415 public void visitAnnotationType(ClassTree node) { 416 sync(node); 417 builder.open(ZERO); 418 visitAndBreakModifiers( 419 node.getModifiers(), 420 Direction.VERTICAL, 421 /* declarationAnnotationBreak= */ Optional.empty()); 422 builder.open(ZERO); 423 token("@"); 424 token("interface"); 425 builder.breakOp(" "); 426 visit(node.getSimpleName()); 427 builder.close(); 428 builder.close(); 429 if (node.getMembers() == null) { 430 builder.open(plusFour); 431 token(";"); 432 builder.close(); 433 } else { 434 addBodyDeclarations(node.getMembers(), BracesOrNot.YES, FirstDeclarationsOrNot.YES); 435 } 436 builder.guessToken(";"); 437 } 438 439 @Override visitArrayAccess(ArrayAccessTree node, Void unused)440 public Void visitArrayAccess(ArrayAccessTree node, Void unused) { 441 sync(node); 442 visitDot(node); 443 return null; 444 } 445 446 @Override visitNewArray(NewArrayTree node, Void unused)447 public Void visitNewArray(NewArrayTree node, Void unused) { 448 if (node.getType() != null) { 449 builder.open(plusFour); 450 token("new"); 451 builder.space(); 452 453 TypeWithDims extractedDims = DimensionHelpers.extractDims(node.getType(), SortedDims.YES); 454 Tree base = extractedDims.node; 455 456 Deque<ExpressionTree> dimExpressions = new ArrayDeque<>(node.getDimensions()); 457 458 Deque<List<? extends AnnotationTree>> annotations = new ArrayDeque<>(); 459 annotations.add(ImmutableList.copyOf(node.getAnnotations())); 460 annotations.addAll(node.getDimAnnotations()); 461 annotations.addAll(extractedDims.dims); 462 463 scan(base, null); 464 builder.open(ZERO); 465 maybeAddDims(dimExpressions, annotations); 466 builder.close(); 467 builder.close(); 468 } 469 if (node.getInitializers() != null) { 470 if (node.getType() != null) { 471 builder.space(); 472 } 473 visitArrayInitializer(node.getInitializers()); 474 } 475 return null; 476 } 477 visitArrayInitializer(List<? extends ExpressionTree> expressions)478 public boolean visitArrayInitializer(List<? extends ExpressionTree> expressions) { 479 int cols; 480 if (expressions.isEmpty()) { 481 tokenBreakTrailingComment("{", plusTwo); 482 if (builder.peekToken().equals(Optional.of(","))) { 483 token(","); 484 } 485 token("}", plusTwo); 486 } else if ((cols = argumentsAreTabular(expressions)) != -1) { 487 builder.open(plusTwo); 488 token("{"); 489 builder.forcedBreak(); 490 boolean first = true; 491 for (Iterable<? extends ExpressionTree> row : Iterables.partition(expressions, cols)) { 492 if (!first) { 493 builder.forcedBreak(); 494 } 495 builder.open(row.iterator().next().getKind() == NEW_ARRAY || cols == 1 ? ZERO : plusFour); 496 boolean firstInRow = true; 497 for (ExpressionTree item : row) { 498 if (!firstInRow) { 499 token(","); 500 builder.breakToFill(" "); 501 } 502 scan(item, null); 503 firstInRow = false; 504 } 505 builder.guessToken(","); 506 builder.close(); 507 first = false; 508 } 509 builder.breakOp(minusTwo); 510 builder.close(); 511 token("}", plusTwo); 512 } else { 513 // Special-case the formatting of array initializers inside annotations 514 // to more eagerly use a one-per-line layout. 515 boolean inMemberValuePair = false; 516 // walk up past the enclosing NewArrayTree (and maybe an enclosing AssignmentTree) 517 TreePath path = getCurrentPath(); 518 for (int i = 0; i < 2; i++) { 519 if (path == null) { 520 break; 521 } 522 if (path.getLeaf().getKind() == ANNOTATION) { 523 inMemberValuePair = true; 524 break; 525 } 526 path = path.getParentPath(); 527 } 528 boolean shortItems = hasOnlyShortItems(expressions); 529 boolean allowFilledElementsOnOwnLine = shortItems || !inMemberValuePair; 530 531 builder.open(plusTwo); 532 tokenBreakTrailingComment("{", plusTwo); 533 boolean hasTrailingComma = hasTrailingToken(builder.getInput(), expressions, ","); 534 builder.breakOp(hasTrailingComma ? FillMode.FORCED : FillMode.UNIFIED, "", ZERO); 535 if (allowFilledElementsOnOwnLine) { 536 builder.open(ZERO); 537 } 538 boolean first = true; 539 FillMode fillMode = shortItems ? FillMode.INDEPENDENT : FillMode.UNIFIED; 540 for (ExpressionTree expression : expressions) { 541 if (!first) { 542 token(","); 543 builder.breakOp(fillMode, " ", ZERO); 544 } 545 scan(expression, null); 546 first = false; 547 } 548 builder.guessToken(","); 549 if (allowFilledElementsOnOwnLine) { 550 builder.close(); 551 } 552 builder.breakOp(minusTwo); 553 builder.close(); 554 token("}", plusTwo); 555 } 556 return false; 557 } 558 hasOnlyShortItems(List<? extends ExpressionTree> expressions)559 private boolean hasOnlyShortItems(List<? extends ExpressionTree> expressions) { 560 for (ExpressionTree expression : expressions) { 561 int startPosition = getStartPosition(expression); 562 if (builder.actualSize( 563 startPosition, getEndPosition(expression, getCurrentPath()) - startPosition) 564 >= MAX_ITEM_LENGTH_FOR_FILLING) { 565 return false; 566 } 567 } 568 return true; 569 } 570 571 @Override visitArrayType(ArrayTypeTree node, Void unused)572 public Void visitArrayType(ArrayTypeTree node, Void unused) { 573 sync(node); 574 visitAnnotatedArrayType(node); 575 return null; 576 } 577 visitAnnotatedArrayType(Tree node)578 private void visitAnnotatedArrayType(Tree node) { 579 TypeWithDims extractedDims = DimensionHelpers.extractDims(node, SortedDims.YES); 580 builder.open(plusFour); 581 scan(extractedDims.node, null); 582 Deque<List<? extends AnnotationTree>> dims = new ArrayDeque<>(extractedDims.dims); 583 maybeAddDims(dims); 584 Verify.verify(dims.isEmpty()); 585 builder.close(); 586 } 587 588 @Override visitAssert(AssertTree node, Void unused)589 public Void visitAssert(AssertTree node, Void unused) { 590 sync(node); 591 builder.open(ZERO); 592 token("assert"); 593 builder.space(); 594 builder.open(node.getDetail() == null ? ZERO : plusFour); 595 scan(node.getCondition(), null); 596 if (node.getDetail() != null) { 597 builder.breakOp(" "); 598 token(":"); 599 builder.space(); 600 scan(node.getDetail(), null); 601 } 602 builder.close(); 603 builder.close(); 604 token(";"); 605 return null; 606 } 607 608 @Override visitAssignment(AssignmentTree node, Void unused)609 public Void visitAssignment(AssignmentTree node, Void unused) { 610 sync(node); 611 builder.open(plusFour); 612 scan(node.getVariable(), null); 613 builder.space(); 614 splitToken(operatorName(node)); 615 builder.breakOp(" "); 616 scan(node.getExpression(), null); 617 builder.close(); 618 return null; 619 } 620 621 @Override visitBlock(BlockTree node, Void unused)622 public Void visitBlock(BlockTree node, Void unused) { 623 visitBlock(node, CollapseEmptyOrNot.NO, AllowLeadingBlankLine.NO, AllowTrailingBlankLine.NO); 624 return null; 625 } 626 627 @Override visitCompoundAssignment(CompoundAssignmentTree node, Void unused)628 public Void visitCompoundAssignment(CompoundAssignmentTree node, Void unused) { 629 sync(node); 630 builder.open(plusFour); 631 scan(node.getVariable(), null); 632 builder.space(); 633 splitToken(operatorName(node)); 634 builder.breakOp(" "); 635 scan(node.getExpression(), null); 636 builder.close(); 637 return null; 638 } 639 640 @Override visitBreak(BreakTree node, Void unused)641 public Void visitBreak(BreakTree node, Void unused) { 642 sync(node); 643 builder.open(plusFour); 644 token("break"); 645 if (node.getLabel() != null) { 646 builder.breakOp(" "); 647 visit(node.getLabel()); 648 } 649 builder.close(); 650 token(";"); 651 return null; 652 } 653 654 @Override visitTypeCast(TypeCastTree node, Void unused)655 public Void visitTypeCast(TypeCastTree node, Void unused) { 656 sync(node); 657 builder.open(plusFour); 658 token("("); 659 scan(node.getType(), null); 660 token(")"); 661 builder.breakOp(" "); 662 scan(node.getExpression(), null); 663 builder.close(); 664 return null; 665 } 666 667 @Override visitNewClass(NewClassTree node, Void unused)668 public Void visitNewClass(NewClassTree node, Void unused) { 669 sync(node); 670 builder.open(ZERO); 671 if (node.getEnclosingExpression() != null) { 672 scan(node.getEnclosingExpression(), null); 673 builder.breakOp(); 674 token("."); 675 } 676 token("new"); 677 builder.space(); 678 addTypeArguments(node.getTypeArguments(), plusFour); 679 if (node.getClassBody() != null) { 680 builder.addAll( 681 visitModifiers( 682 node.getClassBody().getModifiers(), Direction.HORIZONTAL, Optional.empty())); 683 } 684 scan(node.getIdentifier(), null); 685 addArguments(node.getArguments(), plusFour); 686 builder.close(); 687 if (node.getClassBody() != null) { 688 addBodyDeclarations( 689 node.getClassBody().getMembers(), BracesOrNot.YES, FirstDeclarationsOrNot.YES); 690 } 691 return null; 692 } 693 694 @Override visitConditionalExpression(ConditionalExpressionTree node, Void unused)695 public Void visitConditionalExpression(ConditionalExpressionTree node, Void unused) { 696 sync(node); 697 builder.open(plusFour); 698 scan(node.getCondition(), null); 699 builder.breakOp(" "); 700 token("?"); 701 builder.space(); 702 scan(node.getTrueExpression(), null); 703 builder.breakOp(" "); 704 token(":"); 705 builder.space(); 706 scan(node.getFalseExpression(), null); 707 builder.close(); 708 return null; 709 } 710 711 @Override visitContinue(ContinueTree node, Void unused)712 public Void visitContinue(ContinueTree node, Void unused) { 713 sync(node); 714 builder.open(plusFour); 715 token("continue"); 716 if (node.getLabel() != null) { 717 builder.breakOp(" "); 718 visit(node.getLabel()); 719 } 720 token(";"); 721 builder.close(); 722 return null; 723 } 724 725 @Override visitDoWhileLoop(DoWhileLoopTree node, Void unused)726 public Void visitDoWhileLoop(DoWhileLoopTree node, Void unused) { 727 sync(node); 728 token("do"); 729 visitStatement( 730 node.getStatement(), 731 CollapseEmptyOrNot.YES, 732 AllowLeadingBlankLine.YES, 733 AllowTrailingBlankLine.YES); 734 if (node.getStatement().getKind() == BLOCK) { 735 builder.space(); 736 } else { 737 builder.breakOp(" "); 738 } 739 token("while"); 740 builder.space(); 741 token("("); 742 scan(skipParen(node.getCondition()), null); 743 token(")"); 744 token(";"); 745 return null; 746 } 747 748 @Override visitEmptyStatement(EmptyStatementTree node, Void unused)749 public Void visitEmptyStatement(EmptyStatementTree node, Void unused) { 750 sync(node); 751 dropEmptyDeclarations(); 752 return null; 753 } 754 755 @Override visitEnhancedForLoop(EnhancedForLoopTree node, Void unused)756 public Void visitEnhancedForLoop(EnhancedForLoopTree node, Void unused) { 757 sync(node); 758 builder.open(ZERO); 759 token("for"); 760 builder.space(); 761 token("("); 762 builder.open(ZERO); 763 visitToDeclare( 764 DeclarationKind.NONE, 765 Direction.HORIZONTAL, 766 node.getVariable(), 767 Optional.of(node.getExpression()), 768 ":", 769 /* trailing= */ Optional.empty()); 770 builder.close(); 771 token(")"); 772 builder.close(); 773 visitStatement( 774 node.getStatement(), 775 CollapseEmptyOrNot.YES, 776 AllowLeadingBlankLine.YES, 777 AllowTrailingBlankLine.NO); 778 return null; 779 } 780 visitEnumConstantDeclaration(VariableTree enumConstant)781 private void visitEnumConstantDeclaration(VariableTree enumConstant) { 782 for (AnnotationTree annotation : enumConstant.getModifiers().getAnnotations()) { 783 scan(annotation, null); 784 builder.forcedBreak(); 785 } 786 visit(enumConstant.getName()); 787 NewClassTree init = ((NewClassTree) enumConstant.getInitializer()); 788 if (init.getArguments().isEmpty()) { 789 builder.guessToken("("); 790 builder.guessToken(")"); 791 } else { 792 addArguments(init.getArguments(), plusFour); 793 } 794 if (init.getClassBody() != null) { 795 addBodyDeclarations( 796 init.getClassBody().getMembers(), BracesOrNot.YES, FirstDeclarationsOrNot.YES); 797 } 798 } 799 visitEnumDeclaration(ClassTree node)800 public boolean visitEnumDeclaration(ClassTree node) { 801 sync(node); 802 builder.open(ZERO); 803 visitAndBreakModifiers( 804 node.getModifiers(), 805 Direction.VERTICAL, 806 /* declarationAnnotationBreak= */ Optional.empty()); 807 builder.open(plusFour); 808 token("enum"); 809 builder.breakOp(" "); 810 visit(node.getSimpleName()); 811 builder.close(); 812 builder.close(); 813 if (!node.getImplementsClause().isEmpty()) { 814 builder.open(plusFour); 815 builder.breakOp(" "); 816 builder.open(plusFour); 817 token("implements"); 818 builder.breakOp(" "); 819 builder.open(ZERO); 820 boolean first = true; 821 for (Tree superInterfaceType : node.getImplementsClause()) { 822 if (!first) { 823 token(","); 824 builder.breakToFill(" "); 825 } 826 scan(superInterfaceType, null); 827 first = false; 828 } 829 builder.close(); 830 builder.close(); 831 builder.close(); 832 } 833 builder.space(); 834 tokenBreakTrailingComment("{", plusTwo); 835 ArrayList<VariableTree> enumConstants = new ArrayList<>(); 836 ArrayList<Tree> members = new ArrayList<>(); 837 for (Tree member : node.getMembers()) { 838 if (member instanceof JCTree.JCVariableDecl) { 839 JCTree.JCVariableDecl variableDecl = (JCTree.JCVariableDecl) member; 840 if ((variableDecl.mods.flags & Flags.ENUM) == Flags.ENUM) { 841 enumConstants.add(variableDecl); 842 continue; 843 } 844 } 845 members.add(member); 846 } 847 if (enumConstants.isEmpty() && members.isEmpty()) { 848 if (builder.peekToken().equals(Optional.of(";"))) { 849 builder.open(plusTwo); 850 builder.forcedBreak(); 851 token(";"); 852 builder.forcedBreak(); 853 dropEmptyDeclarations(); 854 builder.close(); 855 builder.open(ZERO); 856 builder.forcedBreak(); 857 builder.blankLineWanted(BlankLineWanted.NO); 858 token("}", plusTwo); 859 builder.close(); 860 } else { 861 builder.open(ZERO); 862 builder.blankLineWanted(BlankLineWanted.NO); 863 token("}"); 864 builder.close(); 865 } 866 } else { 867 builder.open(plusTwo); 868 builder.blankLineWanted(BlankLineWanted.NO); 869 builder.forcedBreak(); 870 builder.open(ZERO); 871 boolean first = true; 872 for (VariableTree enumConstant : enumConstants) { 873 if (!first) { 874 token(","); 875 builder.forcedBreak(); 876 builder.blankLineWanted(BlankLineWanted.PRESERVE); 877 } 878 markForPartialFormat(); 879 visitEnumConstantDeclaration(enumConstant); 880 first = false; 881 } 882 if (builder.peekToken().orElse("").equals(",")) { 883 token(","); 884 builder.forcedBreak(); // The ";" goes on its own line. 885 } 886 builder.close(); 887 builder.close(); 888 if (builder.peekToken().equals(Optional.of(";"))) { 889 builder.open(plusTwo); 890 token(";"); 891 builder.forcedBreak(); 892 dropEmptyDeclarations(); 893 builder.close(); 894 } 895 builder.open(ZERO); 896 addBodyDeclarations(members, BracesOrNot.NO, FirstDeclarationsOrNot.NO); 897 builder.forcedBreak(); 898 builder.blankLineWanted(BlankLineWanted.NO); 899 token("}", plusTwo); 900 builder.close(); 901 } 902 builder.guessToken(";"); 903 return false; 904 } 905 906 @Override visitMemberReference(MemberReferenceTree node, Void unused)907 public Void visitMemberReference(MemberReferenceTree node, Void unused) { 908 sync(node); 909 builder.open(plusFour); 910 scan(node.getQualifierExpression(), null); 911 builder.breakOp(); 912 builder.op("::"); 913 addTypeArguments(node.getTypeArguments(), plusFour); 914 switch (node.getMode()) { 915 case INVOKE: 916 visit(node.getName()); 917 break; 918 case NEW: 919 token("new"); 920 break; 921 default: 922 throw new AssertionError(node.getMode()); 923 } 924 builder.close(); 925 return null; 926 } 927 928 @Override visitExpressionStatement(ExpressionStatementTree node, Void unused)929 public Void visitExpressionStatement(ExpressionStatementTree node, Void unused) { 930 sync(node); 931 scan(node.getExpression(), null); 932 token(";"); 933 return null; 934 } 935 936 @Override visitVariable(VariableTree node, Void unused)937 public Void visitVariable(VariableTree node, Void unused) { 938 sync(node); 939 visitVariables( 940 ImmutableList.of(node), 941 DeclarationKind.NONE, 942 fieldAnnotationDirection(node.getModifiers())); 943 return null; 944 } 945 visitVariables( List<VariableTree> fragments, DeclarationKind declarationKind, Direction annotationDirection)946 void visitVariables( 947 List<VariableTree> fragments, 948 DeclarationKind declarationKind, 949 Direction annotationDirection) { 950 if (fragments.size() == 1) { 951 VariableTree fragment = fragments.get(0); 952 declareOne( 953 declarationKind, 954 annotationDirection, 955 Optional.of(fragment.getModifiers()), 956 fragment.getType(), 957 /* name= */ fragment.getName(), 958 "", 959 "=", 960 Optional.ofNullable(fragment.getInitializer()), 961 Optional.of(";"), 962 /* receiverExpression= */ Optional.empty(), 963 Optional.ofNullable(variableFragmentDims(true, 0, fragment.getType()))); 964 965 } else { 966 declareMany(fragments, annotationDirection); 967 } 968 } 969 variableFragmentDims(boolean first, int leadingDims, Tree type)970 private TypeWithDims variableFragmentDims(boolean first, int leadingDims, Tree type) { 971 if (type == null) { 972 return null; 973 } 974 if (first) { 975 return DimensionHelpers.extractDims(type, SortedDims.YES); 976 } 977 TypeWithDims dims = DimensionHelpers.extractDims(type, SortedDims.NO); 978 return new TypeWithDims( 979 null, leadingDims > 0 ? dims.dims.subList(0, dims.dims.size() - leadingDims) : dims.dims); 980 } 981 982 @Override visitForLoop(ForLoopTree node, Void unused)983 public Void visitForLoop(ForLoopTree node, Void unused) { 984 sync(node); 985 token("for"); 986 builder.space(); 987 token("("); 988 builder.open(plusFour); 989 builder.open( 990 node.getInitializer().size() > 1 991 && node.getInitializer().get(0).getKind() == Tree.Kind.EXPRESSION_STATEMENT 992 ? plusFour 993 : ZERO); 994 if (!node.getInitializer().isEmpty()) { 995 if (node.getInitializer().get(0).getKind() == VARIABLE) { 996 PeekingIterator<StatementTree> it = 997 Iterators.peekingIterator(node.getInitializer().iterator()); 998 visitVariables( 999 variableFragments(it, it.next()), DeclarationKind.NONE, Direction.HORIZONTAL); 1000 } else { 1001 boolean first = true; 1002 builder.open(ZERO); 1003 for (StatementTree t : node.getInitializer()) { 1004 if (!first) { 1005 token(","); 1006 builder.breakOp(" "); 1007 } 1008 scan(((ExpressionStatementTree) t).getExpression(), null); 1009 first = false; 1010 } 1011 token(";"); 1012 builder.close(); 1013 } 1014 } else { 1015 token(";"); 1016 } 1017 builder.close(); 1018 builder.breakOp(" "); 1019 if (node.getCondition() != null) { 1020 scan(node.getCondition(), null); 1021 } 1022 token(";"); 1023 if (!node.getUpdate().isEmpty()) { 1024 builder.breakOp(" "); 1025 builder.open(node.getUpdate().size() <= 1 ? ZERO : plusFour); 1026 boolean firstUpdater = true; 1027 for (ExpressionStatementTree updater : node.getUpdate()) { 1028 if (!firstUpdater) { 1029 token(","); 1030 builder.breakToFill(" "); 1031 } 1032 scan(updater.getExpression(), null); 1033 firstUpdater = false; 1034 } 1035 builder.guessToken(";"); 1036 builder.close(); 1037 } else { 1038 builder.space(); 1039 } 1040 builder.close(); 1041 token(")"); 1042 visitStatement( 1043 node.getStatement(), 1044 CollapseEmptyOrNot.YES, 1045 AllowLeadingBlankLine.YES, 1046 AllowTrailingBlankLine.NO); 1047 return null; 1048 } 1049 1050 @Override visitIf(IfTree node, Void unused)1051 public Void visitIf(IfTree node, Void unused) { 1052 sync(node); 1053 // Collapse chains of else-ifs. 1054 List<ExpressionTree> expressions = new ArrayList<>(); 1055 List<StatementTree> statements = new ArrayList<>(); 1056 while (true) { 1057 expressions.add(node.getCondition()); 1058 statements.add(node.getThenStatement()); 1059 if (node.getElseStatement() != null && node.getElseStatement().getKind() == IF) { 1060 node = (IfTree) node.getElseStatement(); 1061 } else { 1062 break; 1063 } 1064 } 1065 builder.open(ZERO); 1066 boolean first = true; 1067 boolean followingBlock = false; 1068 int expressionsN = expressions.size(); 1069 for (int i = 0; i < expressionsN; i++) { 1070 if (!first) { 1071 if (followingBlock) { 1072 builder.space(); 1073 } else { 1074 builder.forcedBreak(); 1075 } 1076 token("else"); 1077 builder.space(); 1078 } 1079 token("if"); 1080 builder.space(); 1081 token("("); 1082 scan(skipParen(expressions.get(i)), null); 1083 token(")"); 1084 // An empty block can collapse to "{}" if there are no if/else or else clauses 1085 boolean onlyClause = expressionsN == 1 && node.getElseStatement() == null; 1086 // Trailing blank lines are permitted if this isn't the last clause 1087 boolean trailingClauses = i < expressionsN - 1 || node.getElseStatement() != null; 1088 visitStatement( 1089 statements.get(i), 1090 CollapseEmptyOrNot.valueOf(onlyClause), 1091 AllowLeadingBlankLine.YES, 1092 AllowTrailingBlankLine.valueOf(trailingClauses)); 1093 followingBlock = statements.get(i).getKind() == BLOCK; 1094 first = false; 1095 } 1096 if (node.getElseStatement() != null) { 1097 if (followingBlock) { 1098 builder.space(); 1099 } else { 1100 builder.forcedBreak(); 1101 } 1102 token("else"); 1103 visitStatement( 1104 node.getElseStatement(), 1105 CollapseEmptyOrNot.NO, 1106 AllowLeadingBlankLine.YES, 1107 AllowTrailingBlankLine.NO); 1108 } 1109 builder.close(); 1110 return null; 1111 } 1112 1113 @Override 1114 public Void visitImport(ImportTree node, Void unused) { 1115 sync(node); 1116 token("import"); 1117 builder.space(); 1118 if (node.isStatic()) { 1119 token("static"); 1120 builder.space(); 1121 } 1122 visitName(node.getQualifiedIdentifier()); 1123 token(";"); 1124 // TODO(cushon): remove this if https://bugs.openjdk.java.net/browse/JDK-8027682 is fixed 1125 dropEmptyDeclarations(); 1126 return null; 1127 } 1128 1129 @Override 1130 public Void visitBinary(BinaryTree node, Void unused) { 1131 sync(node); 1132 /* 1133 * Collect together all operators with same precedence to clean up indentation. 1134 */ 1135 List<ExpressionTree> operands = new ArrayList<>(); 1136 List<String> operators = new ArrayList<>(); 1137 walkInfix(precedence(node), node, operands, operators); 1138 FillMode fillMode = hasOnlyShortItems(operands) ? INDEPENDENT : UNIFIED; 1139 builder.open(plusFour); 1140 scan(operands.get(0), null); 1141 int operatorsN = operators.size(); 1142 for (int i = 0; i < operatorsN; i++) { 1143 builder.breakOp(fillMode, " ", ZERO); 1144 builder.op(operators.get(i)); 1145 builder.space(); 1146 scan(operands.get(i + 1), null); 1147 } 1148 builder.close(); 1149 return null; 1150 } 1151 1152 @Override 1153 public Void visitInstanceOf(InstanceOfTree node, Void unused) { 1154 sync(node); 1155 builder.open(plusFour); 1156 scan(node.getExpression(), null); 1157 builder.breakOp(" "); 1158 builder.open(ZERO); 1159 token("instanceof"); 1160 builder.breakOp(" "); 1161 scan(node.getType(), null); 1162 builder.close(); 1163 builder.close(); 1164 return null; 1165 } 1166 1167 @Override 1168 public Void visitIntersectionType(IntersectionTypeTree node, Void unused) { 1169 sync(node); 1170 builder.open(plusFour); 1171 boolean first = true; 1172 for (Tree type : node.getBounds()) { 1173 if (!first) { 1174 builder.breakToFill(" "); 1175 token("&"); 1176 builder.space(); 1177 } 1178 scan(type, null); 1179 first = false; 1180 } 1181 builder.close(); 1182 return null; 1183 } 1184 1185 @Override 1186 public Void visitLabeledStatement(LabeledStatementTree node, Void unused) { 1187 sync(node); 1188 builder.open(ZERO); 1189 visit(node.getLabel()); 1190 token(":"); 1191 builder.forcedBreak(); 1192 builder.close(); 1193 scan(node.getStatement(), null); 1194 return null; 1195 } 1196 1197 @Override 1198 public Void visitLambdaExpression(LambdaExpressionTree node, Void unused) { 1199 sync(node); 1200 boolean statementBody = node.getBodyKind() == LambdaExpressionTree.BodyKind.STATEMENT; 1201 boolean parens = builder.peekToken().equals(Optional.of("(")); 1202 builder.open(parens ? plusFour : ZERO); 1203 if (parens) { 1204 token("("); 1205 } 1206 boolean first = true; 1207 for (VariableTree parameter : node.getParameters()) { 1208 if (!first) { 1209 token(","); 1210 builder.breakOp(" "); 1211 } 1212 scan(parameter, null); 1213 first = false; 1214 } 1215 if (parens) { 1216 token(")"); 1217 } 1218 builder.close(); 1219 builder.space(); 1220 builder.op("->"); 1221 builder.open(statementBody ? ZERO : plusFour); 1222 if (statementBody) { 1223 builder.space(); 1224 } else { 1225 builder.breakOp(" "); 1226 } 1227 if (node.getBody().getKind() == Tree.Kind.BLOCK) { 1228 visitBlock( 1229 (BlockTree) node.getBody(), 1230 CollapseEmptyOrNot.YES, 1231 AllowLeadingBlankLine.NO, 1232 AllowTrailingBlankLine.NO); 1233 } else { 1234 scan(node.getBody(), null); 1235 } 1236 builder.close(); 1237 return null; 1238 } 1239 1240 @Override 1241 public Void visitAnnotation(AnnotationTree node, Void unused) { 1242 sync(node); 1243 1244 if (visitSingleMemberAnnotation(node)) { 1245 return null; 1246 } 1247 1248 builder.open(ZERO); 1249 token("@"); 1250 scan(node.getAnnotationType(), null); 1251 if (!node.getArguments().isEmpty()) { 1252 builder.open(plusFour); 1253 token("("); 1254 builder.breakOp(); 1255 boolean first = true; 1256 1257 // Format the member value pairs one-per-line if any of them are 1258 // initialized with arrays. 1259 boolean hasArrayInitializer = 1260 Iterables.any(node.getArguments(), JavaInputAstVisitor::isArrayValue); 1261 for (ExpressionTree argument : node.getArguments()) { 1262 if (!first) { 1263 token(","); 1264 if (hasArrayInitializer) { 1265 builder.forcedBreak(); 1266 } else { 1267 builder.breakOp(" "); 1268 } 1269 } 1270 if (argument instanceof AssignmentTree) { 1271 visitAnnotationArgument((AssignmentTree) argument); 1272 } else { 1273 scan(argument, null); 1274 } 1275 first = false; 1276 } 1277 token(")"); 1278 builder.close(); 1279 builder.close(); 1280 return null; 1281 1282 } else if (builder.peekToken().equals(Optional.of("("))) { 1283 token("("); 1284 token(")"); 1285 } 1286 builder.close(); 1287 return null; 1288 } 1289 1290 private static boolean isArrayValue(ExpressionTree argument) { 1291 if (!(argument instanceof AssignmentTree)) { 1292 return false; 1293 } 1294 ExpressionTree expression = ((AssignmentTree) argument).getExpression(); 1295 return expression instanceof NewArrayTree && ((NewArrayTree) expression).getType() == null; 1296 } 1297 1298 public void visitAnnotationArgument(AssignmentTree node) { 1299 boolean isArrayInitializer = node.getExpression().getKind() == NEW_ARRAY; 1300 sync(node); 1301 builder.open(isArrayInitializer ? ZERO : plusFour); 1302 scan(node.getVariable(), null); 1303 builder.space(); 1304 token("="); 1305 if (isArrayInitializer) { 1306 builder.space(); 1307 } else { 1308 builder.breakOp(" "); 1309 } 1310 scan(node.getExpression(), null); 1311 builder.close(); 1312 } 1313 1314 @Override 1315 public Void visitAnnotatedType(AnnotatedTypeTree node, Void unused) { 1316 sync(node); 1317 ExpressionTree base = node.getUnderlyingType(); 1318 if (base instanceof MemberSelectTree) { 1319 MemberSelectTree selectTree = (MemberSelectTree) base; 1320 scan(selectTree.getExpression(), null); 1321 token("."); 1322 visitAnnotations(node.getAnnotations(), BreakOrNot.NO, BreakOrNot.NO); 1323 builder.breakToFill(" "); 1324 visit(selectTree.getIdentifier()); 1325 } else if (base instanceof ArrayTypeTree) { 1326 visitAnnotatedArrayType(node); 1327 } else { 1328 visitAnnotations(node.getAnnotations(), BreakOrNot.NO, BreakOrNot.NO); 1329 builder.breakToFill(" "); 1330 scan(base, null); 1331 } 1332 return null; 1333 } 1334 1335 // TODO(cushon): Use Flags if/when we drop support for Java 11 1336 1337 protected static final long COMPACT_RECORD_CONSTRUCTOR = 1L << 51; 1338 1339 protected static final long RECORD = 1L << 61; 1340 1341 @Override 1342 public Void visitMethod(MethodTree node, Void unused) { 1343 sync(node); 1344 List<? extends AnnotationTree> annotations = node.getModifiers().getAnnotations(); 1345 List<? extends AnnotationTree> returnTypeAnnotations = ImmutableList.of(); 1346 1347 boolean isRecordConstructor = 1348 (((JCMethodDecl) node).mods.flags & COMPACT_RECORD_CONSTRUCTOR) 1349 == COMPACT_RECORD_CONSTRUCTOR; 1350 1351 if (!node.getTypeParameters().isEmpty() && !annotations.isEmpty()) { 1352 int typeParameterStart = getStartPosition(node.getTypeParameters().get(0)); 1353 for (int i = 0; i < annotations.size(); i++) { 1354 if (getStartPosition(annotations.get(i)) > typeParameterStart) { 1355 returnTypeAnnotations = annotations.subList(i, annotations.size()); 1356 annotations = annotations.subList(0, i); 1357 break; 1358 } 1359 } 1360 } 1361 builder.addAll( 1362 visitModifiers( 1363 annotations, Direction.VERTICAL, /* declarationAnnotationBreak= */ Optional.empty())); 1364 1365 Tree baseReturnType = null; 1366 Deque<List<? extends AnnotationTree>> dims = null; 1367 if (node.getReturnType() != null) { 1368 TypeWithDims extractedDims = 1369 DimensionHelpers.extractDims(node.getReturnType(), SortedDims.YES); 1370 baseReturnType = extractedDims.node; 1371 dims = new ArrayDeque<>(extractedDims.dims); 1372 } 1373 1374 builder.open(plusFour); 1375 BreakTag breakBeforeName = genSym(); 1376 BreakTag breakBeforeType = genSym(); 1377 builder.open(ZERO); 1378 { 1379 boolean first = true; 1380 if (!node.getTypeParameters().isEmpty()) { 1381 token("<"); 1382 typeParametersRest(node.getTypeParameters(), plusFour); 1383 if (!returnTypeAnnotations.isEmpty()) { 1384 builder.breakToFill(" "); 1385 visitAnnotations(returnTypeAnnotations, BreakOrNot.NO, BreakOrNot.NO); 1386 } 1387 first = false; 1388 } 1389 1390 boolean openedNameAndTypeScope = false; 1391 // constructor-like declarations that don't match the name of the enclosing class are 1392 // parsed as method declarations with a null return type 1393 if (baseReturnType != null) { 1394 if (!first) { 1395 builder.breakOp(INDEPENDENT, " ", ZERO, Optional.of(breakBeforeType)); 1396 } else { 1397 first = false; 1398 } 1399 if (!openedNameAndTypeScope) { 1400 builder.open(make(breakBeforeType, plusFour, ZERO)); 1401 openedNameAndTypeScope = true; 1402 } 1403 scan(baseReturnType, null); 1404 maybeAddDims(dims); 1405 } 1406 if (!first) { 1407 builder.breakOp(Doc.FillMode.INDEPENDENT, " ", ZERO, Optional.of(breakBeforeName)); 1408 } else { 1409 first = false; 1410 } 1411 if (!openedNameAndTypeScope) { 1412 builder.open(ZERO); 1413 openedNameAndTypeScope = true; 1414 } 1415 String name = node.getName().toString(); 1416 if (name.equals("<init>")) { 1417 name = builder.peekToken().get(); 1418 } 1419 token(name); 1420 if (!isRecordConstructor) { 1421 token("("); 1422 } 1423 // end of name and type scope 1424 builder.close(); 1425 } 1426 builder.close(); 1427 1428 builder.open(Indent.If.make(breakBeforeName, plusFour, ZERO)); 1429 builder.open(Indent.If.make(breakBeforeType, plusFour, ZERO)); 1430 builder.open(ZERO); 1431 { 1432 if (!isRecordConstructor) { 1433 if (!node.getParameters().isEmpty() || node.getReceiverParameter() != null) { 1434 // Break before args. 1435 builder.breakToFill(""); 1436 visitFormals(Optional.ofNullable(node.getReceiverParameter()), node.getParameters()); 1437 } 1438 token(")"); 1439 } 1440 if (dims != null) { 1441 maybeAddDims(dims); 1442 } 1443 if (!node.getThrows().isEmpty()) { 1444 builder.breakToFill(" "); 1445 builder.open(plusFour); 1446 { 1447 visitThrowsClause(node.getThrows()); 1448 } 1449 builder.close(); 1450 } 1451 if (node.getDefaultValue() != null) { 1452 builder.space(); 1453 token("default"); 1454 if (node.getDefaultValue().getKind() == Tree.Kind.NEW_ARRAY) { 1455 builder.open(minusFour); 1456 { 1457 builder.space(); 1458 scan(node.getDefaultValue(), null); 1459 } 1460 builder.close(); 1461 } else { 1462 builder.open(ZERO); 1463 { 1464 builder.breakToFill(" "); 1465 scan(node.getDefaultValue(), null); 1466 } 1467 builder.close(); 1468 } 1469 } 1470 } 1471 builder.close(); 1472 builder.close(); 1473 builder.close(); 1474 if (node.getBody() == null) { 1475 token(";"); 1476 } else { 1477 builder.space(); 1478 builder.token("{", Doc.Token.RealOrImaginary.REAL, plusTwo, Optional.of(plusTwo)); 1479 } 1480 builder.close(); 1481 1482 if (node.getBody() != null) { 1483 methodBody(node); 1484 } 1485 1486 return null; 1487 } 1488 1489 private void methodBody(MethodTree node) { 1490 if (node.getBody().getStatements().isEmpty()) { 1491 builder.blankLineWanted(BlankLineWanted.NO); 1492 } else { 1493 builder.open(plusTwo); 1494 builder.forcedBreak(); 1495 builder.blankLineWanted(BlankLineWanted.PRESERVE); 1496 visitStatements(node.getBody().getStatements()); 1497 builder.close(); 1498 builder.forcedBreak(); 1499 builder.blankLineWanted(BlankLineWanted.NO); 1500 markForPartialFormat(); 1501 } 1502 token("}", plusTwo); 1503 } 1504 1505 @Override 1506 public Void visitMethodInvocation(MethodInvocationTree node, Void unused) { 1507 sync(node); 1508 if (handleLogStatement(node)) { 1509 return null; 1510 } 1511 visitDot(node); 1512 return null; 1513 } 1514 1515 /** 1516 * Special-cases log statements, to output: 1517 * 1518 * <pre>{@code 1519 * logger.atInfo().log( 1520 * "Number of foos: %d, foos.size()); 1521 * }</pre> 1522 * 1523 * <p>Instead of: 1524 * 1525 * <pre>{@code 1526 * logger 1527 * .atInfo() 1528 * .log( 1529 * "Number of foos: %d, foos.size()); 1530 * }</pre> 1531 */ 1532 private boolean handleLogStatement(MethodInvocationTree node) { 1533 if (!getMethodName(node).contentEquals("log")) { 1534 return false; 1535 } 1536 Deque<ExpressionTree> parts = new ArrayDeque<>(); 1537 ExpressionTree curr = node; 1538 while (curr instanceof MethodInvocationTree) { 1539 MethodInvocationTree method = (MethodInvocationTree) curr; 1540 parts.addFirst(method); 1541 if (!LOG_METHODS.contains(getMethodName(method).toString())) { 1542 return false; 1543 } 1544 curr = Trees.getMethodReceiver(method); 1545 } 1546 if (!(curr instanceof IdentifierTree)) { 1547 return false; 1548 } 1549 parts.addFirst(curr); 1550 visitDotWithPrefix( 1551 ImmutableList.copyOf(parts), false, ImmutableList.of(parts.size() - 1), INDEPENDENT); 1552 return true; 1553 } 1554 1555 static final ImmutableSet<String> LOG_METHODS = 1556 ImmutableSet.of( 1557 "at", 1558 "atConfig", 1559 "atDebug", 1560 "atFine", 1561 "atFiner", 1562 "atFinest", 1563 "atInfo", 1564 "atMostEvery", 1565 "atSevere", 1566 "atWarning", 1567 "every", 1568 "log", 1569 "logVarargs", 1570 "perUnique", 1571 "withCause", 1572 "withStackTrace"); 1573 1574 private static List<Long> handleStream(List<ExpressionTree> parts) { 1575 return indexes( 1576 parts.stream(), 1577 p -> { 1578 if (!(p instanceof MethodInvocationTree)) { 1579 return false; 1580 } 1581 Name name = getMethodName((MethodInvocationTree) p); 1582 return Stream.of("stream", "parallelStream", "toBuilder") 1583 .anyMatch(name::contentEquals); 1584 }) 1585 .collect(toList()); 1586 } 1587 1588 private static <T> Stream<Long> indexes(Stream<T> stream, Predicate<T> predicate) { 1589 return Streams.mapWithIndex(stream, (x, i) -> predicate.apply(x) ? i : -1).filter(x -> x != -1); 1590 } 1591 1592 @Override 1593 public Void visitMemberSelect(MemberSelectTree node, Void unused) { 1594 sync(node); 1595 visitDot(node); 1596 return null; 1597 } 1598 1599 @Override 1600 public Void visitLiteral(LiteralTree node, Void unused) { 1601 sync(node); 1602 String sourceForNode = getSourceForNode(node, getCurrentPath()); 1603 // A negative numeric literal -n is usually represented as unary minus on n, 1604 // but that doesn't work for integer or long MIN_VALUE. The parser works 1605 // around that by representing it directly as a signed literal (with no 1606 // unary minus), but the lexer still expects two tokens. 1607 if (sourceForNode.startsWith("-")) { 1608 token("-"); 1609 sourceForNode = sourceForNode.substring(1).trim(); 1610 } 1611 token(sourceForNode); 1612 return null; 1613 } 1614 1615 private void visitPackage( 1616 ExpressionTree packageName, List<? extends AnnotationTree> packageAnnotations) { 1617 if (!packageAnnotations.isEmpty()) { 1618 for (AnnotationTree annotation : packageAnnotations) { 1619 builder.forcedBreak(); 1620 scan(annotation, null); 1621 } 1622 builder.forcedBreak(); 1623 } 1624 builder.open(plusFour); 1625 token("package"); 1626 builder.space(); 1627 visitName(packageName); 1628 builder.close(); 1629 token(";"); 1630 } 1631 1632 @Override 1633 public Void visitParameterizedType(ParameterizedTypeTree node, Void unused) { 1634 sync(node); 1635 if (node.getTypeArguments().isEmpty()) { 1636 scan(node.getType(), null); 1637 token("<"); 1638 token(">"); 1639 } else { 1640 builder.open(plusFour); 1641 scan(node.getType(), null); 1642 token("<"); 1643 builder.breakOp(); 1644 builder.open(ZERO); 1645 boolean first = true; 1646 for (Tree typeArgument : node.getTypeArguments()) { 1647 if (!first) { 1648 token(","); 1649 builder.breakOp(" "); 1650 } 1651 scan(typeArgument, null); 1652 first = false; 1653 } 1654 builder.close(); 1655 builder.close(); 1656 token(">"); 1657 } 1658 return null; 1659 } 1660 1661 @Override 1662 public Void visitParenthesized(ParenthesizedTree node, Void unused) { 1663 token("("); 1664 scan(node.getExpression(), null); 1665 token(")"); 1666 return null; 1667 } 1668 1669 @Override 1670 public Void visitUnary(UnaryTree node, Void unused) { 1671 sync(node); 1672 String operatorName = operatorName(node); 1673 if (((JCTree) node).getTag().isPostUnaryOp()) { 1674 scan(node.getExpression(), null); 1675 splitToken(operatorName); 1676 } else { 1677 splitToken(operatorName); 1678 if (ambiguousUnaryOperator(node, operatorName)) { 1679 builder.space(); 1680 } 1681 scan(node.getExpression(), null); 1682 } 1683 return null; 1684 } 1685 1686 private void splitToken(String operatorName) { 1687 for (int i = 0; i < operatorName.length(); i++) { 1688 token(String.valueOf(operatorName.charAt(i))); 1689 } 1690 } 1691 1692 private boolean ambiguousUnaryOperator(UnaryTree node, String operatorName) { 1693 switch (node.getKind()) { 1694 case UNARY_MINUS: 1695 case UNARY_PLUS: 1696 break; 1697 default: 1698 return false; 1699 } 1700 if (!(node.getExpression() instanceof UnaryTree)) { 1701 return false; 1702 } 1703 JCTree.Tag tag = ((JCTree) node.getExpression()).getTag(); 1704 if (tag.isPostUnaryOp()) { 1705 return false; 1706 } 1707 if (!operatorName(node).startsWith(operatorName)) { 1708 return false; 1709 } 1710 return true; 1711 } 1712 1713 @Override 1714 public Void visitPrimitiveType(PrimitiveTypeTree node, Void unused) { 1715 sync(node); 1716 switch (node.getPrimitiveTypeKind()) { 1717 case BOOLEAN: 1718 token("boolean"); 1719 break; 1720 case BYTE: 1721 token("byte"); 1722 break; 1723 case SHORT: 1724 token("short"); 1725 break; 1726 case INT: 1727 token("int"); 1728 break; 1729 case LONG: 1730 token("long"); 1731 break; 1732 case CHAR: 1733 token("char"); 1734 break; 1735 case FLOAT: 1736 token("float"); 1737 break; 1738 case DOUBLE: 1739 token("double"); 1740 break; 1741 case VOID: 1742 token("void"); 1743 break; 1744 default: 1745 throw new AssertionError(node.getPrimitiveTypeKind()); 1746 } 1747 return null; 1748 } 1749 1750 public boolean visit(Name name) { 1751 token(name.toString()); 1752 return false; 1753 } 1754 1755 @Override 1756 public Void visitReturn(ReturnTree node, Void unused) { 1757 sync(node); 1758 token("return"); 1759 if (node.getExpression() != null) { 1760 builder.space(); 1761 scan(node.getExpression(), null); 1762 } 1763 token(";"); 1764 return null; 1765 } 1766 1767 // TODO(cushon): is this worth special-casing? 1768 boolean visitSingleMemberAnnotation(AnnotationTree node) { 1769 if (node.getArguments().size() != 1) { 1770 return false; 1771 } 1772 ExpressionTree value = getOnlyElement(node.getArguments()); 1773 if (value.getKind() == ASSIGNMENT) { 1774 return false; 1775 } 1776 boolean isArrayInitializer = value.getKind() == NEW_ARRAY; 1777 builder.open(isArrayInitializer ? ZERO : plusFour); 1778 token("@"); 1779 scan(node.getAnnotationType(), null); 1780 token("("); 1781 if (!isArrayInitializer) { 1782 builder.breakOp(); 1783 } 1784 scan(value, null); 1785 builder.close(); 1786 token(")"); 1787 return true; 1788 } 1789 1790 @Override 1791 public Void visitCase(CaseTree node, Void unused) { 1792 sync(node); 1793 markForPartialFormat(); 1794 builder.forcedBreak(); 1795 if (node.getExpression() == null) { 1796 token("default", plusTwo); 1797 token(":"); 1798 } else { 1799 token("case", plusTwo); 1800 builder.space(); 1801 scan(node.getExpression(), null); 1802 token(":"); 1803 } 1804 builder.open(plusTwo); 1805 visitStatements(node.getStatements()); 1806 builder.close(); 1807 return null; 1808 } 1809 1810 @Override 1811 public Void visitSwitch(SwitchTree node, Void unused) { 1812 sync(node); 1813 visitSwitch(node.getExpression(), node.getCases()); 1814 return null; 1815 } 1816 1817 protected void visitSwitch(ExpressionTree expression, List<? extends CaseTree> cases) { 1818 token("switch"); 1819 builder.space(); 1820 token("("); 1821 scan(skipParen(expression), null); 1822 token(")"); 1823 builder.space(); 1824 tokenBreakTrailingComment("{", plusTwo); 1825 builder.blankLineWanted(BlankLineWanted.NO); 1826 builder.open(plusTwo); 1827 boolean first = true; 1828 for (CaseTree caseTree : cases) { 1829 if (!first) { 1830 builder.blankLineWanted(BlankLineWanted.PRESERVE); 1831 } 1832 scan(caseTree, null); 1833 first = false; 1834 } 1835 builder.close(); 1836 builder.forcedBreak(); 1837 builder.blankLineWanted(BlankLineWanted.NO); 1838 token("}", plusFour); 1839 } 1840 1841 @Override 1842 public Void visitSynchronized(SynchronizedTree node, Void unused) { 1843 sync(node); 1844 token("synchronized"); 1845 builder.space(); 1846 token("("); 1847 builder.open(plusFour); 1848 builder.breakOp(); 1849 scan(skipParen(node.getExpression()), null); 1850 builder.close(); 1851 token(")"); 1852 builder.space(); 1853 scan(node.getBlock(), null); 1854 return null; 1855 } 1856 1857 @Override 1858 public Void visitThrow(ThrowTree node, Void unused) { 1859 sync(node); 1860 token("throw"); 1861 builder.space(); 1862 scan(node.getExpression(), null); 1863 token(";"); 1864 return null; 1865 } 1866 1867 @Override 1868 public Void visitTry(TryTree node, Void unused) { 1869 sync(node); 1870 builder.open(ZERO); 1871 token("try"); 1872 builder.space(); 1873 if (!node.getResources().isEmpty()) { 1874 token("("); 1875 builder.open(node.getResources().size() > 1 ? plusFour : ZERO); 1876 boolean first = true; 1877 for (Tree resource : node.getResources()) { 1878 if (!first) { 1879 builder.forcedBreak(); 1880 } 1881 if (resource instanceof VariableTree) { 1882 VariableTree variableTree = (VariableTree) resource; 1883 declareOne( 1884 DeclarationKind.PARAMETER, 1885 fieldAnnotationDirection(variableTree.getModifiers()), 1886 Optional.of(variableTree.getModifiers()), 1887 variableTree.getType(), 1888 /* name= */ variableTree.getName(), 1889 "", 1890 "=", 1891 Optional.ofNullable(variableTree.getInitializer()), 1892 /* trailing= */ Optional.empty(), 1893 /* receiverExpression= */ Optional.empty(), 1894 /* typeWithDims= */ Optional.empty()); 1895 } else { 1896 // TODO(cushon): think harder about what to do with `try (resource1; resource2) {}` 1897 scan(resource, null); 1898 } 1899 if (builder.peekToken().equals(Optional.of(";"))) { 1900 token(";"); 1901 builder.space(); 1902 } 1903 first = false; 1904 } 1905 if (builder.peekToken().equals(Optional.of(";"))) { 1906 token(";"); 1907 builder.space(); 1908 } 1909 token(")"); 1910 builder.close(); 1911 builder.space(); 1912 } 1913 // An empty try-with-resources body can collapse to "{}" if there are no trailing catch or 1914 // finally blocks. 1915 boolean trailingClauses = !node.getCatches().isEmpty() || node.getFinallyBlock() != null; 1916 visitBlock( 1917 node.getBlock(), 1918 CollapseEmptyOrNot.valueOf(!trailingClauses), 1919 AllowLeadingBlankLine.YES, 1920 AllowTrailingBlankLine.valueOf(trailingClauses)); 1921 for (int i = 0; i < node.getCatches().size(); i++) { 1922 CatchTree catchClause = node.getCatches().get(i); 1923 trailingClauses = i < node.getCatches().size() - 1 || node.getFinallyBlock() != null; 1924 visitCatchClause(catchClause, AllowTrailingBlankLine.valueOf(trailingClauses)); 1925 } 1926 if (node.getFinallyBlock() != null) { 1927 builder.space(); 1928 token("finally"); 1929 builder.space(); 1930 visitBlock( 1931 node.getFinallyBlock(), 1932 CollapseEmptyOrNot.NO, 1933 AllowLeadingBlankLine.YES, 1934 AllowTrailingBlankLine.NO); 1935 } 1936 builder.close(); 1937 return null; 1938 } 1939 1940 public void visitClassDeclaration(ClassTree node) { 1941 sync(node); 1942 List<Op> breaks = 1943 visitModifiers( 1944 node.getModifiers(), 1945 Direction.VERTICAL, 1946 /* declarationAnnotationBreak= */ Optional.empty()); 1947 boolean hasSuperclassType = node.getExtendsClause() != null; 1948 boolean hasSuperInterfaceTypes = !node.getImplementsClause().isEmpty(); 1949 builder.addAll(breaks); 1950 token(node.getKind() == Tree.Kind.INTERFACE ? "interface" : "class"); 1951 builder.space(); 1952 visit(node.getSimpleName()); 1953 if (!node.getTypeParameters().isEmpty()) { 1954 token("<"); 1955 } 1956 builder.open(plusFour); 1957 { 1958 if (!node.getTypeParameters().isEmpty()) { 1959 typeParametersRest( 1960 node.getTypeParameters(), 1961 hasSuperclassType || hasSuperInterfaceTypes ? plusFour : ZERO); 1962 } 1963 if (hasSuperclassType) { 1964 builder.breakToFill(" "); 1965 token("extends"); 1966 builder.space(); 1967 scan(node.getExtendsClause(), null); 1968 } 1969 if (hasSuperInterfaceTypes) { 1970 builder.breakToFill(" "); 1971 builder.open(node.getImplementsClause().size() > 1 ? plusFour : ZERO); 1972 token(node.getKind() == Tree.Kind.INTERFACE ? "extends" : "implements"); 1973 builder.space(); 1974 boolean first = true; 1975 for (Tree superInterfaceType : node.getImplementsClause()) { 1976 if (!first) { 1977 token(","); 1978 builder.breakOp(" "); 1979 } 1980 scan(superInterfaceType, null); 1981 first = false; 1982 } 1983 builder.close(); 1984 } 1985 } 1986 builder.close(); 1987 if (node.getMembers() == null) { 1988 token(";"); 1989 } else { 1990 addBodyDeclarations(node.getMembers(), BracesOrNot.YES, FirstDeclarationsOrNot.YES); 1991 } 1992 dropEmptyDeclarations(); 1993 } 1994 1995 @Override 1996 public Void visitTypeParameter(TypeParameterTree node, Void unused) { 1997 sync(node); 1998 builder.open(ZERO); 1999 visitAnnotations(node.getAnnotations(), BreakOrNot.NO, BreakOrNot.YES); 2000 visit(node.getName()); 2001 if (!node.getBounds().isEmpty()) { 2002 builder.space(); 2003 token("extends"); 2004 builder.open(plusFour); 2005 builder.breakOp(" "); 2006 builder.open(plusFour); 2007 boolean first = true; 2008 for (Tree typeBound : node.getBounds()) { 2009 if (!first) { 2010 builder.breakToFill(" "); 2011 token("&"); 2012 builder.space(); 2013 } 2014 scan(typeBound, null); 2015 first = false; 2016 } 2017 builder.close(); 2018 builder.close(); 2019 } 2020 builder.close(); 2021 return null; 2022 } 2023 2024 @Override 2025 public Void visitUnionType(UnionTypeTree node, Void unused) { 2026 throw new IllegalStateException("expected manual descent into union types"); 2027 } 2028 2029 @Override 2030 public Void visitWhileLoop(WhileLoopTree node, Void unused) { 2031 sync(node); 2032 token("while"); 2033 builder.space(); 2034 token("("); 2035 scan(skipParen(node.getCondition()), null); 2036 token(")"); 2037 visitStatement( 2038 node.getStatement(), 2039 CollapseEmptyOrNot.YES, 2040 AllowLeadingBlankLine.YES, 2041 AllowTrailingBlankLine.NO); 2042 return null; 2043 } 2044 2045 @Override 2046 public Void visitWildcard(WildcardTree node, Void unused) { 2047 sync(node); 2048 builder.open(ZERO); 2049 token("?"); 2050 if (node.getBound() != null) { 2051 builder.open(plusFour); 2052 builder.space(); 2053 token(node.getKind() == EXTENDS_WILDCARD ? "extends" : "super"); 2054 builder.breakOp(" "); 2055 scan(node.getBound(), null); 2056 builder.close(); 2057 } 2058 builder.close(); 2059 return null; 2060 } 2061 2062 // Helper methods. 2063 2064 /** Helper method for annotations. */ 2065 void visitAnnotations( 2066 List<? extends AnnotationTree> annotations, BreakOrNot breakBefore, BreakOrNot breakAfter) { 2067 if (!annotations.isEmpty()) { 2068 if (breakBefore.isYes()) { 2069 builder.breakToFill(" "); 2070 } 2071 boolean first = true; 2072 for (AnnotationTree annotation : annotations) { 2073 if (!first) { 2074 builder.breakToFill(" "); 2075 } 2076 scan(annotation, null); 2077 first = false; 2078 } 2079 if (breakAfter.isYes()) { 2080 builder.breakToFill(" "); 2081 } 2082 } 2083 } 2084 2085 /** Helper method for blocks. */ 2086 private void visitBlock( 2087 BlockTree node, 2088 CollapseEmptyOrNot collapseEmptyOrNot, 2089 AllowLeadingBlankLine allowLeadingBlankLine, 2090 AllowTrailingBlankLine allowTrailingBlankLine) { 2091 sync(node); 2092 if (node.isStatic()) { 2093 token("static"); 2094 builder.space(); 2095 } 2096 if (collapseEmptyOrNot.isYes() && node.getStatements().isEmpty()) { 2097 if (builder.peekToken().equals(Optional.of(";"))) { 2098 // TODO(cushon): is this needed? 2099 token(";"); 2100 } else { 2101 tokenBreakTrailingComment("{", plusTwo); 2102 builder.blankLineWanted(BlankLineWanted.NO); 2103 token("}", plusTwo); 2104 } 2105 } else { 2106 builder.open(ZERO); 2107 builder.open(plusTwo); 2108 tokenBreakTrailingComment("{", plusTwo); 2109 if (allowLeadingBlankLine == AllowLeadingBlankLine.NO) { 2110 builder.blankLineWanted(BlankLineWanted.NO); 2111 } else { 2112 builder.blankLineWanted(BlankLineWanted.PRESERVE); 2113 } 2114 visitStatements(node.getStatements()); 2115 builder.close(); 2116 builder.forcedBreak(); 2117 builder.close(); 2118 if (allowTrailingBlankLine == AllowTrailingBlankLine.NO) { 2119 builder.blankLineWanted(BlankLineWanted.NO); 2120 } else { 2121 builder.blankLineWanted(BlankLineWanted.PRESERVE); 2122 } 2123 markForPartialFormat(); 2124 token("}", plusTwo); 2125 } 2126 } 2127 2128 /** Helper method for statements. */ 2129 private void visitStatement( 2130 StatementTree node, 2131 CollapseEmptyOrNot collapseEmptyOrNot, 2132 AllowLeadingBlankLine allowLeadingBlank, 2133 AllowTrailingBlankLine allowTrailingBlank) { 2134 sync(node); 2135 switch (node.getKind()) { 2136 case BLOCK: 2137 builder.space(); 2138 visitBlock((BlockTree) node, collapseEmptyOrNot, allowLeadingBlank, allowTrailingBlank); 2139 break; 2140 default: 2141 builder.open(plusTwo); 2142 builder.breakOp(" "); 2143 scan(node, null); 2144 builder.close(); 2145 } 2146 } 2147 2148 protected void visitStatements(List<? extends StatementTree> statements) { 2149 boolean first = true; 2150 PeekingIterator<StatementTree> it = Iterators.peekingIterator(statements.iterator()); 2151 dropEmptyDeclarations(); 2152 while (it.hasNext()) { 2153 StatementTree tree = it.next(); 2154 builder.forcedBreak(); 2155 if (!first) { 2156 builder.blankLineWanted(BlankLineWanted.PRESERVE); 2157 } 2158 markForPartialFormat(); 2159 first = false; 2160 List<VariableTree> fragments = variableFragments(it, tree); 2161 if (!fragments.isEmpty()) { 2162 visitVariables( 2163 fragments, 2164 DeclarationKind.NONE, 2165 canLocalHaveHorizontalAnnotations(fragments.get(0).getModifiers())); 2166 } else { 2167 scan(tree, null); 2168 } 2169 } 2170 } 2171 2172 /** Output combined modifiers and annotations and the trailing break. */ 2173 void visitAndBreakModifiers( 2174 ModifiersTree modifiers, 2175 Direction annotationDirection, 2176 Optional<BreakTag> declarationAnnotationBreak) { 2177 builder.addAll(visitModifiers(modifiers, annotationDirection, declarationAnnotationBreak)); 2178 } 2179 2180 @Override 2181 public Void visitModifiers(ModifiersTree node, Void unused) { 2182 throw new IllegalStateException("expected manual descent into modifiers"); 2183 } 2184 2185 /** Output combined modifiers and annotations and returns the trailing break. */ 2186 protected List<Op> visitModifiers( 2187 ModifiersTree modifiersTree, 2188 Direction annotationsDirection, 2189 Optional<BreakTag> declarationAnnotationBreak) { 2190 return visitModifiers( 2191 modifiersTree.getAnnotations(), annotationsDirection, declarationAnnotationBreak); 2192 } 2193 2194 protected List<Op> visitModifiers( 2195 List<? extends AnnotationTree> annotationTrees, 2196 Direction annotationsDirection, 2197 Optional<BreakTag> declarationAnnotationBreak) { 2198 if (annotationTrees.isEmpty() && !nextIsModifier()) { 2199 return EMPTY_LIST; 2200 } 2201 Deque<AnnotationTree> annotations = new ArrayDeque<>(annotationTrees); 2202 builder.open(ZERO); 2203 boolean first = true; 2204 boolean lastWasAnnotation = false; 2205 while (!annotations.isEmpty()) { 2206 if (nextIsModifier()) { 2207 break; 2208 } 2209 if (!first) { 2210 builder.addAll( 2211 annotationsDirection.isVertical() 2212 ? forceBreakList(declarationAnnotationBreak) 2213 : breakList(declarationAnnotationBreak)); 2214 } 2215 scan(annotations.removeFirst(), null); 2216 first = false; 2217 lastWasAnnotation = true; 2218 } 2219 builder.close(); 2220 ImmutableList<Op> trailingBreak = 2221 annotationsDirection.isVertical() 2222 ? forceBreakList(declarationAnnotationBreak) 2223 : breakList(declarationAnnotationBreak); 2224 if (annotations.isEmpty() && !nextIsModifier()) { 2225 return trailingBreak; 2226 } 2227 if (lastWasAnnotation) { 2228 builder.addAll(trailingBreak); 2229 } 2230 2231 builder.open(ZERO); 2232 first = true; 2233 while (nextIsModifier() || !annotations.isEmpty()) { 2234 if (!first) { 2235 builder.addAll(breakFillList(Optional.empty())); 2236 } 2237 if (nextIsModifier()) { 2238 token(builder.peekToken().get()); 2239 } else { 2240 scan(annotations.removeFirst(), null); 2241 lastWasAnnotation = true; 2242 } 2243 first = false; 2244 } 2245 builder.close(); 2246 return breakFillList(Optional.empty()); 2247 } 2248 2249 boolean nextIsModifier() { 2250 switch (builder.peekToken().get()) { 2251 case "public": 2252 case "protected": 2253 case "private": 2254 case "abstract": 2255 case "static": 2256 case "final": 2257 case "transient": 2258 case "volatile": 2259 case "synchronized": 2260 case "native": 2261 case "strictfp": 2262 case "default": 2263 return true; 2264 default: 2265 return false; 2266 } 2267 } 2268 2269 @Override 2270 public Void visitCatch(CatchTree node, Void unused) { 2271 throw new IllegalStateException("expected manual descent into catch trees"); 2272 } 2273 2274 /** Helper method for {@link CatchTree}s. */ 2275 private void visitCatchClause(CatchTree node, AllowTrailingBlankLine allowTrailingBlankLine) { 2276 sync(node); 2277 builder.space(); 2278 token("catch"); 2279 builder.space(); 2280 token("("); 2281 builder.open(plusFour); 2282 VariableTree ex = node.getParameter(); 2283 if (ex.getType().getKind() == UNION_TYPE) { 2284 builder.open(ZERO); 2285 visitUnionType(ex); 2286 builder.close(); 2287 } else { 2288 // TODO(cushon): don't break after here for consistency with for, while, etc. 2289 builder.breakToFill(); 2290 builder.open(ZERO); 2291 scan(ex, null); 2292 builder.close(); 2293 } 2294 builder.close(); 2295 token(")"); 2296 builder.space(); 2297 visitBlock( 2298 node.getBlock(), CollapseEmptyOrNot.NO, AllowLeadingBlankLine.YES, allowTrailingBlankLine); 2299 } 2300 2301 /** Formats a union type declaration in a catch clause. */ 2302 private void visitUnionType(VariableTree declaration) { 2303 UnionTypeTree type = (UnionTypeTree) declaration.getType(); 2304 builder.open(ZERO); 2305 sync(declaration); 2306 visitAndBreakModifiers( 2307 declaration.getModifiers(), 2308 Direction.HORIZONTAL, 2309 /* declarationAnnotationBreak= */ Optional.empty()); 2310 List<? extends Tree> union = type.getTypeAlternatives(); 2311 boolean first = true; 2312 for (int i = 0; i < union.size() - 1; i++) { 2313 if (!first) { 2314 builder.breakOp(" "); 2315 token("|"); 2316 builder.space(); 2317 } else { 2318 first = false; 2319 } 2320 scan(union.get(i), null); 2321 } 2322 builder.breakOp(" "); 2323 token("|"); 2324 builder.space(); 2325 Tree last = union.get(union.size() - 1); 2326 declareOne( 2327 DeclarationKind.NONE, 2328 Direction.HORIZONTAL, 2329 /* modifiers= */ Optional.empty(), 2330 last, 2331 /* name= */ declaration.getName(), 2332 /* op= */ "", 2333 "=", 2334 Optional.ofNullable(declaration.getInitializer()), 2335 /* trailing= */ Optional.empty(), 2336 /* receiverExpression= */ Optional.empty(), 2337 /* typeWithDims= */ Optional.empty()); 2338 builder.close(); 2339 } 2340 2341 /** Accumulate the operands and operators. */ 2342 private static void walkInfix( 2343 int precedence, 2344 ExpressionTree expression, 2345 List<ExpressionTree> operands, 2346 List<String> operators) { 2347 if (expression instanceof BinaryTree) { 2348 BinaryTree binaryTree = (BinaryTree) expression; 2349 if (precedence(binaryTree) == precedence) { 2350 walkInfix(precedence, binaryTree.getLeftOperand(), operands, operators); 2351 operators.add(operatorName(expression)); 2352 walkInfix(precedence, binaryTree.getRightOperand(), operands, operators); 2353 } else { 2354 operands.add(expression); 2355 } 2356 } else { 2357 operands.add(expression); 2358 } 2359 } 2360 2361 protected void visitFormals( 2362 Optional<VariableTree> receiver, List<? extends VariableTree> parameters) { 2363 if (!receiver.isPresent() && parameters.isEmpty()) { 2364 return; 2365 } 2366 builder.open(ZERO); 2367 boolean first = true; 2368 if (receiver.isPresent()) { 2369 // TODO(jdd): Use builders. 2370 declareOne( 2371 DeclarationKind.PARAMETER, 2372 Direction.HORIZONTAL, 2373 Optional.of(receiver.get().getModifiers()), 2374 receiver.get().getType(), 2375 /* name= */ receiver.get().getName(), 2376 "", 2377 "", 2378 /* initializer= */ Optional.empty(), 2379 !parameters.isEmpty() ? Optional.of(",") : Optional.empty(), 2380 Optional.of(receiver.get().getNameExpression()), 2381 /* typeWithDims= */ Optional.empty()); 2382 first = false; 2383 } 2384 for (int i = 0; i < parameters.size(); i++) { 2385 VariableTree parameter = parameters.get(i); 2386 if (!first) { 2387 builder.breakOp(" "); 2388 } 2389 visitToDeclare( 2390 DeclarationKind.PARAMETER, 2391 Direction.HORIZONTAL, 2392 parameter, 2393 /* initializer= */ Optional.empty(), 2394 "=", 2395 i < parameters.size() - 1 ? Optional.of(",") : /* a= */ Optional.empty()); 2396 first = false; 2397 } 2398 builder.close(); 2399 } 2400 2401 // /** Helper method for {@link MethodDeclaration}s. */ 2402 private void visitThrowsClause(List<? extends ExpressionTree> thrownExceptionTypes) { 2403 token("throws"); 2404 builder.breakToFill(" "); 2405 boolean first = true; 2406 for (ExpressionTree thrownExceptionType : thrownExceptionTypes) { 2407 if (!first) { 2408 token(","); 2409 builder.breakToFill(" "); 2410 } 2411 scan(thrownExceptionType, null); 2412 first = false; 2413 } 2414 } 2415 2416 @Override 2417 public Void visitIdentifier(IdentifierTree node, Void unused) { 2418 sync(node); 2419 token(node.getName().toString()); 2420 return null; 2421 } 2422 2423 @Override 2424 public Void visitModule(ModuleTree node, Void unused) { 2425 for (AnnotationTree annotation : node.getAnnotations()) { 2426 scan(annotation, null); 2427 builder.forcedBreak(); 2428 } 2429 if (node.getModuleType() == ModuleTree.ModuleKind.OPEN) { 2430 token("open"); 2431 builder.space(); 2432 } 2433 token("module"); 2434 builder.space(); 2435 scan(node.getName(), null); 2436 builder.space(); 2437 if (node.getDirectives().isEmpty()) { 2438 tokenBreakTrailingComment("{", plusTwo); 2439 builder.blankLineWanted(BlankLineWanted.NO); 2440 token("}", plusTwo); 2441 } else { 2442 builder.open(plusTwo); 2443 token("{"); 2444 builder.forcedBreak(); 2445 Optional<Tree.Kind> previousDirective = Optional.empty(); 2446 for (DirectiveTree directiveTree : node.getDirectives()) { 2447 markForPartialFormat(); 2448 builder.blankLineWanted( 2449 previousDirective.map(k -> !k.equals(directiveTree.getKind())).orElse(false) 2450 ? BlankLineWanted.YES 2451 : BlankLineWanted.NO); 2452 builder.forcedBreak(); 2453 scan(directiveTree, null); 2454 previousDirective = Optional.of(directiveTree.getKind()); 2455 } 2456 builder.close(); 2457 builder.forcedBreak(); 2458 token("}"); 2459 } 2460 return null; 2461 } 2462 2463 private void visitDirective( 2464 String name, 2465 String separator, 2466 ExpressionTree nameExpression, 2467 @Nullable List<? extends ExpressionTree> items) { 2468 token(name); 2469 builder.space(); 2470 scan(nameExpression, null); 2471 if (items != null) { 2472 builder.open(plusFour); 2473 builder.space(); 2474 token(separator); 2475 builder.forcedBreak(); 2476 boolean first = true; 2477 for (ExpressionTree item : items) { 2478 if (!first) { 2479 token(","); 2480 builder.forcedBreak(); 2481 } 2482 scan(item, null); 2483 first = false; 2484 } 2485 token(";"); 2486 builder.close(); 2487 } else { 2488 token(";"); 2489 } 2490 } 2491 2492 @Override 2493 public Void visitExports(ExportsTree node, Void unused) { 2494 visitDirective("exports", "to", node.getPackageName(), node.getModuleNames()); 2495 return null; 2496 } 2497 2498 @Override 2499 public Void visitOpens(OpensTree node, Void unused) { 2500 visitDirective("opens", "to", node.getPackageName(), node.getModuleNames()); 2501 return null; 2502 } 2503 2504 @Override 2505 public Void visitProvides(ProvidesTree node, Void unused) { 2506 visitDirective("provides", "with", node.getServiceName(), node.getImplementationNames()); 2507 return null; 2508 } 2509 2510 @Override 2511 public Void visitRequires(RequiresTree node, Void unused) { 2512 token("requires"); 2513 builder.space(); 2514 while (true) { 2515 if (builder.peekToken().equals(Optional.of("static"))) { 2516 token("static"); 2517 builder.space(); 2518 } else if (builder.peekToken().equals(Optional.of("transitive"))) { 2519 token("transitive"); 2520 builder.space(); 2521 } else { 2522 break; 2523 } 2524 } 2525 scan(node.getModuleName(), null); 2526 token(";"); 2527 return null; 2528 } 2529 2530 @Override 2531 public Void visitUses(UsesTree node, Void unused) { 2532 token("uses"); 2533 builder.space(); 2534 scan(node.getServiceName(), null); 2535 token(";"); 2536 return null; 2537 } 2538 2539 /** Helper method for import declarations, names, and qualified names. */ 2540 private void visitName(Tree node) { 2541 Deque<Name> stack = new ArrayDeque<>(); 2542 for (; node instanceof MemberSelectTree; node = ((MemberSelectTree) node).getExpression()) { 2543 stack.addFirst(((MemberSelectTree) node).getIdentifier()); 2544 } 2545 stack.addFirst(((IdentifierTree) node).getName()); 2546 boolean first = true; 2547 for (Name name : stack) { 2548 if (!first) { 2549 token("."); 2550 } 2551 token(name.toString()); 2552 first = false; 2553 } 2554 } 2555 2556 private void visitToDeclare( 2557 DeclarationKind kind, 2558 Direction annotationsDirection, 2559 VariableTree node, 2560 Optional<ExpressionTree> initializer, 2561 String equals, 2562 Optional<String> trailing) { 2563 sync(node); 2564 Optional<TypeWithDims> typeWithDims; 2565 Tree type; 2566 if (node.getType() != null) { 2567 TypeWithDims extractedDims = DimensionHelpers.extractDims(node.getType(), SortedDims.YES); 2568 typeWithDims = Optional.of(extractedDims); 2569 type = extractedDims.node; 2570 } else { 2571 typeWithDims = Optional.empty(); 2572 type = null; 2573 } 2574 declareOne( 2575 kind, 2576 annotationsDirection, 2577 Optional.of(node.getModifiers()), 2578 type, 2579 node.getName(), 2580 "", 2581 equals, 2582 initializer, 2583 trailing, 2584 /* receiverExpression= */ Optional.empty(), 2585 typeWithDims); 2586 } 2587 2588 /** Does not omit the leading {@code "<"}, which should be associated with the type name. */ 2589 protected void typeParametersRest( 2590 List<? extends TypeParameterTree> typeParameters, Indent plusIndent) { 2591 builder.open(plusIndent); 2592 builder.breakOp(); 2593 builder.open(ZERO); 2594 boolean first = true; 2595 for (TypeParameterTree typeParameter : typeParameters) { 2596 if (!first) { 2597 token(","); 2598 builder.breakOp(" "); 2599 } 2600 scan(typeParameter, null); 2601 first = false; 2602 } 2603 token(">"); 2604 builder.close(); 2605 builder.close(); 2606 } 2607 2608 /** Collapse chains of {@code .} operators, across multiple {@link ASTNode} types. */ 2609 2610 /** 2611 * Output a "." node. 2612 * 2613 * @param node0 the "." node 2614 */ 2615 void visitDot(ExpressionTree node0) { 2616 ExpressionTree node = node0; 2617 2618 // collect a flattened list of "."-separated items 2619 // e.g. ImmutableList.builder().add(1).build() -> [ImmutableList, builder(), add(1), build()] 2620 Deque<ExpressionTree> stack = new ArrayDeque<>(); 2621 LOOP: 2622 do { 2623 stack.addFirst(node); 2624 if (node.getKind() == ARRAY_ACCESS) { 2625 node = getArrayBase(node); 2626 } 2627 switch (node.getKind()) { 2628 case MEMBER_SELECT: 2629 node = ((MemberSelectTree) node).getExpression(); 2630 break; 2631 case METHOD_INVOCATION: 2632 node = getMethodReceiver((MethodInvocationTree) node); 2633 break; 2634 case IDENTIFIER: 2635 node = null; 2636 break LOOP; 2637 default: 2638 // If the dot chain starts with a primary expression 2639 // (e.g. a class instance creation, or a conditional expression) 2640 // then remove it from the list and deal with it first. 2641 node = stack.removeFirst(); 2642 break LOOP; 2643 } 2644 } while (node != null); 2645 List<ExpressionTree> items = new ArrayList<>(stack); 2646 2647 boolean needDot = false; 2648 2649 // The dot chain started with a primary expression: output it normally, and indent 2650 // the rest of the chain +4. 2651 if (node != null) { 2652 // Exception: if it's an anonymous class declaration, we don't need to 2653 // break and indent after the trailing '}'. 2654 if (node.getKind() == NEW_CLASS && ((NewClassTree) node).getClassBody() != null) { 2655 builder.open(ZERO); 2656 scan(getArrayBase(node), null); 2657 token("."); 2658 } else { 2659 builder.open(plusFour); 2660 scan(getArrayBase(node), null); 2661 builder.breakOp(); 2662 needDot = true; 2663 } 2664 formatArrayIndices(getArrayIndices(node)); 2665 if (stack.isEmpty()) { 2666 builder.close(); 2667 return; 2668 } 2669 } 2670 2671 Set<Integer> prefixes = new LinkedHashSet<>(); 2672 2673 // Check if the dot chain has a prefix that looks like a type name, so we can 2674 // treat the type name-shaped part as a single syntactic unit. 2675 TypeNameClassifier.typePrefixLength(simpleNames(stack)).ifPresent(prefixes::add); 2676 2677 int invocationCount = 0; 2678 int firstInvocationIndex = -1; 2679 { 2680 for (int i = 0; i < items.size(); i++) { 2681 ExpressionTree expression = items.get(i); 2682 if (expression.getKind() == METHOD_INVOCATION) { 2683 if (i > 0 || node != null) { 2684 // we only want dereference invocations 2685 invocationCount++; 2686 } 2687 if (firstInvocationIndex < 0) { 2688 firstInvocationIndex = i; 2689 } 2690 } 2691 } 2692 } 2693 2694 // If there's only one invocation, treat leading field accesses as a single 2695 // unit. In the normal case we want to preserve the alignment of subsequent 2696 // method calls, and would emit e.g.: 2697 // 2698 // myField 2699 // .foo() 2700 // .bar(); 2701 // 2702 // But if there's no 'bar()' to worry about the alignment of we prefer: 2703 // 2704 // myField.foo(); 2705 // 2706 // to: 2707 // 2708 // myField 2709 // .foo(); 2710 // 2711 if (invocationCount == 1 && firstInvocationIndex > 0) { 2712 prefixes.add(firstInvocationIndex); 2713 } 2714 2715 if (prefixes.isEmpty() && items.get(0) instanceof IdentifierTree) { 2716 switch (((IdentifierTree) items.get(0)).getName().toString()) { 2717 case "this": 2718 case "super": 2719 prefixes.add(1); 2720 break; 2721 default: 2722 break; 2723 } 2724 } 2725 2726 List<Long> streamPrefixes = handleStream(items); 2727 streamPrefixes.forEach(x -> prefixes.add(x.intValue())); 2728 if (!prefixes.isEmpty()) { 2729 visitDotWithPrefix( 2730 items, needDot, prefixes, streamPrefixes.isEmpty() ? INDEPENDENT : UNIFIED); 2731 } else { 2732 visitRegularDot(items, needDot); 2733 } 2734 2735 if (node != null) { 2736 builder.close(); 2737 } 2738 } 2739 2740 /** 2741 * Output a "regular" chain of dereferences, possibly in builder-style. Break before every dot. 2742 * 2743 * @param items in the chain 2744 * @param needDot whether a leading dot is needed 2745 */ 2746 private void visitRegularDot(List<ExpressionTree> items, boolean needDot) { 2747 boolean trailingDereferences = items.size() > 1; 2748 boolean needDot0 = needDot; 2749 if (!needDot0) { 2750 builder.open(plusFour); 2751 } 2752 // don't break after the first element if it is every small, unless the 2753 // chain starts with another expression 2754 int minLength = indentMultiplier * 4; 2755 int length = needDot0 ? minLength : 0; 2756 for (ExpressionTree e : items) { 2757 if (needDot) { 2758 if (length > minLength) { 2759 builder.breakOp(FillMode.UNIFIED, "", ZERO); 2760 } 2761 token("."); 2762 length++; 2763 } 2764 if (!fillFirstArgument(e, items, trailingDereferences ? ZERO : minusFour)) { 2765 BreakTag tyargTag = genSym(); 2766 dotExpressionUpToArgs(e, Optional.of(tyargTag)); 2767 Indent tyargIndent = Indent.If.make(tyargTag, plusFour, ZERO); 2768 dotExpressionArgsAndParen( 2769 e, tyargIndent, (trailingDereferences || needDot) ? plusFour : ZERO); 2770 } 2771 length += getLength(e, getCurrentPath()); 2772 needDot = true; 2773 } 2774 if (!needDot0) { 2775 builder.close(); 2776 } 2777 } 2778 2779 // avoid formattings like: 2780 // 2781 // when( 2782 // something 2783 // .happens()) 2784 // .thenReturn(result); 2785 // 2786 private boolean fillFirstArgument(ExpressionTree e, List<ExpressionTree> items, Indent indent) { 2787 // is there a trailing dereference? 2788 if (items.size() < 2) { 2789 return false; 2790 } 2791 // don't special-case calls nested inside expressions 2792 if (e.getKind() != METHOD_INVOCATION) { 2793 return false; 2794 } 2795 MethodInvocationTree methodInvocation = (MethodInvocationTree) e; 2796 Name name = getMethodName(methodInvocation); 2797 if (!(methodInvocation.getMethodSelect() instanceof IdentifierTree) 2798 || name.length() > 4 2799 || !methodInvocation.getTypeArguments().isEmpty() 2800 || methodInvocation.getArguments().size() != 1) { 2801 return false; 2802 } 2803 builder.open(ZERO); 2804 builder.open(indent); 2805 visit(name); 2806 token("("); 2807 ExpressionTree arg = getOnlyElement(methodInvocation.getArguments()); 2808 scan(arg, null); 2809 builder.close(); 2810 token(")"); 2811 builder.close(); 2812 return true; 2813 } 2814 2815 /** 2816 * Output a chain of dereferences where some prefix should be treated as a single syntactic unit, 2817 * either because it looks like a type name or because there is only a single method invocation in 2818 * the chain. 2819 * 2820 * @param items in the chain 2821 * @param needDot whether a leading dot is needed 2822 * @param prefixes the terminal indices of 'prefixes' of the expression that should be treated as 2823 * a syntactic unit 2824 */ 2825 private void visitDotWithPrefix( 2826 List<ExpressionTree> items, 2827 boolean needDot, 2828 Collection<Integer> prefixes, 2829 FillMode prefixFillMode) { 2830 // Are there method invocations or field accesses after the prefix? 2831 boolean trailingDereferences = !prefixes.isEmpty() && getLast(prefixes) < items.size() - 1; 2832 2833 builder.open(plusFour); 2834 for (int times = 0; times < prefixes.size(); times++) { 2835 builder.open(ZERO); 2836 } 2837 2838 Deque<Integer> unconsumedPrefixes = new ArrayDeque<>(ImmutableSortedSet.copyOf(prefixes)); 2839 BreakTag nameTag = genSym(); 2840 for (int i = 0; i < items.size(); i++) { 2841 ExpressionTree e = items.get(i); 2842 if (needDot) { 2843 FillMode fillMode; 2844 if (!unconsumedPrefixes.isEmpty() && i <= unconsumedPrefixes.peekFirst()) { 2845 fillMode = prefixFillMode; 2846 } else { 2847 fillMode = FillMode.UNIFIED; 2848 } 2849 2850 builder.breakOp(fillMode, "", ZERO, Optional.of(nameTag)); 2851 token("."); 2852 } 2853 BreakTag tyargTag = genSym(); 2854 dotExpressionUpToArgs(e, Optional.of(tyargTag)); 2855 if (!unconsumedPrefixes.isEmpty() && i == unconsumedPrefixes.peekFirst()) { 2856 builder.close(); 2857 unconsumedPrefixes.removeFirst(); 2858 } 2859 2860 Indent tyargIndent = Indent.If.make(tyargTag, plusFour, ZERO); 2861 Indent argsIndent = Indent.If.make(nameTag, plusFour, trailingDereferences ? plusFour : ZERO); 2862 dotExpressionArgsAndParen(e, tyargIndent, argsIndent); 2863 2864 needDot = true; 2865 } 2866 2867 builder.close(); 2868 } 2869 2870 /** Returns the simple names of expressions in a "." chain. */ 2871 private List<String> simpleNames(Deque<ExpressionTree> stack) { 2872 ImmutableList.Builder<String> simpleNames = ImmutableList.builder(); 2873 OUTER: 2874 for (ExpressionTree expression : stack) { 2875 boolean isArray = expression.getKind() == ARRAY_ACCESS; 2876 expression = getArrayBase(expression); 2877 switch (expression.getKind()) { 2878 case MEMBER_SELECT: 2879 simpleNames.add(((MemberSelectTree) expression).getIdentifier().toString()); 2880 break; 2881 case IDENTIFIER: 2882 simpleNames.add(((IdentifierTree) expression).getName().toString()); 2883 break; 2884 case METHOD_INVOCATION: 2885 simpleNames.add(getMethodName((MethodInvocationTree) expression).toString()); 2886 break OUTER; 2887 default: 2888 break OUTER; 2889 } 2890 if (isArray) { 2891 break OUTER; 2892 } 2893 } 2894 return simpleNames.build(); 2895 } 2896 2897 private void dotExpressionUpToArgs(ExpressionTree expression, Optional<BreakTag> tyargTag) { 2898 expression = getArrayBase(expression); 2899 switch (expression.getKind()) { 2900 case MEMBER_SELECT: 2901 MemberSelectTree fieldAccess = (MemberSelectTree) expression; 2902 visit(fieldAccess.getIdentifier()); 2903 break; 2904 case METHOD_INVOCATION: 2905 MethodInvocationTree methodInvocation = (MethodInvocationTree) expression; 2906 if (!methodInvocation.getTypeArguments().isEmpty()) { 2907 builder.open(plusFour); 2908 addTypeArguments(methodInvocation.getTypeArguments(), ZERO); 2909 // TODO(jdd): Should indent the name -4. 2910 builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO, tyargTag); 2911 builder.close(); 2912 } 2913 visit(getMethodName(methodInvocation)); 2914 break; 2915 case IDENTIFIER: 2916 visit(((IdentifierTree) expression).getName()); 2917 break; 2918 default: 2919 scan(expression, null); 2920 break; 2921 } 2922 } 2923 2924 /** 2925 * Returns the base expression of an erray access, e.g. given {@code foo[0][0]} returns {@code 2926 * foo}. 2927 */ 2928 private ExpressionTree getArrayBase(ExpressionTree node) { 2929 while (node instanceof ArrayAccessTree) { 2930 node = ((ArrayAccessTree) node).getExpression(); 2931 } 2932 return node; 2933 } 2934 2935 private ExpressionTree getMethodReceiver(MethodInvocationTree methodInvocation) { 2936 ExpressionTree select = methodInvocation.getMethodSelect(); 2937 return select instanceof MemberSelectTree ? ((MemberSelectTree) select).getExpression() : null; 2938 } 2939 2940 private void dotExpressionArgsAndParen( 2941 ExpressionTree expression, Indent tyargIndent, Indent indent) { 2942 Deque<ExpressionTree> indices = getArrayIndices(expression); 2943 expression = getArrayBase(expression); 2944 switch (expression.getKind()) { 2945 case METHOD_INVOCATION: 2946 builder.open(tyargIndent); 2947 MethodInvocationTree methodInvocation = (MethodInvocationTree) expression; 2948 addArguments(methodInvocation.getArguments(), indent); 2949 builder.close(); 2950 break; 2951 default: 2952 break; 2953 } 2954 formatArrayIndices(indices); 2955 } 2956 2957 /** Lays out one or more array indices. Does not output the expression for the array itself. */ 2958 private void formatArrayIndices(Deque<ExpressionTree> indices) { 2959 if (indices.isEmpty()) { 2960 return; 2961 } 2962 builder.open(ZERO); 2963 do { 2964 token("["); 2965 builder.breakToFill(); 2966 scan(indices.removeLast(), null); 2967 token("]"); 2968 } while (!indices.isEmpty()); 2969 builder.close(); 2970 } 2971 2972 /** 2973 * Returns all array indices for the given expression, e.g. given {@code foo[0][0]} returns the 2974 * expressions for {@code [0][0]}. 2975 */ 2976 private Deque<ExpressionTree> getArrayIndices(ExpressionTree expression) { 2977 Deque<ExpressionTree> indices = new ArrayDeque<>(); 2978 while (expression instanceof ArrayAccessTree) { 2979 ArrayAccessTree array = (ArrayAccessTree) expression; 2980 indices.addLast(array.getIndex()); 2981 expression = array.getExpression(); 2982 } 2983 return indices; 2984 } 2985 2986 /** Helper methods for method invocations. */ 2987 void addTypeArguments(List<? extends Tree> typeArguments, Indent plusIndent) { 2988 if (typeArguments == null || typeArguments.isEmpty()) { 2989 return; 2990 } 2991 token("<"); 2992 builder.open(plusIndent); 2993 boolean first = true; 2994 for (Tree typeArgument : typeArguments) { 2995 if (!first) { 2996 token(","); 2997 builder.breakToFill(" "); 2998 } 2999 scan(typeArgument, null); 3000 first = false; 3001 } 3002 builder.close(); 3003 token(">"); 3004 } 3005 3006 /** 3007 * Add arguments to a method invocation, etc. The arguments indented {@code plusFour}, filled, 3008 * from the current indent. The arguments may be output two at a time if they seem to be arguments 3009 * to a map constructor, etc. 3010 * 3011 * @param arguments the arguments 3012 * @param plusIndent the extra indent for the arguments 3013 */ 3014 void addArguments(List<? extends ExpressionTree> arguments, Indent plusIndent) { 3015 builder.open(plusIndent); 3016 token("("); 3017 if (!arguments.isEmpty()) { 3018 if (arguments.size() % 2 == 0 && argumentsAreTabular(arguments) == 2) { 3019 builder.forcedBreak(); 3020 builder.open(ZERO); 3021 boolean first = true; 3022 for (int i = 0; i < arguments.size() - 1; i += 2) { 3023 ExpressionTree argument0 = arguments.get(i); 3024 ExpressionTree argument1 = arguments.get(i + 1); 3025 if (!first) { 3026 token(","); 3027 builder.forcedBreak(); 3028 } 3029 builder.open(plusFour); 3030 scan(argument0, null); 3031 token(","); 3032 builder.breakOp(" "); 3033 scan(argument1, null); 3034 builder.close(); 3035 first = false; 3036 } 3037 builder.close(); 3038 } else if (isFormatMethod(arguments)) { 3039 builder.breakOp(); 3040 builder.open(ZERO); 3041 scan(arguments.get(0), null); 3042 token(","); 3043 builder.breakOp(" "); 3044 builder.open(ZERO); 3045 argList(arguments.subList(1, arguments.size())); 3046 builder.close(); 3047 builder.close(); 3048 } else { 3049 builder.breakOp(); 3050 argList(arguments); 3051 } 3052 } 3053 token(")"); 3054 builder.close(); 3055 } 3056 3057 private void argList(List<? extends ExpressionTree> arguments) { 3058 builder.open(ZERO); 3059 boolean first = true; 3060 FillMode fillMode = hasOnlyShortItems(arguments) ? FillMode.INDEPENDENT : FillMode.UNIFIED; 3061 for (ExpressionTree argument : arguments) { 3062 if (!first) { 3063 token(","); 3064 builder.breakOp(fillMode, " ", ZERO); 3065 } 3066 scan(argument, null); 3067 first = false; 3068 } 3069 builder.close(); 3070 } 3071 3072 /** 3073 * Identifies String formatting methods like {@link String#format} which we prefer to format as: 3074 * 3075 * <pre>{@code 3076 * String.format( 3077 * "the format string: %s %s %s", 3078 * arg, arg, arg); 3079 * }</pre> 3080 * 3081 * <p>And not: 3082 * 3083 * <pre>{@code 3084 * String.format( 3085 * "the format string: %s %s %s", 3086 * arg, 3087 * arg, 3088 * arg); 3089 * }</pre> 3090 */ 3091 private boolean isFormatMethod(List<? extends ExpressionTree> arguments) { 3092 if (arguments.size() < 2) { 3093 return false; 3094 } 3095 return isStringConcat(arguments.get(0)); 3096 } 3097 3098 private static final Pattern FORMAT_SPECIFIER = Pattern.compile("%|\\{[0-9]\\}"); 3099 3100 private boolean isStringConcat(ExpressionTree first) { 3101 final boolean[] stringLiteral = {true}; 3102 final boolean[] formatString = {false}; 3103 new TreeScanner() { 3104 @Override 3105 public void scan(JCTree tree) { 3106 if (tree == null) { 3107 return; 3108 } 3109 switch (tree.getKind()) { 3110 case STRING_LITERAL: 3111 break; 3112 case PLUS: 3113 super.scan(tree); 3114 break; 3115 default: 3116 stringLiteral[0] = false; 3117 break; 3118 } 3119 if (tree.getKind() == STRING_LITERAL) { 3120 Object value = ((LiteralTree) tree).getValue(); 3121 if (value instanceof String && FORMAT_SPECIFIER.matcher(value.toString()).find()) { 3122 formatString[0] = true; 3123 } 3124 } 3125 } 3126 }.scan((JCTree) first); 3127 return stringLiteral[0] && formatString[0]; 3128 } 3129 3130 /** Returns the number of columns if the arguments arg laid out in a grid, or else {@code -1}. */ 3131 private int argumentsAreTabular(List<? extends ExpressionTree> arguments) { 3132 if (arguments.isEmpty()) { 3133 return -1; 3134 } 3135 List<List<ExpressionTree>> rows = new ArrayList<>(); 3136 PeekingIterator<ExpressionTree> it = Iterators.peekingIterator(arguments.iterator()); 3137 int start0 = actualColumn(it.peek()); 3138 { 3139 List<ExpressionTree> row = new ArrayList<>(); 3140 row.add(it.next()); 3141 while (it.hasNext() && actualColumn(it.peek()) > start0) { 3142 row.add(it.next()); 3143 } 3144 if (!it.hasNext()) { 3145 return -1; 3146 } 3147 if (rowLength(row) <= 1) { 3148 return -1; 3149 } 3150 rows.add(row); 3151 } 3152 while (it.hasNext()) { 3153 List<ExpressionTree> row = new ArrayList<>(); 3154 int start = actualColumn(it.peek()); 3155 if (start != start0) { 3156 return -1; 3157 } 3158 row.add(it.next()); 3159 while (it.hasNext() && actualColumn(it.peek()) > start0) { 3160 row.add(it.next()); 3161 } 3162 rows.add(row); 3163 } 3164 int size0 = rows.get(0).size(); 3165 if (!expressionsAreParallel(rows, 0, rows.size())) { 3166 return -1; 3167 } 3168 for (int i = 1; i < size0; i++) { 3169 if (!expressionsAreParallel(rows, i, rows.size() / 2 + 1)) { 3170 return -1; 3171 } 3172 } 3173 // if there are only two rows, they must be the same length 3174 if (rows.size() == 2) { 3175 if (size0 == rows.get(1).size()) { 3176 return size0; 3177 } 3178 return -1; 3179 } 3180 // allow a ragged trailing row for >= 3 columns 3181 for (int i = 1; i < rows.size() - 1; i++) { 3182 if (size0 != rows.get(i).size()) { 3183 return -1; 3184 } 3185 } 3186 if (size0 < getLast(rows).size()) { 3187 return -1; 3188 } 3189 return size0; 3190 } 3191 3192 static int rowLength(List<? extends ExpressionTree> row) { 3193 int size = 0; 3194 for (ExpressionTree tree : row) { 3195 if (tree.getKind() != NEW_ARRAY) { 3196 size++; 3197 continue; 3198 } 3199 NewArrayTree array = (NewArrayTree) tree; 3200 if (array.getInitializers() == null) { 3201 size++; 3202 continue; 3203 } 3204 size += rowLength(array.getInitializers()); 3205 } 3206 return size; 3207 } 3208 3209 private Integer actualColumn(ExpressionTree expression) { 3210 Map<Integer, Integer> positionToColumnMap = builder.getInput().getPositionToColumnMap(); 3211 return positionToColumnMap.get(builder.actualStartColumn(getStartPosition(expression))); 3212 } 3213 3214 /** Returns true if {@code atLeastM} of the expressions in the given column are the same kind. */ 3215 private static boolean expressionsAreParallel( 3216 List<List<ExpressionTree>> rows, int column, int atLeastM) { 3217 Multiset<Tree.Kind> nodeTypes = HashMultiset.create(); 3218 for (List<? extends ExpressionTree> row : rows) { 3219 if (column >= row.size()) { 3220 continue; 3221 } 3222 // Treat UnaryTree expressions as their underlying type for the comparison (so, for example 3223 // -ve and +ve numeric literals are considered the same). 3224 if (row.get(column) instanceof UnaryTree) { 3225 nodeTypes.add(((UnaryTree) row.get(column)).getExpression().getKind()); 3226 } else { 3227 nodeTypes.add(row.get(column).getKind()); 3228 } 3229 } 3230 for (Multiset.Entry<Tree.Kind> nodeType : nodeTypes.entrySet()) { 3231 if (nodeType.getCount() >= atLeastM) { 3232 return true; 3233 } 3234 } 3235 return false; 3236 } 3237 3238 // General helper functions. 3239 3240 enum DeclarationKind { 3241 NONE, 3242 FIELD, 3243 PARAMETER 3244 } 3245 3246 /** Declare one variable or variable-like thing. */ 3247 int declareOne( 3248 DeclarationKind kind, 3249 Direction annotationsDirection, 3250 Optional<ModifiersTree> modifiers, 3251 Tree type, 3252 Name name, 3253 String op, 3254 String equals, 3255 Optional<ExpressionTree> initializer, 3256 Optional<String> trailing, 3257 Optional<ExpressionTree> receiverExpression, 3258 Optional<TypeWithDims> typeWithDims) { 3259 3260 BreakTag typeBreak = genSym(); 3261 BreakTag verticalAnnotationBreak = genSym(); 3262 3263 // If the node is a field declaration, try to output any declaration 3264 // annotations in-line. If the entire declaration doesn't fit on a single 3265 // line, fall back to one-per-line. 3266 boolean isField = kind == DeclarationKind.FIELD; 3267 3268 if (isField) { 3269 builder.blankLineWanted(BlankLineWanted.conditional(verticalAnnotationBreak)); 3270 } 3271 3272 Deque<List<? extends AnnotationTree>> dims = 3273 new ArrayDeque<>( 3274 typeWithDims.isPresent() ? typeWithDims.get().dims : Collections.emptyList()); 3275 int baseDims = 0; 3276 3277 builder.open( 3278 kind == DeclarationKind.PARAMETER 3279 && (modifiers.isPresent() && !modifiers.get().getAnnotations().isEmpty()) 3280 ? plusFour 3281 : ZERO); 3282 { 3283 if (modifiers.isPresent()) { 3284 visitAndBreakModifiers( 3285 modifiers.get(), annotationsDirection, Optional.of(verticalAnnotationBreak)); 3286 } 3287 boolean isVar = 3288 builder.peekToken().get().equals("var") 3289 && (!name.contentEquals("var") || builder.peekToken(1).get().equals("var")); 3290 boolean hasType = type != null || isVar; 3291 builder.open(hasType ? plusFour : ZERO); 3292 { 3293 builder.open(ZERO); 3294 { 3295 builder.open(ZERO); 3296 { 3297 if (typeWithDims.isPresent() && typeWithDims.get().node != null) { 3298 scan(typeWithDims.get().node, null); 3299 int totalDims = dims.size(); 3300 builder.open(plusFour); 3301 maybeAddDims(dims); 3302 builder.close(); 3303 baseDims = totalDims - dims.size(); 3304 } else if (isVar) { 3305 token("var"); 3306 } else { 3307 scan(type, null); 3308 } 3309 } 3310 builder.close(); 3311 3312 if (hasType) { 3313 builder.breakOp(Doc.FillMode.INDEPENDENT, " ", ZERO, Optional.of(typeBreak)); 3314 } 3315 3316 // conditionally ident the name and initializer +4 if the type spans 3317 // multiple lines 3318 builder.open(Indent.If.make(typeBreak, plusFour, ZERO)); 3319 if (receiverExpression.isPresent()) { 3320 scan(receiverExpression.get(), null); 3321 } else { 3322 visit(name); 3323 } 3324 builder.op(op); 3325 } 3326 maybeAddDims(dims); 3327 builder.close(); 3328 } 3329 builder.close(); 3330 3331 if (initializer.isPresent()) { 3332 builder.space(); 3333 token(equals); 3334 if (initializer.get().getKind() == Tree.Kind.NEW_ARRAY 3335 && ((NewArrayTree) initializer.get()).getType() == null) { 3336 builder.open(minusFour); 3337 builder.space(); 3338 initializer.get().accept(this, null); 3339 builder.close(); 3340 } else { 3341 builder.open(Indent.If.make(typeBreak, plusFour, ZERO)); 3342 { 3343 builder.breakToFill(" "); 3344 scan(initializer.get(), null); 3345 } 3346 builder.close(); 3347 } 3348 } 3349 if (trailing.isPresent() && builder.peekToken().equals(trailing)) { 3350 builder.guessToken(trailing.get()); 3351 } 3352 3353 // end of conditional name and initializer indent 3354 builder.close(); 3355 } 3356 builder.close(); 3357 3358 if (isField) { 3359 builder.blankLineWanted(BlankLineWanted.conditional(verticalAnnotationBreak)); 3360 } 3361 3362 return baseDims; 3363 } 3364 3365 private void maybeAddDims(Deque<List<? extends AnnotationTree>> annotations) { 3366 maybeAddDims(new ArrayDeque<>(), annotations); 3367 } 3368 3369 /** 3370 * The compiler does not always preserve the concrete syntax of annotated array dimensions, and 3371 * mixed-notation array dimensions. Use look-ahead to preserve the original syntax. 3372 * 3373 * <p>It is assumed that any number of regular dimension specifiers ({@code []} with no 3374 * annotations) may be present in the input. 3375 * 3376 * @param dimExpressions an ordered list of dimension expressions (e.g. the {@code 0} in {@code 3377 * new int[0]} 3378 * @param annotations an ordered list of type annotations grouped by dimension (e.g. {@code 3379 * [[@A, @B], [@C]]} for {@code int @A [] @B @C []} 3380 */ 3381 private void maybeAddDims( 3382 Deque<ExpressionTree> dimExpressions, Deque<List<? extends AnnotationTree>> annotations) { 3383 boolean lastWasAnnotation = false; 3384 while (builder.peekToken().isPresent()) { 3385 switch (builder.peekToken().get()) { 3386 case "@": 3387 if (annotations.isEmpty()) { 3388 return; 3389 } 3390 List<? extends AnnotationTree> dimAnnotations = annotations.removeFirst(); 3391 if (dimAnnotations.isEmpty()) { 3392 continue; 3393 } 3394 builder.breakToFill(" "); 3395 visitAnnotations(dimAnnotations, BreakOrNot.NO, BreakOrNot.NO); 3396 lastWasAnnotation = true; 3397 break; 3398 case "[": 3399 if (lastWasAnnotation) { 3400 builder.breakToFill(" "); 3401 } else { 3402 builder.breakToFill(); 3403 } 3404 token("["); 3405 if (!builder.peekToken().get().equals("]")) { 3406 scan(dimExpressions.removeFirst(), null); 3407 } 3408 token("]"); 3409 lastWasAnnotation = false; 3410 break; 3411 case ".": 3412 if (!builder.peekToken().get().equals(".") || !builder.peekToken(1).get().equals(".")) { 3413 return; 3414 } 3415 if (lastWasAnnotation) { 3416 builder.breakToFill(" "); 3417 } else { 3418 builder.breakToFill(); 3419 } 3420 builder.op("..."); 3421 lastWasAnnotation = false; 3422 break; 3423 default: 3424 return; 3425 } 3426 } 3427 } 3428 3429 private void declareMany(List<VariableTree> fragments, Direction annotationDirection) { 3430 builder.open(ZERO); 3431 3432 ModifiersTree modifiers = fragments.get(0).getModifiers(); 3433 Tree type = fragments.get(0).getType(); 3434 3435 visitAndBreakModifiers( 3436 modifiers, annotationDirection, /* declarationAnnotationBreak= */ Optional.empty()); 3437 builder.open(plusFour); 3438 builder.open(ZERO); 3439 TypeWithDims extractedDims = DimensionHelpers.extractDims(type, SortedDims.YES); 3440 Deque<List<? extends AnnotationTree>> dims = new ArrayDeque<>(extractedDims.dims); 3441 scan(extractedDims.node, null); 3442 int baseDims = dims.size(); 3443 maybeAddDims(dims); 3444 baseDims = baseDims - dims.size(); 3445 boolean first = true; 3446 for (VariableTree fragment : fragments) { 3447 if (!first) { 3448 token(","); 3449 } 3450 TypeWithDims fragmentDims = variableFragmentDims(first, baseDims, fragment.getType()); 3451 dims = new ArrayDeque<>(fragmentDims.dims); 3452 builder.breakOp(" "); 3453 builder.open(ZERO); 3454 maybeAddDims(dims); 3455 visit(fragment.getName()); 3456 maybeAddDims(dims); 3457 ExpressionTree initializer = fragment.getInitializer(); 3458 if (initializer != null) { 3459 builder.space(); 3460 token("="); 3461 builder.open(plusFour); 3462 builder.breakOp(" "); 3463 scan(initializer, null); 3464 builder.close(); 3465 } 3466 builder.close(); 3467 if (first) { 3468 builder.close(); 3469 } 3470 first = false; 3471 } 3472 builder.close(); 3473 token(";"); 3474 builder.close(); 3475 } 3476 3477 /** Add a list of declarations. */ 3478 protected void addBodyDeclarations( 3479 List<? extends Tree> bodyDeclarations, BracesOrNot braces, FirstDeclarationsOrNot first0) { 3480 if (bodyDeclarations.isEmpty()) { 3481 if (braces.isYes()) { 3482 builder.space(); 3483 tokenBreakTrailingComment("{", plusTwo); 3484 builder.blankLineWanted(BlankLineWanted.NO); 3485 builder.open(ZERO); 3486 token("}", plusTwo); 3487 builder.close(); 3488 } 3489 } else { 3490 if (braces.isYes()) { 3491 builder.space(); 3492 tokenBreakTrailingComment("{", plusTwo); 3493 builder.open(ZERO); 3494 } 3495 builder.open(plusTwo); 3496 boolean first = first0.isYes(); 3497 boolean lastOneGotBlankLineBefore = false; 3498 PeekingIterator<Tree> it = Iterators.peekingIterator(bodyDeclarations.iterator()); 3499 while (it.hasNext()) { 3500 Tree bodyDeclaration = it.next(); 3501 dropEmptyDeclarations(); 3502 builder.forcedBreak(); 3503 boolean thisOneGetsBlankLineBefore = 3504 bodyDeclaration.getKind() != VARIABLE || hasJavaDoc(bodyDeclaration); 3505 if (first) { 3506 builder.blankLineWanted(PRESERVE); 3507 } else if (!first && (thisOneGetsBlankLineBefore || lastOneGotBlankLineBefore)) { 3508 builder.blankLineWanted(YES); 3509 } 3510 markForPartialFormat(); 3511 3512 if (bodyDeclaration.getKind() == VARIABLE) { 3513 visitVariables( 3514 variableFragments(it, bodyDeclaration), 3515 DeclarationKind.FIELD, 3516 fieldAnnotationDirection(((VariableTree) bodyDeclaration).getModifiers())); 3517 } else { 3518 scan(bodyDeclaration, null); 3519 } 3520 first = false; 3521 lastOneGotBlankLineBefore = thisOneGetsBlankLineBefore; 3522 } 3523 dropEmptyDeclarations(); 3524 builder.forcedBreak(); 3525 builder.close(); 3526 builder.forcedBreak(); 3527 markForPartialFormat(); 3528 if (braces.isYes()) { 3529 builder.blankLineWanted(BlankLineWanted.NO); 3530 token("}", plusTwo); 3531 builder.close(); 3532 } 3533 } 3534 } 3535 3536 /** 3537 * The parser expands multi-variable declarations into separate single-variable declarations. All 3538 * of the fragments in the original declaration have the same start position, so we use that as a 3539 * signal to collect them and preserve the multi-variable declaration in the output. 3540 * 3541 * <p>e.g. {@code int x, y;} is parsed as {@code int x; int y;}. 3542 */ 3543 private List<VariableTree> variableFragments(PeekingIterator<? extends Tree> it, Tree first) { 3544 List<VariableTree> fragments = new ArrayList<>(); 3545 if (first.getKind() == VARIABLE) { 3546 int start = getStartPosition(first); 3547 fragments.add((VariableTree) first); 3548 while (it.hasNext() 3549 && it.peek().getKind() == VARIABLE 3550 && getStartPosition(it.peek()) == start) { 3551 fragments.add((VariableTree) it.next()); 3552 } 3553 } 3554 return fragments; 3555 } 3556 3557 /** Does this declaration have javadoc preceding it? */ 3558 private boolean hasJavaDoc(Tree bodyDeclaration) { 3559 int position = ((JCTree) bodyDeclaration).getStartPosition(); 3560 Input.Token token = builder.getInput().getPositionTokenMap().get(position); 3561 if (token != null) { 3562 for (Input.Tok tok : token.getToksBefore()) { 3563 if (tok.getText().startsWith("/**")) { 3564 return true; 3565 } 3566 } 3567 } 3568 return false; 3569 } 3570 3571 private static Optional<? extends Input.Token> getNextToken(Input input, int position) { 3572 return Optional.ofNullable(input.getPositionTokenMap().get(position)); 3573 } 3574 3575 /** Does this list of trees end with the specified token? */ 3576 private boolean hasTrailingToken(Input input, List<? extends Tree> nodes, String token) { 3577 if (nodes.isEmpty()) { 3578 return false; 3579 } 3580 Tree lastNode = getLast(nodes); 3581 Optional<? extends Input.Token> nextToken = 3582 getNextToken(input, getEndPosition(lastNode, getCurrentPath())); 3583 return nextToken.isPresent() && nextToken.get().getTok().getText().equals(token); 3584 } 3585 3586 /** 3587 * Can a local with a set of modifiers be declared with horizontal annotations? This is currently 3588 * true if there is at most one parameterless annotation, and no others. 3589 * 3590 * @param modifiers the list of {@link ModifiersTree}s 3591 * @return whether the local can be declared with horizontal annotations 3592 */ 3593 private Direction canLocalHaveHorizontalAnnotations(ModifiersTree modifiers) { 3594 int parameterlessAnnotations = 0; 3595 for (AnnotationTree annotation : modifiers.getAnnotations()) { 3596 if (annotation.getArguments().isEmpty()) { 3597 parameterlessAnnotations++; 3598 } 3599 } 3600 return parameterlessAnnotations <= 1 3601 && parameterlessAnnotations == modifiers.getAnnotations().size() 3602 ? Direction.HORIZONTAL 3603 : Direction.VERTICAL; 3604 } 3605 3606 /** 3607 * Should a field with a set of modifiers be declared with horizontal annotations? This is 3608 * currently true if all annotations are parameterless annotations. 3609 */ 3610 private Direction fieldAnnotationDirection(ModifiersTree modifiers) { 3611 for (AnnotationTree annotation : modifiers.getAnnotations()) { 3612 if (!annotation.getArguments().isEmpty()) { 3613 return Direction.VERTICAL; 3614 } 3615 } 3616 return Direction.HORIZONTAL; 3617 } 3618 3619 /** 3620 * Emit a {@link Doc.Token}. 3621 * 3622 * @param token the {@link String} to wrap in a {@link Doc.Token} 3623 */ 3624 protected final void token(String token) { 3625 builder.token( 3626 token, 3627 Doc.Token.RealOrImaginary.REAL, 3628 ZERO, 3629 /* breakAndIndentTrailingComment= */ Optional.empty()); 3630 } 3631 3632 /** 3633 * Emit a {@link Doc.Token}. 3634 * 3635 * @param token the {@link String} to wrap in a {@link Doc.Token} 3636 * @param plusIndentCommentsBefore extra indent for comments before this token 3637 */ 3638 protected final void token(String token, Indent plusIndentCommentsBefore) { 3639 builder.token( 3640 token, 3641 Doc.Token.RealOrImaginary.REAL, 3642 plusIndentCommentsBefore, 3643 /* breakAndIndentTrailingComment= */ Optional.empty()); 3644 } 3645 3646 /** Emit a {@link Doc.Token}, and breaks and indents trailing javadoc or block comments. */ 3647 final void tokenBreakTrailingComment(String token, Indent breakAndIndentTrailingComment) { 3648 builder.token( 3649 token, Doc.Token.RealOrImaginary.REAL, ZERO, Optional.of(breakAndIndentTrailingComment)); 3650 } 3651 3652 protected void markForPartialFormat() { 3653 if (!inExpression()) { 3654 builder.markForPartialFormat(); 3655 } 3656 } 3657 3658 /** 3659 * Sync to position in the input. If we've skipped outputting any tokens that were present in the 3660 * input tokens, output them here and complain. 3661 * 3662 * @param node the ASTNode holding the input position 3663 */ 3664 protected final void sync(Tree node) { 3665 builder.sync(((JCTree) node).getStartPosition()); 3666 } 3667 3668 final BreakTag genSym() { 3669 return new BreakTag(); 3670 } 3671 3672 @Override 3673 public final String toString() { 3674 return MoreObjects.toStringHelper(this).add("builder", builder).toString(); 3675 } 3676 } 3677