• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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