Upgrade JDT in AspectJ to Mars.2 R4_5_Maintenance branch #A7BBA8B15
diff --git a/org.eclipse.jdt.core/META-INF/MANIFEST.MF b/org.eclipse.jdt.core/META-INF/MANIFEST.MF
index 16c0394..21a1907 100644
--- a/org.eclipse.jdt.core/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.core/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.jdt.core; singleton:=true
-Bundle-Version: 3.11.0.qualifier
+Bundle-Version: 3.11.2.qualifier
 Bundle-Activator: org.eclipse.jdt.core.JavaCore
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
index 1c362ea..d4efab1 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
@@ -26,7 +26,7 @@
 #Format: compiler.name = word1 word2 word3
 compiler.name = Eclipse Compiler for Java(TM)
 #Format: compiler.version = (The place holders will be automatically filled. Do not remove or alter it)
-compiler.version = MARS_1007a0215, 3.10.0
+compiler.version = MARS_A7BBA8B15, 3.11.2
 compiler.copyright = Copyright IBM Corp 2000, 2015. All rights reserved.
 
 ### progress
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
index 0572ffc..7ce5155 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
@@ -5303,6 +5303,7 @@
 								char[] typeName = currentType.qualifiedSourceName();
 								
 								InternalCompletionProposal proposal = createProposal(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, this.actualCompletionPosition);
+								proposal.setBinding(constructor);
 								proposal.setDeclarationSignature(getSignature(currentType));
 								proposal.setDeclarationKey(currentType.computeUniqueKey());
 								proposal.setSignature(getSignature(constructor));
@@ -5463,6 +5464,7 @@
 								char[] typeName = currentType.qualifiedSourceName();
 								int constructorRelevance = relevance + computeRelevanceForConstructor();
 								InternalCompletionProposal proposal =  createProposal(CompletionProposal.CONSTRUCTOR_INVOCATION, this.actualCompletionPosition);
+								proposal.setBinding(constructor);
 								proposal.setDeclarationSignature(getSignature(currentType));
 								proposal.setSignature(getSignature(constructor));
 								MethodBinding original = constructor.original();
