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