Preserve tabular array initializers

MOE_MIGRATED_REVID=127779883
diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java
index 7ec5a9c..336138a 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java
@@ -539,22 +539,50 @@
   @Override
   public boolean visit(ArrayInitializer node) {
     sync(node);
-    if (node.expressions().isEmpty()) {
+    List<Expression> expressions = node.expressions();
+    int cols;
+    if (expressions.isEmpty()) {
       tokenBreakTrailingComment("{", plusTwo);
       builder.blankLineWanted(BlankLineWanted.NO);
       token("}", plusTwo);
+    } else if ((cols = argumentsAreTabular(expressions)) != -1) {
+      builder.open(plusTwo);
+      token("{");
+      builder.forcedBreak();
+      boolean first = true;
+      for (Iterable<Expression> row : Iterables.partition(expressions, cols)) {
+        if (!first) {
+          builder.forcedBreak();
+        }
+        builder.open(
+            row.iterator().next().getNodeType() == ASTNode.ARRAY_INITIALIZER ? ZERO : plusFour);
+        boolean firstInRow = true;
+        for (Expression item : row) {
+          if (!firstInRow) {
+            token(",");
+            builder.breakToFill(" ");
+          }
+          item.accept(this);
+          firstInRow = false;
+        }
+        builder.guessToken(",");
+        builder.close();
+        first = false;
+      }
+      builder.breakOp(minusTwo);
+      builder.close();
+      token("}", plusTwo);
     } else {
       // Special-case the formatting of array initializers inside annotations
       // to more eagerly use a one-per-line layout.
       boolean inMemberValuePair =
           node.getParent().getNodeType() == ASTNode.MEMBER_VALUE_PAIR
               || node.getParent().getNodeType() == ASTNode.SINGLE_MEMBER_ANNOTATION;
-      boolean shortItems = hasOnlyShortItems(node.expressions());
+      boolean shortItems = hasOnlyShortItems(expressions);
       boolean allowFilledElementsOnOwnLine = shortItems || !inMemberValuePair;
 
       builder.open(plusTwo);
       tokenBreakTrailingComment("{", plusTwo);
-      builder.blankLineWanted(BlankLineWanted.NO);
       boolean hasTrailingComma = hasTrailingToken(builder.getInput(), node.expressions(), ",");
       builder.breakOp(hasTrailingComma ? FillMode.FORCED : FillMode.UNIFIED, "", ZERO);
       if (allowFilledElementsOnOwnLine) {
@@ -562,7 +590,7 @@
       }
       boolean first = true;
       FillMode fillMode = shortItems ? FillMode.INDEPENDENT : FillMode.UNIFIED;
-      for (Expression expression : (List<Expression>) node.expressions()) {
+      for (Expression expression : expressions) {
         if (!first) {
           token(",");
           builder.breakOp(fillMode, " ", ZERO);
@@ -575,7 +603,6 @@
         builder.close();
       }
       builder.breakOp(minusTwo);
-      builder.blankLineWanted(BlankLineWanted.NO);
       builder.close();
       token("}", plusTwo);
     }
@@ -3481,11 +3508,12 @@
         input.getPositionTokenMap().ceilingEntry(position);
     return ceilingEntry == null
         ? Optional.<JavaInput.Token>absent()
-        : Optional.of(ceilingEntry.getValue());
+        : Optional.<Input.Token>of(ceilingEntry.getValue());
   }
 
   /**
    * Does this list of {@link ASTNode}s ends with the specified token?
+   *
    * @param input the {@link Input}
    * @param nodes list of {@link ASTNode}s
    * @return whether the list has an extra trailing comma
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20128588.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20128588.output
index a8f5cca..bf097ba 100644
--- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20128588.output
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20128588.output
@@ -126,8 +126,9 @@
     in = edu.oswego.cs.dl.util.concurrent.F.class,
     enable = false,
     methods = {
-      "foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo",
-      "foo", "foo",
+      "foo", "foo", "foo", "foo", "foo",
+      "foo", "foo", "foo", "foo", "foo",
+      "foo", "foo", "foo", "foo", "foo",
     }
   ),
   @Mirror(
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20341001.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20341001.output
index 6986680..09a8f1b 100644
--- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20341001.output
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20341001.output
@@ -1,10 +1,13 @@
 public class B20341001 {
   int[] xs = {
     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1,
     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1,
     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1,
     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-    1, 1, 1, 1
+        1
   };
   int[] xs = {
     Foo.CONSTxx,
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B22815364.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B22815364.input
new file mode 100644
index 0000000..44f6b7c
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B22815364.input
@@ -0,0 +1,46 @@
+class B22815364 {
+  @Xxx({
+      "=0|=0", "Xxx xxxx xx xxxxxxxx xx xxxxxxxxxxxxx.",
+      "=0|xxx", "Xxx xxxx x xxxxxxxxxxxx.",
+      "xxx|=0", "Xxx xxxx x xxxxxxx.",
+      "xxx|xxx", "Xxx xxxx xxx xxxxxxx xxx xxx xxxxxxxxxxxx.",
+      "xxxxx|xxx", "Xxx xxxx {0} xxxxxxxx xxx xxx xxxxxxxxxxxx.",
+      "xxx|xxxxx", "Xxx xxxx xxx xxxxxxx xxx {1} xxxxxxxxxxxxx."
+  })
+  int x;
+
+  @Xxx({
+    "a", "b",
+    "c", "d",
+    //
+  })
+  int y;
+
+  int[] xs = {
+    1, 2, 3,
+    1, 2, 3,
+    1, 2, 3,
+  };
+
+  int[] xs = {
+    1, 2, 3,
+    1, 2, 3,
+    1, 2,
+  };
+
+  int[] xs = {
+    1, 2,
+    1,
+  };
+
+  int[][] xs = {
+    {11111111111111111111, 11111111111111111111, 11111111111111111111, 11111111111111111111,
+        11111111111111111111, 11111111111111111111, 11111111111111111111}, {11111111111111111111,
+        11111111111111111111, 11111111111111111111, 11111111111111111111, 11111111111111111111,
+        11111111111111111111, 11111111111111111111},
+    {11111111111111111111, 11111111111111111111, 11111111111111111111, 11111111111111111111,
+        11111111111111111111, 11111111111111111111, 11111111111111111111}, {11111111111111111111,
+        11111111111111111111, 11111111111111111111, 11111111111111111111, 11111111111111111111,
+        11111111111111111111, 11111111111111111111},
+  };
+}
diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B22815364.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B22815364.output
new file mode 100644
index 0000000..5d84052
--- /dev/null
+++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B22815364.output
@@ -0,0 +1,73 @@
+class B22815364 {
+  @Xxx({
+    "=0|=0", "Xxx xxxx xx xxxxxxxx xx xxxxxxxxxxxxx.",
+    "=0|xxx", "Xxx xxxx x xxxxxxxxxxxx.",
+    "xxx|=0", "Xxx xxxx x xxxxxxx.",
+    "xxx|xxx", "Xxx xxxx xxx xxxxxxx xxx xxx xxxxxxxxxxxx.",
+    "xxxxx|xxx", "Xxx xxxx {0} xxxxxxxx xxx xxx xxxxxxxxxxxx.",
+    "xxx|xxxxx", "Xxx xxxx xxx xxxxxxx xxx {1} xxxxxxxxxxxxx."
+  })
+  int x;
+
+  @Xxx({
+    "a", "b",
+    "c", "d",
+    //
+  })
+  int y;
+
+  int[] xs = {
+    1, 2, 3,
+    1, 2, 3,
+    1, 2, 3,
+  };
+
+  int[] xs = {
+    1, 2, 3,
+    1, 2, 3,
+    1, 2,
+  };
+
+  int[] xs = {
+    1, 2, 1,
+  };
+
+  int[][] xs = {
+    {
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111
+    },
+    {
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111
+    },
+    {
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111
+    },
+    {
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111,
+      11111111111111111111
+    },
+  };
+}