@@ -5506,6 +5508,7 @@
 						} else {
 							if(!isIgnored(CompletionProposal.METHOD_REF, missingElements != null) && (this.assistNodeInJavadoc & CompletionOnJavadoc.ONLY_INLINE_TAG) == 0) {
 								InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
+								proposal.setBinding(constructor);
 								proposal.setDeclarationSignature(getSignature(currentType));
 								proposal.setSignature(getSignature(constructor));
 								MethodBinding original = constructor.original();
@@ -5547,6 +5550,7 @@
 							if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0 && !this.requestor.isIgnored(CompletionProposal.JAVADOC_METHOD_REF)) {
 								char[] javadocCompletion = inlineTagCompletion(completion, JavadocTagConstants.TAG_LINK);
 								InternalCompletionProposal proposal =  createProposal(CompletionProposal.JAVADOC_METHOD_REF, this.actualCompletionPosition);
+								proposal.setBinding(constructor);
 								proposal.setDeclarationSignature(getSignature(currentType));
 								proposal.setSignature(getSignature(constructor));
 								MethodBinding original = constructor.original();
@@ -5790,6 +5794,7 @@
 
 				if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
 					InternalCompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
+					proposal.setBinding(field);
 					proposal.setDeclarationSignature(getSignature(field.declaringClass));
 					proposal.setSignature(getSignature(field.type));
 					proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
@@ -5843,6 +5848,7 @@
 						ReferenceBinding fieldType = (ReferenceBinding)field.type;
 
 						InternalCompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
+						proposal.setBinding(field);
 						proposal.setDeclarationSignature(getSignature(field.declaringClass));
 						proposal.setSignature(getSignature(field.type));
 						proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
@@ -6184,6 +6190,7 @@
 					this.noProposal = false;
 					if(!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
 						InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
+						proposal.setBinding(constructor);
 						proposal.setDeclarationSignature(getSignature(currentType));
 						proposal.setSignature(getSignature(constructor));
 						MethodBinding original = constructor.original();
@@ -6417,6 +6424,7 @@
 				// Standard proposal
 				if (!this.isIgnored(CompletionProposal.FIELD_REF, missingElements != null) && (this.assistNodeInJavadoc & CompletionOnJavadoc.ONLY_INLINE_TAG) == 0) {
 					InternalCompletionProposal proposal =  createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
+					proposal.setBinding(field);
 					proposal.setDeclarationSignature(getSignature(field.declaringClass));
 					proposal.setSignature(getSignature(field.type));
 					proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
@@ -6451,6 +6459,7 @@
 				if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0 && !this.requestor.isIgnored(CompletionProposal.JAVADOC_FIELD_REF)) {
 					char[] javadocCompletion = inlineTagCompletion(completion, JavadocTagConstants.TAG_LINK);
 					InternalCompletionProposal proposal =  createProposal(CompletionProposal.JAVADOC_FIELD_REF, this.actualCompletionPosition);
+					proposal.setBinding(field);
 					proposal.setDeclarationSignature(getSignature(field.declaringClass));
 					proposal.setSignature(getSignature(field.type));
 					proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
@@ -6493,6 +6502,7 @@
 			} else {
 				if(!this.isIgnored(CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER, missingElements != null)) {
 					InternalCompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER, this.actualCompletionPosition);
+					proposal.setBinding(field);
 					proposal.setDeclarationSignature(getSignature(field.declaringClass));
 					proposal.setSignature(getSignature(field.type));
 					proposal.setReceiverSignature(getSignature(receiverType));
@@ -7571,6 +7581,7 @@
 					char[] completion = CharOperation.concat(receiverType.sourceName, field.name, '.');
 
 					InternalCompletionProposal proposal =  createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
+					proposal.setBinding(field);
 					proposal.setDeclarationSignature(getSignature(field.declaringClass));
 					proposal.setSignature(getSignature(field.type));
 					proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
@@ -7613,6 +7624,7 @@
 					char[] completion = field.name;
 
 					InternalCompletionProposal proposal =  createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
+					proposal.setBinding(field);
 					proposal.setDeclarationSignature(getSignature(field.declaringClass));
 					proposal.setSignature(getSignature(field.type));
 					proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
@@ -7847,6 +7859,7 @@
 			this.noProposal = false;
 			if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
 				InternalCompletionProposal proposal =  createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
+				proposal.setBinding(field);
 				proposal.setDeclarationSignature(getSignature(field.declaringClass));
 				proposal.setSignature(getSignature(field.type));
 				proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
@@ -8445,6 +8458,7 @@
 			this.noProposal = false;
 			if(!this.requestor.isIgnored(CompletionProposal.METHOD_DECLARATION)) {
 				InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_DECLARATION, this.actualCompletionPosition);
+				proposal.setBinding(method);
 				proposal.setDeclarationSignature(getSignature(method.declaringClass));
 				proposal.setDeclarationKey(method.declaringClass.computeUniqueKey());
 				proposal.setSignature(getSignature(method));
@@ -8724,6 +8738,7 @@
 				// Standard proposal
 				if(!this.isIgnored(CompletionProposal.METHOD_REF, missingElements != null) && (this.assistNodeInJavadoc & CompletionOnJavadoc.ONLY_INLINE_TAG) == 0) {
 					InternalCompletionProposal proposal =  createProposal(completionOnReferenceExpressionName ? CompletionProposal.METHOD_NAME_REFERENCE : CompletionProposal.METHOD_REF, this.actualCompletionPosition);
+					proposal.setBinding(method);
 					proposal.setDeclarationSignature(getSignature(method.declaringClass));
 					proposal.setSignature(getSignature(method));
 					MethodBinding original = method.original();
@@ -8765,6 +8780,7 @@
 				if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0 && !this.requestor.isIgnored(CompletionProposal.JAVADOC_METHOD_REF)) {
 					char[] javadocCompletion = inlineTagCompletion(completion, JavadocTagConstants.TAG_LINK);
 					InternalCompletionProposal proposal =  createProposal(CompletionProposal.JAVADOC_METHOD_REF, this.actualCompletionPosition);
+					proposal.setBinding(method);
 					proposal.setDeclarationSignature(getSignature(method.declaringClass));
 					proposal.setSignature(getSignature(method));
 					MethodBinding original = method.original();
@@ -8793,6 +8809,7 @@
 			} else {
 				if(!this.isIgnored(CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER, missingElements != null)) {
 					InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER, this.actualCompletionPosition);
+					proposal.setBinding(method);
 					proposal.setDeclarationSignature(getSignature(method.declaringClass));
 					proposal.setSignature(getSignature(method));
 					MethodBinding original = method.original();
@@ -8983,6 +9000,7 @@
 							completion = CharOperation.concat(receiverType.sourceName, completion, '.');
 
 							InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
+							proposal.setBinding(method);
 							proposal.setDeclarationSignature(getSignature(method.declaringClass));
 							proposal.setSignature(getSignature(method));
 							MethodBinding original = method.original();
@@ -9012,6 +9030,7 @@
 						completion = CharOperation.concat(receiverType.sourceName, completion, '.');
 
 						InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
+						proposal.setBinding(method);
 						proposal.setDeclarationSignature(getSignature(method.declaringClass));
 						proposal.setSignature(getSignature(method));
 						MethodBinding original = method.original();
@@ -9059,6 +9078,7 @@
 				} else {
 					if (!this.isIgnored(CompletionProposal.METHOD_REF, CompletionProposal.METHOD_IMPORT)) {
 						InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
+						proposal.setBinding(method);
 						proposal.setDeclarationSignature(getSignature(method.declaringClass));
 						proposal.setSignature(getSignature(method));
 						MethodBinding original = method.original();
@@ -9211,6 +9231,7 @@
 			this.noProposal = false;
 			if(!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
 				InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
+				proposal.setBinding(method);
 				proposal.setDeclarationSignature(getSignature(method.declaringClass));
 				proposal.setSignature(getSignature(method));
 				MethodBinding original = method.original();
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java
index 9e324c5..b2427c4 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java
@@ -29,6 +29,9 @@
 import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.internal.compiler.env.IBinaryMethod;
 import org.eclipse.jdt.internal.compiler.env.IBinaryType;
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
+import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
 import org.eclipse.jdt.internal.core.BinaryType;
 import org.eclipse.jdt.internal.core.JavaElement;
 import org.eclipse.jdt.internal.core.JavaModelManager;
@@ -134,6 +137,13 @@
 	private char[] name = null;
 
 	/**
+	 * Binding of the method or constructor being proposed, or
+	 * <code>null</code> if none.
+	 * Defaults to null.
+	 */
+	private Binding binding = null;
+
+	/**
 	 * Signature of the method, field type, member type,
 	 * relevant in the context, or <code>null</code> if none.
 	 * Defaults to null.
@@ -907,6 +917,45 @@
 	}
 
 	/**
+	 * Returns a binding of the method or field corresponding to this proposal or <code>null</code> if none.
+	 * <p>
+	 * The binding <em>may</em> be available for the following kinds of completion proposals:
+	 * <ul>
+	 *  <li><code>ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION</code> - {@link MethodBinding}
+	 * of the constructor being proposed</li>
+	 *  <li><code>CONSTRUCTOR_INVOCATION</code> - {@link MethodBinding}
+	 * of the constructor being proposed</li>
+	 *  <li><code>FIELD_REF</code> - {@link FieldBinding}
+	 * of the field being proposed</li>
+	 *  <li><code>FIELD_REF_WITH_CASTED_RECEIVER</code> - {@link FieldBinding}
+	 * of the field being proposed</li>
+	 *  <li><code>JAVADOC_FIELD_REF</code> - {@link FieldBinding}
+	 * of the field being proposed</li>
+	 *  <li><code>JAVADOC_METHOD_REF</code> - {@link MethodBinding}
+	 * of the method or constructor being proposed</li>
+	 *  <li><code>METHOD_DECLARATION</code> - {@link MethodBinding}
+	 * of the method or constructor being proposed</li>
+	 *  <li><code>METHOD_NAME_REFERENCE</code> - {@link MethodBinding}
+	 * of the method or constructor being proposed</li>
+	 *  <li><code>METHOD_REF</code> - {@link MethodBinding}
+	 * of the method or constructor being proposed</li>
+	 *  <li><code>METHOD_REF_WITH_CASTED_RECEIVER</code> - {@link MethodBinding}
+	 * of the method or constructor being proposed</li>
+	 * </ul>
+	 * For other kinds of completion proposals, this method returns <code>null</code>.
+	 * </p>
+	 *
+	 * @return the binding corresponding to this proposal (if available), or <code>null</code> if none
+	 */
+	public Binding getBinding() {
+		return this.binding;
+	}
+
+	public void setBinding(Binding binding) {
+		this.binding = binding;
+	}
+
+	/**
 	 * Returns the signature of the method or type
 	 * relevant in the context, or <code>null</code> if none.
 	 * <p>
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java
index 6d049f8..0d4d89b 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java
@@ -273,7 +273,7 @@
 
 	// perform some emulation work in case there is some and we are inside a local type only
 	if (allocatedTypeErasure.isNestedType()
-		&& (currentScope.enclosingSourceType().isLocalType() || currentScope.isLambdaScope())) {
+		&& (currentScope.enclosingSourceType().isLocalType() || currentScope.isLambdaSubscope())) {
 
 		if (allocatedTypeErasure.isLocalType()) {
 			((LocalTypeBinding) allocatedTypeErasure).addInnerEmulationDependent(currentScope, false);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
index 23fd4a3..94b8b08 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
@@ -21,6 +21,7 @@
  *								Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault
  *								Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations
  *								Bug 457210 - [1.8][compiler][null] Wrong Nullness errors given on full build build but not on incremental build?
+ *								Bug 469584 - ClassCastException in Annotation.detectStandardAnnotation (320) 
  *        Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
  *                          Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work)
  *                          Bug 409517 - [1.8][compiler] Type annotation problems on more elaborate array references
@@ -319,13 +320,11 @@
 			case TypeIds.T_JavaLangAnnotationRetention :
 				if (valueAttribute != null) {
 					Expression expr = valueAttribute.value;
-					if ((expr.bits & Binding.VARIABLE) == Binding.FIELD) {
-						if (expr instanceof Reference) { // New AspectJ Extension (pr148537)
+					if ((expr.bits & Binding.VARIABLE) == Binding.FIELD && expr instanceof Reference) { // anything but Reference would be a type error anyway
 						FieldBinding field = ((Reference)expr).fieldBinding();
 						if (field != null && field.declaringClass.id == T_JavaLangAnnotationRetentionPolicy) {
 							tagBits |= getRetentionPolicy(field.name);
 						}
-						} // New AspectJ Extension - end of if()
 					}
 				}
 				break;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java
index 4f9cd46..cc61fc0 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java
index 89f4b72..1beec31 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java
@@ -34,6 +34,7 @@
 import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
 import org.eclipse.jdt.internal.compiler.lookup.Binding;
 import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.eclipse.jdt.internal.compiler.lookup.CaptureBinding;
 import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
@@ -424,6 +425,11 @@
 		TypeBinding expectedCollectionType = null;
 		if (elementType != null && collectionType != null) {
 			boolean isTargetJsr14 = this.scope.compilerOptions().targetJDK == ClassFileConstants.JDK1_4;
+			if (collectionType.isCapture()) {
+				TypeBinding upperBound = ((CaptureBinding)collectionType).firstBound;
+				if (upperBound.isArrayType())
+					collectionType = upperBound; // partially anticipating the fix for https://bugs.openjdk.java.net/browse/JDK-8013843
+			}
 			if (collectionType.isArrayType()) { // for(E e : E[])
 				this.kind = ARRAY;
 				this.collectionElementType = ((ArrayBinding) collectionType).elementsType();
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
index 8fc1d41..a615681 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2014 IBM Corporation and others.
+ * Copyright (c) 2012, 2015 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
@@ -36,6 +36,7 @@
  *							Bug 452788 - [1.8][compiler] Type not correctly inferred in lambda expression
  *							Bug 453483 - [compiler][null][loop] Improve null analysis for loops
  *							Bug 455723 - Nonnull argument not correctly inferred in loop
+ *							Bug 463728 - [1.8][compiler][inference] Ternary operator in lambda derives wrong type
  *     Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
  *                          Bug 405104 - [1.8][compiler][codegen] Implement support for serializeable lambdas
  *******************************************************************************/
@@ -424,6 +425,8 @@
 		if (this.body instanceof Expression) {
 			Expression expression = (Expression) this.body;
 			new ReturnStatement(expression, expression.sourceStart, expression.sourceEnd, true).resolve(this.scope); // :-) ;-)
+			if (expression.resolvedType == TypeBinding.VOID && !expression.statementExpression())
+				this.scope.problemReporter().invalidExpressionAsStatement(expression);
 		} else {
 			this.body.resolve(this.scope);
 			/* At this point, shape analysis is complete for ((see returnsExpression(...))
@@ -981,13 +984,11 @@
 			this.returnsValue = true;
 			this.voidCompatible = false;
 			this.valueCompatible = !this.returnsVoid;
-			if (resultType != null) {
-				Expression [] returnExpressions = this.resultExpressions;
-				int resultsLength = returnExpressions.length;
-				System.arraycopy(returnExpressions, 0, returnExpressions = new Expression[resultsLength + 1], 0, resultsLength);
-				returnExpressions[resultsLength] = expression;
-				this.resultExpressions = returnExpressions;
-			}
+			Expression [] returnExpressions = this.resultExpressions;
+			int resultsLength = returnExpressions.length;
+			System.arraycopy(returnExpressions, 0, returnExpressions = new Expression[resultsLength + 1], 0, resultsLength);
+			returnExpressions[resultsLength] = expression;
+			this.resultExpressions = returnExpressions;
 		} else {
 			this.returnsVoid = true;
 			this.valueCompatible = false;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
index 7f1eb02..c738446 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
@@ -52,6 +52,7 @@
  *								Bug 452788 - [1.8][compiler] Type not correctly inferred in lambda expression
  *								Bug 456487 - [1.8][null] @Nullable type variant of @NonNull-constrained type parameter causes grief
  *								Bug 407414 - [compiler][null] Incorrect warning on a primitive type being null
+ *								Bug 470958 - [1.8] Unable to convert lambda 
  *     Jesper S Moller - Contributions for
  *								Bug 378674 - "The method can be declared as static" is wrong
  *        Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
@@ -82,6 +83,7 @@
 import org.eclipse.jdt.internal.compiler.lookup.IPrivilegedHandler;
 import org.eclipse.jdt.internal.compiler.lookup.ImplicitNullAnnotationVerifier;
 import org.eclipse.jdt.internal.compiler.lookup.InferenceContext18;
+import org.eclipse.jdt.internal.compiler.lookup.InferenceVariable;
 import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
 import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
 import org.eclipse.jdt.internal.compiler.lookup.MissingTypeBinding;
@@ -472,7 +474,7 @@
 		codeStream.invoke(Opcodes.OPC_invokestatic, this.syntheticAccessor, null /* default declaringClass */, this.typeArguments);
 		} else {
 			codeStream.invoke(Opcodes.OPC_invokevirtual, this.syntheticAccessor, null /* default declaringClass */, this.typeArguments);
-		    }
+		}
 		// End AspectJ extension
 	}
 	// required cast must occur even if no value is required
@@ -663,6 +665,9 @@
 //		scope.problemReporter().genericInferenceError("Receiver was unexpectedly found resolved", this); //$NON-NLS-1$
 	// AspectJ Extension: End
 	this.actualReceiverType = this.receiver.resolveType(scope);
+		if (this.actualReceiverType instanceof InferenceVariable) {
+			return null; // not yet ready for resolving
+		}
 		this.receiverIsType = this.receiver instanceof NameReference && (((NameReference) this.receiver).bits & Binding.TYPE) != 0;
 	if (receiverCast && this.actualReceiverType != null) {
 		 // due to change of declaring class with receiver type, only identity cast should be notified
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java
index 8f590f7..ccaa1ce 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java
@@ -85,6 +85,7 @@
 	public static int checkAssignment(BlockScope currentScope, FlowContext flowContext,
 									   VariableBinding var, FlowInfo flowInfo, int nullStatus, Expression expression, TypeBinding providedType)
 	{
+		if (providedType == null) return FlowInfo.UNKNOWN; // assume we already reported an error
 		long lhsTagBits = 0L;
 		boolean hasReported = false;
 		if (!currentScope.environment().usesNullTypeAnnotations()) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java
index ff66190..416812e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -256,7 +256,7 @@
 
 		// perform some extra emulation work in case there is some and we are inside a local type only
 		if (allocatedTypeErasure.isNestedType()
-			&& (currentScope.enclosingSourceType().isLocalType() || currentScope.isLambdaScope())) {
+			&& (currentScope.enclosingSourceType().isLocalType() || currentScope.isLambdaSubscope())) {
 
 			if (allocatedTypeErasure.isLocalType()) {
 				((LocalTypeBinding) allocatedTypeErasure).addInnerEmulationDependent(currentScope, this.enclosingInstance != null);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
index 6df8946..2343f42 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
@@ -36,6 +36,7 @@
  *							Bug 448709 - [1.8][null] ensure we don't infer types that violate null constraints on a type parameter's bound
  *							Bug 459967 - [null] compiler should know about nullness of special methods like MyEnum.valueOf()
  *							Bug 466713 - Null Annotations: NullPointerException using <int @Nullable []> as Type Param
+ *							Bug 470542 - NullPointerException in ReferenceExpression.isPotentiallyCompatibleWith (962)
  *        Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contribution for
  *                          Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work)
  *******************************************************************************/
@@ -843,7 +844,11 @@
 				return null;
 			int n = functionType.parameters.length;
 			int k = this.exactMethodBinding.parameters.length;
-			return (n == k || n == k + 1) ? this : null;
+			
+			if (!this.haveReceiver && this.isMethodReference() && !this.exactMethodBinding.isStatic()) {
+				k++;
+			}
+			return (n == k) ? this : null;
 		}
 		// descriptors parameters should be free of inference variables.
 		ReferenceExpression copy = cachedResolvedCopy(targetType); 
@@ -959,12 +964,16 @@
 	public boolean isPotentiallyCompatibleWith(TypeBinding targetType, Scope scope) {
 
         final boolean isConstructorRef = isConstructorReference();
-		if (isConstructorRef && this.receiverType.isArrayType()) {
+		if (isConstructorRef) {
+			if (this.receiverType == null)
+				return false;
+			if (this.receiverType.isArrayType()) {
 			final TypeBinding leafComponentType = this.receiverType.leafComponentType();
 			if (!leafComponentType.isReifiable()) {
 				return false;
 			}
 		}
+		}
 
 		// We get here only when the reference expression is NOT pertinent to applicability.
 		if (!super.isPertinentToApplicability(targetType, null))
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java
index 07cd801..f8fc493 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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/compiler/org/eclipse/jdt/internal/compiler/classfmt/NonNullDefaultAwareTypeAnnotationWalker.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/NonNullDefaultAwareTypeAnnotationWalker.java
index ed80d9b..aafe24b 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/NonNullDefaultAwareTypeAnnotationWalker.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/NonNullDefaultAwareTypeAnnotationWalker.java
@@ -109,6 +109,12 @@
 	}
 	
 	@Override
+	public ITypeAnnotationWalker toSupertype(short index, char[] superTypeSignature) {
+		if (this.isEmpty) return restrict(this.matches, this.pathPtr);
+		return super.toSupertype(index, superTypeSignature);
+	}
+
+	@Override
 	public ITypeAnnotationWalker toMethodParameter(short index) {
 		// don't set nextIsDefaultLocation, because signature-level nullness is handled by ImplicitNullAnnotationVerifier
 		if (this.isEmpty) return restrict(this.matches, this.pathPtr);
@@ -131,6 +137,14 @@
 	}
 
 	@Override
+	public ITypeAnnotationWalker toWildcardBound() {
+		this.nextIsDefaultLocation = (this.defaultNullness & Binding.DefaultLocationTypeBound) != 0;
+		this.nextIsTypeBound = true;
+		if (this.isEmpty) return restrict(this.matches, this.pathPtr);
+		return super.toWildcardBound();
+	}
+
+	@Override
 	public ITypeAnnotationWalker toTypeParameterBounds(boolean isClassTypeParameter, int parameterRank) {
 		this.nextIsDefaultLocation = (this.defaultNullness & Binding.DefaultLocationTypeBound) != 0;
 		this.nextIsTypeBound = true;
@@ -155,6 +169,12 @@
 	}
 
 	@Override
+	protected ITypeAnnotationWalker toNextDetail(int detailKind) {
+		if (this.isEmpty) return restrict(this.matches, this.pathPtr);
+		return super.toNextDetail(detailKind);
+	}
+
+	@Override
 	public IBinaryAnnotation[] getAnnotationsAtCursor(int currentTypeId) {
 		IBinaryAnnotation[] normalAnnotations = this.isEmpty ? null : super.getAnnotationsAtCursor(currentTypeId);
 		if (this.atDefaultLocation &&
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
index d0cf15e..96ada5b 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -65,10 +65,6 @@
 	public static final int LABELS_INCREMENT = 5;
 	// local variable attributes output
 	public static final int LOCALS_INCREMENT = 10;
-	static ExceptionLabel[] noExceptionHandlers = new ExceptionLabel[LABELS_INCREMENT];
-	static BranchLabel[] noLabels = new BranchLabel[LABELS_INCREMENT];
-	static LocalVariableBinding[] noLocals = new LocalVariableBinding[LOCALS_INCREMENT];
-	static LocalVariableBinding[] noVisibleLocals = new LocalVariableBinding[LOCALS_INCREMENT];
 	public static final CompilationResult RESTART_IN_WIDE_MODE = new CompilationResult((char[])null, 0, 0, 0);
 	public static final CompilationResult RESTART_CODE_GEN_FOR_UNUSED_LOCALS_MODE = new CompilationResult((char[])null, 0, 0, 0);
 
@@ -4115,32 +4111,12 @@
 	this.startingClassFileOffset = this.classFileOffset;
 	this.pcToSourceMapSize = 0;
 	this.lastEntryPC = 0;
-	int length = this.visibleLocals.length;
-	if (noVisibleLocals.length < length) {
-		noVisibleLocals = new LocalVariableBinding[length];
-	}
-	System.arraycopy(noVisibleLocals, 0, this.visibleLocals, 0, length);
 	this.visibleLocalsCount = 0;
 
-	length = this.locals.length;
-	if (noLocals.length < length) {
-		noLocals = new LocalVariableBinding[length];
-	}
-	System.arraycopy(noLocals, 0, this.locals, 0, length);
 	this.allLocalsCounter = 0;
 
-	length = this.exceptionLabels.length;
-	if (noExceptionHandlers.length < length) {
-		noExceptionHandlers = new ExceptionLabel[length];
-	}
-	System.arraycopy(noExceptionHandlers, 0, this.exceptionLabels, 0, length);
 	this.exceptionLabelsCounter = 0;
 
-	length = this.labels.length;
-	if (noLabels.length < length) {
-		noLabels = new BranchLabel[length];
-	}
-	System.arraycopy(noLabels, 0, this.labels, 0, length);
 	this.countLabels = 0;
 	this.lastAbruptCompletion = -1;
 
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/AnnotationBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/AnnotationBinding.java
index 02070bd..cfd1fa7 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/AnnotationBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/AnnotationBinding.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -245,7 +245,12 @@
 }
 
 public int hashCode() {
-	return this.type.hashCode();
+	int result = 17;
+	int c = this.type.hashCode();
+	result = 31 * result + c;
+	c =  Arrays.hashCode(this.pairs);
+	result = 31 * result + c;
+	return result;
 }
 public boolean equals(Object object) {
 	if (this == object)
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
index edefccf..67a5b6a 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
@@ -34,6 +34,7 @@
  *								Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations
  *								Bug 453475 - [1.8][null] Contradictory null annotations (4.5 M3 edition)
  *								Bug 454182 - Internal compiler error when using 1.8 compliance for simple project
+ *								Bug 447661 - [1.8][null] Incorrect 'expression needs unchecked conversion' warning
  *    Jesper Steen Moller - Contributions for
  *								Bug 412150 [1.8] [compiler] Enable reflected parameter names during annotation processing
  *								Bug 412153 - [1.8][compiler] Check validity of annotations which may be repeatable
@@ -412,7 +413,7 @@
 			// need annotations on the type before processing null annotations on members respecting any @NonNullByDefault:
 			scanTypeForNullDefaultAnnotation(binaryType, this.fPackage);
 		}
-		ITypeAnnotationWalker walker = getTypeAnnotationWalker(binaryType.getTypeAnnotations());
+		ITypeAnnotationWalker walker = getTypeAnnotationWalker(binaryType.getTypeAnnotations(), Binding.NO_NULL_DEFAULT);
 		ITypeAnnotationWalker toplevelWalker = binaryType.enrichWithExternalAnnotationsFor(walker, null, this.environment);
 		char[] typeSignature = binaryType.getGenericSignature(); // use generic signature even in 1.4
 		this.tagBits |= binaryType.getTagBits();
@@ -533,24 +534,38 @@
 	}
 }
 
-private ITypeAnnotationWalker getTypeAnnotationWalker(IBinaryTypeAnnotation[] annotations) {
+/* When creating a method we need to pass in any default 'nullness' from a @NNBD immediately on this method. */
+private ITypeAnnotationWalker getTypeAnnotationWalker(IBinaryTypeAnnotation[] annotations, int nullness) {
 	if (!isPrototype()) throw new IllegalStateException();
 	if (annotations == null || annotations.length == 0 || !this.environment.usesAnnotatedTypeSystem()) {
 		if (this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
-			int nullness = getNullDefault();
+			if (nullness == Binding.NO_NULL_DEFAULT)
+				nullness = getNullDefault();
 			if (nullness > Binding.NULL_UNSPECIFIED_BY_DEFAULT)
 				return new NonNullDefaultAwareTypeAnnotationWalker(nullness, this.environment);
 		}
 		return ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER;
 	}
 	if (this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
-		int nullness = getNullDefault();
+		if (nullness == Binding.NO_NULL_DEFAULT)
+			nullness = getNullDefault();
 		if (nullness > Binding.NULL_UNSPECIFIED_BY_DEFAULT)
 			return new NonNullDefaultAwareTypeAnnotationWalker(annotations, nullness, this.environment);
 	}
 	return new TypeAnnotationWalker(annotations);
 }
 
+private int getNullDefaultFrom(IBinaryAnnotation[] declAnnotations) {
+	if (declAnnotations != null) {
+		char[][] nonNullByDefaultAnnotationName = this.environment.getNonNullByDefaultAnnotationName();
+		for (IBinaryAnnotation annotation : declAnnotations) {
+			char[][] typeName = signature2qualifiedTypeName(annotation.getTypeName());
+			if (CharOperation.equals(typeName, nonNullByDefaultAnnotationName))
+				return getNonNullByDefaultValue(annotation);
+		}
+	}
+	return Binding.NO_NULL_DEFAULT;
+}
 
 private void createFields(IBinaryField[] iFields, IBinaryType binaryType, long sourceLevel, char[][][] missingTypeNames) {
 	if (!isPrototype()) throw new IllegalStateException();
@@ -565,7 +580,7 @@
 			for (int i = 0; i < size; i++) {
 				IBinaryField binaryField = iFields[i];
 				char[] fieldSignature = use15specifics ? binaryField.getGenericSignature() : null;
-				ITypeAnnotationWalker walker = getTypeAnnotationWalker(binaryField.getTypeAnnotations());
+				ITypeAnnotationWalker walker = getTypeAnnotationWalker(binaryField.getTypeAnnotations(), Binding.NO_NULL_DEFAULT);
 				if (sourceLevel >= ClassFileConstants.JDK1_8) { // below 1.8, external annotations will be attached later
 					walker = binaryType.enrichWithExternalAnnotationsFor(walker, iFields[i], this.environment);
 				}
@@ -637,7 +652,7 @@
 	   variables properly in order to be able to apply substitutions and thus be able to detect
 	   overriding in the presence of generics. Seeing the erased form is not good enough.
 	 */
-	ITypeAnnotationWalker walker = getTypeAnnotationWalker(method.getTypeAnnotations());
+	ITypeAnnotationWalker walker = getTypeAnnotationWalker(method.getTypeAnnotations(), getNullDefaultFrom(method.getAnnotations()));
 	char[] methodSignature = method.getGenericSignature(); // always use generic signature, even in 1.4
 	if (methodSignature == null) { // no generics
 		char[] methodDescriptor = method.getMethodDescriptor();   // of the form (I[Ljava/jang/String;)V
@@ -1593,7 +1608,7 @@
 			char[] annotationTypeName = annotations[i].getTypeName();
 			if (annotationTypeName[0] != Util.C_RESOLVED)
 				continue;
-			char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';'
+			char[][] typeName = signature2qualifiedTypeName(annotationTypeName);
 			if (CharOperation.equals(typeName, nonNullAnnotationName)) {
 				fieldBinding.tagBits |= TagBits.AnnotationNonNull;
 				explicitNullness = true;
@@ -1629,7 +1644,7 @@
 			char[] annotationTypeName = annotations[i].getTypeName();
 			if (annotationTypeName[0] != Util.C_RESOLVED)
 				continue;
-			char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';'
+			char[][] typeName = signature2qualifiedTypeName(annotationTypeName);
 			if (CharOperation.equals(typeName, nonNullByDefaultAnnotationName)) {
 					methodBinding.defaultNullness = getNonNullByDefaultValue(annotations[i]);
 				if (methodBinding.defaultNullness == Binding.NULL_UNSPECIFIED_BY_DEFAULT)
@@ -1663,7 +1678,7 @@
 						char[] annotationTypeName = paramAnnotations[i].getTypeName();
 						if (annotationTypeName[0] != Util.C_RESOLVED)
 							continue;
-						char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';'
+						char[][] typeName = signature2qualifiedTypeName(annotationTypeName);
 						if (CharOperation.equals(typeName, nonNullAnnotationName)) {
 							if (methodBinding.parameterNonNullness == null)
 								methodBinding.parameterNonNullness = new Boolean[numVisibleParams];
@@ -1701,7 +1716,7 @@
 			char[] annotationTypeName = annotations[i].getTypeName();
 			if (annotationTypeName[0] != Util.C_RESOLVED)
 				continue;
-			char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';'
+			char[][] typeName = signature2qualifiedTypeName(annotationTypeName);
 			if (CharOperation.equals(typeName, nonNullByDefaultAnnotationName)) {
 					// using NonNullByDefault we need to inspect the details of the value() attribute:
 					nullness = getNonNullByDefaultValue(annotations[i]);
@@ -1767,7 +1782,7 @@
 // pre: null annotation analysis is enabled
 int getNonNullByDefaultValue(IBinaryAnnotation annotation) {
 	char[] annotationTypeName = annotation.getTypeName();
-	char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';'
+	char[][] typeName = signature2qualifiedTypeName(annotationTypeName);
 	IBinaryElementValuePair[] elementValuePairs = annotation.getElementValuePairs();
 	if (elementValuePairs == null || elementValuePairs.length == 0 ) {
 		// no argument: apply default default
@@ -1793,6 +1808,10 @@
 	}
 }
 
+private char[][] signature2qualifiedTypeName(char[] typeSignature) {
+	return CharOperation.splitOn('/', typeSignature, 1, typeSignature.length-1); // cut off leading 'L' and trailing ';'
+}
+
 @Override
 int getNullDefault() {
 	return this.defaultNullness;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java
index e75257a..dc660b7 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java
@@ -23,6 +23,7 @@
 import org.eclipse.jdt.internal.compiler.ast.ExpressionContext;
 import org.eclipse.jdt.internal.compiler.ast.Invocation;
 import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
 import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression;
 import org.eclipse.jdt.internal.compiler.lookup.InferenceContext18.SuspendedInferenceRecord;
 
@@ -67,8 +68,11 @@
 		}
 		if (!canBePolyExpression(this.left)) {
 			TypeBinding exprType = this.left.resolvedType;
-			if (exprType == null || !exprType.isValidBinding())
+			if (exprType == null || !exprType.isValidBinding()) {
+				if (this.left instanceof MessageSend && ((MessageSend)this.left).actualReceiverType instanceof InferenceVariable)
+					return null; // nothing valuable to infer from this
 				return FALSE;
+			}
 			return ConstraintTypeFormula.create(exprType, this.right, COMPATIBLE, this.isSoft);
 		} else {
 			// shapes of poly expressions (18.2.1)
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java
index 5ea33a7..360a5ab 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java
@@ -64,14 +64,10 @@
 				return this.left.isCompatibleWith(this.right, inferenceContext.scope) || this.left.isBoxingCompatibleWith(this.right, inferenceContext.scope) ? TRUE : FALSE;
 			}
 			if (this.left.isPrimitiveType()) {
-				if (inferenceContext.inferenceKind == InferenceContext18.CHECK_STRICT)
-					inferenceContext.inferenceKind = InferenceContext18.CHECK_LOOSE;
 				TypeBinding sPrime = inferenceContext.environment.computeBoxingType(this.left);
 				return ConstraintTypeFormula.create(sPrime, this.right, COMPATIBLE, this.isSoft);
 			}
 			if (this.right.isPrimitiveType()) {
-				if (inferenceContext.inferenceKind == InferenceContext18.CHECK_STRICT)
-					inferenceContext.inferenceKind = InferenceContext18.CHECK_LOOSE;
 				TypeBinding tPrime = inferenceContext.environment.computeBoxingType(this.right);
 				return ConstraintTypeFormula.create(this.left, tPrime, SAME, this.isSoft);
 			}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java
index 0e06744..7688a6c 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java
@@ -14,6 +14,7 @@
  *								Bug 447088 - [null] @Nullable on fully qualified field type is ignored
  *								Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations
  *								Bug 458396 - NPE in CodeStream.invoke()
+ *								Bug 446217 - [null] @NonNullByDefault in package-info.java causes bogus "null type safety" warning
  *******************************************************************************/
 package org.eclipse.jdt.internal.compiler.lookup;
 
@@ -245,8 +246,11 @@
 
 public void fillInDefaultNonNullness(FieldDeclaration sourceField, Scope scope) {
 	LookupEnvironment environment = scope.environment();
-	if (   this.type != null
-		&& !this.type.isBaseType()
+	if (this.type == null)
+		return;
+	if (environment.usesNullTypeAnnotations() && !this.type.acceptsNonNullDefault())
+		return;
+	if (   !this.type.isBaseType()
 		&& (this.tagBits & TagBits.AnnotationNullMASK) == 0 		// declaration annotation?
 		&& (this.type.tagBits & TagBits.AnnotationNullMASK) == 0)	// type annotation? (java.lang.@Nullable String)
 	{
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java
index 384b4b5..4057cee 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java
@@ -269,7 +269,7 @@
 						break returnType; // compatible by construction, skip complain phase below
 					}
 				}
-				if (hasReturnNonNullDefault && currentMethod.returnType.acceptsNonNullDefault()) { // conflict with inheritance already checked
+				if (hasReturnNonNullDefault && (!useTypeAnnotations || currentMethod.returnType.acceptsNonNullDefault())) { // conflict with inheritance already checked
 					currentNullnessBits = TagBits.AnnotationNonNull;
 					applyReturnNullBits(currentMethod, currentNullnessBits);
 				}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java
index e28b06d..dd33dbb 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java
@@ -493,6 +493,10 @@
 					if (withWildCards != null) {
 						t = ConstraintExpressionFormula.findGroundTargetType(this, skope, lambda, withWildCards);
 					}
+					if (!t.isProperType(true) && t.isParameterizedType()) {
+						// prevent already resolved inference variables from leaking into the lambda
+						t = (ReferenceBinding) Scope.substitute(getResultSubstitution(this.currentBounds, false), t);
+					}
 					MethodBinding functionType;
 					if (t != null && (functionType = t.getSingleAbstractMethod(skope, true)) != null && (lambda = lambda.resolveExpressionExpecting(t, this.scope, this)) != null) {
 						TypeBinding r = functionType.returnType;
@@ -1106,14 +1110,29 @@
 	 * on any uninstantiated variable outside the set.
 	 */
 	private Set<InferenceVariable> getSmallestVariableSet(BoundSet bounds, InferenceVariable[] subSet) {
+		// "Given a set of inference variables to resolve, let V be the union of this set and
+		//  all variables upon which the resolution of at least one variable in this set depends." 
+		Set<InferenceVariable> v = new HashSet<InferenceVariable>();
+		Map<InferenceVariable,Set<InferenceVariable>> dependencies = new HashMap<>(); // compute only once, store for the final loop over 'v'.
+		for (InferenceVariable iv : subSet) {
+			Set<InferenceVariable> tmp = new HashSet<>();
+			addDependencies(bounds, tmp, iv);
+			dependencies.put(iv, tmp);
+			v.addAll(tmp);
+		}
+		// "If every variable in V has an instantiation, then resolution succeeds and this procedure terminates."
+		//  -> (implicit if result remains unassigned)
+		// "Otherwise, let { α1, ..., αn } be a non-empty subset of uninstantiated variables in V such that ...
 		int min = Integer.MAX_VALUE;
 		Set<InferenceVariable> result = null;
-		for (int i = 0; i < subSet.length; i++) {
-			InferenceVariable currentVariable = subSet[i];
+		// "i) for all i (1 ≤ i ≤ n), ..."
+		for (InferenceVariable currentVariable : v) {
 			if (!bounds.isInstantiated(currentVariable)) {
-				Set<InferenceVariable> set = new HashSet<InferenceVariable>();
-				if (!addDependencies(bounds, set, currentVariable, min))
-					continue;
+				// "... if αi depends on the resolution of a variable β, then either β has an instantiation or there is some j such that β = αj; ..."
+				Set<InferenceVariable> set = dependencies.get(currentVariable);
+				if (set == null) // not an element of the original subSet, still need to fetch this var's dependencies
+					addDependencies(bounds, set = new HashSet<>(), currentVariable);
+				//  "... and ii) there exists no non-empty proper subset of { α1, ..., αn } with this property."
 				int cur = set.size();
 				if (cur == 1)
 					return set; // won't get smaller
@@ -1126,19 +1145,15 @@
 		return result;
 	}
 
-	private boolean addDependencies(BoundSet boundSet, Set<InferenceVariable> variableSet, InferenceVariable currentVariable, int min) {
-		if (variableSet.size() >= min)
-			return false; // no improvement
-		if (boundSet.isInstantiated(currentVariable)) return true; // not added
-		if (!variableSet.add(currentVariable)) return true; // already present
+	private void addDependencies(BoundSet boundSet, Set<InferenceVariable> variableSet, InferenceVariable currentVariable) {
+		if (boundSet.isInstantiated(currentVariable)) return; // not added
+		if (!variableSet.add(currentVariable)) return; // already present
 		for (int j = 0; j < this.inferenceVariables.length; j++) {
 			InferenceVariable nextVariable = this.inferenceVariables[j];
 			if (TypeBinding.equalsEquals(nextVariable, currentVariable)) continue;
 			if (boundSet.dependsOnResolutionOf(currentVariable, nextVariable))
-				if (!addDependencies(boundSet, variableSet, nextVariable, min))
-					return false; // abort traversal: no improvement
+				addDependencies(boundSet, variableSet, nextVariable);
 		}
-		return true;
 	}
 
 	private ConstraintFormula pickFromCycle(Set<ConstraintFormula> c) {
@@ -1398,7 +1413,7 @@
 		this.usesUncheckedConversion = record.usesUncheckedConversion;
 	}
 
-	private Substitution getResultSubstitution(final BoundSet result) {
+	private Substitution getResultSubstitution(final BoundSet result, final boolean full) {
 		return new Substitution() {
 			public LookupEnvironment environment() { 
 				return InferenceContext18.this.environment;
@@ -1408,7 +1423,9 @@
 			}
 			public TypeBinding substitute(TypeVariableBinding typeVariable) {
 				if (typeVariable instanceof InferenceVariable) {
-					return result.getInstantiation((InferenceVariable) typeVariable, InferenceContext18.this.environment);
+					TypeBinding instantiation = result.getInstantiation((InferenceVariable) typeVariable, InferenceContext18.this.environment);
+					if (instantiation != null || full)
+						return instantiation;
 				}
 				return typeVariable;
 			}
@@ -1533,7 +1550,7 @@
 	void reportUncheckedConversions(BoundSet solution) {
 		if (this.constraintsWithUncheckedConversion != null) {
 			int len = this.constraintsWithUncheckedConversion.size();
-			Substitution substitution = getResultSubstitution(solution);
+			Substitution substitution = getResultSubstitution(solution, true);
 			for (int i = 0; i < len; i++) {
 				ConstraintTypeFormula constraint = (ConstraintTypeFormula) this.constraintsWithUncheckedConversion.get(i);
 				TypeBinding expectedType = constraint.right;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
index 01aac01..57a81d9 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
@@ -25,6 +25,8 @@
  *								Bug 443347 - [1.8][null] @NonNullByDefault should not affect constructor arguments of an anonymous instantiation
  *								Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations
  *								Bug 466713 - Null Annotations: NullPointerException using <int @Nullable []> as Type Param
+ *								Bug 456584 - [1.8][null] Bogus warning for return type variable's @NonNull annotation being 'redundant'
+ *								Bug 471611 - Error on hover on call to generic method with null annotation
  *     Jesper Steen Moller - Contributions for
  *								Bug 412150 [1.8] [compiler] Enable reflected parameter names during annotation processing
  *******************************************************************************/
@@ -589,8 +591,8 @@
 		if (added)
 			this.tagBits |= TagBits.HasParameterAnnotations;
 	}
-	if (this.returnType != null && hasNonNullDefaultFor(DefaultLocationReturnType, true)) {
-		if (this.returnType.acceptsNonNullDefault() && (this.returnType.tagBits & TagBits.AnnotationNullMASK) == 0) {
+	if (this.returnType != null && hasNonNullDefaultFor(DefaultLocationReturnType, true) && this.returnType.acceptsNonNullDefault()) {
+		if ((this.returnType.tagBits & TagBits.AnnotationNullMASK) == 0) {
 			this.returnType = env.createAnnotatedType(this.returnType, new AnnotationBinding[]{env.getNonNullAnnotation()});
 		} else if (sourceMethod instanceof MethodDeclaration && (this.returnType.tagBits & TagBits.AnnotationNonNull) != 0 
 						&& ((MethodDeclaration)sourceMethod).hasNullTypeAnnotation(AnnotationPosition.MAIN_TYPE)) {
@@ -742,7 +744,7 @@
 		if (this.declaringClass instanceof SourceTypeBinding) {
 			SourceTypeBinding sourceType = (SourceTypeBinding) this.declaringClass;
 			if (sourceType.scope != null) {
-				AbstractMethodDeclaration methodDecl = sourceType.scope.referenceType().declarationOf(this);
+				AbstractMethodDeclaration methodDecl = sourceType.scope.referenceType().declarationOf(originalMethod);
 				for (int i = 0; i < length; i++) {
 					Argument argument = methodDecl.arguments[i];
 					if (argument.annotations != null) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java
index 32b1b07..81d184f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 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
@@ -95,7 +95,13 @@
 		this.rank = prototype.rank;
 		this.firstBound = prototype.firstBound;
 		this.superclass = prototype.superclass;
-		this.superInterfaces = prototype.superInterfaces;
+		if (prototype.superInterfaces != null) {
+			int len = prototype.superInterfaces.length;
+			if (len > 0)
+				System.arraycopy(prototype.superInterfaces, 0, this.superInterfaces = new ReferenceBinding[len], 0, len);
+			else
+				this.superInterfaces = Binding.NO_SUPERINTERFACES;
+		}
 		this.genericTypeSignature = prototype.genericTypeSignature;
 		this.environment = prototype.environment;
 		prototype.tagBits |= TagBits.HasAnnotatedVariants;
@@ -244,13 +250,11 @@
 	}
 
 	public int boundsCount() {
-		if (this.firstBound == null) {
+		if (this.firstBound == null)
 			return 0;
-		} else if (TypeBinding.equalsEquals(this.firstBound, this.superclass)) {
-			return this.superInterfaces.length + 1;
-		} else {
-			return this.superInterfaces.length;
-		}
+		if (this.firstBound.isInterface())
+			return this.superInterfaces.length; // only interface bounds
+		return this.superInterfaces.length + 1; // class or array type isn't contained in superInterfaces
 	}
 
 	/**
@@ -421,10 +425,11 @@
         if (n == 0)
         	return NO_TYPE_BOUNDS;
         TypeBound[] bounds = new TypeBound[n];
-        bounds[0] = TypeBound.createBoundOrDependency(theta, this.firstBound, variable);
-        int ifcOffset = TypeBinding.equalsEquals(this.firstBound, this.superclass) ? -1 : 0;
-        for (int i = 1; i < n; i++)
-			bounds[i] = TypeBound.createBoundOrDependency(theta, this.superInterfaces[i+ifcOffset], variable);
+        int idx = 0;
+        if (!this.firstBound.isInterface())
+        	bounds[idx++] = TypeBound.createBoundOrDependency(theta, this.firstBound, variable);
+        for (int i = 0; i < this.superInterfaces.length; i++)
+			bounds[idx++] = TypeBound.createBoundOrDependency(theta, this.superInterfaces[i], variable);
         return bounds;
 	}
 
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/TheOriginalJDTParserClass.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/TheOriginalJDTParserClass.java
index 2479ee5..613555c 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/TheOriginalJDTParserClass.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/TheOriginalJDTParserClass.java
@@ -2620,7 +2620,7 @@
 						}
 					}
 				}
-				typeDecl.createDefaultConstructor(!this.diet || insideFieldInitializer, true);
+				typeDecl.createDefaultConstructor(!(this.diet && this.dietInt == 0) || insideFieldInitializer, true);
 		}
 	}
 	//always add <clinit> (will be remove at code gen time if empty)
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java
index 8d724bc..5e551ad 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java
@@ -533,7 +533,7 @@
 		return null;
 	}
 
-	class AnnotationIdentityBinding {
+	static class AnnotationIdentityBinding {
 		org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding internalInstance;
 		AnnotationIdentityBinding(org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding internalInstance) {
 			this.internalInstance = internalInstance;
@@ -557,13 +557,9 @@
 			}
 		}
 		Object key =  new AnnotationIdentityBinding(internalInstance);
-		IAnnotationBinding domInstance =
-			(IAnnotationBinding) this.bindingTables.compilerAnnotationBindingsToASTBindings.get(key);
-		if (domInstance != null)
-			return domInstance;
-		domInstance = new AnnotationBinding(internalInstance, this);
-		this.bindingTables.compilerAnnotationBindingsToASTBindings.put(key, domInstance);
-		return domInstance;
+		IAnnotationBinding newDomInstance = new AnnotationBinding(internalInstance, this);
+		IAnnotationBinding domInstance = (IAnnotationBinding) ((ConcurrentHashMap)this.bindingTables.compilerAnnotationBindingsToASTBindings).putIfAbsent(key, newDomInstance);
+		return domInstance != null ? domInstance : newDomInstance;
 	}
 
 	boolean isResolvedTypeInferredFromExpectedType(MethodInvocation methodInvocation) {
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ReorderingImportAdder.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ReorderingImportAdder.java
index adc5e97..3a249d0 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ReorderingImportAdder.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ReorderingImportAdder.java
@@ -19,7 +19,8 @@
 import java.util.Set;
 
 /**
- * Totally sorts new and existing imports together, discarding the order of existing imports.
+ * Totally sorts new and existing imports together, discarding the order of existing imports
+ * and omitting duplicate entries.
  */
 final class ReorderingImportAdder implements ImportAdder {
 	private final Comparator<ImportName> importComparator;
@@ -30,18 +31,14 @@
 
 	@Override
 	public List<ImportName> addImports(Collection<ImportName> existingImports, Collection<ImportName> importsToAdd) {
-		Set<ImportName> existingImportsSet = new HashSet<ImportName>(existingImports);
+		int setCapacity = 2 * (existingImports.size() + importsToAdd.size());
+		Set<ImportName> uniqueImportsWithAdditions = new HashSet<ImportName>(setCapacity);
+		uniqueImportsWithAdditions.addAll(existingImports);
+		uniqueImportsWithAdditions.addAll(importsToAdd);
 
-		List<ImportName> importsWithAdditions = new ArrayList<ImportName>(existingImports.size() + importsToAdd.size());
-		importsWithAdditions.addAll(existingImports);
-		for (ImportName importToAdd : importsToAdd) {
-			if (!existingImportsSet.contains(importToAdd)) {
-				importsWithAdditions.add(importToAdd);
-			}
-		}
+		List<ImportName> sortedImports = new ArrayList<>(uniqueImportsWithAdditions);
+		Collections.sort(sortedImports, this.importComparator);
 
-		Collections.sort(importsWithAdditions, this.importComparator);
-
-		return importsWithAdditions;
+		return sortedImports;
 	}
 }
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/CommentsPreparator.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/CommentsPreparator.java
index e31367b..59ba4e0 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/CommentsPreparator.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/CommentsPreparator.java
@@ -14,10 +14,7 @@
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_BLOCK;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_JAVADOC;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_LINE;
-import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameLBRACE;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameNotAToken;
-import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameRBRACE;
-import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameRPAREN;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameStringLiteral;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameWHITESPACE;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamepackage;
@@ -38,11 +35,13 @@
 import org.eclipse.jdt.core.dom.LineComment;
 import org.eclipse.jdt.core.dom.MemberRef;
 import org.eclipse.jdt.core.dom.MethodRef;
+import org.eclipse.jdt.core.dom.QualifiedName;
 import org.eclipse.jdt.core.dom.TagElement;
 import org.eclipse.jdt.core.formatter.CodeFormatter;
 import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
 import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
+import org.eclipse.jdt.internal.formatter.Token.WrapMode;
 import org.eclipse.jdt.internal.formatter.Token.WrapPolicy;
 
 public class CommentsPreparator extends ASTVisitor {
@@ -54,16 +53,17 @@
 	private final static Pattern HTML_TAG_PATTERN;
 	private final static Pattern HTML_ATTRIBUTE_PATTERN;
 	static {
-		String formatCodeTags = "(pre)?"; //$NON-NLS-1$
-		String separateLineTags = "(dl|hr|nl|p|ul|ol|table|tr)?"; //$NON-NLS-1$
-		String breakBeforeTags = "(dd|dt|li|td|th|h1|h2|h3|h4|h5|h6|q)?"; //$NON-NLS-1$
-		String breakAfterTags = "(br)?"; //$NON-NLS-1$
-		String noFormatTags = "(code|em|tt)?"; //$NON-NLS-1$
-		String otherTags = "([^<>&&\\S]+)??"; //$NON-NLS-1$
-		String ws = "(?:[ \\t]+|[\\r\\n]+[ \\t]*\\*?)"; // whitespace or line break with optional asterisk //$NON-NLS-1$
-		String attribute = "(?:" + ws + "+[^=&&\\S]+" + ws + "*(=)" + ws + "*\"?[^\"]*\"?)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
-		HTML_TAG_PATTERN = Pattern.compile("<(/)?" //$NON-NLS-1$
-				+ formatCodeTags + separateLineTags + breakBeforeTags + breakAfterTags + noFormatTags + otherTags
+		String formatCodeTags = "(pre)"; //$NON-NLS-1$
+		String separateLineTags = "(dl|hr|nl|p|ul|ol|table|tr)"; //$NON-NLS-1$
+		String breakBeforeTags = "(dd|dt|li|td|th|h1|h2|h3|h4|h5|h6|q)"; //$NON-NLS-1$
+		String breakAfterTags = "(br)"; //$NON-NLS-1$
+		String noFormatTags = "(code|em|tt)"; //$NON-NLS-1$
+		String otherTags = "([^<>&&\\S]++)"; //$NON-NLS-1$
+		String ws = "(?>[ \\t]++|[\\r\\n]++[ \\t]*+\\*?)"; // whitespace or line break with optional asterisk //$NON-NLS-1$
+		String attributeValue = "(?>\"[^\"]*\")|(?>\'[^\']*\')|[^/>\"\'&&\\S]++"; //$NON-NLS-1$
+		String attribute = "(?>" + ws + "+[^=&&\\S]+" + ws + "*(=)" + ws + "*(?>" + attributeValue  + "))"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+		HTML_TAG_PATTERN = Pattern.compile("<(/)?+(?:" //$NON-NLS-1$
+				+ formatCodeTags + '|' + separateLineTags + '|' + breakBeforeTags + '|' + breakAfterTags + '|' + noFormatTags + '|' + otherTags + ')'
 				+ "(" + attribute + "*)" + ws + "*/?>", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 		HTML_ATTRIBUTE_PATTERN = Pattern.compile(attribute);
 	}
@@ -77,17 +77,12 @@
 	// TODO (frederic) should have another name than 'param' for the following tags
 	// TODO (frederic) investigate how and why this list was created
 	private final static List<String> PARAM_TAGS = Arrays.asList(
-			"@param", //$NON-NLS-1$
-			"@exception", //$NON-NLS-1$
-			"@serialField", //$NON-NLS-1$
-			"@throws"); //$NON-NLS-1$
+			TagElement.TAG_PARAM,
+			TagElement.TAG_EXCEPTION,
+			TagElement.TAG_SERIALFIELD,
+			TagElement.TAG_THROWS);
 
-	private final static List<String> IMMUTABLE_TAGS = Arrays.asList("@code", "@literal"); //$NON-NLS-1$ //$NON-NLS-2$
-
-	private final static int[] NO_INDENT_AFTER_COMMENT = { TokenNameRPAREN, TokenNameLBRACE, TokenNameRBRACE };
-	static {
-		Arrays.sort(NO_INDENT_AFTER_COMMENT);
-	}
+	private final static List<String> IMMUTABLE_TAGS = Arrays.asList(TagElement.TAG_CODE, TagElement.TAG_LITERAL);
 
 	private final TokenManager tm;
 	private final DefaultCodeFormatterOptions options;
@@ -172,7 +167,7 @@
 				if (policy == null) {
 					int lineStart = this.tm.getPositionInLine(this.tm.findFirstTokenInLine(commentIndex - 1));
 					int commentStart = this.tm.getPositionInLine(commentIndex - 1);
-					policy = new WrapPolicy(commentStart - lineStart, commentIndex - 1, true);
+					policy = new WrapPolicy(WrapMode.WHERE_NECESSARY, commentIndex - 1, commentStart - lineStart);
 				}
 				commentToken.setWrapPolicy(policy);
 				this.lastLineComment = commentToken;
@@ -187,7 +182,8 @@
 		if (isContinuation) {
 			Token first = structure.get(0);
 			first.breakBefore();
-			first.setWrapPolicy(new WrapPolicy(this.lastLineCommentPosition, commentIndex - 1, false));
+			first.setWrapPolicy(
+					new WrapPolicy(WrapMode.WHERE_NECESSARY, commentIndex - 1, this.lastLineCommentPosition));
 
 			// merge previous and current line comment
 			Token previous = this.lastLineComment;
@@ -216,11 +212,13 @@
 				structure.get(0).clearSpaceBefore();
 
 			Token previous = this.tm.get(commentIndex - 1);
+			previous.clearSpaceAfter();
 			if (previous.originalEnd + 1 >= commentToken.originalStart)
 				return;
 			if (structure == null || structure.isEmpty()) {
 				structure = new ArrayList<Token>();
 				structure.add(new Token(previous.originalEnd + 1, commentToken.originalEnd, TokenNameCOMMENT_LINE));
+				commentToken.setInternalStructure(structure);
 			} else {
 				structure.add(0, new Token(previous.originalEnd + 1, commentToken.originalStart - 1,
 						TokenNameWHITESPACE));
@@ -418,17 +416,6 @@
 				next = next2;
 			}
 
-			if (previous != null && previous.getLineBreaksAfter() == 0
-					&& next != null && next.getLineBreaksBefore() == 0
-					&& Arrays.binarySearch(NO_INDENT_AFTER_COMMENT, next.tokenType) < 0) {
-				int policyIndent = (commentToken.getIndent() - previous.getIndent());
-				WrapPolicy wrapPolicy = new WrapPolicy(policyIndent, commentIndex - 1, true);
-				if (this.tm.countLineBreaksBetween(previous, commentToken) == 1)
-					commentToken.setWrapPolicy(wrapPolicy);
-				if (this.tm.countLineBreaksBetween(commentToken, next) == 1)
-					next.setWrapPolicy(wrapPolicy);
-			}
-
 			if (existingBreaksBefore < existingBreaksAfter && previous != null) {
 				commentToken.putLineBreaksAfter(previous.getLineBreaksAfter());
 				previous.clearLineBreaksAfter();
@@ -591,6 +578,7 @@
 			}
 
 			Token startTokeen = this.ctm.get(startIndex);
+			if (startIndex > 1)
 			startTokeen.breakBefore();
 			int firstTagIndex;
 			if (this.firstTagToken == null || (firstTagIndex = this.ctm.indexOf(this.firstTagToken)) < 0
@@ -672,6 +660,11 @@
 				handleFormatCodeTag(startPos, endPos, isOpeningTag);
 			}
 			if (this.options.comment_format_html) {
+				if (TagElement.TAG_PARAM.equals(node.getTagName())
+						&& this.ctm.findIndex(startPos, -1, false) == 1 + this.ctm.firstIndexIn(node, -1)) {
+					continue; // it's a generic class parameter name, not an HTML tag
+				}
+
 				if (matcher.start(3) < matcher.end(3)) {
 					handleSeparateLineTag(startPos, endPos);
 				} else if (matcher.start(4) < matcher.end(4)) {
@@ -697,6 +690,12 @@
 		return true;
 	}
 
+	@Override
+	public boolean visit(QualifiedName node) {
+		handleReference(node);
+		return false;
+	}
+
 	private void handleReference(ASTNode node) {
 		ASTNode parent = node.getParent();
 		if ((parent instanceof TagElement) && ((TagElement) parent).isNested()) {
@@ -742,12 +741,12 @@
 		int firstPartIndex = tokenStartingAt(start);
 		int lastPartIndex = tokenEndingAt(end);
 		Token firstPartToken = this.ctm.get(firstPartIndex);
+		firstPartToken.setWrapPolicy(null);
 		if (isOpeningTag) {
 			firstPartToken.breakBefore();
 			this.ctm.get(lastPartIndex + 1).clearSpaceBefore();
 		} else {
 			firstPartToken.clearSpaceBefore();
-			firstPartToken.setWrapPolicy(null);
 		}
 	}
 
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java
index f7a3580..d971284 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java
@@ -74,6 +74,7 @@
 
 	private String sourceString;
 	private char[] sourceArray;
+	private IRegion[] formatRegions;
 
 	private ASTNode astRoot;
 	private List<Token> tokens = new ArrayList<Token>();
@@ -146,11 +147,12 @@
 		if (!regionsSatisfiesPreconditions(regions, source.length())) {
 			throw new IllegalArgumentException();
 		}
+		this.formatRegions = regions;
 
 		updateWorkingOptions(indentationLevel, lineSeparator, kind);
 
 		if ((kind & K_COMMENTS_MASK) != 0)
-			return formatComments(source, kind & K_COMMENTS_MASK, regions);
+			return formatComments(source, kind & K_COMMENTS_MASK);
 
 		if (prepareFormattedCode(source, kind) == null)
 			return this.tokens.isEmpty() ? new MultiTextEdit() : null;
@@ -202,15 +204,17 @@
 
 	private void findHeader() {
 		if (this.astRoot instanceof CompilationUnit) {
-			List<TypeDeclaration> types = ((CompilationUnit) this.astRoot).types();
-			if (!types.isEmpty()) {
-				int headerEndIndex = this.tokenManager.firstIndexIn(types.get(0), -1);
+			CompilationUnit unit = (CompilationUnit) this.astRoot;
+			List<TypeDeclaration> types = unit.types();
+			ASTNode firstElement = types.isEmpty() ? unit.getPackage() : types.get(0);
+			if (firstElement != null) {
+				int headerEndIndex = this.tokenManager.firstIndexIn(firstElement, -1);
 				this.tokenManager.setHeaderEndIndex(headerEndIndex);
 			}
 		}
 	}
 
-	private TextEdit formatComments(String source, int kind, IRegion[] regions) {
+	private TextEdit formatComments(String source, int kind) {
 		MultiTextEdit result = new MultiTextEdit();
 		if (!init(source))
 			return result;
@@ -264,7 +268,8 @@
 
 		this.tokenManager.applyFormatOff();
 
-		TextEditsBuilder resultBuilder = new TextEditsBuilder(source, regions, this.tokenManager, this.workingOptions);
+		TextEditsBuilder resultBuilder = new TextEditsBuilder(source, this.formatRegions, this.tokenManager,
+				this.workingOptions);
 		resultBuilder.setAlignChar(DefaultCodeFormatterOptions.SPACE);
 		for (Token token : this.tokens) {
 			List<Token> structure = token.getInternalStructure();
@@ -282,6 +287,7 @@
 		ASTParser parser = ASTParser.newParser(AST.JLS8);
 		Map<String, String> parserOptions = JavaCore.getOptions();
 		parserOptions.put(CompilerOptions.OPTION_Source, this.sourceLevel);
+		parserOptions.put(CompilerOptions.OPTION_DocCommentSupport, CompilerOptions.ENABLED);
 		parser.setCompilerOptions(parserOptions);
 
 		switch (kind & K_MASK) {
@@ -370,7 +376,7 @@
 	private void prepareWraps(int kind) {
 		WrapPreparator wrapPreparator = new WrapPreparator(this.tokenManager, this.workingOptions, kind);
 		this.astRoot.accept(wrapPreparator);
-		wrapPreparator.finishUp(this.astRoot);
+		wrapPreparator.finishUp(this.astRoot, this.formatRegions);
 	}
 
 	/**
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 f64a154..58ecff8 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
@@ -710,7 +710,7 @@
 				options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, DefaultCodeFormatterConstants.MIXED);
 				break;
 		}
-		options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, Integer.toString(this.tab_char == MIXED ? this.tab_size : this.indentation_size)); // reverse values swapping performed by IndentationTabPage
+		options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, Integer.toString(this.tab_char == SPACE ? this.indentation_size : this.tab_size)); // reverse values swapping performed by IndentationTabPage
 		options.put(DefaultCodeFormatterConstants.FORMATTER_USE_TABS_ONLY_FOR_LEADING_INDENTATIONS, this.use_tabs_only_for_leading_indentations ?  DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE);
 		options.put(DefaultCodeFormatterConstants.FORMATTER_WRAP_BEFORE_BINARY_OPERATOR, this.wrap_before_binary_operator ? DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE);
 		options.put(DefaultCodeFormatterConstants.FORMATTER_WRAP_BEFORE_OR_OPERATOR_MULTICATCH, this.wrap_before_or_operator_multicatch ? DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE);
@@ -1326,7 +1326,7 @@
 			// reverse values swapping performed by IndentationTabPage
 			if (DefaultCodeFormatterConstants.MIXED.equals(settings.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR)))
 				this.indentation_size = indentationSize;
-			else
+			else if (JavaCore.SPACE.equals(settings.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR)))
 				this.tab_size = indentationSize;
 		}
 		final Object insertNewLineAfterOpeningBraceInArrayInitializerOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_INSERT_NEW_LINE_AFTER_OPENING_BRACE_IN_ARRAY_INITIALIZER);
@@ -2098,9 +2098,9 @@
 				// keep default
 			}
 			// reverse values swapping performed by IndentationTabPage
-			if (DefaultCodeFormatterConstants.MIXED.equals(settings.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR)))
+			if (!JavaCore.SPACE.equals(settings.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR)))
 				this.tab_size = tabSize;
-			else
+			if (!DefaultCodeFormatterConstants.MIXED.equals(settings.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR)))			
 				this.indentation_size = tabSize;
 		}
 		final Object useTabsOnlyForLeadingIndentationsOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_USE_TABS_ONLY_FOR_LEADING_INDENTATIONS);
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java
index 32640ec..6cbfa89 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java
@@ -18,6 +18,7 @@
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameLBRACE;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameRBRACE;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameSEMICOLON;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_JAVADOC;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameelse;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamefinally;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamepackage;
@@ -38,6 +39,7 @@
 import org.eclipse.jdt.core.dom.BreakStatement;
 import org.eclipse.jdt.core.dom.CatchClause;
 import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ContinueStatement;
 import org.eclipse.jdt.core.dom.DoStatement;
 import org.eclipse.jdt.core.dom.EmptyStatement;
 import org.eclipse.jdt.core.dom.EnhancedForStatement;
@@ -97,12 +99,10 @@
 		List<AnnotationTypeDeclaration> types = node.types();
 		if (!types.isEmpty()) {
 			if (!imports.isEmpty())
-				this.tm.firstTokenIn(types.get(0), -1).putLineBreaksBefore(this.options.blank_lines_after_imports + 1);
-			for (int i = 1; i < types.size(); i++) {
-				this.tm.firstTokenIn(types.get(i), -1).putLineBreaksBefore(
-						this.options.blank_lines_between_type_declarations + 1);
+				putBlankLinesBefore(types.get(0), this.options.blank_lines_after_imports);
+			for (int i = 1; i < types.size(); i++)
+				putBlankLinesBefore(types.get(i), this.options.blank_lines_between_type_declarations);
 			}
-		}
 		return true;
 	}
 
@@ -146,21 +146,22 @@
 		BodyDeclaration previous = null;
 		for (BodyDeclaration bodyDeclaration : bodyDeclarations) {
 			if (previous == null) {
-				this.tm.firstTokenIn(bodyDeclaration, -1).putLineBreaksBefore(
-						this.options.blank_lines_before_first_class_body_declaration + 1);
+				putBlankLinesBefore(bodyDeclaration, this.options.blank_lines_before_first_class_body_declaration);
 			} else {
-				int blankLines;
-				if (bodyDeclaration instanceof FieldDeclaration)
+				int blankLines = 0;
+				if (bodyDeclaration instanceof FieldDeclaration) {
 					blankLines = this.options.blank_lines_before_field;
-				else if (bodyDeclaration instanceof AbstractTypeDeclaration)
+				} else if (bodyDeclaration instanceof AbstractTypeDeclaration) {
 					blankLines = this.options.blank_lines_before_member_type;
-				else
+				} else if (bodyDeclaration instanceof MethodDeclaration
+						|| bodyDeclaration instanceof AnnotationTypeMemberDeclaration) {
 					blankLines = this.options.blank_lines_before_method;
+				}
 
 				if (!sameChunk(previous, bodyDeclaration))
 					blankLines = Math.max(blankLines, this.options.blank_lines_before_new_chunk);
 
-				this.tm.firstTokenIn(bodyDeclaration, -1).putLineBreaksBefore(blankLines + 1);
+				putBlankLinesBefore(bodyDeclaration, blankLines);
 			}
 			previous = bodyDeclaration;
 		}
@@ -171,12 +172,19 @@
 			return true;
 		if (bd1 instanceof AbstractTypeDeclaration && bd2 instanceof AbstractTypeDeclaration)
 			return true;
-		if ((bd1 instanceof MethodDeclaration || bd1 instanceof Initializer)
-				&& (bd2 instanceof MethodDeclaration || bd2 instanceof Initializer))
+		if ((bd1 instanceof FieldDeclaration || bd1 instanceof Initializer)
+				&& (bd2 instanceof FieldDeclaration || bd2 instanceof Initializer))
 			return true;
 		return false;
 	}
 
+	private void putBlankLinesBefore(ASTNode node, int linesCount) {
+		int index = this.tm.firstIndexIn(node, -1);
+		while (index > 0 && this.tm.get(index - 1).tokenType == TokenNameCOMMENT_JAVADOC)
+			index--;
+		this.tm.get(index).putLineBreaksBefore(linesCount + 1);
+	}
+
 	@Override
 	public boolean visit(EnumDeclaration node) {
 		handleBracedCode(node, node.getName(), this.options.brace_position_for_enum_declaration,
@@ -185,9 +193,11 @@
 		handleBodyDeclarations(node.bodyDeclarations());
 
 		List<EnumConstantDeclaration> enumConstants = node.enumConstants();
-		for (int i = 0; i < enumConstants.size() - 1; i++) {
+		for (int i = 0; i < enumConstants.size(); i++) {
 			EnumConstantDeclaration declaration = enumConstants.get(i);
-			if (declaration.getAnonymousClassDeclaration() != null)
+			if (declaration.getJavadoc() != null)
+				this.tm.firstTokenIn(declaration, TokenNameCOMMENT_JAVADOC).breakBefore();
+			if (declaration.getAnonymousClassDeclaration() != null && i < enumConstants.size() - 1)
 				this.tm.firstTokenAfter(declaration, TokenNameCOMMA).breakAfter();
 		}
 
@@ -239,11 +249,16 @@
 
 	@Override
 	public boolean visit(MethodDeclaration node) {
+		this.declarationModifierVisited = false;
+
+		if (node.getBody() == null)
+			return true;
+
 		if (node.isConstructor()) {
 			handleBracedCode(node.getBody(), null, this.options.brace_position_for_constructor_declaration,
 					this.options.indent_statements_compare_to_body,
 					this.options.insert_new_line_in_empty_method_body);
-		} else if (node.getBody() != null) {
+		} else {
 			handleBracedCode(node.getBody(), null, this.options.brace_position_for_method_declaration,
 					this.options.indent_statements_compare_to_body,
 					this.options.insert_new_line_in_empty_method_body);
@@ -251,7 +266,6 @@
 			if (openBrace.getLineBreaksAfter() > 0) // if not, these are empty braces
 				openBrace.putLineBreaksAfter(this.options.blank_lines_at_beginning_of_method_body + 1);
 		}
-		this.declarationModifierVisited = false;
 		return true;
 	}
 
@@ -297,6 +311,10 @@
 		if (this.options.indent_switchstatements_compare_to_cases) {
 			int nonBreakStatementEnd = -1;
 			for (Statement statement : statements) {
+				boolean isBreaking = statement instanceof BreakStatement || statement instanceof ReturnStatement
+						|| statement instanceof ContinueStatement || statement instanceof Block;
+				if (isBreaking)
+					addEmptyLineTokenAfter(this.tm.lastIndexIn(statement, -1));
 				if (statement instanceof SwitchCase) {
 					if (nonBreakStatementEnd >= 0) {
 						// indent only comments between previous and current statement
@@ -306,8 +324,7 @@
 				} else if (!(statement instanceof BreakStatement || statement instanceof Block)) {
 					indent(statement);
 				}
-				nonBreakStatementEnd = (statement instanceof BreakStatement || statement instanceof ReturnStatement)
-						? -1 : this.tm.lastIndexIn(statement, -1);
+				nonBreakStatementEnd = isBreaking ? -1 : this.tm.lastIndexIn(statement, -1);
 			}
 			if (nonBreakStatementEnd >= 0) {
 				// indent comments between last statement and closing brace 
@@ -354,28 +371,34 @@
 	@Override
 	public boolean visit(ArrayInitializer node) {
 		int openBraceIndex = this.tm.firstIndexIn(node, TokenNameLBRACE);
-		Token afterOpenBraceToken = this.tm.get(openBraceIndex + 1);
-		boolean isEmpty = afterOpenBraceToken.tokenType == TokenNameRBRACE;
-		if (isEmpty && this.options.keep_empty_array_initializer_on_one_line)
-			return true;
+		int closeBraceIndex = this.tm.lastIndexIn(node, TokenNameRBRACE);
+
+		boolean isEmpty = openBraceIndex + 1 == closeBraceIndex;
+		if (isEmpty) {
+			addEmptyLineTokenAfter(openBraceIndex);
+			closeBraceIndex = this.tm.lastIndexIn(node, TokenNameRBRACE);
+		}
 
 		Token openBraceToken = this.tm.get(openBraceIndex);
-		int closeBraceIndex = this.tm.lastIndexIn(node, TokenNameRBRACE);
+		Token closeBraceToken = this.tm.get(closeBraceIndex);
+
+		if (!(node.getParent() instanceof ArrayInitializer)) {
+		Token afterOpenBraceToken = this.tm.get(openBraceIndex + 1);
+			for (int i = 0; i < this.options.continuation_indentation_for_array_initializer; i++) {
+				afterOpenBraceToken.indent();
+				closeBraceToken.unindent();
+			}
+		}
+
+		if (!isEmpty || !this.options.keep_empty_array_initializer_on_one_line)
 		handleBracePosition(openBraceToken, closeBraceIndex, this.options.brace_position_for_array_initializer);
 
 		if (!isEmpty) {
-			Token closeBraceToken = this.tm.get(closeBraceIndex);
 			if (this.options.insert_new_line_after_opening_brace_in_array_initializer)
 				openBraceToken.breakAfter();
 			if (this.options.insert_new_line_before_closing_brace_in_array_initializer)
 				closeBraceToken.breakBefore();
-			if (!(node.getParent() instanceof ArrayInitializer)) {
-				for (int i = 0; i < this.options.continuation_indentation_for_array_initializer; i++) {
-					afterOpenBraceToken.indent();
-					closeBraceToken.unindent();
 				}
-			}
-		}
 		return true;
 	}
 
@@ -449,9 +472,11 @@
 			breakAfter = this.options.insert_new_line_after_annotation_on_type;
 		} else if (parentNode instanceof FieldDeclaration) {
 			breakAfter = this.options.insert_new_line_after_annotation_on_field;
-		} else if (parentNode instanceof MethodDeclaration
-				|| parentNode instanceof AnnotationTypeMemberDeclaration) {
+		} else if (parentNode instanceof MethodDeclaration) {
 			breakAfter = this.options.insert_new_line_after_annotation_on_method;
+		} else if (parentNode instanceof AnnotationTypeMemberDeclaration) {
+			breakAfter = this.options.insert_new_line_after_annotation_on_method
+					&& ((AnnotationTypeMemberDeclaration) parentNode).getDefault() != node;
 		} else if (parentNode instanceof VariableDeclarationStatement
 				|| parentNode instanceof VariableDeclarationExpression) {
 			breakAfter = this.options.insert_new_line_after_annotation_on_local_variable;
@@ -483,12 +508,15 @@
 	}
 
 	private void handleLoopBody(Statement body) {
-		if (!(body instanceof Block)
-				&& !(body instanceof EmptyStatement && !this.options.put_empty_statement_on_new_line)) {
+		if (body instanceof Block)
+			return;
+		if (body instanceof EmptyStatement && !this.options.put_empty_statement_on_new_line
+				&& !(body.getParent() instanceof IfStatement))
+			return;
 			breakLineBefore(body);
+		addEmptyLineTokenAfter(this.tm.lastIndexIn(body, -1));
 			indent(body);
 		}
-	}
 
 	@Override
 	public boolean visit(IfStatement node) {
@@ -498,21 +526,16 @@
 			if (this.options.insert_new_line_before_else_in_if_statement || !(thenNode instanceof Block))
 				this.tm.firstTokenBefore(elseNode, TokenNameelse).breakBefore();
 
-			boolean keepElseOnSameLine = (elseNode instanceof Block)
-					|| (this.options.keep_else_statement_on_same_line)
+			boolean keepElseOnSameLine = (this.options.keep_else_statement_on_same_line)
 					|| (this.options.compact_else_if && (elseNode instanceof IfStatement));
-			if (!keepElseOnSameLine) {
-				breakLineBefore(elseNode);
-				indent(elseNode);
+			if (!keepElseOnSameLine)
+				handleLoopBody(elseNode);
 			}
-		}
 
 		boolean keepThenOnSameLine = this.options.keep_then_statement_on_same_line
 				|| (this.options.keep_simple_if_on_one_line && elseNode == null);
-		if (!keepThenOnSameLine && !(thenNode instanceof Block)) {
-			breakLineBefore(thenNode);
-			indent(thenNode);
-		}
+		if (!keepThenOnSameLine)
+			handleLoopBody(thenNode);
 
 		return true;
 	}
@@ -543,6 +566,7 @@
 				: this.tm.firstIndexAfter(nodeBeforeOpenBrace, TokenNameLBRACE);
 		int closeBraceIndex = this.tm.lastIndexIn(node, TokenNameRBRACE);
 		Token openBraceToken = this.tm.get(openBraceIndex);
+		Token closeBraceToken = this.tm.get(closeBraceIndex);
 		handleBracePosition(openBraceToken, closeBraceIndex, bracePosition);
 
 		boolean isEmpty = true;
@@ -552,13 +576,16 @@
 				break;
 			}
 		}
+
+		addEmptyLineTokenAfter(openBraceIndex);
+
 		if (!isEmpty || newLineInEmpty) {
 			openBraceToken.breakAfter();
-			this.tm.get(closeBraceIndex).breakBefore();
+			closeBraceToken.breakBefore();
 		}
 		if (indentBody) {
 			this.tm.get(openBraceIndex + 1).indent();
-			this.tm.get(closeBraceIndex).unindent();
+			closeBraceToken.unindent();
 		}
 	}
 
@@ -575,6 +602,32 @@
 		}
 	}
 
+	private void addEmptyLineTokenAfter(int tokenIndex) {
+		if (tokenIndex + 1 >= this.tm.size())
+			return;
+		Token token = this.tm.get(tokenIndex);
+		Token next = this.tm.get(tokenIndex + 1);
+		if (this.tm.countLineBreaksBetween(token, next) < 2 || !this.options.indent_empty_lines)
+			return;
+
+		// find a line break and make a token out of it
+		for (int i = token.originalEnd + 1; i < next.originalStart; i++) {
+			char c = this.tm.charAt(i);
+			char c2 = this.tm.charAt(i + 1);
+			int lineBreakStart = (c == '\r' || c == '\n') ? i : -1;
+			int lineBreakEnd = ((c2 == '\r' || c2 == '\n') && c2 != c) ? i + 1 : lineBreakStart;
+			if (lineBreakStart >= 0) {
+				Token emptyLineToken = new Token(lineBreakStart, lineBreakEnd, Token.TokenNameEMPTY_LINE);
+				emptyLineToken.breakBefore();
+				emptyLineToken.breakAfter();
+				emptyLineToken.setToEscape(true); // force text builder to use toString()
+				this.tm.insert(tokenIndex + 1, emptyLineToken);
+				return;
+			}
+		}
+		assert false;
+	}
+
 	private void indent(ASTNode node) {
 		int startIndex = this.tm.firstIndexIn(node, -1);
 		while (startIndex > 0 && this.tm.get(startIndex - 1).isComment())
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/SpacePreparator.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/SpacePreparator.java
index 63a49a4..bfc18dc 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/SpacePreparator.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/SpacePreparator.java
@@ -1,5 +1,5 @@
 /*******************************************************************************

- * Copyright (c) 2014, 2015 Mateusz Matela and others.

+ * 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

@@ -46,6 +46,7 @@
 import org.eclipse.jdt.core.dom.FieldDeclaration;

 import org.eclipse.jdt.core.dom.ForStatement;

 import org.eclipse.jdt.core.dom.IfStatement;

+import org.eclipse.jdt.core.dom.ImportDeclaration;

 import org.eclipse.jdt.core.dom.InfixExpression;

 import org.eclipse.jdt.core.dom.InstanceofExpression;

 import org.eclipse.jdt.core.dom.IntersectionType;

@@ -56,6 +57,7 @@
 import org.eclipse.jdt.core.dom.MethodDeclaration;

 import org.eclipse.jdt.core.dom.MethodInvocation;

 import org.eclipse.jdt.core.dom.NormalAnnotation;

+import org.eclipse.jdt.core.dom.PackageDeclaration;

 import org.eclipse.jdt.core.dom.ParameterizedType;

 import org.eclipse.jdt.core.dom.ParenthesizedExpression;

 import org.eclipse.jdt.core.dom.PostfixExpression;

@@ -102,6 +104,18 @@
 	}

 

 	@Override

+	public boolean visit(PackageDeclaration node) {

+		handleSemicolon(node);

+		return true;

+	}

+

+	@Override

+	public boolean visit(ImportDeclaration node) {

+		handleSemicolon(node);

+		return true;

+	}

+

+	@Override

 	public boolean visit(TypeDeclaration node) {

 		if (node.getName().getStartPosition() == -1)

 			return true; // this is a fake type created by parsing in class body mode

@@ -111,6 +125,11 @@
 		List<TypeParameter> typeParameters = node.typeParameters();

 		handleTypeParameters(typeParameters);

 

+		if (!node.isInterface() && !node.superInterfaceTypes().isEmpty()) {

+			// fix for: class A<E> extends ArrayList<String>implements Callable<String>

+			handleToken(node.getName(), TokenNameimplements, true, false);

+		}

+

 		handleToken(node.getName(), TokenNameLBRACE,

 				this.options.insert_space_before_opening_brace_in_type_declaration, false);

 		handleCommas(node.superInterfaceTypes(), this.options.insert_space_before_comma_in_superinterfaces,

@@ -122,6 +141,8 @@
 	public boolean visit(EnumDeclaration node) {

 		handleToken(node.getName(), TokenNameLBRACE,

 				this.options.insert_space_before_opening_brace_in_enum_declaration, false);

+		handleCommas(node.superInterfaceTypes(), this.options.insert_space_before_comma_in_superinterfaces,

+				this.options.insert_space_after_comma_in_superinterfaces);

 		handleCommas(node.enumConstants(), this.options.insert_space_before_comma_in_enum_declarations,

 				this.options.insert_space_after_comma_in_enum_declarations);

 		return true;

@@ -186,9 +207,14 @@
 		} else {

 			handleToken(node.getName(), TokenNameLPAREN, spaceBeforeOpenParen, spaceAfterOpenParen);

 

-			if (node.isConstructor() ? this.options.insert_space_before_closing_paren_in_constructor_declaration

-					: this.options.insert_space_before_closing_paren_in_method_declaration)

-				handleToken(node.getName(), TokenNameRPAREN, true, false);

+			boolean spaceBeforeCloseParen = node.isConstructor()

+					? this.options.insert_space_before_closing_paren_in_constructor_declaration

+					: this.options.insert_space_before_closing_paren_in_method_declaration;

+			if (spaceBeforeCloseParen) {

+				List<SingleVariableDeclaration> params = node.parameters();

+				ASTNode beforeBrace = params.isEmpty() ? node.getName() : params.get(params.size() - 1);

+				handleTokenAfter(beforeBrace, TokenNameRPAREN, true, false);

+			}

 		}

 

 		if ((node.isConstructor() ? this.options.insert_space_before_opening_brace_in_constructor_declaration

@@ -222,6 +248,8 @@
 			handleTokenBefore(typeParameters.get(0), TokenNameLESS, true, false);

 			handleTokenAfter(typeParameters.get(typeParameters.size() - 1), TokenNameGREATER, false, true);

 		}

+

+		handleSemicolon(node);

 		return true;

 	}

 

@@ -243,6 +271,7 @@
 		handleToken((ASTNode) node.fragments().get(0), TokenNameIdentifier, true, false);

 		handleCommas(node.fragments(), this.options.insert_space_before_comma_in_multiple_field_declarations,

 				this.options.insert_space_after_comma_in_multiple_field_declarations);

+		handleSemicolon(node);

 		return true;

 	}

 

@@ -287,6 +316,7 @@
 				this.options.insert_space_before_closing_paren_in_switch, false);

 		handleTokenAfter(node.getExpression(), TokenNameLBRACE,

 				this.options.insert_space_before_opening_brace_in_switch, false);

+		handleSemicolon(node.statements());

 		return true;

 	}

 

@@ -369,6 +399,7 @@
 

 	@Override

 	public boolean visit(AssertStatement node) {

+		this.tm.firstTokenIn(node, TokenNameassert).spaceAfter();

 		if (node.getMessage() != null) {

 			handleTokenBefore(node.getMessage(), TokenNameCOLON, this.options.insert_space_before_colon_in_assert,

 					this.options.insert_space_after_colon_in_assert);

@@ -416,6 +447,7 @@
 

 	@Override

 	public boolean visit(AnnotationTypeMemberDeclaration node) {

+		handleToken(node.getName(), TokenNameIdentifier, true, false);

 		handleToken(node.getName(), TokenNameLPAREN,

 				this.options.insert_space_before_opening_paren_in_annotation_type_member_declaration, false);

 		handleEmptyParens(node.getName(),

@@ -457,11 +489,16 @@
 		if (handleParenthesis) {

 			handleToken(node, TokenNameLPAREN, this.options.insert_space_before_opening_paren_in_annotation,

 					this.options.insert_space_after_opening_paren_in_annotation);

-			handleToken(node, TokenNameRPAREN, this.options.insert_space_before_closing_paren_in_annotation, false);

+			if (this.options.insert_space_before_closing_paren_in_annotation)

+				this.tm.lastTokenIn(node, TokenNameRPAREN).spaceBefore();

 		}

 

 		ASTNode parent = node.getParent();

-		if (!(parent instanceof Annotation) && !(parent instanceof ArrayInitializer))

+		boolean skipSpaceAfter = parent instanceof Annotation || parent instanceof MemberValuePair

+				|| (parent instanceof AnnotationTypeMemberDeclaration

+						&& ((AnnotationTypeMemberDeclaration) parent).getDefault() == node)

+				|| parent instanceof ArrayInitializer;

+		if (!skipSpaceAfter)

 			this.tm.lastTokenIn(node, -1).spaceAfter();

 	}

 

@@ -479,7 +516,7 @@
 						this.options.insert_space_before_opening_paren_in_method_declaration,

 						this.options.insert_space_after_opening_paren_in_method_declaration);

 

-				handleToken(node, TokenNameRPAREN,

+				handleTokenBefore(node.getBody(), TokenNameRPAREN,

 						this.options.insert_space_before_closing_paren_in_method_declaration, false);

 			}

 			handleCommas(parameters, this.options.insert_space_before_comma_in_method_declaration_parameters,

@@ -490,20 +527,20 @@
 

 	@Override

 	public boolean visit(Block node) {

-		if (node.getParent().getLength() == 0)

+		handleSemicolon(node.statements());

+

+		ASTNode parent = node.getParent();

+		if (parent.getLength() == 0)

 			return true; // this is a fake block created by parsing in statements mode

-		if (node.getParent() instanceof MethodDeclaration)

+		if (parent instanceof MethodDeclaration)

 			return true; // spaces handled in #visit(MethodDeclaration)

 

 		handleToken(node, TokenNameLBRACE, this.options.insert_space_before_opening_brace_in_block, false);

-		if (this.options.insert_space_after_closing_brace_in_block) {

+		if (this.options.insert_space_after_closing_brace_in_block

+				&& (parent instanceof Statement || parent instanceof CatchClause)) {

 			int closeBraceIndex = this.tm.lastIndexIn(node, TokenNameRBRACE);

-			if (closeBraceIndex + 1 < this.tm.size()) {

-				int nextToken = this.tm.get(closeBraceIndex + 1).tokenType;

-				if (nextToken != TokenNameSEMICOLON && nextToken != TokenNameRPAREN)

 					this.tm.get(closeBraceIndex).spaceAfter();

 			}

-		}

 		return true;

 	}

 

@@ -511,14 +548,19 @@
 	public boolean visit(IfStatement node) {

 		handleToken(node, TokenNameLPAREN, this.options.insert_space_before_opening_paren_in_if,

 				this.options.insert_space_after_opening_paren_in_if);

-		handleTokenBefore(node.getThenStatement(), TokenNameRPAREN,

-				this.options.insert_space_before_closing_paren_in_if, true);

 

 		Statement thenStatement = node.getThenStatement();

-		if (thenStatement instanceof Block && this.tm.isGuardClause((Block) node.getThenStatement())) {

+		int closingParenIndex = this.tm.firstIndexBefore(thenStatement, TokenNameRPAREN);

+		handleToken(this.tm.get(closingParenIndex), this.options.insert_space_before_closing_paren_in_if,

+				/* space before then statement may be needed if it will stay on the same line */

+				!(thenStatement instanceof Block) && !this.tm.get(closingParenIndex + 1).isComment());

+

+		if (thenStatement instanceof Block && this.tm.isGuardClause((Block) thenStatement)) {

 			handleToken(thenStatement, TokenNameLBRACE, false, true);

 			this.tm.lastTokenIn(node, TokenNameRBRACE).spaceBefore();

 		}

+

+		handleSemicolon(thenStatement);

 		return true;

 	}

 

@@ -588,7 +630,7 @@
 

 	@Override

 	public boolean visit(ClassInstanceCreation node) {

-		handleInvocation(node, node.getType(), node.typeArguments());

+		handleInvocation(node, node.getType(), node.typeArguments(), node.getAnonymousClassDeclaration());

 		handleCommas(node.arguments(), this.options.insert_space_before_comma_in_allocation_expression,

 				this.options.insert_space_after_comma_in_allocation_expression);

 		return true;

@@ -613,6 +655,11 @@
 	}

 

 	private void handleInvocation(ASTNode invocationNode, ASTNode nodeBeforeOpeningParen, List<Type> typeArguments) {

+		handleInvocation(invocationNode, nodeBeforeOpeningParen, typeArguments, null);

+	}

+

+	private void handleInvocation(ASTNode invocationNode, ASTNode nodeBeforeOpeningParen, List<Type> typeArguments,

+			ASTNode nodeAfterClosingParen) {

 		if (handleEmptyParens(nodeBeforeOpeningParen,

 				this.options.insert_space_between_empty_parens_in_method_invocation)) {

 			handleToken(nodeBeforeOpeningParen, TokenNameLPAREN,

@@ -622,7 +669,10 @@
 					this.options.insert_space_before_opening_paren_in_method_invocation,

 					this.options.insert_space_after_opening_paren_in_method_invocation);

 			if (this.options.insert_space_before_closing_paren_in_method_invocation) {

-				this.tm.lastTokenIn(invocationNode, TokenNameRPAREN).spaceBefore();

+				Token closingParen = nodeAfterClosingParen == null

+						? this.tm.lastTokenIn(invocationNode, TokenNameRPAREN)

+						: this.tm.firstTokenBefore(nodeAfterClosingParen, TokenNameRPAREN);

+				closingParen.spaceBefore();

 			}

 		}

 

@@ -808,14 +858,11 @@
 

 	@Override

 	public boolean visit(ParameterizedType node) {

+		List<Type> typeArguments = node.typeArguments();

+		if (!typeArguments.isEmpty()) {

 		handleTokenAfter(node.getType(), TokenNameLESS,

 				this.options.insert_space_before_opening_angle_bracket_in_parameterized_type_reference,

 				this.options.insert_space_after_opening_angle_bracket_in_parameterized_type_reference);

-		List<Type> typeArguments = node.typeArguments();

-		if (typeArguments.isEmpty()) {

-			handleTokenAfter(node.getType(), TokenNameGREATER,

-					this.options.insert_space_before_closing_angle_bracket_in_parameterized_type_reference, false);

-		} else {

 			handleTokenAfter(typeArguments.get(typeArguments.size() - 1), TokenNameGREATER,

 					this.options.insert_space_before_closing_angle_bracket_in_parameterized_type_reference, false);

 			handleCommas(node.typeArguments(),

@@ -914,6 +961,7 @@
 	private void handleToken(ASTNode node, int tokenType, boolean spaceBefore, boolean spaceAfter) {

 		if (spaceBefore || spaceAfter) {

 			Token token = this.tm.get(this.tm.findIndex(node.getStartPosition(), tokenType, true));

+			// ^not the same as "firstTokenIn(node, tokenType)" - do not assert the token is inside the node

 			handleToken(token, spaceBefore, spaceAfter);

 		}

 	}

@@ -973,6 +1021,21 @@
 		return false;

 	}

 

+	private void handleSemicolon(ASTNode node) {

+		if (this.options.insert_space_before_semicolon) {

+			Token lastToken = this.tm.lastTokenIn(node, -1);

+			if (lastToken.tokenType == TokenNameSEMICOLON)

+				lastToken.spaceBefore();

+		}

+	}

+

+	private void handleSemicolon(List<ASTNode> nodes) {

+		if (this.options.insert_space_before_semicolon) {

+			for (ASTNode node : nodes)

+				handleSemicolon(node);

+		}

+	}

+

 	public void finishUp() {

 		this.tm.traverse(0, new TokenTraverser() {

 			boolean isPreviousJIDP = false;

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 f1b92e1..37bbe6e 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
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2015 Mateusz Matela and others.
+ * 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
@@ -22,6 +22,7 @@
 import java.util.List;
 
 import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
+import org.eclipse.jdt.internal.formatter.Token.WrapMode;
 import org.eclipse.jdt.internal.formatter.Token.WrapPolicy;
 import org.eclipse.jface.text.IRegion;
 import org.eclipse.jface.text.Region;
@@ -152,8 +153,16 @@
 			this.stringLiteralsInLine.clear();
 			if (getLineBreaksBefore() > 1) {
 				Token indentToken = null;
-				if (this.options.indent_empty_lines && token.tokenType != TokenNameNotAToken)
-					indentToken = token.getIndent() > getPrevious().getIndent() ? token : getPrevious();
+				if (this.options.indent_empty_lines && token.tokenType != TokenNameNotAToken) {
+					if (index == 0) {
+						indentToken = token;
+					} else {
+						boolean isForced = token.getWrapPolicy() != null
+								&& token.getWrapPolicy().wrapMode == WrapMode.FORCED;
+						Token previous = this.tm.get(this.tm.findFirstTokenInLine(index - 1, true, !isForced));
+						indentToken = (token.getIndent() > previous.getIndent()) ? token : previous;
+					}
+				}
 				for (int i = 1; i < getLineBreaksBefore(); i++) {
 					bufferLineSeparator(token, true);
 					if (indentToken != null)
@@ -222,15 +231,24 @@
 			int wrapRootIndent = indent;
 			if (index == -1) { // this means we print a line separator in a multi-line comment
 				TokenManager tm2 = this.parent.tm;
-				wrapRootIndent = tm2.get(tm2.findFirstTokenInLine(this.parentTokenIndex, true)).getIndent();
+				wrapRootIndent = tm2.get(tm2.findFirstTokenInLine(this.parentTokenIndex, true, true)).getIndent();
 			} else if (wrapPolicy != null) {
-				wrapRootIndent = this.tm.get(this.tm.findFirstTokenInLine(index, true)).getIndent();
+				wrapRootIndent = this.tm.get(this.tm.findFirstTokenInLine(index, true, true)).getIndent();
 			}
 			additionalSpaces = indent - wrapRootIndent;
 			indent = wrapRootIndent;
 
-			if (wrapPolicy != null && wrapPolicy.isForced) {
-				int extraIndent = wrapPolicy.extraIndent;
+			if (wrapPolicy != null && wrapPolicy.wrapMode == WrapMode.FORCED) {
+				int parentIndex = wrapPolicy.wrapParentIndex;
+				int parentAlign = 0;
+				int lineStart = this.tm.findFirstTokenInLine(parentIndex);
+				for (int i = parentIndex; i >= lineStart; i--) {
+					parentAlign = this.tm.get(i).getAlign();
+					if (parentAlign > 0)
+						break;
+				}
+
+				int extraIndent = parentAlign == 0 ? wrapPolicy.extraIndent : (token.getIndent() - parentAlign);
 				additionalSpaces -= extraIndent;
 				indent += extraIndent;
 			}
@@ -316,29 +334,43 @@
 		String buffered = this.buffer.toString();
 		boolean sourceMatch = this.source.startsWith(buffered, this.counter)
 				&& this.counter + buffered.length() == currentPosition;
-		if (!sourceMatch && checkRegions(this.counter, currentPosition)) {
-			TextEdit edit = getReplaceEdit(this.counter, currentPosition, buffered);
-			this.edits.add(edit);
+		while (!sourceMatch && this.currentRegion < this.regions.size()) {
+			IRegion region = this.regions.get(this.currentRegion);
+			if (currentPosition < region.getOffset())
+				break;
+			int regionEnd = region.getOffset() + region.getLength();
+			if (this.counter >= regionEnd) {
+				this.currentRegion++;
+				continue;
+		}
+			if (this.currentRegion == this.regions.size() - 1
+					|| this.regions.get(this.currentRegion + 1).getOffset() > currentPosition) {
+				this.edits.add(getReplaceEdit(this.counter, currentPosition, buffered, region));
+				break;
+	}
+
+			// this edit will span more than one region, split it
+			IRegion nextRegion = this.regions.get(this.currentRegion + 1);
+			int bestSplit = 0;
+			int bestSplitScore = Integer.MAX_VALUE;
+			for (int i = 0; i < buffered.length(); i++) {
+				ReplaceEdit edit1 = getReplaceEdit(this.counter, regionEnd, buffered.substring(0, i), region);
+				ReplaceEdit edit2 = getReplaceEdit(regionEnd, currentPosition, buffered.substring(i), nextRegion);
+				int score = edit1.getLength() + edit1.getText().length() + edit2.getLength() + edit2.getText().length();
+				if (score < bestSplitScore) {
+					bestSplit = i;
+					bestSplitScore = score;
+				}
+			}
+			this.edits.add(getReplaceEdit(this.counter, regionEnd, buffered.substring(0, bestSplit), region));
+			buffered = buffered.substring(bestSplit);
+			this.counter = regionEnd;
 		}
 		this.buffer.setLength(0);
 		this.counter = currentPosition;
 	}
 
-	private boolean checkRegions(int editStart, int editEnd) {
-		while (true) {
-			if (this.currentRegion >= this.regions.size())
-				return false;
-			IRegion region = this.regions.get(this.currentRegion);
-			if (editEnd < region.getOffset())
-				return false;
-			if (editStart < region.getOffset() + region.getLength())
-				return true;
-			this.currentRegion++;
-		}
-	}
-
-	private TextEdit getReplaceEdit(int editStart, int editEnd, String text) {
-		IRegion region = this.regions.get(this.currentRegion);
+	private ReplaceEdit getReplaceEdit(int editStart, int editEnd, String text, IRegion region) {
 		int regionEnd = region.getOffset() + region.getLength();
 		if (editStart < region.getOffset() && regionEnd < editEnd) {
 			int breaksInReplacement = this.tm.countLineBreaksBetween(text, 0, text.length());
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/Token.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/Token.java
index 4e6ad4f..ffd5e02 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/Token.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/Token.java
@@ -7,6 +7,7 @@
  *
  * 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
+ *     Till Brychcy - Java Code Formatter breaks code if single line comments contain unicode escape - https://bugs.eclipse.org/471090
  *******************************************************************************/
 package org.eclipse.jdt.internal.formatter;
 
@@ -24,50 +25,64 @@
  */
 public class Token {
 
+	public static enum WrapMode {
+		/**
+		 * Wrap mode for the "Do not wrap" policy. Tokens still should be indented as if wrapped when a preceding line
+		 * break cannot be removed due to a line comment or formatting region restriction.
+		 */
+		DISABLED,
+		/** Wrap mode for the "Wrap where necessary" policies. */
+		WHERE_NECESSARY,
+		/** Wrap mode for the "Wrap all elements" policies. */
+		TOP_PRIORITY,
+		/**
+		 * Wrap mode for tokens that are already in new line before wrapping, but their indentation should be adjusted
+		 * in similar way to wrapping. Used for anonymous class body, lambda body and comments inside code.
+		 */
+		FORCED
+	}
+
 	public static class WrapPolicy {
 
 		/** Policy used for internal structure of multiline comments to mark tokens that should never be wrapped */
-		public final static WrapPolicy DISABLE_WRAP = new WrapPolicy(0, 0, false);
+		public final static WrapPolicy DISABLE_WRAP = new WrapPolicy(WrapMode.DISABLED, 0, 0);
 
 		/**
 		 * Policy used for internal structure of multiline comments to mark tokens that can be wrapped only in lines
 		 * that have no other tokens to wrap.
 		 */
-		public final static WrapPolicy SUBSTITUTE_ONLY = new WrapPolicy(0, 0, false);
+		public final static WrapPolicy SUBSTITUTE_ONLY = new WrapPolicy(WrapMode.DISABLED, 0, 0);
 
-		public final int extraIndent;
+		public final WrapMode wrapMode;
 		public final int wrapParentIndex;
+		public final int groupEndIndex;
+		public final int extraIndent;
 		public final int structureDepth;
 		public final float penaltyMultiplier;
 		public final boolean isFirstInGroup;
 		public final boolean indentOnColumn;
-		public final int topPriorityGroupEnd;
-		/**
-		 * If true, it means the token was in new line even before wrapping, but should be treaded as wrapped token for
-		 * indentation purposes. Used for anonymous class body, lambda body and comments inside code.
-		 */
-		public final boolean isForced;
 
-		public WrapPolicy(int extraIndent, int wrapParentIndex, int structureDepth, float penaltyMultiplier,
-				boolean isFirstInGroup, boolean indentOnColumn, int topPriorityGroupEnd, boolean isForced) {
-			this.extraIndent = extraIndent;
+		public WrapPolicy(WrapMode wrapMode, int wrapParentIndex, int groupEndIndex, int extraIndent,
+				int structureDepth, float penaltyMultiplier, boolean isFirstInGroup, boolean indentOnColumn) {
+			assert wrapMode != null && (wrapParentIndex < groupEndIndex || groupEndIndex == -1);
+
+			this.wrapMode = wrapMode;
 			this.wrapParentIndex = wrapParentIndex;
+			this.groupEndIndex = groupEndIndex;
+			this.extraIndent = extraIndent;
 			this.structureDepth = structureDepth;
 			this.penaltyMultiplier = penaltyMultiplier;
 			this.isFirstInGroup = isFirstInGroup;
 			this.indentOnColumn = indentOnColumn;
-			this.topPriorityGroupEnd = topPriorityGroupEnd;
-			this.isForced = isForced;
 		}
 
-		public WrapPolicy(int extraIndent, int wrapParentIndex, boolean isForced) {
-			this(extraIndent, wrapParentIndex, 0, 1, false, false, -1, isForced);
+		public WrapPolicy(WrapMode wrapMode, int wrapParentIndex, int extraIndent) {
+			this(wrapMode, wrapParentIndex, -1, extraIndent, 0, 1, false, false);
+		}
 		}
 
-		public boolean isTopPriority() {
-			return this.topPriorityGroupEnd >= 0;
-		}
-	}
+	/** Special token type used to mark tokens that store empty line indentation */
+	public static final int TokenNameEMPTY_LINE = 10000;
 
 	/** Position in source of the first character. */
 	public final int originalStart;
@@ -119,9 +134,8 @@
 		int end = scanner.getCurrentTokenEndPosition();
 		if (currentToken == TokenNameCOMMENT_LINE) {
 			// don't include line separator
-			String source = scanner.getCurrentTokenString();
-			for (int i = source.length() - 1; i > 0; i--) {
-				char c = source.charAt(i);
+			while(end >= start) {
+				char c = scanner.source[end];
 				if (c != '\r' && c != '\n')
 					break;
 				end--;
@@ -242,7 +256,8 @@
 	}
 
 	public boolean isWrappable() {
-		return this.wrapPolicy != null && !this.wrapPolicy.isForced;
+		WrapPolicy wp = this.wrapPolicy;
+		return wp != null && wp.wrapMode != WrapMode.DISABLED && wp.wrapMode != WrapMode.FORCED;
 	}
 
 	public void setNLSTag(Token nlsTagToken) {
@@ -276,6 +291,8 @@
 	}
 
 	public String toString(String source) {
+		if (this.tokenType == TokenNameEMPTY_LINE)
+			return ""; //$NON-NLS-1$
 		return source.substring(this.originalStart, this.originalEnd + 1);
 	}
 
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TokenManager.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TokenManager.java
index 054d8b2..8d18424 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TokenManager.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TokenManager.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2015 Mateusz Matela and others.
+ * 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
@@ -10,7 +10,12 @@
  *******************************************************************************/
 package org.eclipse.jdt.internal.formatter;
 
-import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.*;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_BLOCK;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_JAVADOC;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameLBRACE;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameNotAToken;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameStringLiteral;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameWHITESPACE;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -24,6 +29,7 @@
 import org.eclipse.jdt.core.dom.IfStatement;
 import org.eclipse.jdt.core.dom.ReturnStatement;
 import org.eclipse.jdt.core.dom.ThrowStatement;
+import org.eclipse.jdt.internal.formatter.Token.WrapMode;
 import org.eclipse.jdt.internal.formatter.linewrap.CommentWrapExecutor;
 
 /**
@@ -379,16 +385,19 @@
 	}
 
 	public int findFirstTokenInLine(int startIndex) {
-		return findFirstTokenInLine(startIndex, false);
+		return findFirstTokenInLine(startIndex, false, false);
 	}
 
-	public int findFirstTokenInLine(int startIndex, boolean includeWraps) {
+	public int findFirstTokenInLine(int startIndex, boolean includeWraps, boolean includeForced) {
 		Token previous = get(startIndex); // going backwards, previous has higher index than current
 		for (int i = startIndex - 1; i >= 0; i--) {
 			Token token = get(i);
-			int lineBreaks = Math.max(token.getLineBreaksAfter(), previous.getLineBreaksBefore());
-			if (lineBreaks > 0 && (!includeWraps || previous.getWrapPolicy() == null))
+			if (token.getLineBreaksAfter() > 0 || previous.getLineBreaksBefore() > 0) {
+				boolean include = previous.getWrapPolicy() != null
+						&& (previous.getWrapPolicy().wrapMode == WrapMode.FORCED ? includeForced : includeWraps);
+				if (!include)
 				return i + 1;
+			}
 			previous = token;
 		}
 		return 0;
@@ -430,8 +439,10 @@
 		if (this.formatOffTagPairs == null)
 			return;
 		for (Token[] pair : this.formatOffTagPairs) {
-			int index1 = indexOf(pair[0]);
-			int index2 = indexOf(pair[1]);
+			int index1 = findIndex(pair[0].originalStart, -1, false);
+			int index2 = findIndex(pair[1].originalEnd, -1, false);
+			pair[0] = get(index1);
+			pair[1] = get(index2);
 			Token unformatted = new Token(pair[0].originalStart, pair[1].originalEnd, TokenNameWHITESPACE);
 			unformatted.setIndent(Math.min(pair[0].getIndent(), findSourcePositionInLine(pair[0].originalStart)));
 			unformatted.putLineBreaksBefore(pair[0].getLineBreaksBefore());
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/CommentWrapExecutor.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/CommentWrapExecutor.java
index 9eb02b7..05fab8d 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/CommentWrapExecutor.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/CommentWrapExecutor.java
@@ -23,6 +23,7 @@
 import org.eclipse.jdt.internal.formatter.Token;
 import org.eclipse.jdt.internal.formatter.TokenManager;
 import org.eclipse.jdt.internal.formatter.TokenTraverser;
+import org.eclipse.jdt.internal.formatter.Token.WrapMode;
 import org.eclipse.jdt.internal.formatter.Token.WrapPolicy;
 
 public class CommentWrapExecutor extends TokenTraverser {
@@ -225,16 +226,18 @@
 		if (prefix.tokenType == TokenNameWHITESPACE) {
 			whitespace = new Token(prefix);
 			whitespace.breakBefore();
-			whitespace.setWrapPolicy(new WrapPolicy(0, commentIndex, false));
+			whitespace.setIndent(indent);
+			whitespace.setWrapPolicy(new WrapPolicy(WrapMode.WHERE_NECESSARY, commentIndex, 0));
 			prefix = structure.get(1);
+			assert prefix.tokenType == TokenNameCOMMENT_LINE;
 		}
 		int prefixEnd = commentToken.originalStart + 1;
-		if (prefix.tokenType == TokenNameCOMMENT_LINE)
-			prefixEnd = Math.max(prefixEnd, prefix.originalEnd);
+		if (!prefix.hasNLSTag())
+			prefixEnd = Math.max(prefixEnd, prefix.originalEnd); // comments can start with more than 2 slashes
 		prefix = new Token(commentToken.originalStart, prefixEnd, TokenNameCOMMENT_LINE);
 		if (whitespace == null) {
 			prefix.breakBefore();
-			prefix.setWrapPolicy(new WrapPolicy(0, commentIndex, false));
+			prefix.setWrapPolicy(new WrapPolicy(WrapMode.WHERE_NECESSARY, commentIndex, 0));
 		}
 
 		int lineStartIndex = whitespace == null ? 0 : 1;
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
index 8321a34..916751f 100644
--- 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
@@ -21,9 +21,7 @@
 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.TypeDeclaration;
 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;
@@ -62,56 +60,27 @@
 		}
 	}
 
-	private final List<List<FieldDeclaration>> fieldAlignGroups = new ArrayList<List<FieldDeclaration>>();
+	private final List<List<FieldDeclaration>> fieldAlignGroups = new ArrayList<>();
 
 	final TokenManager tm;
 
-	private final DefaultCodeFormatterOptions options;
-
-	public FieldAligner(TokenManager tokenManager, DefaultCodeFormatterOptions options) {
+	public FieldAligner(TokenManager tokenManager) {
 		this.tm = tokenManager;
-		this.options = options;
 	}
 
-	public void prepareAlign(TypeDeclaration node) {
-		List<FieldDeclaration> bodyDeclarations = node.bodyDeclarations();
-		ArrayList<FieldDeclaration> alignGroup = new ArrayList<FieldDeclaration>();
-		BodyDeclaration previous = null;
+	public void prepareAlign(List<FieldDeclaration> bodyDeclarations) {
+		ArrayList<FieldDeclaration> alignGroup = new ArrayList<>();
 		for (BodyDeclaration declaration : bodyDeclarations) {
-			if (!alignGroup.isEmpty()) {
-				if ((declaration instanceof FieldDeclaration) && !areSeparated(previous, declaration)) {
+			if ((declaration instanceof FieldDeclaration)) {
 					alignGroup.add((FieldDeclaration) declaration);
 				} else {
 					alignFields(alignGroup);
-					alignGroup = new ArrayList<FieldDeclaration>();
-				}
+				alignGroup = new ArrayList<>();
 			}
-			if (alignGroup.isEmpty()) {
-				if (declaration instanceof FieldDeclaration)
-					alignGroup.add((FieldDeclaration) declaration);
 			}
-			previous = declaration;
-		}
 		alignFields(alignGroup);
 	}
 
-	private boolean areSeparated(BodyDeclaration declaration1, BodyDeclaration declaration2) {
-		// check if there are more empty lines between fields than normal
-		if (this.options.number_of_empty_lines_to_preserve <= this.options.blank_lines_before_field)
-			return false;
-		int maxLineBreaks = 0;
-		int from = this.tm.lastIndexIn(declaration1, -1);
-		int to = this.tm.firstIndexIn(declaration2, -1);
-
-		Token previous = this.tm.get(from);
-		for (int i = from + 1; i <= to; i++) {
-			Token token = this.tm.get(i);
-			maxLineBreaks = Math.max(maxLineBreaks, this.tm.countLineBreaksBetween(previous, token));
-			previous = token;
-		}
-		return maxLineBreaks - 1 > this.options.blank_lines_before_field;
-	}
-
 	private void alignFields(ArrayList<FieldDeclaration> alignGroup) {
 		if (alignGroup.size() < 2)
 			return;
@@ -125,7 +94,7 @@
 			int positionInLine = this.tm.getPositionInLine(nameIndex);
 			maxNameAlign = Math.max(maxNameAlign, positionInLine);
 		}
-		maxNameAlign = this.tm.toIndent(maxNameAlign, true);
+		maxNameAlign = this.tm.toIndent(maxNameAlign, false);
 
 		int maxAssignAlign = 0;
 		for (FieldDeclaration declaration : alignGroup) {
@@ -142,7 +111,7 @@
 				maxAssignAlign = Math.max(maxAssignAlign, positionInLine);
 			}
 		}
-		maxAssignAlign = this.tm.toIndent(maxAssignAlign, true);
+		maxAssignAlign = this.tm.toIndent(maxAssignAlign, false);
 
 		for (FieldDeclaration declaration : alignGroup) {
 			List<VariableDeclarationFragment> fragments = declaration.fragments();
@@ -151,16 +120,9 @@
 				int assingIndex = this.tm.firstIndexAfter(fragment.getName(), TokenNameEQUAL);
 				Token assignToken = this.tm.get(assingIndex);
 				assignToken.setAlign(maxAssignAlign);
-
-				int baseIndent = this.tm.getPositionInLine(assingIndex + 1) - assignToken.getIndent();
-				int lastIndex = this.tm.lastIndexIn(declaration, -1);
-				for (int i = assingIndex + 1; i <= lastIndex; i++) {
-					Token token = this.tm.get(i);
-					token.setIndent(baseIndent + token.getIndent());
 				}
 			}
 		}
-	}
 
 	public void alignComments() {
 		if (this.fieldAlignGroups.isEmpty())
@@ -176,7 +138,7 @@
 				maxCommentAlign = Math.max(maxCommentAlign,
 						positionCounter.findMaxPosition(firstIndexInLine, lastIndex));
 			}
-			maxCommentAlign = this.tm.toIndent(maxCommentAlign, true);
+			maxCommentAlign = this.tm.toIndent(maxCommentAlign, false);
 
 			for (FieldDeclaration declaration : alignGroup) {
 				int typeIndex = this.tm.firstIndexIn(declaration.getType(), -1);
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java
index 791c385..5f906dc 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java
@@ -26,6 +26,7 @@
 import org.eclipse.jdt.internal.formatter.Token;
 import org.eclipse.jdt.internal.formatter.TokenManager;
 import org.eclipse.jdt.internal.formatter.TokenTraverser;
+import org.eclipse.jdt.internal.formatter.Token.WrapMode;
 import org.eclipse.jdt.internal.formatter.Token.WrapPolicy;
 
 public class WrapExecutor {
@@ -74,8 +75,6 @@
 
 		public static final WrapResult NO_WRAP_NEEDED = new WrapResult(0, 0, null);
 
-		public static final WrapResult TOP_PRIORITY_WRAP_MET = new WrapResult(0, 0, null);
-
 		public final double penalty;
 		public final int totalExtraLines;
 		/**
@@ -91,6 +90,17 @@
 		}
 	}
 
+	private static class WrapRestartThrowable extends Throwable {
+		private static final long serialVersionUID = -2980600077230803443L; // backward compatible
+
+		public final int topPriorityWrap;
+
+		public WrapRestartThrowable(int topPriorityWrap) {
+			super(null, null, false, false);
+			this.topPriorityWrap = topPriorityWrap;
+		}
+	}
+
 	private class LineAnalyzer extends TokenTraverser {
 
 		final private CommentWrapExecutor commentWrapper;
@@ -133,10 +143,10 @@
 
 			if (token.isWrappable()) {
 				WrapPolicy wrapPolicy = token.getWrapPolicy();
-				if (wrapPolicy.isTopPriority() && getLineBreaksBefore() == 0
+				if (wrapPolicy.wrapMode == WrapMode.TOP_PRIORITY && getLineBreaksBefore() == 0
 						&& index > this.currentTopPriorityGroupEnd) {
 					this.topPriorityGroupStarts.add(index);
-					this.currentTopPriorityGroupEnd = wrapPolicy.topPriorityGroupEnd;
+					this.currentTopPriorityGroupEnd = wrapPolicy.groupEndIndex;
 				}
 				if (this.firstPotentialWrap < 0 && getWrapIndent(token) < this.counter)
 					this.firstPotentialWrap = index;
@@ -274,8 +284,6 @@
 	final TokenManager tm;
 	final DefaultCodeFormatterOptions options;
 
-	private int topPriorityWrapIndex;
-
 	private final WrapInfo wrapInfoTemp = new WrapInfo();
 
 	public WrapExecutor(TokenManager tokenManager, DefaultCodeFormatterOptions options) {
@@ -286,86 +294,102 @@
 
 	public void executeWraps() {
 		int index = 0;
-		mainLoop: while (index < this.tm.size()) {
+		while (index < this.tm.size()) {
 			Token token = this.tm.get(index);
-			handleOnColumnIndent(index, token.getWrapPolicy());
-			// this might be a pre-existing wrap that should trigger other top priority wraps
-			int jumpToIndex = handleTopPriorityWraps(index);
-			if (jumpToIndex >= 0) {
-				index = jumpToIndex;
-				continue mainLoop;
+			while (true) {
+				try {
+					int currentIndent = getWrapIndent(token);
+					this.wrapSearchResults.clear();
+					index = applyWraps(index, currentIndent);
+					break;
+				} catch (WrapRestartThrowable e) {
+					handleTopPriorityWraps(e);
+				}
 			}
-
-			// determine wraps for incoming line
-			int currentIndent = getWrapIndent(token);
-			boolean isLineWrapped = token.isWrappable();
 			this.wrapSearchResults.clear();
-			WrapResult wrapResult = findWraps(index, currentIndent);
-			if (wrapResult == WrapResult.TOP_PRIORITY_WRAP_MET) {
-				jumpToIndex = handleTopPriorityWraps(this.topPriorityWrapIndex);
-				assert jumpToIndex >= 0;
-				index = Math.min(index, jumpToIndex);
-				continue mainLoop;
+			this.usedTopPriorityWraps.clear();
 			}
 
-			// apply wraps and indents
-			WrapInfo wrapInfo = wrapResult.nextWrap;
-			while (wrapInfo != null) {
-				isLineWrapped = true;
-				for (; index < wrapInfo.wrapTokenIndex; index++) {
-					token = this.tm.get(index);
-					if (shouldForceWrap(token, currentIndent)) {
-						currentIndent = token.getIndent();
-						wrapInfo = new WrapInfo(index, currentIndent);
-						findWrapsCached(index, currentIndent);
-						break;
-					}
-					token.setIndent(currentIndent);
-				}
-				token = this.tm.get(index);
-				token.breakBefore();
-				token.setIndent(currentIndent = wrapInfo.indent);
-				handleOnColumnIndent(index, token.getWrapPolicy());
-				jumpToIndex = handleTopPriorityWraps(index);
-				if (jumpToIndex >= 0) {
-					index = jumpToIndex;
-					continue mainLoop;
-				}
-				wrapInfo = this.wrapSearchResults.get(wrapInfo).nextWrap;
-			}
-
-			// apply indent until the beginning of the next line
-			token.setIndent(currentIndent);
-			for (index++; index < this.tm.size(); index++) {
-				if (token.getLineBreaksAfter() > 0)
-					break;
-				token = this.tm.get(index);
-				if (token.isNextLineOnWrap() && isLineWrapped)
-					token.breakBefore();
-				if (token.getLineBreaksBefore() > 0)
-					break;
-				if (shouldForceWrap(token, currentIndent))
-					currentIndent = token.getIndent();
-				token.setIndent(currentIndent);
-			}
-		}
-		this.wrapSearchResults.clear();
-		this.usedTopPriorityWraps.clear();
-
 		this.tm.traverse(0, new NLSTagHandler());
+					}
+
+	private int applyWraps(int index, int indent) throws WrapRestartThrowable {
+		WrapInfo wrapInfo = findWrapsCached(index, indent).nextWrap;
+		Token token = this.tm.get(index);
+		index++;
+		token.setIndent(indent);
+		int groupEnd = token.getWrapPolicy() != null ? token.getWrapPolicy().groupEndIndex : -1;
+		while (index < this.tm.size()) {
+				token = this.tm.get(index);
+			if (token.isNextLineOnWrap() && this.tm.get(this.tm.findFirstTokenInLine(index)).isWrappable()) {
+				token.breakBefore();
+				return index;
+				}
+			while (wrapInfo != null && wrapInfo.wrapTokenIndex < index)
+				wrapInfo = this.wrapSearchResults.get(wrapInfo).nextWrap;
+			if (wrapInfo != null && wrapInfo.wrapTokenIndex == index) {
+				token.breakBefore();
+				handleOnColumnIndent(index, token.getWrapPolicy());
+				checkTopPriorityWraps(index);
+				index = applyWraps(index, wrapInfo.indent);
+				continue;
+			}
+
+			boolean isNewLine = this.tm.get(index - 1).getLineBreaksAfter() > 0 || token.getLineBreaksBefore() > 0;
+			if (isNewLine) {
+				if (token.getWrapPolicy() != null) {
+					handleOnColumnIndent(index, token.getWrapPolicy());
+					checkTopPriorityWraps(index);
+					int newIndent = getWrapIndent(token);
+					if (newIndent < indent)
+						return index;
+					wrapInfo = findWrapsCached(index, newIndent).nextWrap;
+					if (newIndent > indent) {
+						index = applyWraps(index, newIndent);
+						continue;
+					}
+				} else if (index > groupEnd) {
+					return index;
+			}
+			} else {
+				checkForceWrap(token, index, indent);
+		}
+
+			token.setIndent(indent);
+			index++;
+		}
+		return index;
 	}
 
-	private WrapResult findWrapsCached(int startTokenIndex, int indent) {
+	private WrapResult findWrapsCached(int startTokenIndex, int indent) throws WrapRestartThrowable {
 		this.wrapInfoTemp.wrapTokenIndex = startTokenIndex;
 		this.wrapInfoTemp.indent = indent;
 		WrapResult wrapResult = this.wrapSearchResults.get(this.wrapInfoTemp);
+		if (wrapResult == null && this.wrapSearchResults.containsKey(this.wrapInfoTemp))
+			return null; // no wrap needed
+
+		// pre-existing result may be based on different wrapping of earlier tokens and therefore be wrong
+		WrapResult wr = wrapResult;
+		while (wr != null && wr.nextWrap != null) {
+			WrapInfo wi = wr.nextWrap;
+			Token token = this.tm.get(wi.wrapTokenIndex);
+			if (token.getWrapPolicy().wrapParentIndex < startTokenIndex && getWrapIndent(token) != wi.indent) {
+				wrapResult = null;
+				break;
+			}
+			wr = this.wrapSearchResults.get(wi);
+		}
+
 		if (wrapResult == null) {
 			Token token = this.tm.get(startTokenIndex);
 			boolean wasLineBreak = token.getLineBreaksBefore() > 0;
 			token.breakBefore();
+			try {
 			wrapResult = findWraps(startTokenIndex, indent);
+			} finally {
 			if (!wasLineBreak)
 				token.clearLineBreaksBefore();
+			}
 
 			WrapInfo wrapInfo = new WrapInfo(startTokenIndex, indent);
 			this.wrapSearchResults.put(wrapInfo, wrapResult);
@@ -377,7 +401,7 @@
 	 * The main algorithm that looks for optimal places to wrap.
 	 * Calls itself recursively to get results for wrapped sub-lines.  
 	 */
-	private WrapResult findWraps(int wrapTokenIndex, int indent) {
+	private WrapResult findWraps(int wrapTokenIndex, int indent) throws WrapRestartThrowable {
 		final int lastIndex = this.lineAnalyzer.analyzeLine(wrapTokenIndex, indent);
 		final boolean lineExceeded = this.lineAnalyzer.lineExceeded;
 		final int lastPosition = this.lineAnalyzer.getLastPosition();
@@ -390,7 +414,7 @@
 		final int[] topPriorityGroupStarts = toArray(this.lineAnalyzer.topPriorityGroupStarts);
 		int topPriorityIndex = topPriorityGroupStarts.length - 1;
 		int nearestGroupEnd = topPriorityIndex == -1 ? 0
-				: this.tm.get(topPriorityGroupStarts[topPriorityIndex]).getWrapPolicy().topPriorityGroupEnd;
+				: this.tm.get(topPriorityGroupStarts[topPriorityIndex]).getWrapPolicy().groupEndIndex;
 
 		double bestTotalPenalty = getWrapPenalty(wrapTokenIndex, indent, lastIndex + 1, -1, WrapResult.NO_WRAP_NEEDED);
 		int bestExtraLines = lineExceeded ? Integer.MAX_VALUE : extraLines; // if line is exceeded, accept every wrap
@@ -402,7 +426,8 @@
 
 		if ((!lineExceeded || firstPotentialWrap < 0) && lastIndex + 1 < this.tm.size()) {
 			Token nextLineToken = this.tm.get(lastIndex + 1);
-			if (nextLineToken.isWrappable() && (this.tm.get(lastIndex).isComment() || nextLineToken.isComment())) {
+			if ((nextLineToken.getWrapPolicy() != null && nextLineToken.getWrapPolicy().wrapMode != WrapMode.FORCED)
+					&& (this.tm.get(lastIndex).isComment() || nextLineToken.isComment())) {
 				// this might be a pre-existing wrap forced by a comment, calculate penalties as normal
 				bestIndent = getWrapIndent(nextLineToken);
 				bestNextWrap = lastIndex + 1;
@@ -414,8 +439,7 @@
 
 		if (firstPotentialWrap < 0 && lineExceeded) {
 			if (topPriorityGroupStarts.length > 0) {
-				this.topPriorityWrapIndex = topPriorityGroupStarts[0];
-				return WrapResult.TOP_PRIORITY_WRAP_MET;
+				checkTopPriorityWraps(topPriorityGroupStarts[0]);
 			}
 
 			// Report high number of extra lines to encourage the algorithm to look
@@ -439,7 +463,7 @@
 				assert i == topPriorityGroupStarts[topPriorityIndex];
 				topPriorityIndex--;
 				nearestGroupEnd = topPriorityIndex == -1 ? 0
-						: this.tm.get(topPriorityGroupStarts[topPriorityIndex]).getWrapPolicy().topPriorityGroupEnd;
+						: this.tm.get(topPriorityGroupStarts[topPriorityIndex]).getWrapPolicy().groupEndIndex;
 			}
 
 			if (!token.isWrappable())
@@ -448,9 +472,6 @@
 			int nextWrapIndent = getWrapIndent(token);
 			WrapResult nextWrapResult = findWrapsCached(i, nextWrapIndent);
 
-			if (nextWrapResult == WrapResult.TOP_PRIORITY_WRAP_MET)
-				continue;
-
 			double totalPenalty = getWrapPenalty(wrapTokenIndex, indent, i, nextWrapIndent, nextWrapResult);
 			int totalExtraLines = extraLines + nextWrapResult.totalExtraLines;
 			boolean isBetter = totalExtraLines < bestExtraLines || bestExtraLines == Integer.MAX_VALUE;
@@ -468,8 +489,7 @@
 		}
 
 		if (bestNextWrap == -1 && lineExceeded && topPriorityGroupStarts.length > 0) {
-			this.topPriorityWrapIndex = topPriorityGroupStarts[0];
-			return WrapResult.TOP_PRIORITY_WRAP_MET;
+			checkTopPriorityWraps(topPriorityGroupStarts[0]);
 		}
 
 		return new WrapResult(bestTotalPenalty, bestExtraLines,
@@ -477,7 +497,7 @@
 	}
 
 	private double getWrapPenalty(int lineStartIndex, int lineIndent, int wrapIndex, int wrapIndent,
-			WrapResult wrapResult) {
+			WrapResult wrapResult) throws WrapRestartThrowable {
 		WrapPolicy wrapPolicy = null;
 		Token wrapToken = null;
 		if (wrapIndex < this.tm.size()) {
@@ -521,12 +541,12 @@
 		// a wrap of the same parent (bar2). If so, then bar1 must be wrapped (so give it negative penalty).
 		// Update: Actually, every token that is followed by a higher level depth wrap should be also wrapped,
 		// as long as this next wrap is not the last in line and the token is not the first in its wrap group.
-		WrapResult nextWrapResult = wrapResult;
+		WrapInfo nextWrap = wrapResult.nextWrap;
 		boolean checkDepth = wrapToken != null && wrapToken.isWrappable()
 				&& (lineStartWrapPolicy == null || wrapPolicy.structureDepth >= lineStartWrapPolicy.structureDepth);
 		double penaltyDiff = 0;
-		while (checkDepth && nextWrapResult.nextWrap != null) {
-			WrapPolicy nextPolicy = this.tm.get(nextWrapResult.nextWrap.wrapTokenIndex).getWrapPolicy();
+		while (checkDepth && nextWrap != null) {
+			WrapPolicy nextPolicy = this.tm.get(nextWrap.wrapTokenIndex).getWrapPolicy();
 			if (nextPolicy.wrapParentIndex == wrapPolicy.wrapParentIndex
 					|| (penaltyDiff != 0 && !wrapPolicy.isFirstInGroup)) {
 				penalty -= penaltyDiff * 1.25;
@@ -535,7 +555,7 @@
 			if (nextPolicy.structureDepth <= wrapPolicy.structureDepth)
 				break;
 			penaltyDiff = Math.max(penaltyDiff, getPenalty(nextPolicy));
-			nextWrapResult = this.wrapSearchResults.get(nextWrapResult.nextWrap);
+			nextWrap = findWrapsCached(nextWrap.wrapTokenIndex, nextWrap.indent).nextWrap;
 		}
 
 		return penalty + wrapResult.penalty;
@@ -545,40 +565,40 @@
 		return Math.exp(policy.structureDepth) * policy.penaltyMultiplier;
 	}
 
-	private boolean shouldForceWrap(Token token, int currentIndent) {
+	private void checkForceWrap(Token token, int index, int currentIndent) throws WrapRestartThrowable {
 		// A token that will have smaller indent when wrapped than the current line indent,
 		// should be wrapped because it's a low depth token following some complex wraps of higher depth.
 		// This rule could not be implemented in getWrapPenalty() because a token's wrap indent may depend
 		// on wraps in previous lines, which are not determined yet when the token's penalty is calculated.
-		if (token.isWrappable() && this.options.wrap_outer_expressions_when_nested) {
-			int indent = getWrapIndent(token);
-			if (indent < currentIndent) {
+		if (token.isWrappable() && this.options.wrap_outer_expressions_when_nested
+				&& getWrapIndent(token) < currentIndent) {
+			WrapPolicy lineStartPolicy = this.tm.get(this.tm.findFirstTokenInLine(index, false, true)).getWrapPolicy();
+			if (lineStartPolicy != null && lineStartPolicy.wrapMode != WrapMode.FORCED) {
 				token.breakBefore();
-				token.setIndent(indent);
-				return true;
+				throw new WrapRestartThrowable(-1);
+			}
 			}
 		}
-		return false;
+
+	private void checkTopPriorityWraps(int wrapIndex) throws WrapRestartThrowable {
+		WrapPolicy wrapPolicy = this.tm.get(wrapIndex).getWrapPolicy();
+		if (wrapPolicy != null && wrapPolicy.wrapMode == WrapMode.TOP_PRIORITY
+				&& !this.usedTopPriorityWraps.contains(wrapPolicy))
+			throw new WrapRestartThrowable(wrapIndex);
 	}
 
-	/**
-	 * @return index of the first token in the top priority group that given token belongs to or -1 if it doesn't belong
-	 *         to any top priority.
-	 */
-	private int handleTopPriorityWraps(int wrapIndex) {
-		// wrap all tokens in the same top priority group and jump back to the first one
+	private void handleTopPriorityWraps(WrapRestartThrowable restartException) {
+		int wrapIndex = restartException.topPriorityWrap;
+		if (wrapIndex < 0)
+			return;
 		WrapPolicy wrapPolicy = this.tm.get(wrapIndex).getWrapPolicy();
-		if (wrapPolicy == null || !wrapPolicy.isTopPriority() || this.usedTopPriorityWraps.contains(wrapPolicy))
-			return -1;
-		int firstTokenIndex = -1;
 		int parentIndex = wrapPolicy.wrapParentIndex;
 		for (int i = wrapIndex; i > parentIndex; i--) {
 			Token token = this.tm.get(i);
 			wrapPolicy = token.getWrapPolicy();
 			if (wrapPolicy != null && wrapPolicy.wrapParentIndex == parentIndex) {
-				if (wrapPolicy.isTopPriority()) {
+				if (wrapPolicy.wrapMode == WrapMode.TOP_PRIORITY) {
 					token.breakBefore();
-					firstTokenIndex = i;
 					this.usedTopPriorityWraps.add(wrapPolicy);
 				}
 				if (wrapPolicy.isFirstInGroup)
@@ -594,14 +614,13 @@
 			} else if (wrapPolicy != null && wrapPolicy.wrapParentIndex == parentIndex) {
 				if (wrapPolicy.isFirstInGroup)
 					break;
-				if (wrapPolicy.isTopPriority()) {
+				if (wrapPolicy.wrapMode == WrapMode.TOP_PRIORITY) {
 					token.breakBefore();
 					this.usedTopPriorityWraps.add(wrapPolicy);
 				}
 			}
 			breakAfterPrevious = token.getLineBreaksAfter() > 0;
 		}
-		return firstTokenIndex;
 	}
 
 	private int[] toArray(List<Integer> list) {
@@ -634,8 +653,8 @@
 
 	int getWrapIndent(Token token) {
 		WrapPolicy policy = token.getWrapPolicy();
-		if (policy == null || (token.getLineBreaksBefore() > 1 && !policy.isForced && !policy.isTopPriority()))
-			return token.getIndent(); // no additional indentation after an empty line
+		if (policy == null)
+			return token.getIndent();
 
 		if (this.options.never_indent_line_comments_on_first_column && token.tokenType == TokenNameCOMMENT_LINE
 				&& token.getIndent() == 0)
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 b973b6f..d4f54d5 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
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2015 Mateusz Matela and others.
+ * 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
@@ -12,6 +12,7 @@
 package org.eclipse.jdt.internal.formatter.linewrap;
 
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOLON;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMA;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_BLOCK;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_JAVADOC;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_LINE;
@@ -23,11 +24,14 @@
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameQUESTION;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameRBRACE;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameRPAREN;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameSEMICOLON;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameStringLiteral;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameextends;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameimplements;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameIdentifier;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamenew;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamesuper;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamethis;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamethrows;
 
 import java.util.ArrayList;
@@ -38,6 +42,7 @@
 
 import org.eclipse.jdt.core.dom.ASTNode;
 import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
 import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
 import org.eclipse.jdt.core.dom.ArrayInitializer;
 import org.eclipse.jdt.core.dom.Assignment;
@@ -49,6 +54,7 @@
 import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
 import org.eclipse.jdt.core.dom.EnumDeclaration;
 import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.FieldAccess;
 import org.eclipse.jdt.core.dom.FieldDeclaration;
 import org.eclipse.jdt.core.dom.IfStatement;
 import org.eclipse.jdt.core.dom.ImportDeclaration;
@@ -58,10 +64,13 @@
 import org.eclipse.jdt.core.dom.MethodDeclaration;
 import org.eclipse.jdt.core.dom.MethodInvocation;
 import org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.eclipse.jdt.core.dom.QualifiedName;
 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
 import org.eclipse.jdt.core.dom.Statement;
 import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
+import org.eclipse.jdt.core.dom.SuperFieldAccess;
 import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.ThisExpression;
 import org.eclipse.jdt.core.dom.TryStatement;
 import org.eclipse.jdt.core.dom.Type;
 import org.eclipse.jdt.core.dom.TypeDeclaration;
@@ -74,12 +83,54 @@
 import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions;
 import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions.Alignment;
 import org.eclipse.jdt.internal.formatter.Token;
+import org.eclipse.jdt.internal.formatter.Token.WrapMode;
 import org.eclipse.jdt.internal.formatter.Token.WrapPolicy;
 import org.eclipse.jdt.internal.formatter.TokenManager;
 import org.eclipse.jdt.internal.formatter.TokenTraverser;
+import org.eclipse.jface.text.IRegion;
 
 public class WrapPreparator extends ASTVisitor {
 
+	/**
+	 * Helper for common handling of all expressions that should be treated the same as {@link FieldAccess}
+	 */
+	private static class FieldAccessAdapter {
+		final Expression accessExpression;
+
+		public FieldAccessAdapter(Expression expression) {
+			this.accessExpression = expression;
+		}
+
+		public static boolean isFieldAccess(ASTNode expr) {
+			return expr instanceof FieldAccess || expr instanceof QualifiedName || expr instanceof ThisExpression
+					|| expr instanceof SuperFieldAccess;
+		}
+
+		public Expression getExpression() {
+			if (this.accessExpression instanceof FieldAccess)
+				return ((FieldAccess) this.accessExpression).getExpression();
+			if (this.accessExpression instanceof QualifiedName)
+				return ((QualifiedName) this.accessExpression).getQualifier();
+			if (this.accessExpression instanceof ThisExpression)
+				return ((ThisExpression) this.accessExpression).getQualifier();
+			if (this.accessExpression instanceof SuperFieldAccess)
+				return ((SuperFieldAccess) this.accessExpression).getQualifier();
+			throw new AssertionError();
+		}
+
+		public int getIdentifierIndex(TokenManager tm) {
+			if (this.accessExpression instanceof FieldAccess)
+				return tm.firstIndexIn(((FieldAccess) this.accessExpression).getName(), TokenNameIdentifier);
+			if (this.accessExpression instanceof QualifiedName)
+				return tm.firstIndexIn(((QualifiedName) this.accessExpression).getName(), TokenNameIdentifier);
+			if (this.accessExpression instanceof ThisExpression)
+				return tm.lastIndexIn(this.accessExpression, TokenNamethis);
+			if (this.accessExpression instanceof SuperFieldAccess)
+				return tm.lastIndexIn(this.accessExpression, TokenNamesuper);
+			throw new AssertionError();
+		}
+	}
+
 	private final static Map<Operator, Integer> OPERATOR_PRECEDENCE;
 	static {
 		HashMap<Operator, Integer> precedence = new HashMap<Operator, Integer>();
@@ -106,7 +157,7 @@
 	final DefaultCodeFormatterOptions options;
 	final int kind;
 	
-	FieldAligner fieldAligner;
+	final FieldAligner fieldAligner;
 
 	int importsStart = -1, importsEnd = -1;
 
@@ -115,6 +166,8 @@
 	 * parameters
 	 */
 	private List<Integer> wrapIndexes = new ArrayList<Integer>();
+	/** Indexes for wraps that shouldn't happen but should be indented if cannot be removed */
+	private List<Integer> secondaryWrapIndexes = new ArrayList<Integer>();
 	private List<Float> wrapPenalties = new ArrayList<Float>();
 	private int wrapParentIndex = -1;
 	private int wrapGroupEnd = -1;
@@ -125,11 +178,17 @@
 		this.tm = tokenManager;
 		this.options = options;
 		this.kind = kind;
+
+		this.fieldAligner = new FieldAligner(this.tm);
 	}
 
 	@Override
 	public boolean preVisit2(ASTNode node) {
 		this.currentDepth++;
+
+		assert this.wrapIndexes.isEmpty() && this.secondaryWrapIndexes.isEmpty() && this.wrapPenalties.isEmpty();
+		assert this.wrapParentIndex == -1 && this.wrapGroupEnd == -1;
+
 		boolean isMalformed = (node.getFlags() & ASTNode.MALFORMED) != 0;
 		if (isMalformed) {
 			this.tm.addDisableFormatTokenPair(this.tm.firstTokenIn(node, -1), this.tm.lastTokenIn(node, -1));
@@ -173,19 +232,28 @@
 		if (!superInterfaceTypes.isEmpty()) {
 			int implementsToken = node.isInterface() ? TokenNameextends : TokenNameimplements;
 			this.wrapParentIndex = this.tm.lastIndexIn(node.getName(), -1);
-			this.wrapGroupEnd = this.tm.lastIndexIn(superInterfaceTypes.get(superInterfaceTypes.size() - 1), -1);
 			this.wrapIndexes.add(this.tm.firstIndexBefore(superInterfaceTypes.get(0), implementsToken));
-			for (Type type : superInterfaceTypes)
-				this.wrapIndexes.add(this.tm.firstIndexIn(type, -1));
+			prepareElementsList(superInterfaceTypes, TokenNameCOMMA, -1);
 			handleWrap(this.options.alignment_for_superinterfaces_in_type_declaration, PREFERRED);
 		}
 
-		if (this.options.align_type_members_on_columns) {
-			if (this.fieldAligner == null) {
-				this.fieldAligner = new FieldAligner(this.tm, this.options);
+		if (this.options.align_type_members_on_columns)
+			this.fieldAligner.prepareAlign(node.bodyDeclarations());
+
+		return true;
 			}
-			this.fieldAligner.prepareAlign(node);
+
+	@Override
+	public boolean visit(AnnotationTypeDeclaration node) {
+		if (this.options.align_type_members_on_columns)
+			this.fieldAligner.prepareAlign(node.bodyDeclarations());
+		return true;
 		}
+
+	@Override
+	public boolean visit(AnonymousClassDeclaration node) {
+		if (this.options.align_type_members_on_columns)
+			this.fieldAligner.prepareAlign(node.bodyDeclarations());
 		return true;
 	}
 
@@ -205,26 +273,28 @@
 
 		List<Type> exceptionTypes = node.thrownExceptionTypes();
 		if (!exceptionTypes.isEmpty()) {
-			this.wrapParentIndex = this.tm.firstIndexBefore(exceptionTypes.get(0), TokenNameRPAREN);
-			this.wrapGroupEnd = this.tm.lastIndexIn(exceptionTypes.get(exceptionTypes.size() - 1), -1);
 			int wrappingOption = node.isConstructor()
 					? this.options.alignment_for_throws_clause_in_constructor_declaration
 					: this.options.alignment_for_throws_clause_in_method_declaration;
-			for (Type exceptionType : exceptionTypes)
-				this.wrapIndexes.add(this.tm.firstIndexIn(exceptionType, -1));
+			if ((wrappingOption & Alignment.M_INDENT_ON_COLUMN) == 0)
+				this.wrapParentIndex = this.tm.firstIndexAfter(node.getName(), TokenNameLPAREN);
+			prepareElementsList(exceptionTypes, TokenNameCOMMA, TokenNameRPAREN);
 			// instead of the first exception type, wrap the "throws" token
 			this.wrapIndexes.set(0, this.tm.firstIndexBefore(exceptionTypes.get(0), TokenNamethrows));
 			handleWrap(wrappingOption, 0.5f);
 		}
 
 		if (!node.isConstructor()) {
+			this.wrapParentIndex = this.tm.findFirstTokenInLine(this.tm.firstIndexIn(node.getName(), -1));
 			List<TypeParameter> typeParameters = node.typeParameters();
 			if (!typeParameters.isEmpty())
 				this.wrapIndexes.add(this.tm.firstIndexIn(typeParameters.get(0), -1));
-			if (node.getReturnType2() != null)
-				this.wrapIndexes.add(this.tm.firstIndexIn(node.getReturnType2(), -1));
+			if (node.getReturnType2() != null) {
+				int returTypeIndex = this.tm.firstIndexIn(node.getReturnType2(), -1);
+				if (returTypeIndex != this.wrapParentIndex)
+					this.wrapIndexes.add(returTypeIndex);
+			}
 			this.wrapIndexes.add(this.tm.firstIndexIn(node.getName(), -1));
-			this.wrapParentIndex = this.tm.findFirstTokenInLine(this.tm.firstIndexIn(node.getName(), -1));
 			this.wrapGroupEnd = this.tm.lastIndexIn(node.getName(), -1);
 			handleWrap(this.options.alignment_for_method_declaration);
 		}
@@ -234,24 +304,47 @@
 	@Override
 	public boolean visit(EnumDeclaration node) {
 		List<EnumConstantDeclaration> enumConstants = node.enumConstants();
+		int constantsEnd = -1;
 		if (!enumConstants.isEmpty()) {
 			for (EnumConstantDeclaration constant : enumConstants)
 				this.wrapIndexes.add(this.tm.firstIndexIn(constant, -1));
-			this.wrapParentIndex = this.tm.firstIndexBefore(enumConstants.get(0), TokenNameLBRACE);
-			this.wrapGroupEnd = this.tm.lastIndexIn(enumConstants.get(enumConstants.size() - 1), -1);
+			this.wrapParentIndex = (this.options.alignment_for_enum_constants & Alignment.M_INDENT_ON_COLUMN) > 0
+					? this.tm.firstIndexBefore(enumConstants.get(0), TokenNameLBRACE) : this.tm.firstIndexIn(node, -1);
+			this.wrapGroupEnd = constantsEnd = this.tm.lastIndexIn(enumConstants.get(enumConstants.size() - 1), -1);
 			handleWrap(this.options.alignment_for_enum_constants, node);
 		}
 
+		if (!this.options.join_wrapped_lines) {
+			// preserve a line break between the last comma and semicolon
+			int commaIndex = -1;
+			int i = constantsEnd > 0 ? constantsEnd : this.tm.firstIndexAfter(node.getName(), TokenNameLBRACE);
+			while (++i < this.tm.size()) {
+				Token t = this.tm.get(i);
+				if (t.isComment())
+					continue;
+				if (t.tokenType == TokenNameCOMMA) {
+					commaIndex = i;
+					continue;
+				}
+				if (t.tokenType == TokenNameSEMICOLON && commaIndex >= 0
+						&& this.tm.countLineBreaksBetween(this.tm.get(commaIndex), t) == 1) {
+					t.setWrapPolicy(new WrapPolicy(WrapMode.WHERE_NECESSARY, commaIndex, 0));
+				}
+				break;
+			}
+		}
+
 		List<Type> superInterfaceTypes = node.superInterfaceTypes();
 		if (!superInterfaceTypes.isEmpty()) {
-			this.wrapIndexes.add(this.tm.firstIndexBefore(superInterfaceTypes.get(0), TokenNameimplements));
-			for (Type type : superInterfaceTypes)
-				this.wrapIndexes.add(this.tm.firstIndexIn(type, -1));
 			this.wrapParentIndex = this.tm.lastIndexIn(node.getName(), -1);
-			this.wrapGroupEnd = this.tm.lastIndexIn(superInterfaceTypes.get(superInterfaceTypes.size() - 1), -1);
-			this.wrapPenalties.add(PREFERRED);
-			handleWrap(this.options.alignment_for_superinterfaces_in_enum_declaration, node);
+			this.wrapIndexes.add(this.tm.firstIndexBefore(superInterfaceTypes.get(0), TokenNameimplements));
+			prepareElementsList(superInterfaceTypes, TokenNameCOMMA, -1);
+			handleWrap(this.options.alignment_for_superinterfaces_in_enum_declaration, PREFERRED);
 		}
+
+		if (this.options.align_type_members_on_columns)
+			this.fieldAligner.prepareAlign(node.bodyDeclarations());
+
 		return true;
 	}
 
@@ -277,8 +370,10 @@
 			while (expression instanceof MethodInvocation) {
 				invocation = (MethodInvocation) expression;
 				expression = invocation.getExpression();
-				if (expression != null)
+				if (expression != null) {
 					this.wrapIndexes.add(this.tm.firstIndexBefore(invocation.getName(), TokenNameDOT));
+					this.secondaryWrapIndexes.add(this.tm.firstIndexIn(invocation.getName(), TokenNameIdentifier));
+				}
 			}
 			Collections.reverse(this.wrapIndexes);
 			this.wrapParentIndex = (expression != null) ? this.tm.lastIndexIn(expression, -1)
@@ -322,6 +417,62 @@
 	}
 
 	@Override
+	public boolean visit(FieldAccess node) {
+		handleFieldAccess(node);
+		return true;
+	}
+
+	@Override
+	public boolean visit(QualifiedName node) {
+		handleFieldAccess(node);
+		return true;
+	}
+
+	@Override
+	public boolean visit(ThisExpression node) {
+		handleFieldAccess(node);
+		return true;
+	}
+
+	@Override
+	public boolean visit(SuperFieldAccess node) {
+		handleFieldAccess(node);
+		return true;
+	}
+
+	private void handleFieldAccess(Expression node) {
+		boolean isAccessChainRoot = !FieldAccessAdapter.isFieldAccess(node.getParent());
+		if (!isAccessChainRoot)
+			return;
+
+		Expression expression = node;
+		FieldAccessAdapter access = null;
+		while (FieldAccessAdapter.isFieldAccess(expression)) {
+			access = new FieldAccessAdapter(expression);
+			int nameIndex = access.getIdentifierIndex(this.tm);
+			// find a dot preceding the name, may not be there
+			for (int i = nameIndex - 1; i > this.tm.firstIndexIn(node, -1); i--) {
+				Token t = this.tm.get(i);
+				if (t.tokenType == TokenNameDOT) {
+					this.wrapIndexes.add(i);
+					this.secondaryWrapIndexes.add(nameIndex);
+				}
+				if (!t.isComment() && t.tokenType != TokenNamesuper)
+					break;
+			}
+			expression = access.getExpression();
+		}
+		Collections.reverse(this.wrapIndexes);
+		this.wrapParentIndex = this.tm.lastIndexIn(expression != null ? expression : access.accessExpression, -1);
+		boolean isFollowedByInvocation = node.getParent() instanceof MethodInvocation
+				&& node.getLocationInParent() == MethodInvocation.EXPRESSION_PROPERTY;
+		this.wrapGroupEnd = isFollowedByInvocation ? this.tm.lastIndexIn(node.getParent(), -1)
+				: new FieldAccessAdapter(node).getIdentifierIndex(this.tm);
+		// TODO need configuration for this, now only handles line breaks that cannot be removed
+		handleWrap(Alignment.M_NO_ALIGNMENT);
+	}
+
+	@Override
 	public boolean visit(InfixExpression node) {
 		Integer operatorPrecedence = OPERATOR_PRECEDENCE.get(node.getOperator());
 		if (operatorPrecedence == null)
@@ -333,7 +484,8 @@
 		findTokensToWrap(node, 0);
 		this.wrapParentIndex = this.wrapIndexes.remove(0);
 		this.wrapGroupEnd = this.tm.lastIndexIn(node, -1);
-		if ((this.options.alignment_for_binary_expression & Alignment.M_INDENT_ON_COLUMN) != 0)
+		if ((this.options.alignment_for_binary_expression & Alignment.M_INDENT_ON_COLUMN) != 0
+				&& this.wrapParentIndex > 0)
 			this.wrapParentIndex--;
 		for (int i = this.wrapParentIndex; i >= 0; i--) {
 			if (!this.tm.get(i).isComment()) {
@@ -367,6 +519,7 @@
 			assert node.getOperator().toString().equals(this.tm.toString(indexBefore));
 			int indexAfter = this.tm.firstIndexIn(operand, -1);
 			this.wrapIndexes.add(this.options.wrap_before_binary_operator ? indexBefore : indexAfter);
+			this.secondaryWrapIndexes.add(this.options.wrap_before_binary_operator ? indexAfter : indexBefore);
 
 			if (!this.options.join_wrapped_lines) {
 				// TODO there should be an option for never joining wraps on opposite side of the operator
@@ -393,6 +546,8 @@
 	public boolean visit(ConditionalExpression node) {
 		this.wrapIndexes.add(this.tm.firstIndexAfter(node.getExpression(), TokenNameQUESTION));
 		this.wrapIndexes.add(this.tm.firstIndexAfter(node.getThenExpression(), TokenNameCOLON));
+		this.secondaryWrapIndexes.add(this.tm.firstIndexIn(node.getThenExpression(), -1));
+		this.secondaryWrapIndexes.add(this.tm.firstIndexIn(node.getElseExpression(), -1));
 		this.wrapParentIndex = this.tm.lastIndexIn(node.getExpression(), -1);
 		this.wrapGroupEnd = this.tm.lastIndexIn(node, -1);
 		handleWrap(this.options.alignment_for_conditional_expression);
@@ -403,10 +558,7 @@
 	public boolean visit(ArrayInitializer node) {
 		List<Expression> expressions = node.expressions();
 		if (!expressions.isEmpty()) {
-			for (Expression expression : expressions)
-				this.wrapIndexes.add(this.tm.firstIndexIn(expression, -1));
-			this.wrapParentIndex = this.tm.firstIndexBefore(expressions.get(0), TokenNameLBRACE);
-			this.wrapGroupEnd = this.tm.lastIndexIn(node, -1);
+			prepareElementsList(expressions, TokenNameCOMMA, TokenNameLBRACE);
 			handleWrap(this.options.alignment_for_expressions_in_array_initializer, node);
 		}
 		if (!this.options.join_wrapped_lines
@@ -416,8 +568,8 @@
 			Token closingBrace = this.tm.get(closingBraceIndex);
 			if (this.tm.countLineBreaksBetween(this.tm.get(closingBraceIndex - 1), closingBrace) == 1) {
 				int openingBraceIndex = this.tm.firstIndexIn(node, TokenNameLBRACE);
-				closingBrace.setWrapPolicy(
-						new WrapPolicy(0, openingBraceIndex, this.currentDepth, 1, true, false, -1, false));
+				closingBrace.setWrapPolicy(new WrapPolicy(WrapMode.WHERE_NECESSARY, openingBraceIndex,
+						closingBraceIndex, 0, this.currentDepth, 1, true, false));
 			}
 		}
 		return true;
@@ -425,14 +577,18 @@
 
 	@Override
 	public boolean visit(Assignment node) {
-		this.wrapIndexes.add(this.tm.firstIndexIn(node.getRightHandSide(), -1));
+		int wrapIndex = this.tm.firstIndexIn(node.getRightHandSide(), -1);
+		if (this.tm.get(wrapIndex).getLineBreaksBefore() > 0)
+			return true;
 
 		int operatorIndex = this.tm.firstIndexBefore(node.getRightHandSide(), -1);
 		while (this.tm.get(operatorIndex).isComment())
 			operatorIndex--;
 		assert node.getOperator().toString().equals(this.tm.toString(operatorIndex));
 
-		this.wrapParentIndex = operatorIndex;
+		this.wrapIndexes.add(wrapIndex);
+		this.secondaryWrapIndexes.add(operatorIndex);
+		this.wrapParentIndex = operatorIndex - 1;
 		this.wrapGroupEnd = this.tm.lastIndexIn(node.getRightHandSide(), -1);
 		handleWrap(this.options.alignment_for_assignment);
 		return true;
@@ -440,12 +596,18 @@
 
 	@Override
 	public boolean visit(VariableDeclarationFragment node) {
-		if (node.getInitializer() != null) {
-			this.wrapIndexes.add(this.tm.firstIndexIn(node.getInitializer(), -1));
-			this.wrapParentIndex = this.tm.firstIndexBefore(node.getInitializer(), TokenNameEQUAL);
+		if (node.getInitializer() == null)
+			return true;
+		int wrapIndex = this.tm.firstIndexIn(node.getInitializer(), -1);
+		if (this.tm.get(wrapIndex).getLineBreaksBefore() > 0)
+			return true;
+		int equalIndex = this.tm.firstIndexBefore(node.getInitializer(), TokenNameEQUAL);
+
+		this.wrapIndexes.add(wrapIndex);
+		this.secondaryWrapIndexes.add(equalIndex);
+		this.wrapParentIndex = equalIndex - 1;
 			this.wrapGroupEnd = this.tm.lastIndexIn(node.getInitializer(), -1);
 			handleWrap(this.options.alignment_for_assignment);
-		}
 		return true;
 	}
 
@@ -453,45 +615,55 @@
 	public boolean visit(IfStatement node) {
 		if (!(node.getThenStatement() instanceof Block)) {
 			int thenIndex = this.tm.firstIndexIn(node.getThenStatement(), -1);
-			if (this.tm.get(thenIndex).getLineBreaksBefore() == 0)
+			if (this.tm.get(thenIndex).getLineBreaksBefore() == 0) {
 				this.wrapIndexes.add(thenIndex);
+				this.wrapParentIndex = this.tm.firstIndexAfter(node.getExpression(), TokenNameRPAREN);
+				this.wrapGroupEnd = this.tm.lastIndexIn(node.getThenStatement(), -1);
+				handleWrap(this.options.alignment_for_compact_if, node);
+		}
 		}
 		Statement elseStatement = node.getElseStatement();
-		if (elseStatement != null && !(elseStatement instanceof Block)) {
+		if (elseStatement != null && !(elseStatement instanceof Block) && !(elseStatement instanceof IfStatement)) {
 			int elseIndex = this.tm.firstIndexIn(elseStatement, -1);
-			if (this.tm.get(elseIndex).getLineBreaksBefore() == 0)
+			if (this.tm.get(elseIndex).getLineBreaksBefore() == 0) {
 				this.wrapIndexes.add(elseIndex);
-		}
-		if (!this.wrapIndexes.isEmpty()) {
 			this.wrapParentIndex = this.tm.firstIndexAfter(node.getExpression(), TokenNameRPAREN);
-			this.wrapGroupEnd = this.tm.lastIndexIn(node, -1);
+				this.wrapGroupEnd = this.tm.lastIndexIn(elseStatement, -1);
 			handleWrap(this.options.alignment_for_compact_if, node);
 		}
+		}
 		return true;
 	}
 
 	@Override
 	public boolean visit(TryStatement node) {
-		handleArguments(node.resources(), this.options.alignment_for_resources_in_try);
+		prepareElementsList(node.resources(), TokenNameSEMICOLON, TokenNameLPAREN);
+		handleWrap(this.options.alignment_for_resources_in_try);
 		return true;
 	}
 
 	@Override
 	public boolean visit(UnionType node) {
 		List<Type> types = node.types();
-		if (this.options.wrap_before_or_operator_multicatch && !types.isEmpty()) {
+		if (types.isEmpty())
+			return true;
+		if (this.options.wrap_before_or_operator_multicatch) {
 			for (Type type : types) {
 				if (this.wrapIndexes.isEmpty()) {
 					this.wrapIndexes.add(this.tm.firstIndexIn(type, -1));
 				} else {
 					this.wrapIndexes.add(this.tm.firstIndexBefore(type, TokenNameOR));
+					this.secondaryWrapIndexes.add(this.tm.firstIndexIn(type, -1));
 				}
 			}
-			this.wrapParentIndex = this.tm.firstIndexBefore(node, TokenNameLPAREN);
+			this.wrapParentIndex = this.tm.firstIndexBefore(node, -1);
+			while (this.tm.get(this.wrapParentIndex).isComment())
+				this.wrapParentIndex--;
 			this.wrapGroupEnd = this.tm.lastIndexIn(types.get(types.size() - 1), -1);
 			handleWrap(this.options.alignment_for_union_type_in_multicatch);
 		} else {
-			handleArguments(types, this.options.alignment_for_union_type_in_multicatch);
+			prepareElementsList(types, TokenNameOR, TokenNameLPAREN);
+			handleWrap(this.options.alignment_for_union_type_in_multicatch);
 		}
 		return true;
 	}
@@ -528,16 +700,27 @@
 	 * wrap executor will fix their indentation if necessary.
 	 */
 	private void forceContinuousWrapping(ASTNode node, int parentIndex) {
+		int parentIndent = this.tm.get(parentIndex).getIndent();
+		int indentChange = -parentIndent;
+		int lineStart = this.tm.findFirstTokenInLine(parentIndex);
+		for (int i = parentIndex; i >= lineStart; i--) {
+			int align = this.tm.get(i).getAlign();
+			if (align > 0) {
+				indentChange = -2 * parentIndent + align;
+				break;
+			}
+		}
+
+		Token previous = null;
 		int from = this.tm.firstIndexIn(node, -1);
 		int to = this.tm.lastIndexIn(node, -1);
-		Token wrapParent = this.tm.get(parentIndex);
-		Token previous = null;
 		for (int i = from; i <= to; i++) {
 			Token token = this.tm.get(i);
 			if ((token.getLineBreaksBefore() > 0 || (previous != null && previous.getLineBreaksAfter() > 0))
-					&& token.getWrapPolicy() == null) {
-				int indent = (token.getIndent() - wrapParent.getIndent());
-				token.setWrapPolicy(new WrapPolicy(indent, parentIndex, true));
+					&& (token.getWrapPolicy() == null || token.getWrapPolicy().wrapMode == WrapMode.FORCED)) {
+				int extraIndent = token.getIndent() + indentChange;
+				token.setWrapPolicy(new WrapPolicy(WrapMode.FORCED, parentIndex, extraIndent));
+				token.setIndent(parentIndent + extraIndent);
 			}
 			previous = token;
 		}
@@ -545,25 +728,33 @@
 
 	private void handleVariableDeclarations(List<VariableDeclarationFragment> fragments) {
 		if (fragments.size() > 1) {
-			for (int i = 1; i < fragments.size(); i++)
-				this.wrapIndexes.add(this.tm.firstIndexIn(fragments.get(i), -1));
 			this.wrapParentIndex = this.tm.firstIndexIn(fragments.get(0), -1);
-			this.wrapGroupEnd = this.tm.lastIndexIn(fragments.get(fragments.size() - 1), -1);
+			prepareElementsList(fragments, TokenNameCOMMA, -1);
+			this.wrapIndexes.remove(0);
 			handleWrap(this.options.alignment_for_multiple_fields);
 		}
 	}
 
 	private void handleArguments(List<? extends ASTNode> arguments, int wrappingOption) {
-		for (ASTNode argument : arguments)
-			this.wrapIndexes.add(this.tm.firstIndexIn(argument, -1));
+		this.wrapPenalties.add(1 / PREFERRED);
+		prepareElementsList(arguments, TokenNameCOMMA, TokenNameLPAREN);
+		handleWrap(wrappingOption);
+	}
+
+	private void prepareElementsList(List<? extends ASTNode> elements, int separatorType, int wrapParentType) {
+		for (int i = 0; i < elements.size(); i++) {
+			ASTNode element = elements.get(i);
+			this.wrapIndexes.add(this.tm.firstIndexIn(element, -1));
+			if (i > 0)
+				this.secondaryWrapIndexes.add(this.tm.firstIndexBefore(element, separatorType));
+		}
 		// wrapIndexes may have been filled with additional values even if arguments is empty
 		if (!this.wrapIndexes.isEmpty()) {
 			Token firstToken = this.tm.get(this.wrapIndexes.get(0));
-			this.wrapParentIndex = this.tm.findIndex(firstToken.originalStart - 1, TokenNameLPAREN, false);
-			if (!arguments.isEmpty() && this.wrapGroupEnd < 0)
-				this.wrapGroupEnd = this.tm.lastIndexIn(arguments.get(arguments.size() - 1), -1);
-			assert this.wrapGroupEnd >= 0;
-			handleWrap(wrappingOption, 1 / PREFERRED);
+			if (this.wrapParentIndex < 0)
+				this.wrapParentIndex = this.tm.findIndex(firstToken.originalStart - 1, wrapParentType, false);
+			if (!elements.isEmpty() && this.wrapGroupEnd < 0)
+				this.wrapGroupEnd = this.tm.lastIndexIn(elements.get(elements.size() - 1), -1);
 		}
 	}
 
@@ -577,17 +768,23 @@
 	}
 
 	private void handleWrap(int wrappingOption, ASTNode parentNode) {
+		doHandleWrap(wrappingOption, parentNode);
+		this.wrapIndexes.clear();
+		this.secondaryWrapIndexes.clear();
+		this.wrapPenalties.clear();
+		this.wrapParentIndex = this.wrapGroupEnd = -1;
+	}
+
+	private void doHandleWrap(int wrappingOption, ASTNode parentNode) {
 		if (this.wrapIndexes.isEmpty())
 			return;
-		assert this.wrapParentIndex >= 0;
+		assert this.wrapParentIndex >= 0 && this.wrapParentIndex < this.wrapIndexes.get(0);
+		assert this.wrapGroupEnd >= this.wrapIndexes.get(this.wrapIndexes.size() - 1);
 		float penalty = this.wrapPenalties.isEmpty() ? 1 : this.wrapPenalties.get(0);
 		WrapPolicy policy = getWrapPolicy(wrappingOption, penalty, true, parentNode);
-		if (policy == null) {
-			this.wrapIndexes.clear();
-			this.wrapPenalties.clear();
-			this.wrapParentIndex = this.wrapGroupEnd = -1;
+		if (policy == null)
 			return;
-		}
+
 		setTokenWrapPolicy(this.wrapIndexes.get(0), policy, true);
 
 		boolean wrapPreceedingComments = !(parentNode instanceof InfixExpression)
@@ -604,20 +801,28 @@
 			boolean satisfied = false;
 			for (int index : this.wrapIndexes) {
 				Token token = this.tm.get(index);
-				if (token.getWrapPolicy().isTopPriority()) {
+				if (token.getWrapPolicy().wrapMode == WrapMode.TOP_PRIORITY) {
 					token.breakBefore();
 					satisfied = true;
 				}
 			}
 			if (!satisfied) {
-				boolean canWrapFirst = (wrappingOption & Alignment.M_NEXT_PER_LINE_SPLIT) != Alignment.M_NEXT_PER_LINE_SPLIT;
+				boolean canWrapFirst = (wrappingOption
+						& Alignment.M_NEXT_PER_LINE_SPLIT) != Alignment.M_NEXT_PER_LINE_SPLIT;
 				if (canWrapFirst)
 					this.tm.get(this.wrapIndexes.get(0)).breakBefore();
 			}
 		}
-		this.wrapIndexes.clear();
-		this.wrapPenalties.clear();
-		this.wrapParentIndex = this.wrapGroupEnd = -1;
+
+		if (!this.secondaryWrapIndexes.isEmpty()) {
+			int optionNoAlignment = (wrappingOption & ~Alignment.SPLIT_MASK) | Alignment.M_NO_ALIGNMENT;
+			policy = getWrapPolicy(optionNoAlignment, 1, false, parentNode);
+			for (int index : this.secondaryWrapIndexes) {
+				Token token = this.tm.get(index);
+				if (token.getWrapPolicy() == null)
+					token.setWrapPolicy(policy);
+			}
+		}
 	}
 
 	private void setTokenWrapPolicy(int index, WrapPolicy policy, boolean wrapPreceedingComments) {
@@ -635,23 +840,12 @@
 
 		Token token = this.tm.get(index);
 		token.setWrapPolicy(policy);
-		if (this.options.join_wrapped_lines
-				&& (token.tokenType == TokenNameCOMMENT_BLOCK || token.tokenType == TokenNameCOMMENT_JAVADOC)) {
+
+		if (this.options.join_wrapped_lines && token.tokenType == TokenNameCOMMENT_BLOCK) {
 			// allow wrap preparator to decide if this comment should be wrapped
 			token.clearLineBreaksBefore();
 		}
-
-		// extend this policy to a token that is in the next line because of comments
-		for (int i = index + 1; i < this.tm.size(); i++) {
-			Token next = this.tm.get(i);
-			WrapPolicy policy2 = next.getWrapPolicy();
-			if (policy2 != null && policy2.isForced && policy2.extraIndent == 0) {
-				next.setWrapPolicy(policy);
-			} else if (next.tokenType != TokenNameCOMMENT_LINE && next.tokenType != TokenNameCOMMENT_BLOCK) {
-				break;
 			}
-		}
-	}
 
 	private WrapPolicy getWrapPolicy(int wrappingOption, float penaltyMultiplier, boolean isFirst, ASTNode parentNode) {
 		assert this.wrapParentIndex >= 0 && this.wrapGroupEnd >= 0;
@@ -673,10 +867,12 @@
 			extraIndent = this.options.continuation_indentation_for_array_initializer;
 		}
 
+		WrapMode wrapMode = WrapMode.WHERE_NECESSARY;
 		boolean isTopPriority = false;
 		switch (wrappingOption & Alignment.SPLIT_MASK) {
 			case Alignment.M_NO_ALIGNMENT:
-				return null;
+				wrapMode = WrapMode.DISABLED;
+				break;
 			case Alignment.M_COMPACT_FIRST_BREAK_SPLIT:
 				isTopPriority = isFirst;
 				break;
@@ -695,16 +891,18 @@
 
 		if (isAlreadyWrapped)
 			isTopPriority = false; // to avoid triggering top priority wrapping
-		int topPriorityGroupEnd = isTopPriority ? this.wrapGroupEnd : -1;
+		if (isTopPriority)
+			wrapMode = WrapMode.TOP_PRIORITY;
 		extraIndent *= this.options.indentation_size;
-		return new WrapPolicy(extraIndent, this.wrapParentIndex, this.currentDepth, penaltyMultiplier, isFirst,
-				indentOnColumn, topPriorityGroupEnd, false);
+		return new WrapPolicy(wrapMode, this.wrapParentIndex, this.wrapGroupEnd, extraIndent, this.currentDepth,
+				penaltyMultiplier, isFirst, indentOnColumn);
 	}
 
-	public void finishUp(ASTNode astRoot) {
+	public void finishUp(ASTNode astRoot, IRegion[] regions) {
 		preserveExistingLineBreaks();
+		if (regions != null)
+			applyBreaksOutsideRegions(regions);
 		new WrapExecutor(this.tm, this.options).executeWraps();
-		if (this.fieldAligner != null)
 			this.fieldAligner.alignComments();
 		wrapComments();
 		fixEnumConstantIndents(astRoot);
@@ -722,45 +920,26 @@
 
 			@Override
 			protected boolean token(Token token, int index) {
-				int lineBreaks = getLineBreaksBetween(getPrevious(), token);
-				if (index > WrapPreparator.this.importsStart && index < WrapPreparator.this.importsEnd) {
-					lineBreaks = lineBreaks > 1 ? (this.options2.blank_lines_between_import_groups + 1) : 0;
-				} else {
-					lineBreaks = Math.min(lineBreaks, this.options2.number_of_empty_lines_to_preserve + 1);
-				}
+				boolean isBetweenImports = index > WrapPreparator.this.importsStart
+						&& index < WrapPreparator.this.importsEnd;
+				int lineBreaks = getLineBreaksToPreserve(getPrevious(), token, isBetweenImports);
 				if (lineBreaks <= getLineBreaksBefore())
 					return true;
 
-				if (!this.options2.join_wrapped_lines && token.isWrappable() && lineBreaks == 1) {
+				if (lineBreaks == 1) {
+					if ((!this.options2.join_wrapped_lines && token.isWrappable()) || index == 0)
 					token.breakBefore();
 				} else if (lineBreaks > 1) {
-					if (index == 0)
-						lineBreaks--;
 					token.putLineBreaksBefore(lineBreaks);
 				}
 				return true;
 			}
 
-			private int getLineBreaksBetween(Token token1, Token token2) {
-				if (token1 != null) {
-					List<Token> structure1 = token1.getInternalStructure();
-					if (structure1 != null && !structure1.isEmpty())
-						token1 = structure1.get(structure1.size() - 1);
-				}
-				List<Token> structure2 = token2.getInternalStructure();
-				if (structure2 != null && !structure2.isEmpty())
-					token2 = structure2.get(0);
-				int lineBreaks = WrapPreparator.this.tm.countLineBreaksBetween(token1, token2);
-				if (token1 == null)
-					lineBreaks++;
-				return lineBreaks;
-			}
 		});
 
 		Token last = this.tm.get(this.tm.size() - 1);
 		last.clearLineBreaksAfter();
-		int endingBreaks = this.tm.countLineBreaksBetween(last, null);
-		endingBreaks = Math.min(endingBreaks, this.options.number_of_empty_lines_to_preserve);
+		int endingBreaks = getLineBreaksToPreserve(last, null, false);
 		if (endingBreaks > 0) {
 			last.putLineBreaksAfter(endingBreaks);
 		} else if ((this.kind & CodeFormatter.K_COMPILATION_UNIT) != 0
@@ -769,6 +948,53 @@
 		}
 	}
 
+	int getLineBreaksToPreserve(Token token1, Token token2, boolean isBetweenImports) {
+		if (token1 != null) {
+			List<Token> structure = token1.getInternalStructure();
+			if (structure != null && !structure.isEmpty())
+				token1 = structure.get(structure.size() - 1);
+		}
+		if (token2 != null) {
+			List<Token> structure = token2.getInternalStructure();
+			if (structure != null && !structure.isEmpty())
+				token2 = structure.get(0);
+		}
+		int lineBreaks = WrapPreparator.this.tm.countLineBreaksBetween(token1, token2);
+		if (isBetweenImports)
+			return lineBreaks > 1 ? (this.options.blank_lines_between_import_groups + 1) : 0;
+
+		int toPreserve = this.options.number_of_empty_lines_to_preserve;
+		if (token1 != null && token2 != null)
+			toPreserve++; // n empty lines = n+1 line breaks, except for file start and end
+		if (token1 != null && token1.tokenType == Token.TokenNameEMPTY_LINE)
+			toPreserve--;
+		return Math.min(lineBreaks, toPreserve);
+	}
+
+	private void applyBreaksOutsideRegions(IRegion[] regions) {
+		String source = this.tm.getSource();
+		int previousRegionEnd = 0;
+		for (IRegion region : regions) {
+			int index = this.tm.findIndex(previousRegionEnd, -1, true);
+			Token token = this.tm.get(index);
+			if (this.tm.countLineBreaksBetween(source, previousRegionEnd,
+					Math.min(token.originalStart, region.getOffset())) > 0)
+				token.breakBefore();
+			for (index++; index < this.tm.size(); index++) {
+				Token next = this.tm.get(index);
+				if (next.originalStart > region.getOffset()) {
+					if (this.tm.countLineBreaksBetween(source, token.originalEnd, region.getOffset()) > 0)
+						next.breakBefore();
+					break;
+				}
+				if (this.tm.countLineBreaksBetween(token, next) > 0)
+					next.breakBefore();
+				token = next;
+			}
+			previousRegionEnd = region.getOffset() + region.getLength() - 1;
+		}
+	}
+
 	private void wrapComments() {
 		CommentWrapExecutor commentWrapper = new CommentWrapExecutor(this.tm, this.options);
 		boolean isNLSTagInLine = false;
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java
index fdc1e21..2ecd344 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java
@@ -32,6 +32,7 @@
 import org.eclipse.jdt.core.IPackageFragmentRoot;
 import org.eclipse.jdt.core.IType;
 import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.core.dom.IMethodBinding;
 import org.eclipse.jdt.core.dom.ITypeBinding;
 import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider;
@@ -368,19 +369,18 @@
 				signatureToReplace = originalSignature;
 				break;
 			case POSITION_RETURN_TYPE:
-				assert originalSignature.charAt(0) == '(' : "signature must start with '('"; //$NON-NLS-1$
+				assert originalSignature.charAt(0) == '(' || originalSignature.charAt(0) == '<': "signature must start with '(' or '<'"; //$NON-NLS-1$
 				int close = originalSignature.indexOf(')');
 				buf.append(originalSignature, 0, close+1);
 				signatureToReplace = originalSignature.substring(close+1);
 				break;
 			default: // parameter
 				SignatureWrapper wrapper = new SignatureWrapper(originalSignature.toCharArray(), true, true); // may already contain annotations
-				wrapper.start = 1;
+				wrapper.start = CharOperation.indexOf('(', wrapper.signature) + 1; // possibly skipping type parameters
 				for (int i = 0; i < updatePosition; i++)
-					wrapper.start = wrapper.computeEnd() + 1;
+					wrapper.start = wrapper.skipAngleContents(wrapper.computeEnd()) + 1;
 				int start = wrapper.start;
-				int end = wrapper.computeEnd();
-				end = wrapper.skipAngleContents(end);
+				int end = wrapper.skipAngleContents(wrapper.computeEnd());
 				buf.append(originalSignature, 0, start);
 				signatureToReplace = originalSignature.substring(start, end+1);
 				postfix = originalSignature.substring(end+1, originalSignature.length());
@@ -643,7 +643,7 @@
 	{
 		String[] result = new String[4]; // prefix, orig, replacement, postfix
 		StringBuffer buf;
-		assert originalSignature.charAt(0) == '(' : "signature must start with '('"; //$NON-NLS-1$
+		assert originalSignature.charAt(0) == '(' || originalSignature.charAt(0) == '<': "signature must start with '(' or '<'"; //$NON-NLS-1$
 		int close = originalSignature.indexOf(')');
 		result[0] = originalSignature.substring(0, close+1);
 		buf = new StringBuffer();
@@ -675,12 +675,11 @@
 		String[] result = new String[4]; // prefix, orig, replacement, postfix
 		StringBuffer buf;
 		SignatureWrapper wrapper = new SignatureWrapper(originalSignature.toCharArray(), true, true); // may already contain annotations
-		wrapper.start = 1;
+		wrapper.start = CharOperation.indexOf('(', wrapper.signature) + 1; // possibly skip type parameters
 		for (int i = 0; i < paramIdx; i++)
-			wrapper.start = wrapper.computeEnd() + 1;
+			wrapper.start = wrapper.skipAngleContents(wrapper.computeEnd()) + 1;
 		int start = wrapper.start;
-		int end = wrapper.computeEnd();
-		end = wrapper.skipAngleContents(end);
+		int end = wrapper.skipAngleContents(wrapper.computeEnd());
 		result[0] = originalSignature.substring(0, start);				
 		buf = new StringBuffer();
 		result[1] = originalSignature.substring(start, end+1);
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/parser/TypeConverter.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/parser/TypeConverter.java
index 9a6cee6..61d4a55 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/parser/TypeConverter.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/parser/TypeConverter.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2008, 2013 IBM Corporation and others.
+ * Copyright (c) 2008, 2015 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
@@ -113,7 +113,7 @@
 
 		int length = typeName.length;
 		this.namePos = 0;
-		return decodeType(typeName, length, start, end, true);
+		return decodeType2(typeName, length, start, end, true);
 	}
 
 	/*
@@ -126,7 +126,7 @@
 
 		int length = typeName.length;
 		this.namePos = 0;
-		return decodeType(typeName, length, start, end, false);
+		return decodeType2(typeName, length, start, end, false);
 	}
 
 	/*
@@ -366,7 +366,7 @@
 		}
 	}
 
-	private TypeReference decodeType(char[] typeName, int length, int start, int end, boolean includeGenericsAnyway) {
+	private TypeReference decodeType2(char[] typeName, int length, int start, int end, boolean includeGenericsAnyway) {
 		int identCount = 1;
 		int dim = 0;
 		int nameFragmentStart = this.namePos, nameFragmentEnd = -1;
@@ -388,7 +388,7 @@
 								}
 								this.namePos += max;
 								Wildcard result = new Wildcard(Wildcard.SUPER);
-								result.bound = decodeType(typeName, length, start, end, includeGenericsAnyway);
+								result.bound = decodeType2(typeName, length, start, end, includeGenericsAnyway);
 								result.sourceStart = start;
 								result.sourceEnd = end;
 								return result;
@@ -404,7 +404,7 @@
 								}
 								this.namePos += max;
 								Wildcard result = new Wildcard(Wildcard.EXTENDS);
-								result.bound = decodeType(typeName, length, start, end, includeGenericsAnyway);
+								result.bound = decodeType2(typeName, length, start, end, includeGenericsAnyway);
 								result.sourceStart = start;
 								result.sourceEnd = end;
 								return result;
@@ -460,6 +460,18 @@
 			}
 			this.namePos++;
 		}
+		return decodeType3(typeName, length, start, end, identCount, dim, nameFragmentStart, nameFragmentEnd,
+				fragments);
+	}
+
+	/*
+	 * Method should be inlined.
+	 * 
+	 * Only extracted to work around https://bugs.eclipse.org/471835 :
+	 * Random crashes in PhaseIdealLoop::build_loop_late_post when C2 JIT tries to compile TypeConverter::decodeType
+	 */
+	private TypeReference decodeType3(char[] typeName, int length, int start, int end, int identCount, int dim,
+			int nameFragmentStart, int nameFragmentEnd, ArrayList fragments) {
 		if (nameFragmentEnd < 0) nameFragmentEnd = this.namePos-1;
 		if (fragments == null) { // non parameterized
 			/* rebuild identifiers and dimensions */
@@ -542,7 +554,7 @@
 		ArrayList argumentList = new ArrayList(1);
 		int count = 0;
 		argumentsLoop: while (this.namePos < length) {
-			TypeReference argument = decodeType(typeName, length, start, end, includeGenericsAnyway);
+			TypeReference argument = decodeType2(typeName, length, start, end, includeGenericsAnyway);
 			count++;
 			argumentList.add(argument);
 			if (this.namePos >= length) break argumentsLoop;
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/CommentRecorderParser.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/CommentRecorderParser.java
index 6941961..d085650 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/CommentRecorderParser.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/CommentRecorderParser.java
@@ -88,6 +88,13 @@
 	}
 
 	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.internal.compiler.parser.Parser#consumeAnnotationTypeDeclarationHeader()
+	 */
+	protected void consumeAnnotationTypeDeclarationHeader() {
+		pushOnCommentsStack(0, this.scanner.commentPtr);
+		super.consumeAnnotationTypeDeclarationHeader();
+	}
+	/* (non-Javadoc)
 	 * @see org.eclipse.jdt.internal.compiler.parser.Parser#consumeClassHeader()
 	 */
 	protected void consumeClassHeader() {
@@ -102,6 +109,22 @@
 		super.consumeEmptyTypeDeclaration();
 	}
 	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.internal.compiler.parser.Parser#consumeEnterAnonymousClassBody(boolean)
+	 */
+	@Override
+	protected void consumeEnterAnonymousClassBody(boolean qualified) {
+		pushOnCommentsStack(0, this.scanner.commentPtr);
+		super.consumeEnterAnonymousClassBody(qualified);
+	}
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.internal.compiler.parser.Parser#consumeEnumHeader()
+	 */
+	protected void consumeEnumHeader() {
+		pushOnCommentsStack(0, this.scanner.commentPtr);
+		super.consumeEnumHeader();
+	}
+
+	/* (non-Javadoc)
 	 * @see org.eclipse.jdt.internal.compiler.parser.Parser#consumeInterfaceHeader()
 	 */
 	protected void consumeInterfaceHeader() {
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java
index a5647e0..f24bba1 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -1165,6 +1165,7 @@
 	this.lookupEnvironment = new LookupEnvironment(this, this.options, problemReporter, this.nameEnvironment);
 	this.lookupEnvironment.mayTolerateMissingType = true;
 	this.parser = MatchLocatorParser.createParser(problemReporter, this);
+	this.bindings = new SimpleLookupTable(); // For every LE
 
 	// basic parser needs also to be reset as project options may have changed
 	// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=163072