update jdt.core to 4.9 RC1
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java
index 687a381..4784d9b 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java
@@ -14259,4 +14259,114 @@
 	formatSource(input, getCompilationUnit("Formatter", "", "test104910", "G_out.java").getSource());
 }
 
+/**
+ * https://bugs.eclipse.org/530756 - [formatter] Align fields in columns: add option to use spaces
+ */
+public void testBug530756a() throws JavaModelException {
+	this.formatterPrefs.tab_char = DefaultCodeFormatterOptions.TAB;
+	this.formatterPrefs.align_type_members_on_columns = true;
+	String input = getCompilationUnit("Formatter", "", "test530756", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test530756", "A_out.java").getSource());
+}
+/**
+ * https://bugs.eclipse.org/530756 - [formatter] Align fields in columns: add option to use spaces
+ */
+public void testBug530756b() throws JavaModelException {
+	this.formatterPrefs.tab_char = DefaultCodeFormatterOptions.TAB;
+	this.formatterPrefs.align_type_members_on_columns = true;
+	this.formatterPrefs.align_with_spaces = true;
+	String input = getCompilationUnit("Formatter", "", "test530756", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test530756", "B_out.java").getSource());
+}
+/**
+ * https://bugs.eclipse.org/530756 - [formatter] Align fields in columns: add option to use spaces
+ */
+public void testBug530756c() throws JavaModelException {
+	this.formatterPrefs.tab_char = DefaultCodeFormatterOptions.MIXED;
+	this.formatterPrefs.indentation_size = 6;
+	this.formatterPrefs.align_type_members_on_columns = true;
+	this.formatterPrefs.align_with_spaces = true;
+	String input = getCompilationUnit("Formatter", "", "test530756", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test530756", "C_out.java").getSource());
+}
+/**
+ * https://bugs.eclipse.org/530756 - [formatter] Align fields in columns: add option to use spaces
+ */
+public void testBug530756d() throws JavaModelException {
+	this.formatterPrefs.tab_char = DefaultCodeFormatterOptions.SPACE;
+	this.formatterPrefs.align_type_members_on_columns = true;
+	this.formatterPrefs.align_with_spaces = true;
+	String input = getCompilationUnit("Formatter", "", "test530756", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test530756", "D_out.java").getSource());
+}
+/**
+ * https://bugs.eclipse.org/530756 - [formatter] Align fields in columns: add option to use spaces
+ */
+public void testBug530756e() throws JavaModelException {
+	this.formatterPrefs.tab_char = DefaultCodeFormatterOptions.SPACE;
+	this.formatterPrefs.align_type_members_on_columns = true;
+	this.formatterPrefs.align_with_spaces = false;
+	String input = getCompilationUnit("Formatter", "", "test530756", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test530756", "E_out.java").getSource());
+}
+
+/**
+ * https://bugs.eclipse.org/131292 - [format] align assignments in columns
+ */
+public void testBug131292a() throws JavaModelException {
+	this.formatterPrefs.align_type_members_on_columns = true;
+	String input = getCompilationUnit("Formatter", "", "test131292", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test131292", "A_out.java").getSource());
+}
+/**
+ * https://bugs.eclipse.org/131292 - [format] align assignments in columns
+ */
+public void testBug131292b() throws JavaModelException {
+	this.formatterPrefs.align_variable_declarations_on_columns = true;
+	String input = getCompilationUnit("Formatter", "", "test131292", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test131292", "B_out.java").getSource());
+}
+/**
+ * https://bugs.eclipse.org/131292 - [format] align assignments in columns
+ */
+public void testBug131292c() throws JavaModelException {
+	this.formatterPrefs.align_assignment_statements_on_columns = true;
+	String input = getCompilationUnit("Formatter", "", "test131292", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test131292", "C_out.java").getSource());
+}
+/**
+ * https://bugs.eclipse.org/131292 - [format] align assignments in columns
+ */
+public void testBug131292d() throws JavaModelException {
+	this.formatterPrefs.align_type_members_on_columns = true;
+	this.formatterPrefs.align_variable_declarations_on_columns = true;
+	this.formatterPrefs.align_assignment_statements_on_columns = true;
+	this.formatterPrefs.tab_char = DefaultCodeFormatterOptions.SPACE;
+	String input = getCompilationUnit("Formatter", "", "test131292", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test131292", "D_out.java").getSource());
+}
+/**
+ * https://bugs.eclipse.org/131292 - [format] align assignments in columns
+ */
+public void testBug131292e() throws JavaModelException {
+	this.formatterPrefs.align_type_members_on_columns = true;
+	this.formatterPrefs.align_variable_declarations_on_columns = true;
+	this.formatterPrefs.align_assignment_statements_on_columns = true;
+	this.formatterPrefs.tab_char = DefaultCodeFormatterOptions.MIXED;
+	this.formatterPrefs.align_fields_grouping_blank_lines = 1;
+	String input = getCompilationUnit("Formatter", "", "test131292", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test131292", "E_out.java").getSource());
+}
+/**
+ * https://bugs.eclipse.org/131292 - [format] align assignments in columns
+ */
+public void testBug131292f() throws JavaModelException {
+	this.formatterPrefs.align_type_members_on_columns = true;
+	this.formatterPrefs.align_variable_declarations_on_columns = true;
+	this.formatterPrefs.align_assignment_statements_on_columns = true;
+	this.formatterPrefs.align_with_spaces = true;
+	this.formatterPrefs.align_fields_grouping_blank_lines = 2;
+	String input = getCompilationUnit("Formatter", "", "test131292", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test131292", "F_out.java").getSource());
+}
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java
index c06d8bf..937dd6b 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java
@@ -2323,4 +2323,44 @@
 		CompilationUnit reconciled = cu.reconcile(getJSL9(), true, null, new NullProgressMonitor());
 		assertNoProblems(reconciled.getProblems());
 	}
+
+	// Bug 522377 - [null] String.format(""...) shows warning
+	public void testVargs() throws CoreException, IOException {
+		myCreateJavaProject("TestLibs");
+		String lib1Content =
+				"package libs;\n" + 
+				"public abstract class MyString {\n" +
+				"   public static String format(String format, Object... args) {\n" + 
+				"		return null;\n" +
+				"	}\n" +
+				"}\n";
+		addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots", new String[] {
+				"/UnannotatedLib/libs/MyString.java",
+				lib1Content								
+			}, null);
+		createFileInProject("annots/libs", "MyString.eea",
+				"class libs/MyString\n" + 
+				"\n" + 
+				"format\n" + 
+				" (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;\n" + 
+				" (Ljava/lang/String;[Ljava/lang/Object;)L1java/lang/String;\n" + 
+				"\n");
+
+		// type check sources:
+		IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("tests", true, null);
+		ICompilationUnit cu = fragment.createCompilationUnit("Test1.java",
+				"package tests;\n" +
+				"import libs.MyString;\n" + 
+				"\n" + 
+				"import org.eclipse.jdt.annotation.*;\n" + 
+				"\n" + 
+				"public class Test1 {\n" + 
+				"  public @NonNull String test(int var) {\n" + 
+				"    return MyString.format(\"que%03d\", var);\n" + 
+				"  }\n" + 
+				"}\n",
+				true, new NullProgressMonitor()).getWorkingCopy(new NullProgressMonitor());
+		CompilationUnit reconciled = cu.reconcile(getJSL9(), true, null, new NullProgressMonitor());
+		assertNoProblems(reconciled.getProblems());
+	}
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaProjectTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaProjectTests.java
index e4b81e3..8938164 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaProjectTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaProjectTests.java
@@ -1627,8 +1627,8 @@
 		try {
 			zip = new ZipOutputStream(new FileOutputStream(getExternalFile("lib.jar")));
 			// the bug occurred only if META-INF/MANIFEST.MF was before META-INF in the ZIP file
+			// Altered the test for 534624. Usage of Zip file system for traversal no longer sees two different entries, but just the file.
 			zip.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF"));
-			zip.putNextEntry(new ZipEntry("META-INF"));
 		} finally {
 			if (zip != null)
 				zip.close();
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NameLookupTests2.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NameLookupTests2.java
index 1dd9354..bdcf342 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NameLookupTests2.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NameLookupTests2.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/A_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/A_out.java
new file mode 100644
index 0000000..ff98217
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/A_out.java
@@ -0,0 +1,177 @@
+class Example {
+
+	a						b;
+	a						b				= c;
+	int						i;
+	int						j				= 5;
+	private String			someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+			+ "12345678901234567890";
+	private final Object	someObjetc		= new Object() {
+												@Override
+												public String toString() {
+													return super.toString();
+												}
+											};
+
+	void variables() {
+		a b;
+		a b = c;
+		int i;
+		int j = 5;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void variablesReordered() {
+		a b = c;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int j = 5;
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		int i;
+		a b;
+	}
+
+	void variablesGaps() {
+		a b = c;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+
+		int j = 5;
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+
+		// big gap
+
+		int i;
+		a b;
+	}
+
+	void variablesComments() {
+		a /* c1 */ b; // c1
+		a /* c123 */ b /* c123 */ = /* c123 */ c; // c123
+		int /* */ i; /* */
+		int j = 5; /* */
+		String /* c */ someLongString /* c */ = /* c */ "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890" + "12345678901234567890";
+		/* ... */ final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void variablesAnnotations() {
+		@SuppressWarnings
+		a b = c;
+		@Target
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int j = 5;
+		@Deprecated
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		int i;
+		@Test
+		a b;
+	}
+
+	void assignments() {
+		b = c;
+		j = 5;
+		myInteger = 56436345;
+		myOtehrInteger >>= 35534525543;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void assignmentsReordered() {
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		b = c;
+		myInteger = 56436345;
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		myOtehrInteger >>= 35534525543;
+		j = 5;
+	}
+
+	void assignmentsGaps() {
+		b = c;
+		myInteger = 56436345;
+
+		myOtehrInteger >>= 35534525543;
+		j = 5;
+
+		// big gap
+
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+	}
+
+	void assignmentsComments() {
+		/* c1 */ b /* c1 */ = /* c1 */c;// c1
+		j /* */ = /* c12345 */5; // c12345
+		myInteger /* */ = 56436345;
+		/* */ myOtehrInteger >>= 35534525543;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890"; /* ... */
+		someObjetc = /* !!! */ new Object() { // !!!
+			@Override
+			public String toString() {
+				return super.toString(); // !!!
+			}
+		};
+	}
+
+	void mixed() {
+		a b;
+		a b = c;
+		int i;
+		j = 5;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/B_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/B_out.java
new file mode 100644
index 0000000..cc1c279
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/B_out.java
@@ -0,0 +1,177 @@
+class Example {
+
+	a b;
+	a b = c;
+	int i;
+	int j = 5;
+	private String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+			+ "12345678901234567890";
+	private final Object someObjetc = new Object() {
+		@Override
+		public String toString() {
+			return super.toString();
+		}
+	};
+
+	void variables() {
+		a				b;
+		a				b				= c;
+		int				i;
+		int				j				= 5;
+		String			someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object	someObjetc		= new Object() {
+											@Override
+											public String toString() {
+												return super.toString();
+											}
+										};
+	}
+
+	void variablesReordered() {
+		a				b				= c;
+		String			someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int				j				= 5;
+		final Object	someObjetc		= new Object() {
+											@Override
+											public String toString() {
+												return super.toString();
+											}
+										};
+		int				i;
+		a				b;
+	}
+
+	void variablesGaps() {
+		a				b				= c;
+		String			someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+
+		int				j				= 5;
+		final Object	someObjetc		= new Object() {
+											@Override
+											public String toString() {
+												return super.toString();
+											}
+										};
+
+		// big gap
+
+		int				i;
+		a				b;
+	}
+
+	void variablesComments() {
+		a /* c1 */				b;																					// c1
+		a /* c123 */			b /* c123 */			= /* c123 */ c;												// c123
+		int /* */				i;																					/* */
+		int						j						= 5;														/* */
+		String /* c */			someLongString /* c */	= /* c */ "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890" + "12345678901234567890";
+		/* ... */ final Object	someObjetc				= new Object() {
+															@Override
+															public String toString() {
+																return super.toString();
+															}
+														};
+	}
+
+	void variablesAnnotations() {
+		@SuppressWarnings
+		a				b				= c;
+		@Target
+		String			someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int				j				= 5;
+		@Deprecated
+		final Object	someObjetc		= new Object() {
+											@Override
+											public String toString() {
+												return super.toString();
+											}
+										};
+		int				i;
+		@Test
+		a				b;
+	}
+
+	void assignments() {
+		b = c;
+		j = 5;
+		myInteger = 56436345;
+		myOtehrInteger >>= 35534525543;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void assignmentsReordered() {
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		b = c;
+		myInteger = 56436345;
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		myOtehrInteger >>= 35534525543;
+		j = 5;
+	}
+
+	void assignmentsGaps() {
+		b = c;
+		myInteger = 56436345;
+
+		myOtehrInteger >>= 35534525543;
+		j = 5;
+
+		// big gap
+
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+	}
+
+	void assignmentsComments() {
+		/* c1 */ b /* c1 */ = /* c1 */c;// c1
+		j /* */ = /* c12345 */5; // c12345
+		myInteger /* */ = 56436345;
+		/* */ myOtehrInteger >>= 35534525543;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890"; /* ... */
+		someObjetc = /* !!! */ new Object() { // !!!
+			@Override
+			public String toString() {
+				return super.toString(); // !!!
+			}
+		};
+	}
+
+	void mixed() {
+		a	b;
+		a	b	= c;
+		int	i;
+		j = 5;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/C_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/C_out.java
new file mode 100644
index 0000000..a84c6d0
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/C_out.java
@@ -0,0 +1,177 @@
+class Example {
+
+	a b;
+	a b = c;
+	int i;
+	int j = 5;
+	private String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+			+ "12345678901234567890";
+	private final Object someObjetc = new Object() {
+		@Override
+		public String toString() {
+			return super.toString();
+		}
+	};
+
+	void variables() {
+		a b;
+		a b = c;
+		int i;
+		int j = 5;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void variablesReordered() {
+		a b = c;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int j = 5;
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		int i;
+		a b;
+	}
+
+	void variablesGaps() {
+		a b = c;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+
+		int j = 5;
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+
+		// big gap
+
+		int i;
+		a b;
+	}
+
+	void variablesComments() {
+		a /* c1 */ b; // c1
+		a /* c123 */ b /* c123 */ = /* c123 */ c; // c123
+		int /* */ i; /* */
+		int j = 5; /* */
+		String /* c */ someLongString /* c */ = /* c */ "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890" + "12345678901234567890";
+		/* ... */ final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void variablesAnnotations() {
+		@SuppressWarnings
+		a b = c;
+		@Target
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int j = 5;
+		@Deprecated
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		int i;
+		@Test
+		a b;
+	}
+
+	void assignments() {
+		b				= c;
+		j				= 5;
+		myInteger		= 56436345;
+		myOtehrInteger	>>= 35534525543;
+		someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		someObjetc		= new Object() {
+							@Override
+							public String toString() {
+								return super.toString();
+							}
+						};
+	}
+
+	void assignmentsReordered() {
+		someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		b				= c;
+		myInteger		= 56436345;
+		someObjetc		= new Object() {
+							@Override
+							public String toString() {
+								return super.toString();
+							}
+						};
+		myOtehrInteger	>>= 35534525543;
+		j				= 5;
+	}
+
+	void assignmentsGaps() {
+		b				= c;
+		myInteger		= 56436345;
+
+		myOtehrInteger	>>= 35534525543;
+		j				= 5;
+
+		// big gap
+
+		someObjetc		= new Object() {
+							@Override
+							public String toString() {
+								return super.toString();
+							}
+						};
+		someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+	}
+
+	void assignmentsComments() {
+		/* c1 */ b /* c1 */		= /* c1 */c;																// c1
+		j /* */					= /* c12345 */5;															// c12345
+		myInteger /* */			= 56436345;
+		/* */ myOtehrInteger	>>= 35534525543;
+		someLongString			= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";																	/* ... */
+		someObjetc				= /* !!! */ new Object() {													// !!!
+									@Override
+									public String toString() {
+										return super.toString();											// !!!
+									}
+								};
+	}
+
+	void mixed() {
+		a b;
+		a b = c;
+		int i;
+		j				= 5;
+		someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/D_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/D_out.java
new file mode 100644
index 0000000..7358615
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/D_out.java
@@ -0,0 +1,177 @@
+class Example {
+
+    a                    b;
+    a                    b              = c;
+    int                  i;
+    int                  j              = 5;
+    private String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+            + "12345678901234567890";
+    private final Object someObjetc     = new Object() {
+                                            @Override
+                                            public String toString() {
+                                                return super.toString();
+                                            }
+                                        };
+
+    void variables() {
+        a            b;
+        a            b              = c;
+        int          i;
+        int          j              = 5;
+        String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+        final Object someObjetc     = new Object() {
+                                        @Override
+                                        public String toString() {
+                                            return super.toString();
+                                        }
+                                    };
+    }
+
+    void variablesReordered() {
+        a            b              = c;
+        String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+        int          j              = 5;
+        final Object someObjetc     = new Object() {
+                                        @Override
+                                        public String toString() {
+                                            return super.toString();
+                                        }
+                                    };
+        int          i;
+        a            b;
+    }
+
+    void variablesGaps() {
+        a            b              = c;
+        String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+
+        int          j              = 5;
+        final Object someObjetc     = new Object() {
+                                        @Override
+                                        public String toString() {
+                                            return super.toString();
+                                        }
+                                    };
+
+        // big gap
+
+        int          i;
+        a            b;
+    }
+
+    void variablesComments() {
+        a /* c1 */             b;                                                                              // c1
+        a /* c123 */           b /* c123 */           = /* c123 */ c;                                          // c123
+        int /* */              i;                                                                              /* */
+        int                    j                      = 5;                                                     /* */
+        String /* c */         someLongString /* c */ = /* c */ "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890" + "12345678901234567890";
+        /* ... */ final Object someObjetc             = new Object() {
+                                                          @Override
+                                                          public String toString() {
+                                                              return super.toString();
+                                                          }
+                                                      };
+    }
+
+    void variablesAnnotations() {
+        @SuppressWarnings
+        a            b              = c;
+        @Target
+        String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+        int          j              = 5;
+        @Deprecated
+        final Object someObjetc     = new Object() {
+                                        @Override
+                                        public String toString() {
+                                            return super.toString();
+                                        }
+                                    };
+        int          i;
+        @Test
+        a            b;
+    }
+
+    void assignments() {
+        b                = c;
+        j                = 5;
+        myInteger        = 56436345;
+        myOtehrInteger >>= 35534525543;
+        someLongString   = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+        someObjetc       = new Object() {
+                             @Override
+                             public String toString() {
+                                 return super.toString();
+                             }
+                         };
+    }
+
+    void assignmentsReordered() {
+        someLongString   = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+        b                = c;
+        myInteger        = 56436345;
+        someObjetc       = new Object() {
+                             @Override
+                             public String toString() {
+                                 return super.toString();
+                             }
+                         };
+        myOtehrInteger >>= 35534525543;
+        j                = 5;
+    }
+
+    void assignmentsGaps() {
+        b                = c;
+        myInteger        = 56436345;
+
+        myOtehrInteger >>= 35534525543;
+        j                = 5;
+
+        // big gap
+
+        someObjetc       = new Object() {
+                             @Override
+                             public String toString() {
+                                 return super.toString();
+                             }
+                         };
+        someLongString   = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+    }
+
+    void assignmentsComments() {
+        /* c1 */ b /* c1 */    = /* c1 */c;                                                              // c1
+        j /* */                = /* c12345 */5;                                                          // c12345
+        myInteger /* */        = 56436345;
+        /* */ myOtehrInteger >>= 35534525543;
+        someLongString         = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";                                                                /* ... */
+        someObjetc             = /* !!! */ new Object() {                                                // !!!
+                                   @Override
+                                   public String toString() {
+                                       return super.toString();                                          // !!!
+                                   }
+                               };
+    }
+
+    void mixed() {
+        a   b;
+        a   b = c;
+        int i;
+        j              = 5;
+        someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+        final Object someObjetc = new Object() {
+            @Override
+            public String toString() {
+                return super.toString();
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/E_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/E_out.java
new file mode 100644
index 0000000..29b1900
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/E_out.java
@@ -0,0 +1,177 @@
+class Example {
+
+	a					 b;
+	a					 b				= c;
+	int					 i;
+	int					 j				= 5;
+	private String		 someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+			+ "12345678901234567890";
+	private final Object someObjetc		= new Object() {
+											@Override
+											public String toString() {
+												return super.toString();
+											}
+										};
+
+	void variables() {
+		a			 b;
+		a			 b				= c;
+		int			 i;
+		int			 j				= 5;
+		String		 someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc		= new Object() {
+										@Override
+										public String toString() {
+											return super.toString();
+										}
+									};
+	}
+
+	void variablesReordered() {
+		a			 b				= c;
+		String		 someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int			 j				= 5;
+		final Object someObjetc		= new Object() {
+										@Override
+										public String toString() {
+											return super.toString();
+										}
+									};
+		int			 i;
+		a			 b;
+	}
+
+	void variablesGaps() {
+		a	   b			  = c;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+
+		int			 j			= 5;
+		final Object someObjetc	= new Object() {
+									@Override
+									public String toString() {
+										return super.toString();
+									}
+								};
+
+		// big gap
+
+		int	i;
+		a	b;
+	}
+
+	void variablesComments() {
+		a /* c1 */			   b;																			   // c1
+		a /* c123 */		   b /* c123 */			  = /* c123 */ c;										   // c123
+		int /* */			   i;																			   /* */
+		int					   j					  = 5;													   /* */
+		String /* c */		   someLongString /* c */ = /* c */ "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890" + "12345678901234567890";
+		/* ... */ final Object someObjetc			  = new Object() {
+														  @Override
+														  public String toString() {
+															  return super.toString();
+														  }
+													  };
+	}
+
+	void variablesAnnotations() {
+		@SuppressWarnings
+		a			 b				= c;
+		@Target
+		String		 someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int			 j				= 5;
+		@Deprecated
+		final Object someObjetc		= new Object() {
+										@Override
+										public String toString() {
+											return super.toString();
+										}
+									};
+		int			 i;
+		@Test
+		a			 b;
+	}
+
+	void assignments() {
+		b				 = c;
+		j				 = 5;
+		myInteger		 = 56436345;
+		myOtehrInteger >>= 35534525543;
+		someLongString	 = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		someObjetc		 = new Object() {
+							 @Override
+							 public String toString() {
+								 return super.toString();
+							 }
+						 };
+	}
+
+	void assignmentsReordered() {
+		someLongString	 = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		b				 = c;
+		myInteger		 = 56436345;
+		someObjetc		 = new Object() {
+							 @Override
+							 public String toString() {
+								 return super.toString();
+							 }
+						 };
+		myOtehrInteger >>= 35534525543;
+		j				 = 5;
+	}
+
+	void assignmentsGaps() {
+		b		  = c;
+		myInteger = 56436345;
+
+		myOtehrInteger >>= 35534525543;
+		j				 = 5;
+
+		// big gap
+
+		someObjetc	   = new Object() {
+						   @Override
+						   public String toString() {
+							   return super.toString();
+						   }
+					   };
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+	}
+
+	void assignmentsComments() {
+		/* c1 */ b /* c1 */	   = /* c1 */c;																 // c1
+		j /* */				   = /* c12345 */5;															 // c12345
+		myInteger /* */		   = 56436345;
+		/* */ myOtehrInteger >>= 35534525543;
+		someLongString		   = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";																 /* ... */
+		someObjetc			   = /* !!! */ new Object() {												 // !!!
+								   @Override
+								   public String toString() {
+									   return super.toString();											 // !!!
+								   }
+							   };
+	}
+
+	void mixed() {
+		a	b;
+		a	b = c;
+		int	i;
+		j			   = 5;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/F_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/F_out.java
new file mode 100644
index 0000000..8f70457
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/F_out.java
@@ -0,0 +1,177 @@
+class Example {
+
+	a                    b;
+	a                    b              = c;
+	int                  i;
+	int                  j              = 5;
+	private String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+			+ "12345678901234567890";
+	private final Object someObjetc     = new Object() {
+											@Override
+											public String toString() {
+												return super.toString();
+											}
+										};
+
+	void variables() {
+		a            b;
+		a            b              = c;
+		int          i;
+		int          j              = 5;
+		String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc     = new Object() {
+										@Override
+										public String toString() {
+											return super.toString();
+										}
+									};
+	}
+
+	void variablesReordered() {
+		a            b              = c;
+		String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int          j              = 5;
+		final Object someObjetc     = new Object() {
+										@Override
+										public String toString() {
+											return super.toString();
+										}
+									};
+		int          i;
+		a            b;
+	}
+
+	void variablesGaps() {
+		a            b              = c;
+		String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+
+		int          j              = 5;
+		final Object someObjetc     = new Object() {
+										@Override
+										public String toString() {
+											return super.toString();
+										}
+									};
+
+		// big gap
+
+		int i;
+		a   b;
+	}
+
+	void variablesComments() {
+		a /* c1 */             b;                                                                              // c1
+		a /* c123 */           b /* c123 */           = /* c123 */ c;                                          // c123
+		int /* */              i;                                                                              /* */
+		int                    j                      = 5;                                                     /* */
+		String /* c */         someLongString /* c */ = /* c */ "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890" + "12345678901234567890";
+		/* ... */ final Object someObjetc             = new Object() {
+															@Override
+															public String toString() {
+																return super.toString();
+															}
+														};
+	}
+
+	void variablesAnnotations() {
+		@SuppressWarnings
+		a            b              = c;
+		@Target
+		String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int          j              = 5;
+		@Deprecated
+		final Object someObjetc     = new Object() {
+										@Override
+										public String toString() {
+											return super.toString();
+										}
+									};
+		int          i;
+		@Test
+		a            b;
+	}
+
+	void assignments() {
+		b                = c;
+		j                = 5;
+		myInteger        = 56436345;
+		myOtehrInteger >>= 35534525543;
+		someLongString   = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		someObjetc       = new Object() {
+								@Override
+								public String toString() {
+									return super.toString();
+								}
+							};
+	}
+
+	void assignmentsReordered() {
+		someLongString   = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		b                = c;
+		myInteger        = 56436345;
+		someObjetc       = new Object() {
+								@Override
+								public String toString() {
+									return super.toString();
+								}
+							};
+		myOtehrInteger >>= 35534525543;
+		j                = 5;
+	}
+
+	void assignmentsGaps() {
+		b                = c;
+		myInteger        = 56436345;
+
+		myOtehrInteger >>= 35534525543;
+		j                = 5;
+
+		// big gap
+
+		someObjetc     = new Object() {
+							@Override
+							public String toString() {
+								return super.toString();
+							}
+						};
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+	}
+
+	void assignmentsComments() {
+		/* c1 */ b /* c1 */    = /* c1 */c;                                                              // c1
+		j /* */                = /* c12345 */5;                                                          // c12345
+		myInteger /* */        = 56436345;
+		/* */ myOtehrInteger >>= 35534525543;
+		someLongString         = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";                                                                /* ... */
+		someObjetc             = /* !!! */ new Object() {                                                // !!!
+									@Override
+									public String toString() {
+										return super.toString();                                         // !!!
+									}
+								};
+	}
+
+	void mixed() {
+		a   b;
+		a   b = c;
+		int i;
+		j              = 5;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/in.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/in.java
new file mode 100644
index 0000000..58f3b88
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/in.java
@@ -0,0 +1,166 @@
+class Example {
+
+	a b;
+	a b = c;
+	int i;
+	int j = 5;
+	private String someLongString = "12345678901234567890"
+			+ "12345678901234567890" + "12345678901234567890"
+			+ "12345678901234567890";
+	private final Object someObjetc = new Object() {
+		@Override
+		public String toString() {
+			return super.toString();
+		}
+	};
+
+	void variables() {
+		a b;
+		a b = c;
+		int i;
+		int j = 5;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void variablesReordered() {
+		a b = c;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		int j = 5;
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		int i;
+		a b;
+	}
+
+	void variablesGaps() {
+		a b = c;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		
+		int j = 5;
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		
+		// big gap
+		
+		int i;
+		a b;
+	}
+
+	void variablesComments() {
+		a /* c1 */ b; // c1
+		a /* c123 */b /* c123 */ = /* c123 */ c; // c123
+		int /* */i; /* */
+		int j = 5; /* */
+		String /* c */ someLongString /* c */ = /* c */ "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		/* ... */ final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+	
+	void variablesAnnotations() {
+		@SuppressWarnings
+		a b = c;
+		@Target
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		int j = 5;
+		@Deprecated
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		int i;
+		@Test
+		a b;
+	}
+
+	void assignments() {
+		b = c;
+		j = 5;
+		myInteger = 56436345;
+		myOtehrInteger >>= 35534525543;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void assignmentsReordered() {
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		b = c;
+		myInteger = 56436345;
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		myOtehrInteger >>= 35534525543;
+		j = 5;
+	}
+	void assignmentsGaps() {
+		b = c;
+		myInteger = 56436345;
+		
+		myOtehrInteger >>= 35534525543;
+		j = 5;
+
+		// big gap
+
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+	}
+	void assignmentsComments() {
+		/* c1 */ b /* c1 */= /* c1 */c;// c1
+		j /* */= /* c12345 */5; // c12345
+		myInteger /* */ = 56436345;
+		/* */ myOtehrInteger >>= 35534525543;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890"; /* ... */
+		someObjetc = /* !!! */ new Object() { // !!!
+			@Override
+			public String toString() {
+				return super.toString(); // !!!
+			}
+		};
+	}
+
+	void mixed() {
+		a b;
+		a b = c;
+		int i;
+		j = 5;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/A_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/A_out.java
new file mode 100644
index 0000000..79949c3
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/A_out.java
@@ -0,0 +1,5 @@
+class C {
+	public int		a		= 5;
+	String			ssssss	= "1234567890";	// comment 1
+	private boolean	bbbbb	= true;			// comment 2
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/B_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/B_out.java
new file mode 100644
index 0000000..c1675b9
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/B_out.java
@@ -0,0 +1,5 @@
+class C {
+	public int      a      = 5;
+	String          ssssss = "1234567890"; // comment 1
+	private boolean bbbbb  = true;         // comment 2
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/C_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/C_out.java
new file mode 100644
index 0000000..2fdfab0
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/C_out.java
@@ -0,0 +1,5 @@
+class C {
+	  public int      a      = 5;
+	  String          ssssss = "1234567890"; // comment 1
+	  private boolean bbbbb  = true;         // comment 2
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/D_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/D_out.java
new file mode 100644
index 0000000..25cfe09
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/D_out.java
@@ -0,0 +1,5 @@
+class C {
+    public int      a      = 5;
+    String          ssssss = "1234567890"; // comment 1
+    private boolean bbbbb  = true;         // comment 2
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/E_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/E_out.java
new file mode 100644
index 0000000..25cfe09
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/E_out.java
@@ -0,0 +1,5 @@
+class C {
+    public int      a      = 5;
+    String          ssssss = "1234567890"; // comment 1
+    private boolean bbbbb  = true;         // comment 2
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/in.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/in.java
new file mode 100644
index 0000000..20f5bf1
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test530756/in.java
@@ -0,0 +1,5 @@
+class C {
+	public int a = 5;
+	String ssssss = "1234567890"; // comment 1
+	private boolean bbbbb = true; // comment 2
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java
index 93c217f..92f38d6 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java
@@ -58,7 +58,7 @@
 	/**
 	 * <pre>
 	 * FORMATTER / Option to align type members of a type declaration on column
-	 *     - option id:         "org.eclipse.jdt.core.formatter.formatter.align_type_members_on_columns"
+	 *     - option id:         "org.eclipse.jdt.core.formatter.align_type_members_on_columns"
 	 *     - possible values:   { TRUE, FALSE }
 	 *     - default:           FALSE
 	 * </pre>
@@ -70,7 +70,49 @@
 
 	/**
 	 * <pre>
-	 * FORMATTER / Option to align groups of members independently if they are separated by a certain number of blank lines
+	 * FORMATTER / Option to align variable declarations on column
+	 *     - option id:         "org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns"
+	 *     - possible values:   { TRUE, FALSE }
+	 *     - default:           FALSE
+	 * </pre>
+	 * @see #TRUE
+	 * @see #FALSE
+	 * @since 3.15
+	 */
+	public static final String FORMATTER_ALIGN_VARIABLE_DECLARATIONS_ON_COLUMNS = JavaCore.PLUGIN_ID + ".formatter.align_variable_declarations_on_columns";	 //$NON-NLS-1$
+	
+	/**
+	 * <pre>
+	 * FORMATTER / Option to align assignment statements on column
+	 *     - option id:         "org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns"
+	 *     - possible values:   { TRUE, FALSE }
+	 *     - default:           FALSE
+	 * </pre>
+	 * @see #TRUE
+	 * @see #FALSE
+	 * @since 3.15
+	 */
+	public static final String FORMATTER_ALIGN_ASSIGNMENT_STATEMENTS_ON_COLUMNS = JavaCore.PLUGIN_ID + ".formatter.align_assignment_statements_on_columns";	 //$NON-NLS-1$
+	
+	/**
+	 * <pre>
+	 * FORMATTER / Option to use spaces when aligning members, independent of selected tabulation character
+	 *     - option id:         "org.eclipse.jdt.core.formatter.align_with_spaces"
+	 *     - possible values:   { TRUE, FALSE }
+	 *     - default:           FALSE
+	 * </pre>
+	 * @see #TRUE
+	 * @see #FALSE
+	 * @since 3.15
+	 */
+	public static final String FORMATTER_ALIGN_WITH_SPACES = JavaCore.PLUGIN_ID + ".formatter.align_with_spaces";	 //$NON-NLS-1$
+
+	/**
+	 * <pre>
+	 * FORMATTER / Option to affect aligning on columns: groups of items are aligned independently
+	 * if they are separated by at least the selected number of blank lines.
+	 * Note: since 3.15 the 'fields' part is a (potentially misleading) residue as this option
+	 * affects other types of aligning on columns as well.
 	 *     - option id:         "org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines"
 	 *     - possible values:   "&lt;n&gt;", where n is a positive integer
 	 *     - default:           {@code Integer.MAX_VALUE}
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java
index 2459187..3c6499b 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java
@@ -151,6 +151,9 @@
 	public int alignment_for_union_type_in_multicatch;
 
 	public boolean align_type_members_on_columns;
+	public boolean align_variable_declarations_on_columns;
+	public boolean align_assignment_statements_on_columns;
+	public boolean align_with_spaces;
 	public int align_fields_grouping_blank_lines;
 
 	public String brace_position_for_annotation_type_declaration;
@@ -492,7 +495,10 @@
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_TYPE_PARAMETERS, getAlignment(this.alignment_for_type_parameters));
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_UNION_TYPE_IN_MULTICATCH, getAlignment(this.alignment_for_union_type_in_multicatch));
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGN_TYPE_MEMBERS_ON_COLUMNS, this.align_type_members_on_columns ? DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE);
+		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGN_VARIABLE_DECLARATIONS_ON_COLUMNS, this.align_variable_declarations_on_columns ? DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE);
+		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGN_ASSIGNMENT_STATEMENTS_ON_COLUMNS, this.align_assignment_statements_on_columns ? DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE);
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGN_FIELDS_GROUPING_BLANK_LINES, Integer.toString(this.align_fields_grouping_blank_lines));
+		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGN_WITH_SPACES, this.align_with_spaces ? DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE);
 		options.put(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_ANNOTATION_TYPE_DECLARATION, this.brace_position_for_annotation_type_declaration);
 		options.put(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_ANONYMOUS_TYPE_DECLARATION, this.brace_position_for_anonymous_type_declaration);
 		options.put(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_ARRAY_INITIALIZER, this.brace_position_for_array_initializer);
@@ -1062,6 +1068,14 @@
 		if (alignTypeMembersOnColumnsOption != null) {
 			this.align_type_members_on_columns = DefaultCodeFormatterConstants.TRUE.equals(alignTypeMembersOnColumnsOption);
 		}
+		final Object alignVariableDeclarationsOnColumnsOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_ALIGN_VARIABLE_DECLARATIONS_ON_COLUMNS);
+		if (alignVariableDeclarationsOnColumnsOption != null) {
+			this.align_variable_declarations_on_columns = DefaultCodeFormatterConstants.TRUE.equals(alignVariableDeclarationsOnColumnsOption);
+		}
+		final Object alignAssignmentStatementsOnColumnsOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_ALIGN_ASSIGNMENT_STATEMENTS_ON_COLUMNS);
+		if (alignAssignmentStatementsOnColumnsOption != null) {
+			this.align_assignment_statements_on_columns = DefaultCodeFormatterConstants.TRUE.equals(alignAssignmentStatementsOnColumnsOption);
+		}
 		final Object alignGroupSepartionBlankLinesOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_ALIGN_FIELDS_GROUPING_BLANK_LINES);
 		if (alignTypeMembersOnColumnsOption != null) {
 			try {
@@ -1072,6 +1086,10 @@
 				this.align_fields_grouping_blank_lines = Integer.MAX_VALUE;
 			}
 		}
+		final Object alignWithSpaces = settings.get(DefaultCodeFormatterConstants.FORMATTER_ALIGN_WITH_SPACES);
+		if (alignWithSpaces != null) {
+			this.align_with_spaces = DefaultCodeFormatterConstants.TRUE.equals(alignWithSpaces);
+		}
 		final Object bracePositionForAnnotationTypeDeclarationOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_ANNOTATION_TYPE_DECLARATION);
 		if (bracePositionForAnnotationTypeDeclarationOption != null) {
 			try {
@@ -2506,6 +2524,9 @@
 		this.alignment_for_type_parameters = Alignment.M_NO_ALIGNMENT;
 		this.alignment_for_union_type_in_multicatch = Alignment.M_COMPACT_SPLIT;
 		this.align_type_members_on_columns = false;
+		this.align_variable_declarations_on_columns = false;
+		this.align_assignment_statements_on_columns = false;
+		this.align_with_spaces = false;
 		this.align_fields_grouping_blank_lines = Integer.MAX_VALUE;
 		this.brace_position_for_annotation_type_declaration = DefaultCodeFormatterConstants.END_OF_LINE;
 		this.brace_position_for_anonymous_type_declaration = DefaultCodeFormatterConstants.END_OF_LINE;
@@ -2827,6 +2848,9 @@
 		this.alignment_for_type_parameters = Alignment.M_NO_ALIGNMENT;
 		this.alignment_for_union_type_in_multicatch = Alignment.M_COMPACT_SPLIT;
 		this.align_type_members_on_columns = false;
+		this.align_variable_declarations_on_columns = false;
+		this.align_assignment_statements_on_columns = false;
+		this.align_with_spaces = false;
 		this.align_fields_grouping_blank_lines = Integer.MAX_VALUE;
 		this.brace_position_for_annotation_type_declaration = DefaultCodeFormatterConstants.END_OF_LINE;
 		this.brace_position_for_anonymous_type_declaration = DefaultCodeFormatterConstants.END_OF_LINE;
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TextEditsBuilder.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TextEditsBuilder.java
index 060df8d..28dfdc1 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TextEditsBuilder.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TextEditsBuilder.java
@@ -58,7 +58,7 @@
 		this.options = options;
 		this.regions = adaptRegions(regions);
 
-		this.alignChar = this.options.tab_char;
+		this.alignChar = this.options.align_with_spaces ? DefaultCodeFormatterOptions.SPACE : this.options.tab_char;
 		this.sourceLimit = source.length();
 		this.parent = null;
 
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/Aligner.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/Aligner.java
new file mode 100644
index 0000000..47d3357
--- /dev/null
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/Aligner.java
@@ -0,0 +1,281 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 2018 Mateusz Matela and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Mateusz Matela <mateusz.matela@gmail.com> - [formatter] Formatter does not format Java code correctly, especially when max line width is set - https://bugs.eclipse.org/303519
+ *     Lars Vogel <Lars.Vogel@vogella.com> - Contributions for
+ *     						Bug 473178
+ *******************************************************************************/
+package org.eclipse.jdt.internal.formatter.linewrap;
+
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_BLOCK;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_LINE;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameEQUAL;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameIdentifier;
+import static java.util.stream.Collectors.toList;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.OptionalInt;
+import java.util.function.Function;
+import java.util.stream.IntStream;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions;
+import org.eclipse.jdt.internal.formatter.Token;
+import org.eclipse.jdt.internal.formatter.TokenManager;
+import org.eclipse.jdt.internal.formatter.TokenTraverser;
+
+/** Implementation of the "Align items on columns" feature */
+public class Aligner {
+	private class PositionCounter extends TokenTraverser {
+		int stoppingIndex;
+		int maxPosition;
+
+		public PositionCounter() {
+			// nothing to do
+		}
+
+		@Override
+		protected boolean token(Token token, int index) {
+			if (index == this.stoppingIndex)
+				return false;
+			if (getLineBreaksBefore() > 0)
+				this.counter = Aligner.this.tm.getPositionInLine(index);
+			if (token.getAlign() > 0)
+				this.counter = token.getAlign();
+			this.counter += Aligner.this.tm.getLength(token, this.counter);
+			if (isSpaceAfter() && getLineBreaksAfter() == 0)
+				this.counter++;
+			this.maxPosition = Math.max(this.maxPosition, this.counter);
+			return true;
+		}
+
+		public int findMaxPosition(int fromIndex, int toIndex) {
+			this.counter = Aligner.this.tm.getPositionInLine(fromIndex);
+			this.stoppingIndex = toIndex;
+			this.maxPosition = 0;
+			Aligner.this.tm.traverse(fromIndex, this);
+			return this.maxPosition;
+		}
+	}
+
+	@FunctionalInterface
+	private interface AlignIndexFinder<N extends ASTNode> {
+		Optional<Integer> findIndex(N node);
+	}
+
+	private final List<List<? extends ASTNode>> alignGroups = new ArrayList<>();
+
+	private final DefaultCodeFormatterOptions options;
+
+	final TokenManager tm;
+
+	public Aligner(TokenManager tokenManager, DefaultCodeFormatterOptions options) {
+		this.tm = tokenManager;
+		this.options = options;
+	}
+
+	public void handleAlign(List<BodyDeclaration> bodyDeclarations) {
+		if (!this.options.align_type_members_on_columns)
+			return;
+		List<List<FieldDeclaration>> fieldGroups = toAlignGroups(bodyDeclarations,
+				n -> optionalCast(n, FieldDeclaration.class));
+		this.alignGroups.addAll(fieldGroups);
+
+		AlignIndexFinder<FieldDeclaration> nameFinder = fd -> findName(
+				(VariableDeclarationFragment) fd.fragments().get(0));
+		fieldGroups.forEach(fg -> alignNodes(fg, nameFinder));
+
+		AlignIndexFinder<FieldDeclaration> assignFinder = fd -> findAssign(
+				(VariableDeclarationFragment) fd.fragments().get(0));
+		fieldGroups.forEach(fg -> alignNodes(fg, assignFinder));
+	}
+
+	public void handleAlign(Block block) {
+		List<Statement> statements = block.statements();
+		if (this.options.align_variable_declarations_on_columns)
+			alignDeclarations(statements);
+		if (this.options.align_assignment_statements_on_columns)
+			alignAssignmentStatements(statements);
+	}
+
+	private void alignDeclarations(List<Statement> statements) {
+		List<List<VariableDeclarationStatement>> variableGroups = toAlignGroups(statements,
+				n -> optionalCast(n, VariableDeclarationStatement.class));
+		this.alignGroups.addAll(variableGroups);
+
+		AlignIndexFinder<VariableDeclarationStatement> nameFinder = vd -> findName(
+				(VariableDeclarationFragment) vd.fragments().get(0));
+		variableGroups.forEach(vg -> alignNodes(vg, nameFinder));
+
+		AlignIndexFinder<VariableDeclarationStatement> assignFinder = vd -> findAssign(
+				(VariableDeclarationFragment) vd.fragments().get(0));
+		variableGroups.forEach(vg -> alignNodes(vg, assignFinder));
+	}
+
+	private void alignAssignmentStatements(List<Statement> statements) {
+		List<List<ExpressionStatement>> assignmentGroups = toAlignGroups(statements,
+				n -> optionalCast(n, ExpressionStatement.class)
+						.filter(es -> es.getExpression() instanceof Assignment));
+		this.alignGroups.addAll(assignmentGroups);
+
+		AlignIndexFinder<ExpressionStatement> assignFinder = es -> {
+			Assignment a = (Assignment) es.getExpression();
+			int operatorIndex = this.tm.firstIndexBefore(a.getRightHandSide(), -1);
+			while (this.tm.get(operatorIndex).isComment())
+				operatorIndex--;
+			return Optional.of(operatorIndex);
+		};
+		assignmentGroups.forEach(ag -> alignNodes(ag, assignFinder));
+
+		if (this.options.align_with_spaces || this.options.tab_char != DefaultCodeFormatterOptions.TAB) {
+			// align assign operators on their right side (e.g. +=, >>=)
+			for (List<ExpressionStatement> group : assignmentGroups) {
+				List<Token> assignTokens = group.stream()
+						.map(assignFinder::findIndex)
+						.filter(Optional::isPresent)
+						.map(o -> this.tm.get(o.get()))
+						.collect(toList());
+				int maxWidth = assignTokens.stream().mapToInt(Token::countChars).max().orElse(0);
+				for (Token token : assignTokens)
+					token.setAlign(token.getAlign() + maxWidth - token.countChars());
+			}
+		}
+	}
+
+	private <N extends ASTNode> Optional<N> optionalCast(ASTNode node, Class<N> c) {
+		return Optional.of(node).filter(c::isInstance).map(c::cast);
+	}
+
+	private Optional<Integer> findName(VariableDeclarationFragment fragment) {
+		int nameIndex = this.tm.firstIndexIn(fragment.getName(), TokenNameIdentifier);
+		return Optional.of(nameIndex);
+	}
+
+	private Optional<Integer> findAssign(VariableDeclarationFragment fragment) {
+		return Optional.ofNullable(fragment.getInitializer())
+				.map(i -> this.tm.firstIndexBefore(i, TokenNameEQUAL));
+	}
+
+	private <N extends ASTNode> List<List<N>> toAlignGroups(List<? extends ASTNode> nodes,
+			Function<ASTNode, Optional<N>> nodeConverter) {
+		List<List<N>> result = new ArrayList<>();
+		List<N> alignGroup = new ArrayList<>();
+		N previous = null;
+		for (ASTNode node : nodes) {
+			Optional<N> converted = nodeConverter.apply(node);
+			if (converted.isPresent()) {
+				if (isNewGroup(node, previous)) {
+					result.add(alignGroup);
+					alignGroup = new ArrayList<>();
+				}
+				alignGroup.add(converted.get());
+			}
+			previous = converted.orElse(null);
+		}
+		result.add(alignGroup);
+		result.removeIf(l -> l.size() < 2);
+		return result;
+	}
+
+	private boolean isNewGroup(ASTNode node, ASTNode previousNode) {
+		if (previousNode == null)
+			return true;
+		int lineBreaks = 0;
+		int from = this.tm.lastIndexIn(previousNode, -1);
+		int to = this.tm.firstIndexIn(node, -1);
+		Token previousToken = this.tm.get(from);
+		for (int i = from + 1; i <= to; i++) {
+			Token token = this.tm.get(i);
+			lineBreaks += Math.min(this.tm.countLineBreaksBetween(previousToken, token),
+					this.options.number_of_empty_lines_to_preserve + 1);
+			previousToken = token;
+		}
+		return lineBreaks > this.options.align_fields_grouping_blank_lines;
+	}
+
+	private <N extends ASTNode> void alignNodes(List<N> alignGroup, AlignIndexFinder<N> tokenFinder) {
+		int[] tokenIndexes = alignGroup.stream()
+				.map(tokenFinder::findIndex)
+				.filter(Optional::isPresent)
+				.mapToInt(Optional::get).toArray();
+		OptionalInt maxPosition = IntStream.of(tokenIndexes).map(this.tm::getPositionInLine).max();
+		if (maxPosition.isPresent()) {
+			int align = normalizedAlign(maxPosition.getAsInt());
+			for (int tokenIndex : tokenIndexes)
+				this.tm.get(tokenIndex).setAlign(align);
+		}
+	}
+
+	public void alignComments() {
+		boolean alignLineComments = !this.options.comment_preserve_white_space_between_code_and_line_comments;
+		PositionCounter positionCounter = new PositionCounter();
+		// align comments after field declarations
+		for (List<? extends ASTNode> alignGroup : this.alignGroups) {
+			int maxCommentAlign = 0;
+			for (ASTNode node : alignGroup) {
+				int firstIndexInLine = findFirstTokenInLine(node);
+				int lastIndex = this.tm.lastIndexIn(node, -1) + 1;
+				maxCommentAlign = Math.max(maxCommentAlign,
+						positionCounter.findMaxPosition(firstIndexInLine, lastIndex));
+			}
+			maxCommentAlign = normalizedAlign(maxCommentAlign);
+
+			for (ASTNode node : alignGroup) {
+				int firstIndexInLine = findFirstTokenInLine(node);
+				int lastIndex = this.tm.lastIndexIn(node, -1);
+				lastIndex = Math.min(lastIndex, this.tm.size() - 2);
+				for (int i = firstIndexInLine; i <= lastIndex; i++) {
+					Token token = this.tm.get(i);
+					Token next = this.tm.get(i + 1);
+					boolean lineBreak = token.getLineBreaksAfter() > 0 || next.getLineBreaksBefore() > 0;
+					if (lineBreak) {
+						if (token.tokenType == TokenNameCOMMENT_BLOCK) {
+							token.setAlign(maxCommentAlign);
+						} else if (alignLineComments) {
+							this.tm.addNLSAlignIndex(i, maxCommentAlign);
+						}
+					} else if (next.tokenType == TokenNameCOMMENT_LINE && alignLineComments
+							|| (next.tokenType == TokenNameCOMMENT_BLOCK && i == lastIndex)) {
+						next.setAlign(maxCommentAlign);
+					}
+				}
+			}
+		}
+	}
+
+	private int findFirstTokenInLine(ASTNode node) {
+		if (node instanceof FieldDeclaration) {
+			int typeIndex = this.tm.firstIndexIn(((FieldDeclaration) node).getType(), -1);
+			return this.tm.findFirstTokenInLine(typeIndex);
+		}
+		if (node instanceof VariableDeclarationStatement) {
+			int typeIndex = this.tm.firstIndexIn(((VariableDeclarationStatement) node).getType(), -1);
+			return this.tm.findFirstTokenInLine(typeIndex);
+		}
+		if (node instanceof ExpressionStatement) {
+			return this.tm.firstIndexIn(node, -1);
+		}
+		throw new IllegalArgumentException(node.getClass().getName());
+	}
+
+	private int normalizedAlign(int desiredAlign) {
+		if (this.options.align_with_spaces)
+			return desiredAlign;
+		return this.tm.toIndent(desiredAlign, false);
+	}
+}
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/FieldAligner.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/FieldAligner.java
deleted file mode 100644
index 9af0fab..0000000
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/FieldAligner.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014, 2016 Mateusz Matela and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *     Mateusz Matela <mateusz.matela@gmail.com> - [formatter] Formatter does not format Java code correctly, especially when max line width is set - https://bugs.eclipse.org/303519
- *     Lars Vogel <Lars.Vogel@vogella.com> - Contributions for
- *     						Bug 473178
- *******************************************************************************/
-package org.eclipse.jdt.internal.formatter.linewrap;
-
-import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_BLOCK;
-import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_LINE;
-import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameEQUAL;
-import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameIdentifier;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.jdt.core.dom.BodyDeclaration;
-import org.eclipse.jdt.core.dom.FieldDeclaration;
-import org.eclipse.jdt.core.dom.SimpleName;
-import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
-import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions;
-import org.eclipse.jdt.internal.formatter.Token;
-import org.eclipse.jdt.internal.formatter.TokenManager;
-import org.eclipse.jdt.internal.formatter.TokenTraverser;
-
-/** Implementation of the "Align fields on columns" feature */
-public class FieldAligner {
-	private class PositionCounter extends TokenTraverser {
-		int stoppingIndex;
-		int maxPosition;
-
-		public PositionCounter() {
-			// nothing to do
-		}
-
-		@Override
-		protected boolean token(Token token, int index) {
-			if (index == this.stoppingIndex)
-				return false;
-			if (getLineBreaksBefore() > 0)
-				this.counter = FieldAligner.this.tm.getPositionInLine(index);
-			if (token.getAlign() > 0)
-				this.counter = token.getAlign();
-			this.counter += FieldAligner.this.tm.getLength(token, this.counter);
-			if (isSpaceAfter() && getLineBreaksAfter() == 0)
-				this.counter++;
-			this.maxPosition = Math.max(this.maxPosition, this.counter);
-			return true;
-		}
-
-		public int findMaxPosition(int fromIndex, int toIndex) {
-			this.counter = FieldAligner.this.tm.getPositionInLine(fromIndex);
-			this.stoppingIndex = toIndex;
-			this.maxPosition = 0;
-			FieldAligner.this.tm.traverse(fromIndex, this);
-			return this.maxPosition;
-		}
-	}
-
-	private final List<List<FieldDeclaration>> fieldAlignGroups = new ArrayList<>();
-
-	private final DefaultCodeFormatterOptions options;
-
-	final TokenManager tm;
-
-	public FieldAligner(TokenManager tokenManager, DefaultCodeFormatterOptions options) {
-		this.tm = tokenManager;
-		this.options = options;
-	}
-
-	public void handleAlign(List<FieldDeclaration> bodyDeclarations) {
-		if (!this.options.align_type_members_on_columns)
-			return;
-		ArrayList<FieldDeclaration> alignGroup = new ArrayList<>();
-		BodyDeclaration previous = null;
-		for (BodyDeclaration declaration : bodyDeclarations) {
-			if (declaration instanceof FieldDeclaration) {
-				if (isNewGroup(declaration, previous)) {
-					alignFields(alignGroup);
-					alignGroup = new ArrayList<>();
-				}
-				alignGroup.add((FieldDeclaration) declaration);
-			}
-			previous = declaration;
-		}
-		alignFields(alignGroup);
-	}
-
-	private boolean isNewGroup(BodyDeclaration declaration, BodyDeclaration previousDeclaration) {
-		if (!(previousDeclaration instanceof FieldDeclaration))
-			return true;
-		int lineBreaks = 0;
-		int from = this.tm.lastIndexIn(previousDeclaration, -1);
-		int to = this.tm.firstIndexIn(declaration, -1);
-		Token previous = this.tm.get(from);
-		for (int i = from + 1; i <= to; i++) {
-			Token token = this.tm.get(i);
-			lineBreaks += Math.min(this.tm.countLineBreaksBetween(previous, token),
-					this.options.number_of_empty_lines_to_preserve + 1);
-			previous = token;
-		}
-		return lineBreaks > this.options.align_fields_grouping_blank_lines;
-	}
-
-	private void alignFields(ArrayList<FieldDeclaration> alignGroup) {
-		if (alignGroup.size() < 2)
-			return;
-		this.fieldAlignGroups.add(alignGroup);
-
-		int maxNameAlign = 0;
-		for (FieldDeclaration declaration : alignGroup) {
-			List<VariableDeclarationFragment> fragments = declaration.fragments();
-			SimpleName fieldName = fragments.get(0).getName();
-			int nameIndex = this.tm.firstIndexIn(fieldName, TokenNameIdentifier);
-			int positionInLine = this.tm.getPositionInLine(nameIndex);
-			maxNameAlign = Math.max(maxNameAlign, positionInLine);
-		}
-		maxNameAlign = this.tm.toIndent(maxNameAlign, false);
-
-		int maxAssignAlign = 0;
-		for (FieldDeclaration declaration : alignGroup) {
-			List<VariableDeclarationFragment> fragments = declaration.fragments();
-			VariableDeclarationFragment fragment = fragments.get(0);
-			int nameIndex = this.tm.firstIndexIn(fragment.getName(), TokenNameIdentifier);
-			Token nameToken = this.tm.get(nameIndex);
-
-			nameToken.setAlign(maxNameAlign);
-
-			if (fragment.getInitializer() != null) {
-				int equalIndex = this.tm.firstIndexAfter(fragment.getName(), TokenNameEQUAL);
-				int positionInLine = this.tm.getPositionInLine(equalIndex);
-				maxAssignAlign = Math.max(maxAssignAlign, positionInLine);
-			}
-		}
-		maxAssignAlign = this.tm.toIndent(maxAssignAlign, false);
-
-		for (FieldDeclaration declaration : alignGroup) {
-			List<VariableDeclarationFragment> fragments = declaration.fragments();
-			VariableDeclarationFragment fragment = fragments.get(0);
-			if (fragment.getInitializer() != null) {
-				int assingIndex = this.tm.firstIndexAfter(fragment.getName(), TokenNameEQUAL);
-				Token assignToken = this.tm.get(assingIndex);
-				assignToken.setAlign(maxAssignAlign);
-			}
-		}
-	}
-
-	public void alignComments() {
-		if (this.fieldAlignGroups.isEmpty())
-			return;
-		boolean alignLineComments = !this.options.comment_preserve_white_space_between_code_and_line_comments;
-		PositionCounter positionCounter = new PositionCounter();
-		// align comments after field declarations
-		for (List<FieldDeclaration> alignGroup : this.fieldAlignGroups) {
-			int maxCommentAlign = 0;
-			for (FieldDeclaration declaration : alignGroup) {
-				int typeIndex = this.tm.firstIndexIn(declaration.getType(), -1);
-				int firstIndexInLine = this.tm.findFirstTokenInLine(typeIndex);
-				int lastIndex = this.tm.lastIndexIn(declaration, -1) + 1;
-				maxCommentAlign = Math.max(maxCommentAlign,
-						positionCounter.findMaxPosition(firstIndexInLine, lastIndex));
-			}
-			maxCommentAlign = this.tm.toIndent(maxCommentAlign, false);
-
-			for (FieldDeclaration declaration : alignGroup) {
-				int typeIndex = this.tm.firstIndexIn(declaration.getType(), -1);
-				int firstIndexInLine = this.tm.findFirstTokenInLine(typeIndex);
-				int lastIndex = this.tm.lastIndexIn(declaration, -1);
-				lastIndex = Math.min(lastIndex, this.tm.size() - 2);
-				for (int i = firstIndexInLine; i <= lastIndex; i++) {
-					Token token = this.tm.get(i);
-					Token next = this.tm.get(i + 1);
-					boolean lineBreak = token.getLineBreaksAfter() > 0 || next.getLineBreaksBefore() > 0;
-					if (lineBreak) {
-						if (token.tokenType == TokenNameCOMMENT_BLOCK) {
-							token.setAlign(maxCommentAlign);
-						} else if (alignLineComments) {
-							this.tm.addNLSAlignIndex(i, maxCommentAlign);
-						}
-					} else if (next.tokenType == TokenNameCOMMENT_LINE && alignLineComments
-							|| (next.tokenType == TokenNameCOMMENT_BLOCK && i == lastIndex)) {
-						next.setAlign(maxCommentAlign);
-					}
-				}
-			}
-		}
-	}
-}
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java
index 4e1c2f0..937a9fd 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java
@@ -179,7 +179,7 @@
 	final DefaultCodeFormatterOptions options;
 	final int kind;
 
-	final FieldAligner fieldAligner;
+	final Aligner aligner;
 
 	int importsStart = -1, importsEnd = -1;
 
@@ -201,7 +201,7 @@
 		this.options = options;
 		this.kind = kind;
 
-		this.fieldAligner = new FieldAligner(this.tm, this.options);
+		this.aligner = new Aligner(this.tm, this.options);
 	}
 
 	@Override
@@ -262,20 +262,20 @@
 		prepareElementsList(node.typeParameters(), TokenNameCOMMA, TokenNameLESS);
 		handleWrap(this.options.alignment_for_type_parameters);
 
-		this.fieldAligner.handleAlign(node.bodyDeclarations());
+		this.aligner.handleAlign(node.bodyDeclarations());
 
 		return true;
 	}
 
 	@Override
 	public boolean visit(AnnotationTypeDeclaration node) {
-		this.fieldAligner.handleAlign(node.bodyDeclarations());
+		this.aligner.handleAlign(node.bodyDeclarations());
 		return true;
 	}
 
 	@Override
 	public boolean visit(AnonymousClassDeclaration node) {
-		this.fieldAligner.handleAlign(node.bodyDeclarations());
+		this.aligner.handleAlign(node.bodyDeclarations());
 		return true;
 	}
 
@@ -371,7 +371,7 @@
 			handleWrap(this.options.alignment_for_superinterfaces_in_enum_declaration, PREFERRED);
 		}
 
-		this.fieldAligner.handleAlign(node.bodyDeclarations());
+		this.aligner.handleAlign(node.bodyDeclarations());
 
 		return true;
 	}
@@ -387,6 +387,12 @@
 	}
 
 	@Override
+	public boolean visit(Block node) {
+		this.aligner.handleAlign(node);
+		return true;
+	}
+
+	@Override
 	public boolean visit(MethodInvocation node) {
 		handleArguments(node.arguments(), this.options.alignment_for_arguments_in_method_invocation);
 		handleTypeArguments(node.typeArguments());
@@ -1110,7 +1116,7 @@
 		preserveExistingLineBreaks();
 		applyBreaksOutsideRegions(regions);
 		new WrapExecutor(this.tm, this.options).executeWraps();
-		this.fieldAligner.alignComments();
+		this.aligner.alignComments();
 		wrapComments();
 		fixEnumConstantIndents(astRoot);
 	}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JarPackageFragmentRoot.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JarPackageFragmentRoot.java
index 561c608..a38d4a4 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JarPackageFragmentRoot.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JarPackageFragmentRoot.java
@@ -14,8 +14,9 @@
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.jar.Manifest;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipException;
@@ -68,7 +69,6 @@
 	boolean knownToBeModuleLess;
 
 	private boolean multiVersion;
-	public String versionPath;
 
 	/**
 	 * Constructs a package fragment root which is the root of the Java package directory hierarchy
@@ -96,8 +96,8 @@
 	@Override
 	protected boolean computeChildren(OpenableElementInfo info, IResource underlyingResource) throws JavaModelException {
 		final HashtableOfArrayToObject rawPackageInfo = new HashtableOfArrayToObject();
-		final Set<String> overridden = new HashSet<>();
-		IJavaElement[] children;
+		final Map<String, String> overridden = new HashMap<>();
+		IJavaElement[] children = NO_ELEMENTS;
 		try {
 			// always create the default package
 			rawPackageInfo.put(CharOperation.NO_STRINGS, new ArrayList[] { EMPTY_LIST, EMPTY_LIST });
@@ -139,19 +139,38 @@
 				ZipFile jar = null;
 				try {
 					jar = getJar();
-					String version = "META-INF/versions/" + projectCompliance + "/";  //$NON-NLS-1$//$NON-NLS-2$
-					int versionPathLength = version.length();
+					String version = "META-INF/versions/";  //$NON-NLS-1$
+					List<String> versions = new ArrayList<>();
 					if (projectLevel >= ClassFileConstants.JDK9 && jar.getEntry(version) != null) {
-						this.multiVersion = true;
-						this.versionPath = version;
+						int earliestJavaVersion = ClassFileConstants.MAJOR_VERSION_9;
+						long latestJDK = CompilerOptions.releaseToJDKLevel(projectCompliance);
+						int latestJavaVer = (int) (latestJDK >> 16);
+
+						for(int i = latestJavaVer; i >= earliestJavaVersion; i--) {
+							String s = "" + + (i - 44); //$NON-NLS-1$
+							String versionPath = version + s;
+							if (jar.getEntry(versionPath) != null) {
+								versions.add(s);
+							}
+						}
 					}
-					for (Enumeration e= jar.entries(); e.hasMoreElements();) {
-						ZipEntry member= (ZipEntry) e.nextElement();
+					
+					String[] supportedVersions = versions.toArray(new String[versions.size()]);
+					if (supportedVersions.length > 0) {
+						this.multiVersion = true;
+					}
+					int length = version.length();
+					for (Enumeration<? extends ZipEntry> e= jar.entries(); e.hasMoreElements();) {
+						ZipEntry member= e.nextElement();
 						String name = member.getName();
-						if (this.multiVersion && name.length() > versionPathLength && name.startsWith(version)) {
-							name = name.substring(version.length());
-							if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(name)) {
-								overridden.add(name);
+						if (this.multiVersion && name.length() > (length + 2) && name.startsWith(version)) {
+							int end = name.indexOf('/', length);
+							if (end >= name.length()) continue;
+							String versionPath = name.substring(0, end);
+							String ver = name.substring(length, end);
+							if(versions.contains(ver) && org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(name)) {
+								name = name.substring(end + 1);
+								overridden.put(name, versionPath);
 							}
 						}
 						initRawPackageInfo(rawPackageInfo, name, member.isDirectory(), CompilerOptions.versionFromJdkLevel(classLevel));
@@ -180,7 +199,6 @@
 				throw new JavaModelException(e);
 			}
 		}
-
 		info.setChildren(children);
 		((JarPackageFragmentRootInfo) info).rawPackageInfo = rawPackageInfo;
 		((JarPackageFragmentRootInfo) info).overriddenClasses = overridden;
@@ -285,9 +303,8 @@
 			JarPackageFragmentRootInfo elementInfo;
 			try {
 				elementInfo = (JarPackageFragmentRootInfo) getElementInfo();
-				if (elementInfo.overriddenClasses.contains(classname)) {
-					return this.versionPath == null ? classname : this.versionPath + classname;
-				}
+				String versionPath = elementInfo.overriddenClasses.get(classname);
+				return versionPath == null ? classname : versionPath + '/' + classname;
 			} catch (JavaModelException e) {
 				// move on
 			}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JarPackageFragmentRootInfo.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JarPackageFragmentRootInfo.java
index 113c5bf..1aa3bdc 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JarPackageFragmentRootInfo.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JarPackageFragmentRootInfo.java
@@ -10,7 +10,7 @@
  *******************************************************************************/
 package org.eclipse.jdt.internal.core;
 
-import java.util.Set;
+import java.util.Map;
 
 import org.eclipse.jdt.internal.core.util.HashtableOfArrayToObject;
 
@@ -20,6 +20,6 @@
 class JarPackageFragmentRootInfo extends PackageFragmentRootInfo {
 	// a map from package name (String[]) to a size-2 array of Array<String>, the first element being the .class file names, and the second element being the non-Java resource names
 	HashtableOfArrayToObject rawPackageInfo;
-	Set<String> overriddenClasses;
+	Map<String, String> overriddenClasses;
 	
 }
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java
index a0b66c4..314926d 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java
@@ -93,6 +93,8 @@
 	String modInfo = null;
 	for (Enumeration e = this.zipFile.entries(); e.hasMoreElements(); ) {
 		String fileName = ((ZipEntry) e.nextElement()).getName();
+		if (fileName.startsWith("META-INF/")) //$NON-NLS-1$
+			continue;
 		if (modInfo == null) {
 			int folderEnd = fileName.lastIndexOf('/');
 			folderEnd += 1;
@@ -111,11 +113,9 @@
 	try {
 		file = new ZipFile(this.zipFilename);
 		String releasePath = "META-INF/versions/" + this.compliance + '/' + IModule.MODULE_INFO_CLASS; //$NON-NLS-1$
-		System.out.println("Reading for module from: " + this.zipFilename); //$NON-NLS-1$
 		ClassFileReader classfile = null;
 		try {
 			classfile = ClassFileReader.read(file, releasePath);
-			System.out.println("Read classfile : " + classfile); //$NON-NLS-1$
 		} catch (Exception e) {
 			e.printStackTrace();
 			// move on to the default
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathMultiReleaseJar.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathMultiReleaseJar.java
index d595415..9b03ee6 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathMultiReleaseJar.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathMultiReleaseJar.java
@@ -2,7 +2,6 @@
 
 import java.io.IOException;
 import java.net.URI;
-import java.nio.file.DirectoryStream;
 import java.nio.file.FileSystemNotFoundException;
 import java.nio.file.FileSystems;
 import java.nio.file.FileVisitResult;
@@ -12,12 +11,15 @@
 import java.nio.file.Paths;
 import java.nio.file.ProviderNotFoundException;
 import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.function.Predicate;
 import java.util.zip.ZipFile;
 
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.runtime.IPath;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
 import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
 import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationDecorator;
@@ -25,6 +27,7 @@
 import org.eclipse.jdt.internal.compiler.env.IBinaryType;
 import org.eclipse.jdt.internal.compiler.env.IModule;
 import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
 import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.ExternalAnnotationStatus;
 import org.eclipse.jdt.internal.compiler.util.SimpleSet;
 import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
@@ -32,36 +35,84 @@
 public class ClasspathMultiReleaseJar extends ClasspathJar {
 	private java.nio.file.FileSystem fs = null;
 	Path releasePath = null;
+	Path rootPath = null;
+	Path[] supportedVersions;
 
-	ClasspathMultiReleaseJar(IFile resource, AccessRuleSet accessRuleSet, IPath externalAnnotationPath, boolean isOnModulePath, String compliance) {
+	ClasspathMultiReleaseJar(IFile resource, AccessRuleSet accessRuleSet, IPath externalAnnotationPath,
+			boolean isOnModulePath, String compliance) {
 		super(resource, accessRuleSet, externalAnnotationPath, isOnModulePath);
 		this.compliance = compliance;
 		initializeVersions();
 	}
 
-	ClasspathMultiReleaseJar(String zipFilename, long lastModified, AccessRuleSet accessRuleSet, IPath externalAnnotationPath, boolean isOnModulePath, String compliance) {
+	ClasspathMultiReleaseJar(String zipFilename, long lastModified, AccessRuleSet accessRuleSet,
+			IPath externalAnnotationPath, boolean isOnModulePath, String compliance) {
 		super(zipFilename, lastModified, accessRuleSet, externalAnnotationPath, isOnModulePath);
 		this.compliance = compliance;
 		initializeVersions();
 	}
 
-	public ClasspathMultiReleaseJar(ZipFile zipFile, AccessRuleSet accessRuleSet, IPath externalAnnotationPath, boolean isOnModulePath, String compliance) {
+	public ClasspathMultiReleaseJar(ZipFile zipFile, AccessRuleSet accessRuleSet, IPath externalAnnotationPath,
+			boolean isOnModulePath, String compliance) {
 		this(zipFile.getName(), accessRuleSet, externalAnnotationPath, isOnModulePath, compliance);
 		this.zipFile = zipFile;
 		this.closeZipFileAtEnd = true;
 	}
 
-	public ClasspathMultiReleaseJar(String fileName, AccessRuleSet accessRuleSet, IPath externalAnnotationPath, boolean isOnModulePath, String compliance) {
+	public ClasspathMultiReleaseJar(String fileName, AccessRuleSet accessRuleSet, IPath externalAnnotationPath,
+			boolean isOnModulePath, String compliance) {
 		this(fileName, 0, accessRuleSet, externalAnnotationPath, isOnModulePath, compliance);
 		if (externalAnnotationPath != null)
 			this.externalAnnotationPath = externalAnnotationPath.toString();
 	}
+
+	@Override
+	IModule initializeModule() {
+		IModule mod = null;
+		ZipFile file = null;
+		try {
+			file = new ZipFile(this.zipFilename);
+			ClassFileReader classfile = null;
+			try {
+				for (Path path : this.supportedVersions) {
+					classfile = ClassFileReader.read(file, path.toString() + '/' + IModule.MODULE_INFO_CLASS);
+					if (classfile != null)
+						break;
+				}
+
+			} catch (Exception e) {
+				e.printStackTrace();
+				// move on to the default
+			}
+			if (classfile == null) {
+				classfile = ClassFileReader.read(file, IModule.MODULE_INFO_CLASS); // FIXME: use jar cache
+			}
+			if (classfile != null) {
+				mod = classfile.getModuleDeclaration();
+			}
+		} catch (ClassFormatException | IOException e) {
+			// do nothing
+		} finally {
+			try {
+				if (file != null)
+					file.close();
+			} catch (IOException e) {
+				// do nothing
+			}
+		}
+		return mod;
+	}
+
 	private void initializeVersions() {
 		Path filePath = Paths.get(this.zipFilename);
 		if (Files.exists(filePath)) {
-			URI uri = URI.create("jar:" + filePath.toUri());  //$NON-NLS-1$
+			URI uri = URI.create("jar:" + filePath.toUri()); //$NON-NLS-1$
 			try {
-				this.fs = FileSystems.getFileSystem(uri);
+				try {
+					this.fs = FileSystems.getFileSystem(uri);
+				} catch (FileSystemNotFoundException e) {
+					// move on
+				}
 				if (this.fs == null) {
 					HashMap<String, ?> env = new HashMap<>();
 					this.fs = FileSystems.newFileSystem(uri, env);
@@ -71,75 +122,88 @@
 			} catch (IOException e) {
 				// move on
 			}
-			if (this.fs == null) {
-				this.releasePath = null;
-			} else {
-				this.releasePath = this.fs.getPath("/", "META-INF", "versions", this.compliance); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
-				if (!Files.exists(this.releasePath)) {
-					this.releasePath = null;
-					try {
-						this.fs.close();
-					} catch (IOException e) {
-						// ignore
-					}
+			if (this.fs == null)
+				return;
+			this.rootPath = this.fs.getPath("/"); //$NON-NLS-1$
+			int earliestJavaVersion = ClassFileConstants.MAJOR_VERSION_9;
+			long latestJDK = CompilerOptions.releaseToJDKLevel(this.compliance);
+			int latestJavaVer = (int) (latestJDK >> 16);
+			List<Path> versions = new ArrayList<>();
+			for (int i = latestJavaVer; i >= earliestJavaVersion; i--) {
+				Path path = this.fs.getPath("/", "META-INF", "versions", "" + (i - 44)); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+				if (Files.exists(path)) {
+					versions.add(this.rootPath.relativize(path));
+				}
+			}
+			this.supportedVersions = versions.toArray(new Path[versions.size()]);
+			if (this.supportedVersions.length <= 0) {
+				try {
+					this.fs.close();
+				} catch (IOException e) {
+					// ignore
 				}
 			}
 		}
 	}
+
 	@Override
 	protected String readJarContent(final SimpleSet packageSet) {
 		String[] modInfo = new String[1];
 		modInfo[0] = super.readJarContent(packageSet);
 		try {
-			if (this.releasePath != null && Files.exists(this.releasePath)) {
-				// go through the packages
-				try (DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(this.releasePath)) {
-					for (final java.nio.file.Path subdir: stream) {
-						Files.walkFileTree(subdir, new FileVisitor<java.nio.file.Path>() {
-							@Override
-							public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs)
-									throws IOException {
-								return FileVisitResult.CONTINUE;
-							}
-							@Override
-							public FileVisitResult visitFile(java.nio.file.Path file, BasicFileAttributes attrs)
-									throws IOException {
-								Path p = ClasspathMultiReleaseJar.this.releasePath.relativize(file);
-								addToPackageSet(packageSet, p.toString(), false);
-								if (modInfo[0] == null) {
-									if (p.getFileName().toString().equalsIgnoreCase(IModule.MODULE_INFO_CLASS)) {
-										 modInfo[0] = ClasspathMultiReleaseJar.this.releasePath.relativize(file).toString();
-									}
-								}
-								return FileVisitResult.CONTINUE;
-							}
-
-							@Override
-							public FileVisitResult visitFileFailed(java.nio.file.Path file, IOException exc) throws IOException {
-								return FileVisitResult.CONTINUE;
-							}
-
-							@Override
-							public FileVisitResult postVisitDirectory(java.nio.file.Path dir, IOException exc)
-									throws IOException {
-								return FileVisitResult.CONTINUE;
-							}
-						});
+			for (Path path : this.supportedVersions) {
+				Path relativePath = this.rootPath.resolve(path);
+				Files.walkFileTree(path, new FileVisitor<java.nio.file.Path>() {
+					@Override
+					public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs)
+							throws IOException {
+						return FileVisitResult.CONTINUE;
 					}
-				}
+
+					@Override
+					public FileVisitResult visitFile(java.nio.file.Path file, BasicFileAttributes attrs)
+							throws IOException {
+						Path p = relativePath.relativize(file);
+						addToPackageSet(packageSet, p.toString(), false);
+						if (modInfo[0] == null) {
+							if (p.getFileName().toString().equalsIgnoreCase(IModule.MODULE_INFO_CLASS)) {
+								modInfo[0] = relativePath.relativize(file).toString();
+							}
+						}
+						return FileVisitResult.CONTINUE;
+					}
+
+					@Override
+					public FileVisitResult visitFileFailed(java.nio.file.Path file, IOException exc)
+							throws IOException {
+						return FileVisitResult.CONTINUE;
+					}
+
+					@Override
+					public FileVisitResult postVisitDirectory(java.nio.file.Path dir, IOException exc)
+							throws IOException {
+						return FileVisitResult.CONTINUE;
+					}
+				});
 			}
 		} catch (Exception e) {
 			// move on;
 		}
 		return modInfo[0];
 	}
+
 	@Override
-	public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPackageName, String moduleName, String qualifiedBinaryFileName, boolean asBinaryOnly, Predicate<String> moduleNameFilter) {
-		if (!isPackage(qualifiedPackageName, moduleName)) return null; // most common case
-		if (this.releasePath != null) {
+	public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPackageName, String moduleName,
+			String qualifiedBinaryFileName, boolean asBinaryOnly, Predicate<String> moduleNameFilter) {
+		if (!isPackage(qualifiedPackageName, moduleName))
+			return null; // most common case
+		for (Path path : this.supportedVersions) {
+			Path relativePath = this.rootPath.resolve(path);
 			try {
-				Path path = this.releasePath.resolve(qualifiedPackageName).resolve(binaryFileName);
-				byte[] content = Files.readAllBytes(path);
+				Path p = relativePath.resolve(qualifiedPackageName).resolve(binaryFileName);
+				if (!Files.exists(p))
+					continue;
+				byte[] content = Files.readAllBytes(p);
 				IBinaryType reader = null;
 				if (content != null) {
 					reader = new ClassFileReader(content, qualifiedBinaryFileName.toCharArray());
@@ -152,8 +216,9 @@
 							classReader.moduleName = modName;
 						else
 							modName = classReader.moduleName;
-						}
-					String fileNameWithoutExtension = qualifiedBinaryFileName.substring(0, qualifiedBinaryFileName.length() - SuffixConstants.SUFFIX_CLASS.length);
+					}
+					String fileNameWithoutExtension = qualifiedBinaryFileName.substring(0,
+							qualifiedBinaryFileName.length() - SuffixConstants.SUFFIX_CLASS.length);
 					if (this.externalAnnotationPath != null) {
 						try {
 							if (this.annotationZipFile == null) {
@@ -173,14 +238,15 @@
 					}
 					if (this.accessRuleSet == null)
 						return new NameEnvironmentAnswer(reader, null, modName);
-					return new NameEnvironmentAnswer(reader, 
-							this.accessRuleSet.getViolatedRestriction(fileNameWithoutExtension.toCharArray()), 
-							modName);
+					return new NameEnvironmentAnswer(reader,
+							this.accessRuleSet.getViolatedRestriction(fileNameWithoutExtension.toCharArray()), modName);
 				}
 			} catch (IOException | ClassFormatException e) {
+				e.printStackTrace();
 				// treat as if class file is missing
 			}
 		}
-		return super.findClass(binaryFileName, qualifiedPackageName, moduleName, qualifiedBinaryFileName, asBinaryOnly, moduleNameFilter);
+		return super.findClass(binaryFileName, qualifiedPackageName, moduleName, qualifiedBinaryFileName, asBinaryOnly,
+				moduleNameFilter);
 	}
 }
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryTypeFactory.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryTypeFactory.java
index d5e26b9..c783aab 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryTypeFactory.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryTypeFactory.java
@@ -74,7 +74,8 @@
 		String overridePath = root.getClassFilePath(entryName);
 		if (overridePath != entryName) {
 			entryName = overridePath;
-			name = ((JarPackageFragmentRoot) root).versionPath + name;
+			String versionPath = overridePath.substring(0, overridePath.indexOf(entryName));
+			name = versionPath + name;
 		}
 		char[] fieldDescriptor = CharArrayUtils.concat(new char[] { 'L' },
 				name.toCharArray(), new char[] { ';' });