Bug 220713 - [formatter] Formatting of array initializers in method
calls

Change-Id: Ia72181677ccf63c84b3ac6012d183b2f35700104
Signed-off-by: Mateusz Matela <mateusz.matela@gmail.com>
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterBugsTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterBugsTests.java
index 96e0469..3296f5a 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterBugsTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterBugsTests.java
@@ -13036,4 +13036,23 @@
 		"\n" + 
 		"}}");
 }
+/**
+ * https://bugs.eclipse.org/220713 - [formatter] Formatting of array initializers in method calls
+ */
+public void testBug220713() {
+	this.formatterPrefs.alignment_for_arguments_in_method_invocation = Alignment.M_NEXT_PER_LINE_SPLIT | Alignment.M_INDENT_ON_COLUMN | Alignment.M_FORCE;
+	this.formatterPrefs.alignment_for_expressions_in_array_initializer = Alignment.M_NEXT_SHIFTED_SPLIT | Alignment.M_INDENT_ON_COLUMN | Alignment.M_FORCE;
+	this.formatterPrefs.insert_new_line_before_closing_brace_in_array_initializer = true;
+	formatSource(
+		"public class A {\n" + 
+		"	void f() {\n" + 
+		"		methodWithArrays(	new Object[] {\n" + 
+		"											null,\n" + 
+		"							},\n" + 
+		"							new Object[] {\n" + 
+		"											null,\n" + 
+		"							});\n" + 
+		"	}\n" + 
+		"}");
+}
 }
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java
index 2fb28a1..d3b453f 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java
@@ -26,15 +26,15 @@
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
-
+import java.util.function.Predicate;
 import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
 import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions;
 import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions.Alignment;
 import org.eclipse.jdt.internal.formatter.Token;
-import org.eclipse.jdt.internal.formatter.TokenManager;
-import org.eclipse.jdt.internal.formatter.TokenTraverser;
 import org.eclipse.jdt.internal.formatter.Token.WrapMode;
 import org.eclipse.jdt.internal.formatter.Token.WrapPolicy;
+import org.eclipse.jdt.internal.formatter.TokenManager;
+import org.eclipse.jdt.internal.formatter.TokenTraverser;
 
 public class WrapExecutor {
 
@@ -227,6 +227,7 @@
 
 	private class WrapsApplier extends TokenTraverser {
 
+		private final TokenManager tm2 = WrapExecutor.this.tm;
 		private ArrayDeque<Token> stack = new ArrayDeque<>();
 		private int initialIndent;
 		private int currentIndent;
@@ -242,22 +243,57 @@
 				newLine(token, index);
 			} else if ((this.nextWrap != null && index == this.nextWrap.wrapTokenIndex)
 					|| checkForceWrap(token, index, this.currentIndent)
-					|| (token.isNextLineOnWrap() && WrapExecutor.this.tm
-							.get(WrapExecutor.this.tm.findFirstTokenInLine(index)).isWrappable())) {
+					|| (token.isNextLineOnWrap() && this.tm2.get(this.tm2.findFirstTokenInLine(index)).isWrappable())) {
 				token.breakBefore();
 				newLine(token, index);
 			} else {
+				checkOnColumnAlign(token, index);
 				setIndent(token, this.currentIndent);
 			}
 			return true;
 		}
 
+		private void checkOnColumnAlign(Token token, int index) {
+			// if some further tokens in a group are wrapped on column,
+			// the first one should be aligned on column even if it's not wrapped
+			WrapPolicy wrapPolicy = token.getWrapPolicy();
+			if (wrapPolicy == null || !wrapPolicy.indentOnColumn || !wrapPolicy.isFirstInGroup)
+				return;
+			int positionInLine = this.tm2.getPositionInLine(index);
+			if (this.tm2.toIndent(positionInLine, true) == positionInLine)
+				return;
+
+			Predicate<Token> aligner = t -> {
+				WrapPolicy wp = t.getWrapPolicy();
+				if (wp != null && wp.indentOnColumn && wp.wrapParentIndex == wrapPolicy.wrapParentIndex) {
+					this.currentIndent = this.tm2.toIndent(positionInLine, true);
+					token.setAlign(this.currentIndent);
+					this.stack.push(token);
+					return true;
+				}
+				return false;
+			};
+
+			// check all future wraps
+			WrapInfo furtherWrap = this.nextWrap;
+			while (furtherWrap != null) {
+				if (aligner.test(this.tm2.get(furtherWrap.wrapTokenIndex)))
+					return;
+				furtherWrap = WrapExecutor.this.wrapSearchResults.get(furtherWrap).nextWrap;
+			}
+			// check all tokens that are already wrapped
+			for (int i = index; i <= wrapPolicy.groupEndIndex; i++) {
+				Token t = this.tm2.get(i);
+				if (t.getLineBreaksBefore() > 0 && aligner.test(t))
+					return;
+			}
+		}
+
 		private void newLine(Token token, int index) {
 			while (!this.stack.isEmpty() && index > this.stack.peek().getWrapPolicy().groupEndIndex)
 				this.stack.pop();
 			if (token.getWrapPolicy() != null) {
 				setIndent(token, getWrapIndent(token));
-				handleOnColumnIndent(index, token.getWrapPolicy());
 				this.stack.push(token);
 			} else if (this.stack.isEmpty()) {
 				this.initialIndent = token.getIndent();
@@ -638,23 +674,6 @@
 		return result;
 	}
 
-	void handleOnColumnIndent(int tokenIndex, WrapPolicy wrapPolicy) {
-		if (wrapPolicy != null && wrapPolicy.indentOnColumn && !wrapPolicy.isFirstInGroup
-				&& this.options.tab_char == DefaultCodeFormatterOptions.TAB
-				&& !this.options.use_tabs_only_for_leading_indentations) {
-			// special case: first wrap in a group should be aligned on column even if it's not wrapped
-			for (int i = tokenIndex - 1; i >= 0; i--) {
-				Token token = this.tm.get(i);
-				WrapPolicy wrapPolicy2 = token.getWrapPolicy();
-				if (wrapPolicy2 != null && wrapPolicy2.isFirstInGroup
-						&& wrapPolicy2.wrapParentIndex == wrapPolicy.wrapParentIndex) {
-					token.setAlign(getWrapIndent(token));
-					break;
-				}
-			}
-		}
-	}
-
 	int getWrapIndent(Token token) {
 		WrapPolicy policy = token.getWrapPolicy();
 		if (policy == null)