1 /*******************************************************************************
2  * Copyright (c) 2014, 2019 Mateusz Matela and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     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
13  *     Mateusz Matela <mateusz.matela@gmail.com> - [formatter] follow up bug for comments - https://bugs.eclipse.org/458208
14  *******************************************************************************/
15 package org.eclipse.jdt.internal.formatter.linewrap;
16 
17 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOLON;
18 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMA;
19 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_BLOCK;
20 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_JAVADOC;
21 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_LINE;
22 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameDOT;
23 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameEQUAL;
24 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameIdentifier;
25 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameLBRACE;
26 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameLESS;
27 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameLPAREN;
28 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameOR;
29 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameQUESTION;
30 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameRBRACE;
31 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameRPAREN;
32 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameSEMICOLON;
33 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameStringLiteral;
34 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameenum;
35 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameextends;
36 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameimplements;
37 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamenew;
38 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamesuper;
39 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamethis;
40 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamethrows;
41 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameto;
42 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamewhile;
43 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamewith;
44 
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.Collections;
48 import java.util.HashMap;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.function.Predicate;
52 import java.util.function.ToIntFunction;
53 
54 import org.eclipse.jdt.core.dom.ASTNode;
55 import org.eclipse.jdt.core.dom.ASTVisitor;
56 import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
57 import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
58 import org.eclipse.jdt.core.dom.ArrayInitializer;
59 import org.eclipse.jdt.core.dom.Assignment;
60 import org.eclipse.jdt.core.dom.Block;
61 import org.eclipse.jdt.core.dom.CatchClause;
62 import org.eclipse.jdt.core.dom.ClassInstanceCreation;
63 import org.eclipse.jdt.core.dom.ConditionalExpression;
64 import org.eclipse.jdt.core.dom.ConstructorInvocation;
65 import org.eclipse.jdt.core.dom.CreationReference;
66 import org.eclipse.jdt.core.dom.DoStatement;
67 import org.eclipse.jdt.core.dom.EnhancedForStatement;
68 import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
69 import org.eclipse.jdt.core.dom.EnumDeclaration;
70 import org.eclipse.jdt.core.dom.ExportsDirective;
71 import org.eclipse.jdt.core.dom.Expression;
72 import org.eclipse.jdt.core.dom.ExpressionMethodReference;
73 import org.eclipse.jdt.core.dom.FieldAccess;
74 import org.eclipse.jdt.core.dom.FieldDeclaration;
75 import org.eclipse.jdt.core.dom.ForStatement;
76 import org.eclipse.jdt.core.dom.IfStatement;
77 import org.eclipse.jdt.core.dom.InfixExpression;
78 import org.eclipse.jdt.core.dom.InfixExpression.Operator;
79 import org.eclipse.jdt.core.dom.LambdaExpression;
80 import org.eclipse.jdt.core.dom.MethodDeclaration;
81 import org.eclipse.jdt.core.dom.MethodInvocation;
82 import org.eclipse.jdt.core.dom.Name;
83 import org.eclipse.jdt.core.dom.NormalAnnotation;
84 import org.eclipse.jdt.core.dom.OpensDirective;
85 import org.eclipse.jdt.core.dom.ParameterizedType;
86 import org.eclipse.jdt.core.dom.ProvidesDirective;
87 import org.eclipse.jdt.core.dom.QualifiedName;
88 import org.eclipse.jdt.core.dom.RecordDeclaration;
89 import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
90 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
91 import org.eclipse.jdt.core.dom.Statement;
92 import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
93 import org.eclipse.jdt.core.dom.SuperFieldAccess;
94 import org.eclipse.jdt.core.dom.SuperMethodInvocation;
95 import org.eclipse.jdt.core.dom.SuperMethodReference;
96 import org.eclipse.jdt.core.dom.SwitchExpression;
97 import org.eclipse.jdt.core.dom.SwitchStatement;
98 import org.eclipse.jdt.core.dom.ThisExpression;
99 import org.eclipse.jdt.core.dom.TryStatement;
100 import org.eclipse.jdt.core.dom.Type;
101 import org.eclipse.jdt.core.dom.TypeDeclaration;
102 import org.eclipse.jdt.core.dom.TypeMethodReference;
103 import org.eclipse.jdt.core.dom.TypeParameter;
104 import org.eclipse.jdt.core.dom.UnionType;
105 import org.eclipse.jdt.core.dom.VariableDeclaration;
106 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
107 import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
108 import org.eclipse.jdt.core.dom.WhileStatement;
109 import org.eclipse.jdt.core.formatter.CodeFormatter;
110 import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
111 import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions;
112 import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions.Alignment;
113 import org.eclipse.jdt.internal.formatter.Token;
114 import org.eclipse.jdt.internal.formatter.Token.WrapMode;
115 import org.eclipse.jdt.internal.formatter.Token.WrapPolicy;
116 import org.eclipse.jdt.internal.formatter.TokenManager;
117 import org.eclipse.jdt.internal.formatter.TokenTraverser;
118 import org.eclipse.jface.text.IRegion;
119 
120 public class WrapPreparator extends ASTVisitor {
121 
122 	/**
123 	 * Helper for common handling of all expressions that should be treated the same as {@link FieldAccess}
124 	 */
125 	private static class FieldAccessAdapter {
126 		final Expression accessExpression;
127 
FieldAccessAdapter(Expression expression)128 		public FieldAccessAdapter(Expression expression) {
129 			this.accessExpression = expression;
130 		}
131 
isFieldAccess(ASTNode expr)132 		public static boolean isFieldAccess(ASTNode expr) {
133 			return expr instanceof FieldAccess || expr instanceof QualifiedName || expr instanceof ThisExpression
134 					|| expr instanceof SuperFieldAccess;
135 		}
136 
getExpression()137 		public Expression getExpression() {
138 			if (this.accessExpression instanceof FieldAccess)
139 				return ((FieldAccess) this.accessExpression).getExpression();
140 			if (this.accessExpression instanceof QualifiedName)
141 				return ((QualifiedName) this.accessExpression).getQualifier();
142 			if (this.accessExpression instanceof ThisExpression)
143 				return ((ThisExpression) this.accessExpression).getQualifier();
144 			if (this.accessExpression instanceof SuperFieldAccess)
145 				return ((SuperFieldAccess) this.accessExpression).getQualifier();
146 			throw new AssertionError();
147 		}
148 
getIdentifierIndex(TokenManager tm)149 		public int getIdentifierIndex(TokenManager tm) {
150 			if (this.accessExpression instanceof FieldAccess)
151 				return tm.firstIndexIn(((FieldAccess) this.accessExpression).getName(), TokenNameIdentifier);
152 			if (this.accessExpression instanceof QualifiedName)
153 				return tm.firstIndexIn(((QualifiedName) this.accessExpression).getName(), TokenNameIdentifier);
154 			if (this.accessExpression instanceof ThisExpression)
155 				return tm.lastIndexIn(this.accessExpression, TokenNamethis);
156 			if (this.accessExpression instanceof SuperFieldAccess)
157 				return tm.lastIndexIn(this.accessExpression, TokenNamesuper);
158 			throw new AssertionError();
159 		}
160 	}
161 
162 	private static final Map<Operator, Integer> OPERATOR_PRECEDENCE;
163 	private static final Map<Operator, ToIntFunction<DefaultCodeFormatterOptions>> OPERATOR_WRAPPING_OPTION;
164 	private static final Map<Operator, Predicate<DefaultCodeFormatterOptions>> OPERATOR_WRAP_BEFORE_OPTION;
165 	static {
166 		HashMap<Operator, Integer> precedence = new HashMap<>();
167 		HashMap<Operator, ToIntFunction<DefaultCodeFormatterOptions>> wrappingOption = new HashMap<>();
168 		HashMap<Operator, Predicate<DefaultCodeFormatterOptions>> wrapBeforeOption = new HashMap<>();
169 		for (Operator op : Arrays.asList(Operator.TIMES, Operator.DIVIDE, Operator.REMAINDER)) {
precedence.put(op, 1)170 			precedence.put(op, 1);
wrappingOption.put(op, o -> o.alignment_for_multiplicative_operator)171 			wrappingOption.put(op, o -> o.alignment_for_multiplicative_operator);
wrapBeforeOption.put(op, o -> o.wrap_before_multiplicative_operator)172 			wrapBeforeOption.put(op, o -> o.wrap_before_multiplicative_operator);
173 		}
174 		for (Operator op : Arrays.asList(Operator.PLUS, Operator.MINUS)) {
precedence.put(op, 2)175 			precedence.put(op, 2);
wrappingOption.put(op, o -> o.alignment_for_additive_operator)176 			wrappingOption.put(op, o -> o.alignment_for_additive_operator);
wrapBeforeOption.put(op, o -> o.wrap_before_additive_operator)177 			wrapBeforeOption.put(op, o -> o.wrap_before_additive_operator);
178 		}
179 		for (Operator op : Arrays.asList(Operator.LEFT_SHIFT, Operator.RIGHT_SHIFT_SIGNED,
180 				Operator.RIGHT_SHIFT_UNSIGNED)) {
precedence.put(op, 3)181 			precedence.put(op, 3);
wrappingOption.put(op, o -> o.alignment_for_shift_operator)182 			wrappingOption.put(op, o -> o.alignment_for_shift_operator);
wrapBeforeOption.put(op, o -> o.wrap_before_shift_operator)183 			wrapBeforeOption.put(op, o -> o.wrap_before_shift_operator);
184 		}
185 		for (Operator op : Arrays.asList(Operator.LESS, Operator.GREATER, Operator.LESS_EQUALS,
186 				Operator.GREATER_EQUALS)) {
precedence.put(op, 4)187 			precedence.put(op, 4);
wrappingOption.put(op, o -> o.alignment_for_relational_operator)188 			wrappingOption.put(op, o -> o.alignment_for_relational_operator);
wrapBeforeOption.put(op, o -> o.wrap_before_relational_operator)189 			wrapBeforeOption.put(op, o -> o.wrap_before_relational_operator);
190 		}
191 		for (Operator op : Arrays.asList(Operator.EQUALS, Operator.NOT_EQUALS)) {
precedence.put(op, 5)192 			precedence.put(op, 5);
wrappingOption.put(op, o -> o.alignment_for_relational_operator)193 			wrappingOption.put(op, o -> o.alignment_for_relational_operator);
wrapBeforeOption.put(op, o -> o.wrap_before_relational_operator)194 			wrapBeforeOption.put(op, o -> o.wrap_before_relational_operator);
195 		}
196 
precedence.put(Operator.AND, 6)197 		precedence.put(Operator.AND, 6);
precedence.put(Operator.XOR, 7)198 		precedence.put(Operator.XOR, 7);
precedence.put(Operator.OR, 8)199 		precedence.put(Operator.OR, 8);
200 		for (Operator op : Arrays.asList(Operator.AND, Operator.XOR, Operator.OR)) {
wrappingOption.put(op, o -> o.alignment_for_bitwise_operator)201 			wrappingOption.put(op, o -> o.alignment_for_bitwise_operator);
wrapBeforeOption.put(op, o -> o.wrap_before_bitwise_operator)202 			wrapBeforeOption.put(op, o -> o.wrap_before_bitwise_operator);
203 		}
204 
precedence.put(Operator.CONDITIONAL_AND, 9)205 		precedence.put(Operator.CONDITIONAL_AND, 9);
precedence.put(Operator.CONDITIONAL_OR, 10)206 		precedence.put(Operator.CONDITIONAL_OR, 10);
207 		for (Operator op : Arrays.asList(Operator.CONDITIONAL_AND, Operator.CONDITIONAL_OR)) {
wrappingOption.put(op, o -> o.alignment_for_logical_operator)208 			wrappingOption.put(op, o -> o.alignment_for_logical_operator);
wrapBeforeOption.put(op, o -> o.wrap_before_logical_operator)209 			wrapBeforeOption.put(op, o -> o.wrap_before_logical_operator);
210 		}
211 		// ternary and assignment operators not relevant to infix expressions
212 
213 		OPERATOR_PRECEDENCE = Collections.unmodifiableMap(precedence);
214 		OPERATOR_WRAPPING_OPTION = Collections.unmodifiableMap(wrappingOption);
215 		OPERATOR_WRAP_BEFORE_OPTION = Collections.unmodifiableMap(wrapBeforeOption);
216 	}
217 
218 	/** Penalty multiplier for wraps that are preferred */
219 	private final static float PREFERRED = 7f / 8;
220 
221 	final TokenManager tm;
222 	final DefaultCodeFormatterOptions options;
223 	final int kind;
224 
225 	final Aligner aligner;
226 
227 	/*
228 	 * temporary values used when calling {@link #handleWrap(int)} to avoid ArrayList initialization and long lists of
229 	 * parameters
230 	 */
231 	private List<Integer> wrapIndexes = new ArrayList<>();
232 	/** Indexes for wraps that shouldn't happen but should be indented if cannot be removed */
233 	private List<Integer> secondaryWrapIndexes = new ArrayList<>();
234 	private List<Float> wrapPenalties = new ArrayList<>();
235 	private int wrapParentIndex = -1;
236 	private int wrapGroupEnd = -1;
237 
238 	private int currentDepth = 0;
239 
WrapPreparator(TokenManager tokenManager, DefaultCodeFormatterOptions options, int kind)240 	public WrapPreparator(TokenManager tokenManager, DefaultCodeFormatterOptions options, int kind) {
241 		this.tm = tokenManager;
242 		this.options = options;
243 		this.kind = kind;
244 
245 		this.aligner = new Aligner(this.tm, this.options);
246 	}
247 
248 	@Override
preVisit2(ASTNode node)249 	public boolean preVisit2(ASTNode node) {
250 		this.currentDepth++;
251 
252 		assert this.wrapIndexes.isEmpty() && this.secondaryWrapIndexes.isEmpty() && this.wrapPenalties.isEmpty();
253 		assert this.wrapParentIndex == -1 && this.wrapGroupEnd == -1;
254 
255 		boolean isMalformed = (node.getFlags() & ASTNode.MALFORMED) != 0;
256 		if (isMalformed) {
257 			this.tm.addDisableFormatTokenPair(this.tm.firstTokenIn(node, -1), this.tm.lastTokenIn(node, -1));
258 		}
259 		return !isMalformed;
260 	}
261 
262 	@Override
postVisit(ASTNode node)263 	public void postVisit(ASTNode node) {
264 		this.currentDepth--;
265 	}
266 
267 	@Override
visit(NormalAnnotation node)268 	public boolean visit(NormalAnnotation node) {
269 		int lParen = this.tm.firstIndexAfter(node.getTypeName(), TokenNameLPAREN);
270 		int rParen = this.tm.lastIndexIn(node, TokenNameRPAREN);
271 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_annotation);
272 
273 		handleArguments(node.values(), this.options.alignment_for_arguments_in_annotation);
274 		return true;
275 	}
276 
277 	@Override
visit(SingleMemberAnnotation node)278 	public boolean visit(SingleMemberAnnotation node) {
279 		int lParen = this.tm.firstIndexAfter(node.getTypeName(), TokenNameLPAREN);
280 		int rParen = this.tm.lastIndexIn(node, TokenNameRPAREN);
281 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_annotation);
282 		return true;
283 	}
284 
285 	@Override
visit(TypeDeclaration node)286 	public boolean visit(TypeDeclaration node) {
287 		Type superclassType = node.getSuperclassType();
288 		if (superclassType != null) {
289 			this.wrapParentIndex = this.tm.lastIndexIn(node.getName(), -1);
290 			this.wrapGroupEnd = this.tm.lastIndexIn(superclassType, -1);
291 			this.wrapIndexes.add(this.tm.firstIndexBefore(superclassType, TokenNameextends));
292 			this.wrapIndexes.add(this.tm.firstIndexIn(superclassType, -1));
293 			handleWrap(this.options.alignment_for_superclass_in_type_declaration, PREFERRED);
294 		}
295 
296 		List<Type> superInterfaceTypes = node.superInterfaceTypes();
297 		if (!superInterfaceTypes.isEmpty()) {
298 			int implementsToken = node.isInterface() ? TokenNameextends : TokenNameimplements;
299 			this.wrapParentIndex = this.tm.lastIndexIn(node.getName(), -1);
300 			this.wrapIndexes.add(this.tm.firstIndexBefore(superInterfaceTypes.get(0), implementsToken));
301 			prepareElementsList(superInterfaceTypes, TokenNameCOMMA, -1);
302 			handleWrap(this.options.alignment_for_superinterfaces_in_type_declaration, PREFERRED);
303 		}
304 
305 		prepareElementsList(node.typeParameters(), TokenNameCOMMA, TokenNameLESS);
306 		handleWrap(this.options.alignment_for_type_parameters);
307 
308 		this.aligner.handleAlign(node.bodyDeclarations());
309 
310 		return true;
311 	}
312 
313 	@Override
visit(AnnotationTypeDeclaration node)314 	public boolean visit(AnnotationTypeDeclaration node) {
315 		this.aligner.handleAlign(node.bodyDeclarations());
316 		return true;
317 	}
318 
319 	@Override
visit(AnonymousClassDeclaration node)320 	public boolean visit(AnonymousClassDeclaration node) {
321 		this.aligner.handleAlign(node.bodyDeclarations());
322 		return true;
323 	}
324 
325 	@Override
visit(RecordDeclaration node)326 	public boolean visit(RecordDeclaration node) {
327 		int lParen = this.tm.firstIndexAfter(node.getName(), TokenNameLPAREN);
328 		List<SingleVariableDeclaration> components = node.recordComponents();
329 		int rParen = this.tm.firstIndexAfter(
330 				components.isEmpty() ? node.getName() : components.get(components.size() - 1), TokenNameRPAREN);
331 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_record_declaration);
332 
333 		if (!components.isEmpty()) {
334 			int wrappingOption = this.options.alignment_for_record_components;
335 			this.wrapGroupEnd = this.tm.lastIndexIn(components.get(components.size() - 1), -1);
336 			handleArguments(components, wrappingOption);
337 		}
338 
339 		List<Type> superInterfaceTypes = node.superInterfaceTypes();
340 		if (!superInterfaceTypes.isEmpty()) {
341 			this.wrapParentIndex = this.tm.lastIndexIn(node.getName(), -1);
342 			this.wrapIndexes.add(this.tm.firstIndexBefore(superInterfaceTypes.get(0), TokenNameimplements));
343 			prepareElementsList(superInterfaceTypes, TokenNameCOMMA, -1);
344 			handleWrap(this.options.alignment_for_superinterfaces_in_record_declaration, PREFERRED);
345 		}
346 		return true;
347 	}
348 
349 	@Override
visit(MethodDeclaration node)350 	public boolean visit(MethodDeclaration node) {
351 		if (!node.isCompactConstructor()) {
352 			int lParen = this.tm.firstIndexAfter(node.getName(), TokenNameLPAREN);
353 			int rParen = node.getBody() == null ? this.tm.lastIndexIn(node, TokenNameRPAREN)
354 					: this.tm.firstIndexBefore(node.getBody(), TokenNameRPAREN);
355 			handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_method_declaration);
356 		}
357 
358 		List<SingleVariableDeclaration> parameters = node.parameters();
359 		Type receiverType = node.getReceiverType();
360 		if (!parameters.isEmpty() || receiverType != null) {
361 			if (receiverType != null)
362 				this.wrapIndexes.add(this.tm.firstIndexIn(receiverType, -1));
363 			int wrappingOption = node.isConstructor() ? this.options.alignment_for_parameters_in_constructor_declaration
364 					: this.options.alignment_for_parameters_in_method_declaration;
365 			this.wrapGroupEnd = this.tm.lastIndexIn(
366 					parameters.isEmpty() ? receiverType : parameters.get(parameters.size() - 1), -1);
367 			handleArguments(parameters, wrappingOption);
368 		}
369 
370 		List<Type> exceptionTypes = node.thrownExceptionTypes();
371 		if (!exceptionTypes.isEmpty()) {
372 			int wrappingOption = node.isConstructor()
373 					? this.options.alignment_for_throws_clause_in_constructor_declaration
374 					: this.options.alignment_for_throws_clause_in_method_declaration;
375 			if ((wrappingOption & Alignment.M_INDENT_ON_COLUMN) == 0)
376 				this.wrapParentIndex = this.tm.firstIndexAfter(node.getName(), TokenNameLPAREN);
377 			prepareElementsList(exceptionTypes, TokenNameCOMMA, TokenNameRPAREN);
378 			// instead of the first exception type, wrap the "throws" token
379 			this.wrapIndexes.set(0, this.tm.firstIndexBefore(exceptionTypes.get(0), TokenNamethrows));
380 			handleWrap(wrappingOption, 0.5f);
381 		}
382 
383 		if (!node.isConstructor()) {
384 			this.wrapParentIndex = this.tm.findFirstTokenInLine(this.tm.firstIndexIn(node.getName(), -1));
385 			while (this.tm.get(this.wrapParentIndex).isComment())
386 				this.wrapParentIndex++;
387 			List<TypeParameter> typeParameters = node.typeParameters();
388 			if (!typeParameters.isEmpty())
389 				this.wrapIndexes.add(this.tm.firstIndexIn(typeParameters.get(0), -1));
390 			if (node.getReturnType2() != null) {
391 				int returTypeIndex = this.tm.firstIndexIn(node.getReturnType2(), -1);
392 				if (returTypeIndex != this.wrapParentIndex)
393 					this.wrapIndexes.add(returTypeIndex);
394 			}
395 			this.wrapIndexes.add(this.tm.firstIndexIn(node.getName(), -1));
396 			this.wrapGroupEnd = this.tm.lastIndexIn(node.getName(), -1);
397 			handleWrap(this.options.alignment_for_method_declaration);
398 		}
399 
400 		prepareElementsList(node.typeParameters(), TokenNameCOMMA, TokenNameLESS);
401 		handleWrap(this.options.alignment_for_type_parameters);
402 
403 		return true;
404 	}
405 
406 	@Override
visit(EnumDeclaration node)407 	public boolean visit(EnumDeclaration node) {
408 		List<EnumConstantDeclaration> enumConstants = node.enumConstants();
409 		int constantsEnd = -1;
410 		if (!enumConstants.isEmpty()) {
411 			for (EnumConstantDeclaration constant : enumConstants)
412 				this.wrapIndexes.add(this.tm.firstIndexIn(constant, -1));
413 			this.wrapParentIndex = (this.options.alignment_for_enum_constants & Alignment.M_INDENT_ON_COLUMN) > 0
414 					? this.tm.firstIndexBefore(enumConstants.get(0), TokenNameLBRACE)
415 					: this.tm.firstIndexIn(node, TokenNameenum);
416 			this.wrapGroupEnd = constantsEnd = this.tm.lastIndexIn(enumConstants.get(enumConstants.size() - 1), -1);
417 			handleWrap(this.options.alignment_for_enum_constants, node);
418 		}
419 
420 		if (!this.options.join_wrapped_lines) {
421 			// preserve a line break between the last comma and semicolon
422 			int commaIndex = -1;
423 			int i = constantsEnd > 0 ? constantsEnd : this.tm.firstIndexAfter(node.getName(), TokenNameLBRACE);
424 			while (++i < this.tm.size()) {
425 				Token t = this.tm.get(i);
426 				if (t.isComment())
427 					continue;
428 				if (t.tokenType == TokenNameCOMMA) {
429 					commaIndex = i;
430 					continue;
431 				}
432 				if (t.tokenType == TokenNameSEMICOLON && commaIndex >= 0
433 						&& this.tm.countLineBreaksBetween(this.tm.get(commaIndex), t) == 1) {
434 					t.setWrapPolicy(new WrapPolicy(WrapMode.WHERE_NECESSARY, commaIndex, 0));
435 				}
436 				break;
437 			}
438 		}
439 
440 		List<Type> superInterfaceTypes = node.superInterfaceTypes();
441 		if (!superInterfaceTypes.isEmpty()) {
442 			this.wrapParentIndex = this.tm.lastIndexIn(node.getName(), -1);
443 			this.wrapIndexes.add(this.tm.firstIndexBefore(superInterfaceTypes.get(0), TokenNameimplements));
444 			prepareElementsList(superInterfaceTypes, TokenNameCOMMA, -1);
445 			handleWrap(this.options.alignment_for_superinterfaces_in_enum_declaration, PREFERRED);
446 		}
447 
448 		this.aligner.handleAlign(node.bodyDeclarations());
449 
450 		return true;
451 	}
452 
453 	@Override
visit(EnumConstantDeclaration node)454 	public boolean visit(EnumConstantDeclaration node) {
455 		int lParen = this.tm.firstIndexAfter(node.getName(), -1);
456 		while (this.tm.get(lParen).isComment())
457 			lParen++;
458 		if (this.tm.get(lParen).tokenType == TokenNameLPAREN) {
459 			int rParen = node.getAnonymousClassDeclaration() == null ? this.tm.lastIndexIn(node, TokenNameRPAREN)
460 					: this.tm.firstIndexBefore(node.getAnonymousClassDeclaration(), TokenNameRPAREN);
461 			handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_enum_constant_declaration);
462 		}
463 
464 		handleArguments(node.arguments(), this.options.alignment_for_arguments_in_enum_constant);
465 		AnonymousClassDeclaration anonymousClass = node.getAnonymousClassDeclaration();
466 		if (anonymousClass != null) {
467 			forceContinuousWrapping(anonymousClass, this.tm.firstIndexIn(node.getName(), -1));
468 		}
469 		return true;
470 	}
471 
472 	@Override
visit(Block node)473 	public boolean visit(Block node) {
474 		this.aligner.handleAlign(node);
475 		return true;
476 	}
477 
478 	@Override
visit(MethodInvocation node)479 	public boolean visit(MethodInvocation node) {
480 		int lParen = this.tm.firstIndexAfter(node.getName(), TokenNameLPAREN);
481 		int rParen = this.tm.lastIndexIn(node, TokenNameRPAREN);
482 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_method_invocation);
483 
484 		handleArguments(node.arguments(), this.options.alignment_for_arguments_in_method_invocation);
485 		handleTypeArguments(node.typeArguments());
486 
487 		boolean isInvocationChainRoot = !(node.getParent() instanceof MethodInvocation)
488 				|| node.getLocationInParent() != MethodInvocation.EXPRESSION_PROPERTY;
489 		if (isInvocationChainRoot) {
490 			Expression expression = node;
491 			MethodInvocation invocation = node;
492 			while (expression instanceof MethodInvocation) {
493 				invocation = (MethodInvocation) expression;
494 				expression = invocation.getExpression();
495 				if (expression != null) {
496 					this.wrapIndexes.add(this.tm.firstIndexBefore(invocation.getName(), TokenNameDOT));
497 					this.secondaryWrapIndexes.add(this.tm.firstIndexIn(invocation.getName(), TokenNameIdentifier));
498 				}
499 			}
500 			Collections.reverse(this.wrapIndexes);
501 			this.wrapParentIndex = (expression != null) ? this.tm.lastIndexIn(expression, -1)
502 					: this.tm.lastIndexIn(invocation, -1);
503 			this.wrapGroupEnd = this.tm.lastIndexIn(node, -1);
504 			handleWrap(this.options.alignment_for_selector_in_method_invocation);
505 		}
506 		return true;
507 	}
508 
509 	@Override
visit(SuperMethodInvocation node)510 	public boolean visit(SuperMethodInvocation node) {
511 		int lParen = this.tm.firstIndexAfter(node.getName(), TokenNameLPAREN);
512 		int rParen = this.tm.lastIndexIn(node, TokenNameRPAREN);
513 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_method_invocation);
514 
515 		handleArguments(node.arguments(), this.options.alignment_for_arguments_in_method_invocation);
516 		handleTypeArguments(node.typeArguments());
517 		return true;
518 	}
519 
520 	@Override
visit(ClassInstanceCreation node)521 	public boolean visit(ClassInstanceCreation node) {
522 		int lParen = this.tm.firstIndexAfter(node.getType(), TokenNameLPAREN);
523 		int rParen = node.getAnonymousClassDeclaration() == null ? this.tm.lastIndexIn(node, TokenNameRPAREN)
524 				: this.tm.firstIndexBefore(node.getAnonymousClassDeclaration(), TokenNameRPAREN);
525 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_method_invocation);
526 
527 		AnonymousClassDeclaration anonymousClass = node.getAnonymousClassDeclaration();
528 		if (anonymousClass != null) {
529 			forceContinuousWrapping(anonymousClass, this.tm.firstIndexIn(node, TokenNamenew));
530 		}
531 
532 		int wrappingOption = node.getExpression() != null
533 				? this.options.alignment_for_arguments_in_qualified_allocation_expression
534 				: this.options.alignment_for_arguments_in_allocation_expression;
535 		handleArguments(node.arguments(), wrappingOption);
536 
537 		handleTypeArguments(node.typeArguments());
538 		return true;
539 	}
540 
541 	@Override
visit(ConstructorInvocation node)542 	public boolean visit(ConstructorInvocation node) {
543 		int lParen = node.arguments().isEmpty() ? this.tm.lastIndexIn(node, TokenNameLPAREN)
544 				: this.tm.firstIndexBefore((ASTNode) node.arguments().get(0), TokenNameLPAREN);
545 		int rParen = this.tm.lastIndexIn(node, TokenNameRPAREN);
546 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_method_invocation);
547 
548 		handleArguments(node.arguments(), this.options.alignment_for_arguments_in_explicit_constructor_call);
549 		handleTypeArguments(node.typeArguments());
550 		return true;
551 	}
552 
553 	@Override
visit(SuperConstructorInvocation node)554 	public boolean visit(SuperConstructorInvocation node) {
555 		int lParen = node.arguments().isEmpty() ? this.tm.lastIndexIn(node, TokenNameLPAREN)
556 				: this.tm.firstIndexBefore((ASTNode) node.arguments().get(0), TokenNameLPAREN);
557 		int rParen = this.tm.lastIndexIn(node, TokenNameRPAREN);
558 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_method_invocation);
559 
560 		handleArguments(node.arguments(), this.options.alignment_for_arguments_in_explicit_constructor_call);
561 		handleTypeArguments(node.typeArguments());
562 		return true;
563 	}
564 
565 	@Override
visit(FieldAccess node)566 	public boolean visit(FieldAccess node) {
567 		handleFieldAccess(node);
568 		return true;
569 	}
570 
571 	@Override
visit(QualifiedName node)572 	public boolean visit(QualifiedName node) {
573 		handleFieldAccess(node);
574 		return true;
575 	}
576 
577 	@Override
visit(ThisExpression node)578 	public boolean visit(ThisExpression node) {
579 		handleFieldAccess(node);
580 		return true;
581 	}
582 
583 	@Override
visit(SuperFieldAccess node)584 	public boolean visit(SuperFieldAccess node) {
585 		handleFieldAccess(node);
586 		return true;
587 	}
588 
handleFieldAccess(Expression node)589 	private void handleFieldAccess(Expression node) {
590 		boolean isAccessChainRoot = !FieldAccessAdapter.isFieldAccess(node.getParent());
591 		if (!isAccessChainRoot)
592 			return;
593 
594 		Expression expression = node;
595 		FieldAccessAdapter access = null;
596 		while (FieldAccessAdapter.isFieldAccess(expression)) {
597 			access = new FieldAccessAdapter(expression);
598 			int nameIndex = access.getIdentifierIndex(this.tm);
599 			// find a dot preceding the name, may not be there
600 			for (int i = nameIndex - 1; i > this.tm.firstIndexIn(node, -1); i--) {
601 				Token t = this.tm.get(i);
602 				if (t.tokenType == TokenNameDOT) {
603 					this.wrapIndexes.add(i);
604 					this.secondaryWrapIndexes.add(nameIndex);
605 				}
606 				if (!t.isComment() && t.tokenType != TokenNamesuper)
607 					break;
608 			}
609 			expression = access.getExpression();
610 		}
611 		Collections.reverse(this.wrapIndexes);
612 		this.wrapParentIndex = this.tm.lastIndexIn(expression != null ? expression : access.accessExpression, -1);
613 		boolean isFollowedByInvocation = node.getParent() instanceof MethodInvocation
614 				&& node.getLocationInParent() == MethodInvocation.EXPRESSION_PROPERTY;
615 		this.wrapGroupEnd = isFollowedByInvocation ? this.tm.lastIndexIn(node.getParent(), -1)
616 				: new FieldAccessAdapter(node).getIdentifierIndex(this.tm);
617 		// TODO need configuration for this, now only handles line breaks that cannot be removed
618 		handleWrap(Alignment.M_NO_ALIGNMENT);
619 	}
620 
621 	@Override
visit(InfixExpression node)622 	public boolean visit(InfixExpression node) {
623 		Integer operatorPrecedence = OPERATOR_PRECEDENCE.get(node.getOperator());
624 		if (operatorPrecedence == null)
625 			return true;
626 		ASTNode parent = node.getParent();
627 		if ((parent instanceof InfixExpression) && samePrecedence(node, (InfixExpression) parent))
628 			return true; // this node has been handled higher in the AST
629 
630 		int wrappingOption = OPERATOR_WRAPPING_OPTION.get(node.getOperator()).applyAsInt(this.options);
631 		boolean wrapBeforeOperator = OPERATOR_WRAP_BEFORE_OPTION.get(node.getOperator()).test(this.options);
632 		if (this.tm.isStringConcatenation(node)) {
633 			wrappingOption = this.options.alignment_for_string_concatenation;
634 			wrapBeforeOperator = this.options.wrap_before_string_concatenation;
635 		}
636 
637 		findTokensToWrap(node, wrapBeforeOperator, 0);
638 		this.wrapParentIndex = this.wrapIndexes.remove(0);
639 		this.wrapGroupEnd = this.tm.lastIndexIn(node, -1);
640 		if ((wrappingOption & Alignment.M_INDENT_ON_COLUMN) != 0 && this.wrapParentIndex > 0)
641 			this.wrapParentIndex--;
642 		for (int i = this.wrapParentIndex; i >= 0; i--) {
643 			if (!this.tm.get(i).isComment()) {
644 				this.wrapParentIndex = i;
645 				break;
646 			}
647 		}
648 		handleWrap(wrappingOption, !wrapBeforeOperator, node);
649 		return true;
650 	}
651 
findTokensToWrap(InfixExpression node, boolean wrapBeforeOperator, int depth)652 	private void findTokensToWrap(InfixExpression node, boolean wrapBeforeOperator, int depth) {
653 		Expression left = node.getLeftOperand();
654 		if (left instanceof InfixExpression && samePrecedence(node, (InfixExpression) left)) {
655 			findTokensToWrap((InfixExpression) left, wrapBeforeOperator, depth + 1);
656 		} else if (this.wrapIndexes.isEmpty() // always add first operand, it will be taken as wrap parent
657 				|| !wrapBeforeOperator) {
658 			this.wrapIndexes.add(this.tm.firstIndexIn(left, -1));
659 		}
660 
661 		Expression right = node.getRightOperand();
662 		List<Expression> extended = node.extendedOperands();
663 		for (int i = -1; i < extended.size(); i++) {
664 			Expression operand = (i == -1) ? right : extended.get(i);
665 			if (operand instanceof InfixExpression && samePrecedence(node, (InfixExpression) operand)) {
666 				findTokensToWrap((InfixExpression) operand, wrapBeforeOperator, depth + 1);
667 			}
668 			int indexBefore = this.tm.firstIndexBefore(operand, -1);
669 			while (this.tm.get(indexBefore).isComment())
670 				indexBefore--;
671 			assert node.getOperator().toString().equals(this.tm.toString(indexBefore));
672 			int indexAfter = this.tm.firstIndexIn(operand, -1);
673 			this.wrapIndexes.add(wrapBeforeOperator ? indexBefore : indexAfter);
674 			this.secondaryWrapIndexes.add(wrapBeforeOperator ? indexAfter : indexBefore);
675 
676 			if (!this.options.join_wrapped_lines) {
677 				// TODO there should be an option for never joining wraps on opposite side of the operator
678 				if (wrapBeforeOperator) {
679 					if (this.tm.countLineBreaksBetween(this.tm.get(indexAfter - 1), this.tm.get(indexAfter)) > 0)
680 						this.wrapIndexes.add(indexAfter);
681 				} else {
682 					if (this.tm.countLineBreaksBetween(this.tm.get(indexBefore), this.tm.get(indexBefore - 1)) > 0)
683 						this.wrapIndexes.add(indexBefore);
684 				}
685 			}
686 		}
687 	}
688 
samePrecedence(InfixExpression expression1, InfixExpression expression2)689 	private boolean samePrecedence(InfixExpression expression1, InfixExpression expression2) {
690 		Integer precedence1 = OPERATOR_PRECEDENCE.get(expression1.getOperator());
691 		Integer precedence2 = OPERATOR_PRECEDENCE.get(expression2.getOperator());
692 		if (precedence1 == null || precedence2 == null)
693 			return false;
694 		return precedence1.equals(precedence2);
695 	}
696 
697 	@Override
visit(ConditionalExpression node)698 	public boolean visit(ConditionalExpression node) {
699 		boolean chainsMatter = (this.options.alignment_for_conditional_expression_chain
700 				& Alignment.SPLIT_MASK) != Alignment.M_NO_ALIGNMENT;
701 		boolean isNextInChain = node.getParent() instanceof ConditionalExpression
702 				&& node == ((ConditionalExpression) node.getParent()).getElseExpression();
703 		boolean isFirstInChain = node.getElseExpression() instanceof ConditionalExpression && !isNextInChain;
704 		boolean wrapBefore = this.options.wrap_before_conditional_operator;
705 		List<Integer> before = wrapBefore ? this.wrapIndexes : this.secondaryWrapIndexes;
706 		List<Integer> after = wrapBefore ? this.secondaryWrapIndexes : this.wrapIndexes;
707 		if (!chainsMatter || (!isFirstInChain && !isNextInChain)) {
708 			before.add(this.tm.firstIndexAfter(node.getExpression(), TokenNameQUESTION));
709 			before.add(this.tm.firstIndexAfter(node.getThenExpression(), TokenNameCOLON));
710 			after.add(this.tm.firstIndexIn(node.getThenExpression(), -1));
711 			after.add(this.tm.firstIndexIn(node.getElseExpression(), -1));
712 			this.wrapParentIndex = this.tm.lastIndexIn(node.getExpression(), -1);
713 			this.wrapGroupEnd = this.tm.lastIndexIn(node, -1);
714 			handleWrap(this.options.alignment_for_conditional_expression);
715 
716 		} else if (isFirstInChain) {
717 			List<ConditionalExpression> chain = new ArrayList<>();
718 			chain.add(node);
719 			ConditionalExpression next = node;
720 			while (next.getElseExpression() instanceof ConditionalExpression) {
721 				next = (ConditionalExpression) next.getElseExpression();
722 				chain.add(next);
723 			}
724 
725 			for (ConditionalExpression conditional : chain) {
726 				before.add(this.tm.firstIndexAfter(conditional.getThenExpression(), TokenNameCOLON));
727 				after.add(this.tm.firstIndexIn(conditional.getElseExpression(), -1));
728 			}
729 			this.wrapParentIndex = this.tm.firstIndexIn(node.getExpression(), -1);
730 			this.wrapGroupEnd = this.tm.lastIndexIn(node, -1);
731 			handleWrap(this.options.alignment_for_conditional_expression_chain);
732 
733 			this.currentDepth++;
734 			for (ConditionalExpression conditional : chain) {
735 				before.add(this.tm.firstIndexAfter(conditional.getExpression(), TokenNameQUESTION));
736 				after.add(this.tm.firstIndexIn(conditional.getThenExpression(), -1));
737 				this.wrapParentIndex = this.tm.firstIndexIn(conditional.getExpression(), -1);
738 				this.wrapGroupEnd = this.tm.lastIndexIn(conditional.getThenExpression(), -1);
739 				handleWrap(this.options.alignment_for_conditional_expression);
740 			}
741 			this.currentDepth--;
742 		}
743 		return true;
744 	}
745 
746 	@Override
visit(ArrayInitializer node)747 	public boolean visit(ArrayInitializer node) {
748 		List<Expression> expressions = node.expressions();
749 		if (!expressions.isEmpty()) {
750 			prepareElementsList(expressions, TokenNameCOMMA, TokenNameLBRACE);
751 			handleWrap(this.options.alignment_for_expressions_in_array_initializer, node);
752 		}
753 		int openingBraceIndex = this.tm.firstIndexIn(node, TokenNameLBRACE);
754 		Token openingBrace = this.tm.get(openingBraceIndex);
755 		if (openingBrace.isNextLineOnWrap() && openingBrace.getWrapPolicy() == null && openingBraceIndex > 0) {
756 			// add fake wrap policy to make sure the brace indentation is right
757 			openingBrace.setWrapPolicy(new WrapPolicy(WrapMode.DISABLED, openingBraceIndex - 1, 0));
758 		}
759 		if (!this.options.join_wrapped_lines
760 				&& !this.options.insert_new_line_before_closing_brace_in_array_initializer) {
761 			// if there is a line break before the closing brace, formatter should treat it as a valid wrap to preserve
762 			int closingBraceIndex = this.tm.lastIndexIn(node, TokenNameRBRACE);
763 			Token closingBrace = this.tm.get(closingBraceIndex);
764 			if (this.tm.countLineBreaksBetween(this.tm.get(closingBraceIndex - 1), closingBrace) == 1) {
765 				closingBrace.setWrapPolicy(new WrapPolicy(WrapMode.WHERE_NECESSARY, openingBraceIndex,
766 						closingBraceIndex, 0, this.currentDepth, 1, true, false));
767 			}
768 		}
769 		return true;
770 	}
771 
772 	@Override
visit(Assignment node)773 	public boolean visit(Assignment node) {
774 		int rightSideIndex = this.tm.firstIndexIn(node.getRightHandSide(), -1);
775 		if (this.tm.get(rightSideIndex).getLineBreaksBefore() > 0)
776 			return true; // must be an array initializer in new line because of brace_position_for_array_initializer
777 
778 		int operatorIndex = this.tm.firstIndexBefore(node.getRightHandSide(), -1);
779 		while (this.tm.get(operatorIndex).isComment())
780 			operatorIndex--;
781 		assert node.getOperator().toString().equals(this.tm.toString(operatorIndex));
782 
783 		this.wrapIndexes.add(this.options.wrap_before_assignment_operator ? operatorIndex : rightSideIndex);
784 		this.secondaryWrapIndexes.add(this.options.wrap_before_assignment_operator ? rightSideIndex : operatorIndex);
785 		this.wrapParentIndex = operatorIndex - 1;
786 		this.wrapGroupEnd = this.tm.lastIndexIn(node.getRightHandSide(), -1);
787 		handleWrap(this.options.alignment_for_assignment);
788 		return true;
789 	}
790 
791 	@Override
visit(VariableDeclarationFragment node)792 	public boolean visit(VariableDeclarationFragment node) {
793 		if (node.getInitializer() == null)
794 			return true;
795 		int rightSideIndex = this.tm.firstIndexIn(node.getInitializer(), -1);
796 		if (this.tm.get(rightSideIndex).getLineBreaksBefore() > 0)
797 			return true; // must be an array initializer in new line because of brace_position_for_array_initializer
798 		int equalIndex = this.tm.firstIndexBefore(node.getInitializer(), TokenNameEQUAL);
799 
800 		this.wrapIndexes.add(this.options.wrap_before_assignment_operator ? equalIndex : rightSideIndex);
801 		this.secondaryWrapIndexes.add(this.options.wrap_before_assignment_operator ? rightSideIndex : equalIndex);
802 		this.wrapParentIndex = equalIndex - 1;
803 		this.wrapGroupEnd = this.tm.lastIndexIn(node.getInitializer(), -1);
804 		handleWrap(this.options.alignment_for_assignment);
805 		return true;
806 	}
807 
808 	@Override
visit(IfStatement node)809 	public boolean visit(IfStatement node) {
810 		int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN);
811 		int rParen = this.tm.firstIndexAfter(node.getExpression(), TokenNameRPAREN);
812 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_if_while_statement);
813 
814 		Statement elseStatement = node.getElseStatement();
815 		boolean keepThenOnSameLine = this.options.keep_then_statement_on_same_line
816 				|| (this.options.keep_simple_if_on_one_line && elseStatement == null);
817 		if (keepThenOnSameLine)
818 			handleSimpleLoop(node.getThenStatement(), this.options.alignment_for_compact_if);
819 
820 		if (this.options.keep_else_statement_on_same_line && elseStatement != null)
821 			handleSimpleLoop(elseStatement, this.options.alignment_for_compact_if);
822 		return true;
823 	}
824 
825 	@Override
visit(ForStatement node)826 	public boolean visit(ForStatement node) {
827 		int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN);
828 		int rParen = this.tm.firstIndexBefore(node.getBody(), TokenNameRPAREN);
829 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_for_statement);
830 
831 		List<Expression> initializers = node.initializers();
832 		if (!initializers.isEmpty())
833 			this.wrapIndexes.add(this.tm.firstIndexIn(initializers.get(0), -1));
834 		if (node.getExpression() != null)
835 			this.wrapIndexes.add(this.tm.firstIndexIn(node.getExpression(), -1));
836 		List<Expression> updaters = node.updaters();
837 		if (!updaters.isEmpty())
838 			this.wrapIndexes.add(this.tm.firstIndexIn(updaters.get(0), -1));
839 		if (!this.wrapIndexes.isEmpty()) {
840 			this.wrapParentIndex = lParen;
841 			this.wrapGroupEnd = rParen;
842 			handleWrap(this.options.alignment_for_expressions_in_for_loop_header);
843 		}
844 		if (this.options.keep_simple_for_body_on_same_line)
845 			handleSimpleLoop(node.getBody(), this.options.alignment_for_compact_loop);
846 		return true;
847 	}
848 
849 	@Override
visit(EnhancedForStatement node)850 	public boolean visit(EnhancedForStatement node) {
851 		int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN);
852 		int rParen = this.tm.firstIndexBefore(node.getBody(), TokenNameRPAREN);
853 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_for_statement);
854 
855 		if (this.options.keep_simple_for_body_on_same_line)
856 			handleSimpleLoop(node.getBody(), this.options.alignment_for_compact_loop);
857 		return true;
858 	}
859 
860 	@Override
visit(WhileStatement node)861 	public boolean visit(WhileStatement node) {
862 		int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN);
863 		int rParen = this.tm.firstIndexAfter(node.getExpression(), TokenNameRPAREN);
864 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_if_while_statement);
865 
866 		if (this.options.keep_simple_while_body_on_same_line)
867 			handleSimpleLoop(node.getBody(), this.options.alignment_for_compact_loop);
868 		return true;
869 	}
870 
handleSimpleLoop(Statement body, int wrappingOption)871 	private void handleSimpleLoop(Statement body, int wrappingOption) {
872 		if (!(body instanceof Block)) {
873 			this.wrapIndexes.add(this.tm.firstIndexIn(body, -1));
874 			this.wrapParentIndex = this.tm.firstIndexBefore(body, TokenNameRPAREN);
875 			this.wrapGroupEnd = this.tm.lastIndexIn(body, -1);
876 			handleWrap(wrappingOption, body.getParent());
877 
878 			body.accept(new ASTVisitor() {
879 				@Override
880 				public boolean visit(Block node) {
881 					forceContinuousWrapping(node, WrapPreparator.this.tm.firstIndexIn(node, -1));
882 					return false;
883 				}
884 			});
885 		}
886 	}
887 
888 	@Override
endVisit(DoStatement node)889 	public void endVisit(DoStatement node) {
890 		if (this.options.keep_simple_do_while_body_on_same_line && !(node.getBody() instanceof Block)) {
891 			int whileIndex = this.tm.firstIndexAfter(node.getBody(), TokenNamewhile);
892 			this.wrapIndexes.add(whileIndex);
893 			this.wrapParentIndex = this.tm.lastIndexIn(node.getBody(), -1);
894 			this.wrapGroupEnd = this.tm.lastIndexIn(node, -1);
895 
896 			int alignment = this.options.alignment_for_compact_loop;
897 			for (int i = this.tm.firstIndexIn(node, -1) + 1; i < whileIndex; i++) {
898 				Token token = this.tm.get(i);
899 				if (token.getLineBreaksBefore() > 0 || token.getLineBreaksAfter() > 0)
900 					alignment |= Alignment.M_FORCE;
901 			}
902 			handleWrap(alignment, node);
903 		}
904 	}
905 
906 	@Override
visit(TryStatement node)907 	public boolean visit(TryStatement node) {
908 		if (!node.resources().isEmpty()) {
909 			int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN);
910 			int rParen = this.tm.firstIndexBefore(node.getBody(), TokenNameRPAREN);
911 			handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_try_clause);
912 		}
913 		prepareElementsList(node.resources(), TokenNameSEMICOLON, TokenNameLPAREN);
914 		handleWrap(this.options.alignment_for_resources_in_try);
915 		return true;
916 	}
917 
918 	@Override
visit(UnionType node)919 	public boolean visit(UnionType node) {
920 		List<Type> types = node.types();
921 		if (types.isEmpty())
922 			return true;
923 		if (this.options.wrap_before_or_operator_multicatch) {
924 			for (Type type : types) {
925 				if (this.wrapIndexes.isEmpty()) {
926 					this.wrapIndexes.add(this.tm.firstIndexIn(type, -1));
927 				} else {
928 					this.wrapIndexes.add(this.tm.firstIndexBefore(type, TokenNameOR));
929 					this.secondaryWrapIndexes.add(this.tm.firstIndexIn(type, -1));
930 				}
931 			}
932 			this.wrapParentIndex = this.tm.firstIndexBefore(node, -1);
933 			while (this.tm.get(this.wrapParentIndex).isComment())
934 				this.wrapParentIndex--;
935 			this.wrapGroupEnd = this.tm.lastIndexIn(types.get(types.size() - 1), -1);
936 			handleWrap(this.options.alignment_for_union_type_in_multicatch);
937 		} else {
938 			prepareElementsList(types, TokenNameOR, TokenNameLPAREN);
939 			handleWrap(this.options.alignment_for_union_type_in_multicatch);
940 		}
941 		return true;
942 	}
943 
944 	@Override
visit(LambdaExpression node)945 	public boolean visit(LambdaExpression node) {
946 		int lParen = this.tm.firstIndexIn(node, -1);
947 		if (this.tm.get(lParen).tokenType == TokenNameLPAREN) {
948 			int rParen = this.tm.firstIndexBefore(node.getBody(), TokenNameRPAREN);
949 			handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_lambda_declaration);
950 		}
951 		if (node.getBody() instanceof Block) {
952 			forceContinuousWrapping(node.getBody(), this.tm.firstIndexIn(node, -1));
953 
954 			List<Statement> statements = ((Block) node.getBody()).statements();
955 			if (!statements.isEmpty()) {
956 				int openBraceIndex = this.tm.firstIndexBefore(statements.get(0), TokenNameLBRACE);
957 				int closeBraceIndex = this.tm.firstIndexAfter(statements.get(statements.size() - 1), TokenNameRBRACE);
958 				boolean areKeptOnOneLine = statements.stream()
959 						.allMatch(n -> this.tm.firstTokenIn(n, -1).getLineBreaksBefore() == 0);
960 				if (areKeptOnOneLine) {
961 					for (Statement statement : statements)
962 						this.wrapIndexes.add(this.tm.firstIndexIn(statement, -1));
963 					this.wrapParentIndex = openBraceIndex;
964 					this.wrapGroupEnd = closeBraceIndex;
965 					handleWrap(Alignment.M_ONE_PER_LINE_SPLIT, node);
966 					this.tm.get(closeBraceIndex).setWrapPolicy(new WrapPolicy(WrapMode.TOP_PRIORITY, openBraceIndex,
967 							closeBraceIndex, 0, this.currentDepth, 1, false, false));
968 				}
969 			}
970 		}
971 		if (node.hasParentheses()) {
972 			List<VariableDeclaration> parameters = node.parameters();
973 			// the legacy formatter didn't like wrapping lambda parameters, so neither do we
974 			this.currentDepth++;
975 			handleArguments(parameters, this.options.alignment_for_parameters_in_method_declaration);
976 			this.currentDepth--;
977 		}
978 		return true;
979 	}
980 
981 	@Override
visit(FieldDeclaration node)982 	public boolean visit(FieldDeclaration node) {
983 		handleVariableDeclarations(node.fragments());
984 		return true;
985 	}
986 
987 	@Override
visit(VariableDeclarationStatement node)988 	public boolean visit(VariableDeclarationStatement node) {
989 		handleVariableDeclarations(node.fragments());
990 		return true;
991 	}
992 
993 	@Override
visit(ParameterizedType node)994 	public boolean visit(ParameterizedType node) {
995 		prepareElementsList(node.typeArguments(), TokenNameCOMMA, TokenNameLESS);
996 		handleWrap(this.options.alignment_for_parameterized_type_references);
997 		return true;
998 	}
999 
1000 	@Override
visit(TypeMethodReference node)1001 	public boolean visit(TypeMethodReference node) {
1002 		handleTypeArguments(node.typeArguments());
1003 		return true;
1004 	}
1005 
1006 	@Override
visit(ExpressionMethodReference node)1007 	public boolean visit(ExpressionMethodReference node) {
1008 		handleTypeArguments(node.typeArguments());
1009 		return true;
1010 	}
1011 
1012 	@Override
visit(SuperMethodReference node)1013 	public boolean visit(SuperMethodReference node) {
1014 		handleTypeArguments(node.typeArguments());
1015 		return true;
1016 	}
1017 
1018 	@Override
visit(CreationReference node)1019 	public boolean visit(CreationReference node) {
1020 		handleTypeArguments(node.typeArguments());
1021 		return true;
1022 	}
1023 
handleTypeArguments(List<Type> typeArguments)1024 	private void handleTypeArguments(List<Type> typeArguments) {
1025 		if (typeArguments.isEmpty())
1026 			return;
1027 		prepareElementsList(typeArguments, TokenNameCOMMA, TokenNameLESS);
1028 		handleWrap(this.options.alignment_for_type_arguments);
1029 	}
1030 
1031 	@Override
visit(ExportsDirective node)1032 	public boolean visit(ExportsDirective node) {
1033 		handleModuleStatement(node.modules(), TokenNameto);
1034 		return true;
1035 	}
1036 
1037 	@Override
visit(OpensDirective node)1038 	public boolean visit(OpensDirective node) {
1039 		handleModuleStatement(node.modules(), TokenNameto);
1040 		return true;
1041 	}
1042 
1043 	@Override
visit(ProvidesDirective node)1044 	public boolean visit(ProvidesDirective node) {
1045 		handleModuleStatement(node.implementations(), TokenNamewith);
1046 		return true;
1047 	}
1048 
handleModuleStatement(List<Name> names, int joiningTokenType)1049 	private void handleModuleStatement(List<Name> names, int joiningTokenType) {
1050 		if (names.isEmpty())
1051 			return;
1052 		int joiningTokenIndex = this.tm.firstIndexBefore(names.get(0), joiningTokenType);
1053 		this.wrapParentIndex = this.tm.firstIndexBefore(names.get(0), TokenNameIdentifier);
1054 		this.wrapIndexes.add(joiningTokenIndex);
1055 		prepareElementsList(names, TokenNameCOMMA, -1);
1056 		handleWrap(this.options.alignment_for_module_statements, PREFERRED);
1057 	}
1058 
1059 	@Override
visit(CatchClause node)1060 	public boolean visit(CatchClause node) {
1061 		int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN);
1062 		int rParen = this.tm.firstIndexBefore(node.getBody(), TokenNameRPAREN);
1063 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_catch_clause);
1064 		return true;
1065 	}
1066 
1067 	@Override
visit(SwitchStatement node)1068 	public boolean visit(SwitchStatement node) {
1069 		int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN);
1070 		int rParen = this.tm.firstIndexAfter(node.getExpression(), TokenNameRPAREN);
1071 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_switch_statement);
1072 		return true;
1073 	}
1074 
1075 	@Override
visit(SwitchExpression node)1076 	public boolean visit(SwitchExpression node) {
1077 		int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN);
1078 		int rParen = this.tm.firstIndexAfter(node.getExpression(), TokenNameRPAREN);
1079 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_switch_statement);
1080 		return true;
1081 	}
1082 
1083 	@Override
visit(DoStatement node)1084 	public boolean visit(DoStatement node) {
1085 		int lParen = this.tm.firstIndexBefore(node.getExpression(), TokenNameLPAREN);
1086 		int rParen = this.tm.firstIndexAfter(node.getExpression(), TokenNameRPAREN);
1087 		handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_if_while_statement);
1088 		return true;
1089 	}
1090 
1091 	/**
1092 	 * Makes sure all new lines within given node will have wrap policy so that
1093 	 * wrap executor will fix their indentation if necessary.
1094 	 */
forceContinuousWrapping(ASTNode node, int parentIndex)1095 	void forceContinuousWrapping(ASTNode node, int parentIndex) {
1096 		int parentIndent = this.tm.get(parentIndex).getIndent();
1097 		int indentChange = -parentIndent;
1098 		int lineStart = this.tm.findFirstTokenInLine(parentIndex);
1099 		for (int i = parentIndex; i >= lineStart; i--) {
1100 			int align = this.tm.get(i).getAlign();
1101 			if (align > 0) {
1102 				indentChange = -2 * parentIndent + align;
1103 				break;
1104 			}
1105 		}
1106 
1107 		Token previous = null;
1108 		int from = this.tm.firstIndexIn(node, -1);
1109 		int to = this.tm.lastIndexIn(node, -1);
1110 		for (int i = from; i <= to; i++) {
1111 			Token token = this.tm.get(i);
1112 			if ((token.getLineBreaksBefore() > 0 || (previous != null && previous.getLineBreaksAfter() > 0))
1113 					&& (token.getWrapPolicy() == null || token.getWrapPolicy().wrapMode == WrapMode.BLOCK_INDENT)) {
1114 				int extraIndent = token.getIndent() + indentChange;
1115 				token.setWrapPolicy(new WrapPolicy(WrapMode.BLOCK_INDENT, parentIndex, extraIndent));
1116 				token.setIndent(parentIndent + extraIndent);
1117 			}
1118 			previous = token;
1119 		}
1120 	}
1121 
handleVariableDeclarations(List<VariableDeclarationFragment> fragments)1122 	private void handleVariableDeclarations(List<VariableDeclarationFragment> fragments) {
1123 		if (fragments.size() > 1) {
1124 			this.wrapParentIndex = this.tm.firstIndexIn(fragments.get(0), -1);
1125 			prepareElementsList(fragments, TokenNameCOMMA, -1);
1126 			this.wrapIndexes.remove(0);
1127 			handleWrap(this.options.alignment_for_multiple_fields);
1128 		}
1129 	}
1130 
handleArguments(List<? extends ASTNode> arguments, int wrappingOption)1131 	private void handleArguments(List<? extends ASTNode> arguments, int wrappingOption) {
1132 		this.wrapPenalties.add(1 / PREFERRED);
1133 		prepareElementsList(arguments, TokenNameCOMMA, TokenNameLPAREN);
1134 		handleWrap(wrappingOption);
1135 	}
1136 
prepareElementsList(List<? extends ASTNode> elements, int separatorType, int wrapParentType)1137 	private void prepareElementsList(List<? extends ASTNode> elements, int separatorType, int wrapParentType) {
1138 		for (int i = 0; i < elements.size(); i++) {
1139 			ASTNode element = elements.get(i);
1140 			this.wrapIndexes.add(this.tm.firstIndexIn(element, -1));
1141 			if (i > 0)
1142 				this.secondaryWrapIndexes.add(this.tm.firstIndexBefore(element, separatorType));
1143 		}
1144 		// wrapIndexes may have been filled with additional values even if arguments is empty
1145 		if (!this.wrapIndexes.isEmpty()) {
1146 			Token firstToken = this.tm.get(this.wrapIndexes.get(0));
1147 			if (this.wrapParentIndex < 0)
1148 				this.wrapParentIndex = this.tm.findIndex(firstToken.originalStart - 1, wrapParentType, false);
1149 			if (!elements.isEmpty() && this.wrapGroupEnd < 0)
1150 				this.wrapGroupEnd = this.tm.lastIndexIn(elements.get(elements.size() - 1), -1);
1151 		}
1152 	}
1153 
handleWrap(int wrappingOption)1154 	private void handleWrap(int wrappingOption) {
1155 		handleWrap(wrappingOption, null);
1156 	}
1157 
handleWrap(int wrappingOption, float firstPenaltyMultiplier)1158 	private void handleWrap(int wrappingOption, float firstPenaltyMultiplier) {
1159 		this.wrapPenalties.add(firstPenaltyMultiplier);
1160 		handleWrap(wrappingOption, null);
1161 	}
1162 
handleWrap(int wrappingOption, ASTNode parentNode)1163 	private void handleWrap(int wrappingOption, ASTNode parentNode) {
1164 		handleWrap(wrappingOption, true, parentNode);
1165 	}
1166 
handleWrap(int wrappingOption, boolean wrapPreceedingComments, ASTNode parentNode)1167 	private void handleWrap(int wrappingOption, boolean wrapPreceedingComments, ASTNode parentNode) {
1168 		doHandleWrap(wrappingOption, wrapPreceedingComments, parentNode);
1169 		this.wrapIndexes.clear();
1170 		this.secondaryWrapIndexes.clear();
1171 		this.wrapPenalties.clear();
1172 		this.wrapParentIndex = this.wrapGroupEnd = -1;
1173 	}
1174 
doHandleWrap(int wrappingOption, boolean wrapPreceedingComments, ASTNode parentNode)1175 	private void doHandleWrap(int wrappingOption, boolean wrapPreceedingComments, ASTNode parentNode) {
1176 		if (this.wrapIndexes.isEmpty())
1177 			return;
1178 		assert this.wrapParentIndex >= 0 && this.wrapParentIndex < this.wrapIndexes.get(0);
1179 		assert this.wrapGroupEnd >= this.wrapIndexes.get(this.wrapIndexes.size() - 1);
1180 
1181 		while (this.tm.get(this.wrapParentIndex).isComment() && this.wrapParentIndex > 0)
1182 			this.wrapParentIndex--;
1183 
1184 		float penalty = this.wrapPenalties.isEmpty() ? 1 : this.wrapPenalties.get(0);
1185 		WrapPolicy policy = getWrapPolicy(wrappingOption, penalty, true, parentNode);
1186 
1187 		WrapPolicy existing = this.tm.get(this.wrapIndexes.get(0)).getWrapPolicy();
1188 		if (existing != null && existing.wrapMode == WrapMode.TOP_PRIORITY) {
1189 			// SEPARATE_LINES_IF_WRAPPED
1190 			assert existing.wrapParentIndex == this.wrapParentIndex;
1191 			this.wrapGroupEnd = existing.groupEndIndex;
1192 			policy = new WrapPolicy(WrapMode.TOP_PRIORITY, policy.wrapParentIndex, this.wrapGroupEnd, policy.extraIndent,
1193 					policy.structureDepth, policy.penaltyMultiplier, true, policy.indentOnColumn);
1194 		}
1195 
1196 		setTokenWrapPolicy(0, policy, true);
1197 
1198 		for (int i = 1; i < this.wrapIndexes.size(); i++) {
1199 			penalty = this.wrapPenalties.size() > i ? this.wrapPenalties.get(i) : 1;
1200 			if (penalty != policy.penaltyMultiplier || i == 1)
1201 				policy = getWrapPolicy(wrappingOption, penalty, false, parentNode);
1202 			setTokenWrapPolicy(i, policy, wrapPreceedingComments);
1203 		}
1204 
1205 		if (!this.secondaryWrapIndexes.isEmpty()) {
1206 			int optionNoAlignment = (wrappingOption & ~Alignment.SPLIT_MASK) | Alignment.M_NO_ALIGNMENT;
1207 			policy = getWrapPolicy(optionNoAlignment, 1, false, parentNode);
1208 			for (int index : this.secondaryWrapIndexes) {
1209 				Token token = this.tm.get(index);
1210 				if (token.getWrapPolicy() == null)
1211 					token.setWrapPolicy(policy);
1212 			}
1213 		}
1214 	}
1215 
setTokenWrapPolicy(int wrapIndexesIndex, WrapPolicy policy, boolean wrapPreceedingComments)1216 	private void setTokenWrapPolicy(int wrapIndexesIndex, WrapPolicy policy, boolean wrapPreceedingComments) {
1217 		int index = this.wrapIndexes.get(wrapIndexesIndex);
1218 		if (wrapPreceedingComments) {
1219 			for (int i = index - 1; i >= 0; i--) {
1220 				Token previous = this.tm.get(i);
1221 				if (!previous.isComment())
1222 					break;
1223 				if (previous.getWrapPolicy() == WrapPolicy.FORCE_FIRST_COLUMN)
1224 					break;
1225 				if (previous.getLineBreaksAfter() == 0 && i == index - 1)
1226 					index = i;
1227 				if (previous.getLineBreaksBefore() > 0)
1228 					previous.setWrapPolicy(policy);
1229 			}
1230 			this.wrapIndexes.set(wrapIndexesIndex, index);
1231 		}
1232 
1233 		Token token = this.tm.get(index);
1234 		if (token.getWrapPolicy() == WrapPolicy.DISABLE_WRAP)
1235 			return;
1236 
1237 		token.setWrapPolicy(policy);
1238 		if (policy.wrapMode == WrapMode.FORCE) {
1239 			token.breakBefore();
1240 		} else if (this.options.join_wrapped_lines && token.tokenType == TokenNameCOMMENT_BLOCK) {
1241 			// allow wrap preparator to decide if this comment should be wrapped
1242 			token.clearLineBreaksBefore();
1243 		}
1244 	}
1245 
getWrapPolicy(int wrappingOption, float penaltyMultiplier, boolean isFirst, ASTNode parentNode)1246 	private WrapPolicy getWrapPolicy(int wrappingOption, float penaltyMultiplier, boolean isFirst, ASTNode parentNode) {
1247 		assert this.wrapParentIndex >= 0 && this.wrapGroupEnd >= 0;
1248 		int extraIndent = this.options.continuation_indentation;
1249 		boolean indentOnColumn = (wrappingOption & Alignment.M_INDENT_ON_COLUMN) != 0;
1250 		boolean isForceWrap = (wrappingOption & Alignment.M_FORCE) != 0;
1251 		boolean isAlreadyWrapped = false;
1252 		if (indentOnColumn) {
1253 			extraIndent = 0;
1254 		} else if (parentNode instanceof EnumDeclaration) {
1255 			// special behavior for compatibility with legacy formatter
1256 			extraIndent = ((wrappingOption & Alignment.M_INDENT_BY_ONE) != 0) ? 2 : 1;
1257 			if (!this.options.indent_body_declarations_compare_to_enum_declaration_header)
1258 				extraIndent--;
1259 			isAlreadyWrapped = isFirst;
1260 		} else if (parentNode instanceof IfStatement || parentNode instanceof ForStatement
1261 				|| parentNode instanceof EnhancedForStatement || parentNode instanceof WhileStatement) {
1262 			extraIndent = 1;
1263 			this.wrapParentIndex = this.tm.firstIndexIn(parentNode, -1); // only if !indoentOnColumn
1264 		} else if (parentNode instanceof DoStatement) {
1265 			extraIndent = 0;
1266 			this.wrapParentIndex = this.tm.firstIndexIn(parentNode, -1); // only if !indoentOnColumn
1267 		} else if (parentNode instanceof LambdaExpression) {
1268 			extraIndent = 1;
1269 		} else if ((wrappingOption & Alignment.M_INDENT_BY_ONE) != 0) {
1270 			extraIndent = 1;
1271 		} else if (parentNode instanceof ArrayInitializer) {
1272 			extraIndent = this.options.continuation_indentation_for_array_initializer;
1273 			isAlreadyWrapped = isFirst && this.options.insert_new_line_after_opening_brace_in_array_initializer;
1274 		}
1275 
1276 		WrapMode wrapMode = WrapMode.WHERE_NECESSARY;
1277 		boolean isTopPriority = false;
1278 		switch (wrappingOption & Alignment.SPLIT_MASK) {
1279 			case Alignment.M_NO_ALIGNMENT:
1280 				wrapMode = WrapMode.DISABLED;
1281 				isForceWrap = false;
1282 				break;
1283 			case Alignment.M_COMPACT_FIRST_BREAK_SPLIT:
1284 				isTopPriority = isFirst;
1285 				isForceWrap &= isFirst;
1286 				break;
1287 			case Alignment.M_ONE_PER_LINE_SPLIT:
1288 				isTopPriority = true;
1289 				break;
1290 			case Alignment.M_NEXT_SHIFTED_SPLIT:
1291 				isTopPriority = true;
1292 				if (!isFirst)
1293 					extraIndent++;
1294 				break;
1295 			case Alignment.M_NEXT_PER_LINE_SPLIT:
1296 				isTopPriority = !isFirst;
1297 				isForceWrap &= !isFirst;
1298 				break;
1299 		}
1300 
1301 		if (isForceWrap) {
1302 			wrapMode = WrapMode.FORCE;
1303 		} else if (isAlreadyWrapped) {
1304 			wrapMode = WrapMode.DISABLED; // to avoid triggering top priority wrapping
1305 		} else if (isTopPriority) {
1306 			wrapMode = WrapMode.TOP_PRIORITY;
1307 		}
1308 		extraIndent *= this.options.indentation_size;
1309 		return new WrapPolicy(wrapMode, this.wrapParentIndex, this.wrapGroupEnd, extraIndent, this.currentDepth,
1310 				penaltyMultiplier, isFirst, indentOnColumn);
1311 	}
1312 
finishUp(ASTNode astRoot, List<IRegion> regions)1313 	public void finishUp(ASTNode astRoot, List<IRegion> regions) {
1314 		preserveExistingLineBreaks();
1315 		applyBreaksOutsideRegions(regions);
1316 		new WrapExecutor(this.tm, this.options, regions).executeWraps();
1317 		this.aligner.alignComments();
1318 		wrapComments();
1319 		fixEnumConstantIndents(astRoot);
1320 	}
1321 
preserveExistingLineBreaks()1322 	private void preserveExistingLineBreaks() {
1323 		// normally n empty lines = n+1 line breaks, but not at the file start and end
1324 		Token first = this.tm.get(0);
1325 		int startingBreaks = first.getLineBreaksBefore();
1326 		first.clearLineBreaksBefore();
1327 		first.putLineBreaksBefore(startingBreaks - 1);
1328 
1329 		this.tm.traverse(0, new TokenTraverser() {
1330 			boolean join_wrapped_lines = WrapPreparator.this.options.join_wrapped_lines;
1331 
1332 			@Override
1333 			protected boolean token(Token token, int index) {
1334 				int lineBreaks = getLineBreaksToPreserve(getPrevious(), token);
1335 				if (lineBreaks > 1 || (!this.join_wrapped_lines && token.isWrappable()) || index == 0)
1336 					token.putLineBreaksBefore(lineBreaks);
1337 				return true;
1338 			}
1339 
1340 		});
1341 
1342 		Token last = this.tm.get(this.tm.size() - 1);
1343 		last.clearLineBreaksAfter();
1344 		int endingBreaks = getLineBreaksToPreserve(last, null);
1345 		if (endingBreaks > 0) {
1346 			last.putLineBreaksAfter(endingBreaks);
1347 		} else if ((this.kind & (CodeFormatter.K_COMPILATION_UNIT | CodeFormatter.K_MODULE_INFO)) != 0
1348 				&& this.options.insert_new_line_at_end_of_file_if_missing) {
1349 			last.breakAfter();
1350 		}
1351 	}
1352 
getLineBreaksToPreserve(Token token1, Token token2)1353 	int getLineBreaksToPreserve(Token token1, Token token2) {
1354 		if ((token1 != null && !token1.isPreserveLineBreaksAfter())
1355 				|| (token2 != null && !token2.isPreserveLineBreaksBefore())) {
1356 			return 0;
1357 		}
1358 		if (token1 != null) {
1359 			List<Token> structure = token1.getInternalStructure();
1360 			if (structure != null && !structure.isEmpty())
1361 				token1 = structure.get(structure.size() - 1);
1362 		}
1363 		if (token2 != null) {
1364 			List<Token> structure = token2.getInternalStructure();
1365 			if (structure != null && !structure.isEmpty())
1366 				token2 = structure.get(0);
1367 		}
1368 		int lineBreaks = WrapPreparator.this.tm.countLineBreaksBetween(token1, token2);
1369 		int toPreserve = this.options.number_of_empty_lines_to_preserve;
1370 		if (token1 != null && token2 != null)
1371 			toPreserve++; // n empty lines = n+1 line breaks, except for file start and end
1372 		return Math.min(lineBreaks, toPreserve);
1373 	}
1374 
applyBreaksOutsideRegions(List<IRegion> regions)1375 	private void applyBreaksOutsideRegions(List<IRegion> regions) {
1376 		String source = this.tm.getSource();
1377 		int previousRegionEnd = 0;
1378 		for (IRegion region : regions) {
1379 			int index = this.tm.findIndex(previousRegionEnd, -1, true);
1380 			Token token = this.tm.get(index);
1381 			if (this.tm.countLineBreaksBetween(source, previousRegionEnd,
1382 					Math.min(token.originalStart, region.getOffset())) > 0)
1383 				token.breakBefore();
1384 			for (index++; index < this.tm.size(); index++) {
1385 				Token next = this.tm.get(index);
1386 				if (next.originalStart > region.getOffset()) {
1387 					if (this.tm.countLineBreaksBetween(source, token.originalEnd, region.getOffset()) > 0)
1388 						next.breakBefore();
1389 					break;
1390 				}
1391 				if (this.tm.countLineBreaksBetween(token, next) > 0)
1392 					next.breakBefore();
1393 				token = next;
1394 			}
1395 			previousRegionEnd = region.getOffset() + region.getLength() - 1;
1396 		}
1397 	}
1398 
wrapComments()1399 	private void wrapComments() {
1400 		CommentWrapExecutor commentWrapper = new CommentWrapExecutor(this.tm, this.options);
1401 		boolean isNLSTagInLine = false;
1402 		for (int i = 0; i < this.tm.size(); i++) {
1403 			Token token = this.tm.get(i);
1404 			if (token.getLineBreaksBefore() > 0 || token.getLineBreaksAfter() > 0)
1405 				isNLSTagInLine = false;
1406 			if (token.hasNLSTag()) {
1407 				assert token.tokenType == TokenNameStringLiteral;
1408 				isNLSTagInLine = true;
1409 			}
1410 			List<Token> structure = token.getInternalStructure();
1411 			if (token.isComment() && structure != null && !structure.isEmpty() && !isNLSTagInLine) {
1412 				int startPosition = this.tm.getPositionInLine(i);
1413 				if (token.tokenType == TokenNameCOMMENT_LINE) {
1414 					commentWrapper.wrapLineComment(token, startPosition);
1415 				} else {
1416 					assert token.tokenType == TokenNameCOMMENT_BLOCK || token.tokenType == TokenNameCOMMENT_JAVADOC;
1417 					commentWrapper.wrapMultiLineComment(token, startPosition, false, false);
1418 				}
1419 			}
1420 		}
1421 	}
1422 
fixEnumConstantIndents(ASTNode astRoot)1423 	private void fixEnumConstantIndents(ASTNode astRoot) {
1424 		if (this.options.use_tabs_only_for_leading_indentations) {
1425 			// enum constants should be indented like other declarations, not like wrapped elements
1426 			astRoot.accept(new ASTVisitor() {
1427 
1428 				@Override
1429 				public boolean visit(EnumConstantDeclaration node) {
1430 					WrapPreparator.this.tm.firstTokenIn(node, -1).setWrapPolicy(null);
1431 					return true;
1432 				}
1433 			});
1434 		}
1435 	}
1436 
handleParenthesesPositions(int openingParenIndex, int closingParenIndex, String positionsSetting)1437 	private void handleParenthesesPositions(int openingParenIndex, int closingParenIndex, String positionsSetting) {
1438 		boolean isEmpty = openingParenIndex + 1 == closingParenIndex;
1439 		switch (positionsSetting) {
1440 			case DefaultCodeFormatterConstants.COMMON_LINES:
1441 				// nothing to do
1442 				break;
1443 			case DefaultCodeFormatterConstants.SEPARATE_LINES_IF_WRAPPED:
1444 				if (isEmpty)
1445 					break;
1446 				this.tm.get(openingParenIndex + 1).setWrapPolicy(new WrapPolicy(WrapMode.TOP_PRIORITY,
1447 						openingParenIndex, closingParenIndex, this.options.indentation_size, 1, 1, true, false));
1448 				this.tm.get(closingParenIndex).setWrapPolicy(new WrapPolicy(WrapMode.TOP_PRIORITY,
1449 						openingParenIndex, closingParenIndex, 0, 1, 1, false, false));
1450 				break;
1451 			case DefaultCodeFormatterConstants.SEPARATE_LINES_IF_NOT_EMPTY:
1452 				if (isEmpty)
1453 					break;
1454 				//$FALL-THROUGH$
1455 			case DefaultCodeFormatterConstants.SEPARATE_LINES:
1456 			case DefaultCodeFormatterConstants.PRESERVE_POSITIONS:
1457 				boolean always = !positionsSetting.equals(DefaultCodeFormatterConstants.PRESERVE_POSITIONS);
1458 				Token afterOpening = this.tm.get(openingParenIndex + 1);
1459 				if (always || this.tm.countLineBreaksBetween(this.tm.get(openingParenIndex), afterOpening) > 0) {
1460 					afterOpening.setWrapPolicy(
1461 							new WrapPolicy(WrapMode.WHERE_NECESSARY, openingParenIndex, this.options.indentation_size));
1462 					afterOpening.breakBefore();
1463 				}
1464 				Token closingParen = this.tm.get(closingParenIndex);
1465 				if (always || this.tm.countLineBreaksBetween(this.tm.get(closingParenIndex - 1), closingParen) > 0) {
1466 					closingParen.setWrapPolicy(new WrapPolicy(WrapMode.WHERE_NECESSARY, openingParenIndex, 0));
1467 					closingParen.breakBefore();
1468 				}
1469 				break;
1470 			default:
1471 				throw new IllegalArgumentException("Unrecognized parentheses positions setting: " + positionsSetting); //$NON-NLS-1$
1472 		}
1473 	}
1474 }
1475