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