| /******************************************************************************* |
| * Copyright (c) 2014, 2019 Mateusz Matela and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * 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 |
| * Mateusz Matela <mateusz.matela@gmail.com> - [formatter] follow up bug for comments - https://bugs.eclipse.org/458208 |
| *******************************************************************************/ |
| 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; |
| import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameDOT; |
| import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameEQUAL; |
| import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameIdentifier; |
| import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameLBRACE; |
| import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameLESS; |
| import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameLPAREN; |
| import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameOR; |
| 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.TokenNameenum; |
| 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.TokenNamenew; |
| import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameplayedBy; |
| 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 static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameto; |
| import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamewhile; |
| import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamewith; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.function.Predicate; |
| import java.util.function.ToIntFunction; |
| |
| 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; |
| import org.eclipse.jdt.core.dom.BaseCallMessageSend; |
| import org.eclipse.jdt.core.dom.Block; |
| import org.eclipse.jdt.core.dom.CatchClause; |
| import org.eclipse.jdt.core.dom.ClassInstanceCreation; |
| import org.eclipse.jdt.core.dom.ConditionalExpression; |
| import org.eclipse.jdt.core.dom.ConstructorInvocation; |
| import org.eclipse.jdt.core.dom.CreationReference; |
| import org.eclipse.jdt.core.dom.DoStatement; |
| import org.eclipse.jdt.core.dom.EnhancedForStatement; |
| import org.eclipse.jdt.core.dom.EnumConstantDeclaration; |
| import org.eclipse.jdt.core.dom.EnumDeclaration; |
| import org.eclipse.jdt.core.dom.ExportsDirective; |
| import org.eclipse.jdt.core.dom.Expression; |
| import org.eclipse.jdt.core.dom.ExpressionMethodReference; |
| import org.eclipse.jdt.core.dom.FieldAccess; |
| 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.InfixExpression; |
| import org.eclipse.jdt.core.dom.InfixExpression.Operator; |
| import org.eclipse.jdt.core.dom.LambdaExpression; |
| import org.eclipse.jdt.core.dom.MethodDeclaration; |
| import org.eclipse.jdt.core.dom.MethodInvocation; |
| import org.eclipse.jdt.core.dom.MethodSpec; |
| import org.eclipse.jdt.core.dom.Name; |
| import org.eclipse.jdt.core.dom.NormalAnnotation; |
| import org.eclipse.jdt.core.dom.OpensDirective; |
| import org.eclipse.jdt.core.dom.ParameterizedType; |
| import org.eclipse.jdt.core.dom.ProvidesDirective; |
| import org.eclipse.jdt.core.dom.QualifiedName; |
| import org.eclipse.jdt.core.dom.RoleTypeDeclaration; |
| import org.eclipse.jdt.core.dom.SingleMemberAnnotation; |
| 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.SuperMethodReference; |
| import org.eclipse.jdt.core.dom.SwitchExpression; |
| import org.eclipse.jdt.core.dom.SwitchStatement; |
| 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; |
| import org.eclipse.jdt.core.dom.TypeMethodReference; |
| import org.eclipse.jdt.core.dom.TypeParameter; |
| import org.eclipse.jdt.core.dom.UnionType; |
| import org.eclipse.jdt.core.dom.VariableDeclaration; |
| import org.eclipse.jdt.core.dom.VariableDeclarationFragment; |
| import org.eclipse.jdt.core.dom.VariableDeclarationStatement; |
| import org.eclipse.jdt.core.dom.WhileStatement; |
| import org.eclipse.jdt.core.formatter.CodeFormatter; |
| import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; |
| 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 static final Map<Operator, Integer> OPERATOR_PRECEDENCE; |
| private static final Map<Operator, ToIntFunction<DefaultCodeFormatterOptions>> OPERATOR_WRAPPING_OPTION; |
| private static final Map<Operator, Predicate<DefaultCodeFormatterOptions>> OPERATOR_WRAP_BEFORE_OPTION; |
| static { |
| HashMap<Operator, Integer> precedence = new HashMap<>(); |
| HashMap<Operator, ToIntFunction<DefaultCodeFormatterOptions>> wrappingOption = new HashMap<>(); |
| HashMap<Operator, Predicate<DefaultCodeFormatterOptions>> wrapBeforeOption = new HashMap<>(); |
| for (Operator op : Arrays.asList(Operator.TIMES, Operator.DIVIDE, Operator.REMAINDER)) { |
| precedence.put(op, 1); |
| wrappingOption.put(op, o -> o.alignment_for_multiplicative_operator); |
| wrapBeforeOption.put(op, o -> o.wrap_before_multiplicative_operator); |
| } |
| for (Operator op : Arrays.asList(Operator.PLUS, Operator.MINUS)) { |
| precedence.put(op, 2); |
| wrappingOption.put(op, o -> o.alignment_for_additive_operator); |
| wrapBeforeOption.put(op, o -> o.wrap_before_additive_operator); |
| } |
| for (Operator op : Arrays.asList(Operator.LEFT_SHIFT, Operator.RIGHT_SHIFT_SIGNED, |
| Operator.RIGHT_SHIFT_UNSIGNED)) { |
| precedence.put(op, 3); |
| wrappingOption.put(op, o -> o.alignment_for_shift_operator); |
| wrapBeforeOption.put(op, o -> o.wrap_before_shift_operator); |
| } |
| for (Operator op : Arrays.asList(Operator.LESS, Operator.GREATER, Operator.LESS_EQUALS, |
| Operator.GREATER_EQUALS)) { |
| precedence.put(op, 4); |
| wrappingOption.put(op, o -> o.alignment_for_relational_operator); |
| wrapBeforeOption.put(op, o -> o.wrap_before_relational_operator); |
| } |
| for (Operator op : Arrays.asList(Operator.EQUALS, Operator.NOT_EQUALS)) { |
| precedence.put(op, 5); |
| wrappingOption.put(op, o -> o.alignment_for_relational_operator); |
| wrapBeforeOption.put(op, o -> o.wrap_before_relational_operator); |
| } |
| |
| precedence.put(Operator.AND, 6); |
| precedence.put(Operator.XOR, 7); |
| precedence.put(Operator.OR, 8); |
| for (Operator op : Arrays.asList(Operator.AND, Operator.XOR, Operator.OR)) { |
| wrappingOption.put(op, o -> o.alignment_for_bitwise_operator); |
| wrapBeforeOption.put(op, o -> o.wrap_before_bitwise_operator); |
| } |
| |
| precedence.put(Operator.CONDITIONAL_AND, 9); |
| precedence.put(Operator.CONDITIONAL_OR, 10); |
| for (Operator op : Arrays.asList(Operator.CONDITIONAL_AND, Operator.CONDITIONAL_OR)) { |
| wrappingOption.put(op, o -> o.alignment_for_logical_operator); |
| wrapBeforeOption.put(op, o -> o.wrap_before_logical_operator); |
| } |
| // ternary and assignment operators not relevant to infix expressions |
| |
| OPERATOR_PRECEDENCE = Collections.unmodifiableMap(precedence); |
| OPERATOR_WRAPPING_OPTION = Collections.unmodifiableMap(wrappingOption); |
| OPERATOR_WRAP_BEFORE_OPTION = Collections.unmodifiableMap(wrapBeforeOption); |
| } |
| |
| /** Penalty multiplier for wraps that are preferred */ |
| private final static float PREFERRED = 7f / 8; |
| |
| final TokenManager tm; |
| final DefaultCodeFormatterOptions options; |
| final int kind; |
| |
| final Aligner aligner; |
| |
| /* |
| * temporary values used when calling {@link #handleWrap(int)} to avoid ArrayList initialization and long lists of |
| * parameters |
| */ |
| private List<Integer> wrapIndexes = new ArrayList<>(); |
| /** Indexes for wraps that shouldn't happen but should be indented if cannot be removed */ |
| private List<Integer> secondaryWrapIndexes = new ArrayList<>(); |
| private List<Float> wrapPenalties = new ArrayList<>(); |
| private int wrapParentIndex = -1; |
| private int wrapGroupEnd = -1; |
| |
| private int currentDepth = 0; |
| |
| public WrapPreparator(TokenManager tokenManager, DefaultCodeFormatterOptions options, int kind) { |
| this.tm = tokenManager; |
| this.options = options; |
| this.kind = kind; |
| |
| this.aligner = new Aligner(this.tm, this.options); |
| } |
| |
| @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)); |
| } |
| return !isMalformed; |
| } |
| |
| @Override |
| public void postVisit(ASTNode node) { |
| this.currentDepth--; |
| } |
| |
| @Override |
| public boolean visit(NormalAnnotation node) { |
| int lParen = this.tm.firstIndexAfter(node.getTypeName(), TokenNameLPAREN); |
| int rParen = this.tm.lastIndexIn(node, TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_annotation); |
| |
| handleArguments(node.values(), this.options.alignment_for_arguments_in_annotation); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(SingleMemberAnnotation node) { |
| int lParen = this.tm.firstIndexAfter(node.getTypeName(), TokenNameLPAREN); |
| int rParen = this.tm.lastIndexIn(node, TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_annotation); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(TypeDeclaration node) { |
| Type superclassType = node.getSuperclassType(); |
| if (superclassType != null) { |
| this.wrapParentIndex = this.tm.lastIndexIn(node.getName(), -1); |
| this.wrapGroupEnd = this.tm.lastIndexIn(superclassType, -1); |
| this.wrapIndexes.add(this.tm.firstIndexBefore(superclassType, TokenNameextends)); |
| this.wrapIndexes.add(this.tm.firstIndexIn(superclassType, -1)); |
| handleWrap(this.options.alignment_for_superclass_in_type_declaration, PREFERRED); |
| } |
| |
| List<Type> superInterfaceTypes = node.superInterfaceTypes(); |
| if (!superInterfaceTypes.isEmpty()) { |
| int implementsToken = node.isInterface() ? TokenNameextends : TokenNameimplements; |
| this.wrapParentIndex = this.tm.lastIndexIn(node.getName(), -1); |
| this.wrapIndexes.add(this.tm.firstIndexBefore(superInterfaceTypes.get(0), implementsToken)); |
| prepareElementsList(superInterfaceTypes, TokenNameCOMMA, -1); |
| handleWrap(this.options.alignment_for_superinterfaces_in_type_declaration, PREFERRED); |
| } |
| |
| prepareElementsList(node.typeParameters(), TokenNameCOMMA, TokenNameLESS); |
| handleWrap(this.options.alignment_for_type_parameters); |
| |
| this.aligner.handleAlign(node.bodyDeclarations()); |
| |
| return true; |
| } |
| |
| @Override |
| public boolean visit(AnnotationTypeDeclaration node) { |
| this.aligner.handleAlign(node.bodyDeclarations()); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(AnonymousClassDeclaration node) { |
| this.aligner.handleAlign(node.bodyDeclarations()); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(MethodDeclaration node) { |
| int lParen = this.tm.firstIndexAfter(node.getName(), TokenNameLPAREN); |
| int rParen = node.getBody() == null ? this.tm.lastIndexIn(node, TokenNameRPAREN) |
| : this.tm.firstIndexBefore(node.getBody(), TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_method_declaration); |
| |
| List<SingleVariableDeclaration> parameters = node.parameters(); |
| Type receiverType = node.getReceiverType(); |
| if (!parameters.isEmpty() || receiverType != null) { |
| if (receiverType != null) |
| this.wrapIndexes.add(this.tm.firstIndexIn(receiverType, -1)); |
| int wrappingOption = node.isConstructor() ? this.options.alignment_for_parameters_in_constructor_declaration |
| : this.options.alignment_for_parameters_in_method_declaration; |
| this.wrapGroupEnd = this.tm.lastIndexIn( |
| parameters.isEmpty() ? receiverType : parameters.get(parameters.size() - 1), -1); |
| handleArguments(parameters, wrappingOption); |
| } |
| |
| List<Type> exceptionTypes = node.thrownExceptionTypes(); |
| if (!exceptionTypes.isEmpty()) { |
| int wrappingOption = node.isConstructor() |
| ? this.options.alignment_for_throws_clause_in_constructor_declaration |
| : this.options.alignment_for_throws_clause_in_method_declaration; |
| if ((wrappingOption & Alignment.M_INDENT_ON_COLUMN) == 0) |
| this.wrapParentIndex = lParen; |
| 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)); |
| while (this.tm.get(this.wrapParentIndex).isComment()) |
| this.wrapParentIndex++; |
| List<TypeParameter> typeParameters = node.typeParameters(); |
| if (!typeParameters.isEmpty()) |
| this.wrapIndexes.add(this.tm.firstIndexIn(typeParameters.get(0), -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.wrapGroupEnd = this.tm.lastIndexIn(node.getName(), -1); |
| handleWrap(this.options.alignment_for_method_declaration); |
| } |
| |
| prepareElementsList(node.typeParameters(), TokenNameCOMMA, TokenNameLESS); |
| handleWrap(this.options.alignment_for_type_parameters); |
| |
| return true; |
| } |
| |
| @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.options.alignment_for_enum_constants & Alignment.M_INDENT_ON_COLUMN) > 0 |
| ? this.tm.firstIndexBefore(enumConstants.get(0), TokenNameLBRACE) |
| : this.tm.firstIndexIn(node, TokenNameenum); |
| 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.wrapParentIndex = this.tm.lastIndexIn(node.getName(), -1); |
| this.wrapIndexes.add(this.tm.firstIndexBefore(superInterfaceTypes.get(0), TokenNameimplements)); |
| prepareElementsList(superInterfaceTypes, TokenNameCOMMA, -1); |
| handleWrap(this.options.alignment_for_superinterfaces_in_enum_declaration, PREFERRED); |
| } |
| |
| this.aligner.handleAlign(node.bodyDeclarations()); |
| |
| return true; |
| } |
| |
| @Override |
| public boolean visit(EnumConstantDeclaration node) { |
| int lParen = this.tm.firstIndexAfter(node.getName(), -1); |
| while (this.tm.get(lParen).isComment()) |
| lParen++; |
| if (this.tm.get(lParen).tokenType == TokenNameLPAREN) { |
| int rParen = node.getAnonymousClassDeclaration() == null ? this.tm.lastIndexIn(node, TokenNameRPAREN) |
| : this.tm.firstIndexBefore(node.getAnonymousClassDeclaration(), TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_enum_constant_declaration); |
| } |
| |
| handleArguments(node.arguments(), this.options.alignment_for_arguments_in_enum_constant); |
| AnonymousClassDeclaration anonymousClass = node.getAnonymousClassDeclaration(); |
| if (anonymousClass != null) { |
| forceContinuousWrapping(anonymousClass, this.tm.firstIndexIn(node.getName(), -1)); |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean visit(Block node) { |
| this.aligner.handleAlign(node); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(MethodInvocation node) { |
| int lParen = this.tm.firstIndexAfter(node.getName(), TokenNameLPAREN); |
| int rParen = this.tm.lastIndexIn(node, TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_method_invocation); |
| |
| handleArguments(node.arguments(), this.options.alignment_for_arguments_in_method_invocation); |
| handleTypeArguments(node.typeArguments()); |
| |
| boolean isInvocationChainRoot = !(node.getParent() instanceof MethodInvocation) |
| || node.getLocationInParent() != MethodInvocation.EXPRESSION_PROPERTY; |
| if (isInvocationChainRoot) { |
| Expression expression = node; |
| MethodInvocation invocation = node; |
| while (expression instanceof MethodInvocation) { |
| invocation = (MethodInvocation) expression; |
| expression = invocation.getExpression(); |
| 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) |
| : this.tm.lastIndexIn(invocation, -1); |
| this.wrapGroupEnd = this.tm.lastIndexIn(node, -1); |
| handleWrap(this.options.alignment_for_selector_in_method_invocation); |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean visit(SuperMethodInvocation node) { |
| int lParen = this.tm.firstIndexAfter(node.getName(), TokenNameLPAREN); |
| int rParen = this.tm.lastIndexIn(node, TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_method_invocation); |
| |
| handleArguments(node.arguments(), this.options.alignment_for_arguments_in_method_invocation); |
| handleTypeArguments(node.typeArguments()); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(ClassInstanceCreation node) { |
| int lParen = this.tm.firstIndexAfter(node.getType(), TokenNameLPAREN); |
| int rParen = node.getAnonymousClassDeclaration() == null ? this.tm.lastIndexIn(node, TokenNameRPAREN) |
| : this.tm.firstIndexBefore(node.getAnonymousClassDeclaration(), TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_method_invocation); |
| |
| AnonymousClassDeclaration anonymousClass = node.getAnonymousClassDeclaration(); |
| if (anonymousClass != null) { |
| forceContinuousWrapping(anonymousClass, this.tm.firstIndexIn(node, TokenNamenew)); |
| } |
| |
| int wrappingOption = node.getExpression() != null |
| ? this.options.alignment_for_arguments_in_qualified_allocation_expression |
| : this.options.alignment_for_arguments_in_allocation_expression; |
| handleArguments(node.arguments(), wrappingOption); |
| |
| handleTypeArguments(node.typeArguments()); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(ConstructorInvocation node) { |
| int lParen = node.arguments().isEmpty() ? this.tm.lastIndexIn(node, TokenNameLPAREN) |
| : this.tm.firstIndexBefore((ASTNode) node.arguments().get(0), TokenNameLPAREN); |
| int rParen = this.tm.lastIndexIn(node, TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_method_invocation); |
| |
| handleArguments(node.arguments(), this.options.alignment_for_arguments_in_explicit_constructor_call); |
| handleTypeArguments(node.typeArguments()); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(SuperConstructorInvocation node) { |
| int lParen = node.arguments().isEmpty() ? this.tm.lastIndexIn(node, TokenNameLPAREN) |
| : this.tm.firstIndexBefore((ASTNode) node.arguments().get(0), TokenNameLPAREN); |
| int rParen = this.tm.lastIndexIn(node, TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_method_invocation); |
| |
| handleArguments(node.arguments(), this.options.alignment_for_arguments_in_explicit_constructor_call); |
| handleTypeArguments(node.typeArguments()); |
| return true; |
| } |
| |
| @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) |
| return true; |
| ASTNode parent = node.getParent(); |
| if ((parent instanceof InfixExpression) && samePrecedence(node, (InfixExpression) parent)) |
| return true; // this node has been handled higher in the AST |
| |
| int wrappingOption = OPERATOR_WRAPPING_OPTION.get(node.getOperator()).applyAsInt(this.options); |
| boolean wrapBeforeOperator = OPERATOR_WRAP_BEFORE_OPTION.get(node.getOperator()).test(this.options); |
| if (this.tm.isStringConcatenation(node)) { |
| wrappingOption = this.options.alignment_for_string_concatenation; |
| wrapBeforeOperator = this.options.wrap_before_string_concatenation; |
| } |
| |
| findTokensToWrap(node, wrapBeforeOperator, 0); |
| this.wrapParentIndex = this.wrapIndexes.remove(0); |
| this.wrapGroupEnd = this.tm.lastIndexIn(node, -1); |
| if ((wrappingOption & 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()) { |
| this.wrapParentIndex = i; |
| break; |
| } |
| } |
| handleWrap(wrappingOption, !wrapBeforeOperator, node); |
| return true; |
| } |
| |
| private void findTokensToWrap(InfixExpression node, boolean wrapBeforeOperator, int depth) { |
| Expression left = node.getLeftOperand(); |
| if (left instanceof InfixExpression && samePrecedence(node, (InfixExpression) left)) { |
| findTokensToWrap((InfixExpression) left, wrapBeforeOperator, depth + 1); |
| } else if (this.wrapIndexes.isEmpty() // always add first operand, it will be taken as wrap parent |
| || !wrapBeforeOperator) { |
| this.wrapIndexes.add(this.tm.firstIndexIn(left, -1)); |
| } |
| |
| Expression right = node.getRightOperand(); |
| List<Expression> extended = node.extendedOperands(); |
| for (int i = -1; i < extended.size(); i++) { |
| Expression operand = (i == -1) ? right : extended.get(i); |
| if (operand instanceof InfixExpression && samePrecedence(node, (InfixExpression) operand)) { |
| findTokensToWrap((InfixExpression) operand, wrapBeforeOperator, depth + 1); |
| } |
| int indexBefore = this.tm.firstIndexBefore(operand, -1); |
| while (this.tm.get(indexBefore).isComment()) |
| indexBefore--; |
| assert node.getOperator().toString().equals(this.tm.toString(indexBefore)); |
| int indexAfter = this.tm.firstIndexIn(operand, -1); |
| this.wrapIndexes.add(wrapBeforeOperator ? indexBefore : indexAfter); |
| this.secondaryWrapIndexes.add(wrapBeforeOperator ? indexAfter : indexBefore); |
| |
| if (!this.options.join_wrapped_lines) { |
| // TODO there should be an option for never joining wraps on opposite side of the operator |
| if (wrapBeforeOperator) { |
| if (this.tm.countLineBreaksBetween(this.tm.get(indexAfter - 1), this.tm.get(indexAfter)) > 0) |
| this.wrapIndexes.add(indexAfter); |
| } else { |
| if (this.tm.countLineBreaksBetween(this.tm.get(indexBefore), this.tm.get(indexBefore - 1)) > 0) |
| this.wrapIndexes.add(indexBefore); |
| } |
| } |
| } |
| } |
| |
| private boolean samePrecedence(InfixExpression expression1, InfixExpression expression2) { |
| Integer precedence1 = OPERATOR_PRECEDENCE.get(expression1.getOperator()); |
| Integer precedence2 = OPERATOR_PRECEDENCE.get(expression2.getOperator()); |
| if (precedence1 == null || precedence2 == null) |
| return false; |
| return precedence1.equals(precedence2); |
| } |
| |
| @Override |
| public boolean visit(ConditionalExpression node) { |
| boolean chainsMatter = (this.options.alignment_for_conditional_expression_chain |
| & Alignment.SPLIT_MASK) != Alignment.M_NO_ALIGNMENT; |
| boolean isNextInChain = node.getParent() instanceof ConditionalExpression |
| && node == ((ConditionalExpression) node.getParent()).getElseExpression(); |
| boolean isFirstInChain = node.getElseExpression() instanceof ConditionalExpression && !isNextInChain; |
| boolean wrapBefore = this.options.wrap_before_conditional_operator; |
| List<Integer> before = wrapBefore ? this.wrapIndexes : this.secondaryWrapIndexes; |
| List<Integer> after = wrapBefore ? this.secondaryWrapIndexes : this.wrapIndexes; |
| if (!chainsMatter || (!isFirstInChain && !isNextInChain)) { |
| before.add(this.tm.firstIndexAfter(node.getExpression(), TokenNameQUESTION)); |
| before.add(this.tm.firstIndexAfter(node.getThenExpression(), TokenNameCOLON)); |
| after.add(this.tm.firstIndexIn(node.getThenExpression(), -1)); |
| after.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); |
| |
| } else if (isFirstInChain) { |
| List<ConditionalExpression> chain = new ArrayList<>(); |
| chain.add(node); |
| ConditionalExpression next = node; |
| while (next.getElseExpression() instanceof ConditionalExpression) { |
| next = (ConditionalExpression) next.getElseExpression(); |
| chain.add(next); |
| } |
| |
| for (ConditionalExpression conditional : chain) { |
| before.add(this.tm.firstIndexAfter(conditional.getThenExpression(), TokenNameCOLON)); |
| after.add(this.tm.firstIndexIn(conditional.getElseExpression(), -1)); |
| } |
| this.wrapParentIndex = this.tm.firstIndexIn(node.getExpression(), -1); |
| this.wrapGroupEnd = this.tm.lastIndexIn(node, -1); |
| handleWrap(this.options.alignment_for_conditional_expression_chain); |
| |
| this.currentDepth++; |
| for (ConditionalExpression conditional : chain) { |
| before.add(this.tm.firstIndexAfter(conditional.getExpression(), TokenNameQUESTION)); |
| after.add(this.tm.firstIndexIn(conditional.getThenExpression(), -1)); |
| this.wrapParentIndex = this.tm.firstIndexIn(conditional.getExpression(), -1); |
| this.wrapGroupEnd = this.tm.lastIndexIn(conditional.getThenExpression(), -1); |
| handleWrap(this.options.alignment_for_conditional_expression); |
| } |
| this.currentDepth--; |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean visit(ArrayInitializer node) { |
| List<Expression> expressions = node.expressions(); |
| if (!expressions.isEmpty()) { |
| prepareElementsList(expressions, TokenNameCOMMA, TokenNameLBRACE); |
| handleWrap(this.options.alignment_for_expressions_in_array_initializer, node); |
| } |
| int openingBraceIndex = this.tm.firstIndexIn(node, TokenNameLBRACE); |
| Token openingBrace = this.tm.get(openingBraceIndex); |
| if (openingBrace.isNextLineOnWrap() && openingBrace.getWrapPolicy() == null && openingBraceIndex > 0) { |
| // add fake wrap policy to make sure the brace indentation is right |
| openingBrace.setWrapPolicy(new WrapPolicy(WrapMode.DISABLED, openingBraceIndex - 1, 0)); |
| } |
| if (!this.options.join_wrapped_lines |
| && !this.options.insert_new_line_before_closing_brace_in_array_initializer) { |
| // if there is a line break before the closing brace, formatter should treat it as a valid wrap to preserve |
| int closingBraceIndex = this.tm.lastIndexIn(node, TokenNameRBRACE); |
| Token closingBrace = this.tm.get(closingBraceIndex); |
| if (this.tm.countLineBreaksBetween(this.tm.get(closingBraceIndex - 1), closingBrace) == 1) { |
| closingBrace.setWrapPolicy(new WrapPolicy(WrapMode.WHERE_NECESSARY, openingBraceIndex, |
| closingBraceIndex, 0, this.currentDepth, 1, true, false)); |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean visit(Assignment node) { |
| int rightSideIndex = this.tm.firstIndexIn(node.getRightHandSide(), -1); |
| if (this.tm.get(rightSideIndex).getLineBreaksBefore() > 0) |
| return true; // must be an array initializer in new line because of brace_position_for_array_initializer |
| |
| 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.wrapIndexes.add(this.options.wrap_before_assignment_operator ? operatorIndex : rightSideIndex); |
| this.secondaryWrapIndexes.add(this.options.wrap_before_assignment_operator ? rightSideIndex : operatorIndex); |
| this.wrapParentIndex = operatorIndex - 1; |
| this.wrapGroupEnd = this.tm.lastIndexIn(node.getRightHandSide(), -1); |
| handleWrap(this.options.alignment_for_assignment); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(VariableDeclarationFragment node) { |
| if (node.getInitializer() == null) |
| return true; |
| int rightSideIndex = this.tm.firstIndexIn(node.getInitializer(), -1); |
| if (this.tm.get(rightSideIndex).getLineBreaksBefore() > 0) |
| return true; // must be an array initializer in new line because of brace_position_for_array_initializer |
| int equalIndex = this.tm.firstIndexBefore(node.getInitializer(), TokenNameEQUAL); |
| |
| this.wrapIndexes.add(this.options.wrap_before_assignment_operator ? equalIndex : rightSideIndex); |
| this.secondaryWrapIndexes.add(this.options.wrap_before_assignment_operator ? rightSideIndex : equalIndex); |
| this.wrapParentIndex = equalIndex - 1; |
| this.wrapGroupEnd = this.tm.lastIndexIn(node.getInitializer(), -1); |
| handleWrap(this.options.alignment_for_assignment); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(IfStatement node) { |
| int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN); |
| int rParen = this.tm.firstIndexAfter(node.getExpression(), TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_if_while_statement); |
| |
| Statement elseStatement = node.getElseStatement(); |
| boolean keepThenOnSameLine = this.options.keep_then_statement_on_same_line |
| || (this.options.keep_simple_if_on_one_line && elseStatement == null); |
| if (keepThenOnSameLine) |
| handleSimpleLoop(node.getThenStatement(), this.options.alignment_for_compact_if); |
| |
| if (this.options.keep_else_statement_on_same_line && elseStatement != null) |
| handleSimpleLoop(elseStatement, this.options.alignment_for_compact_if); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(ForStatement node) { |
| int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN); |
| int rParen = this.tm.firstIndexBefore(node.getBody(), TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_for_statement); |
| |
| List<Expression> initializers = node.initializers(); |
| if (!initializers.isEmpty()) |
| this.wrapIndexes.add(this.tm.firstIndexIn(initializers.get(0), -1)); |
| if (node.getExpression() != null) |
| this.wrapIndexes.add(this.tm.firstIndexIn(node.getExpression(), -1)); |
| List<Expression> updaters = node.updaters(); |
| if (!updaters.isEmpty()) |
| this.wrapIndexes.add(this.tm.firstIndexIn(updaters.get(0), -1)); |
| if (!this.wrapIndexes.isEmpty()) { |
| this.wrapParentIndex = lParen; |
| this.wrapGroupEnd = rParen; |
| handleWrap(this.options.alignment_for_expressions_in_for_loop_header); |
| } |
| if (this.options.keep_simple_for_body_on_same_line) |
| handleSimpleLoop(node.getBody(), this.options.alignment_for_compact_loop); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(EnhancedForStatement node) { |
| int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN); |
| int rParen = this.tm.firstIndexBefore(node.getBody(), TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_for_statement); |
| |
| if (this.options.keep_simple_for_body_on_same_line) |
| handleSimpleLoop(node.getBody(), this.options.alignment_for_compact_loop); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(WhileStatement node) { |
| int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN); |
| int rParen = this.tm.firstIndexAfter(node.getExpression(), TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_if_while_statement); |
| |
| if (this.options.keep_simple_while_body_on_same_line) |
| handleSimpleLoop(node.getBody(), this.options.alignment_for_compact_loop); |
| return true; |
| } |
| |
| private void handleSimpleLoop(Statement body, int wrappingOption) { |
| if (!(body instanceof Block)) { |
| this.wrapIndexes.add(this.tm.firstIndexIn(body, -1)); |
| this.wrapParentIndex = this.tm.firstIndexBefore(body, TokenNameRPAREN); |
| this.wrapGroupEnd = this.tm.lastIndexIn(body, -1); |
| handleWrap(wrappingOption, body.getParent()); |
| |
| body.accept(new ASTVisitor() { |
| @Override |
| public boolean visit(Block node) { |
| forceContinuousWrapping(node, WrapPreparator.this.tm.firstIndexIn(node, -1)); |
| return false; |
| } |
| }); |
| } |
| } |
| |
| @Override |
| public void endVisit(DoStatement node) { |
| if (this.options.keep_simple_do_while_body_on_same_line && !(node.getBody() instanceof Block)) { |
| int whileIndex = this.tm.firstIndexAfter(node.getBody(), TokenNamewhile); |
| this.wrapIndexes.add(whileIndex); |
| this.wrapParentIndex = this.tm.lastIndexIn(node.getBody(), -1); |
| this.wrapGroupEnd = this.tm.lastIndexIn(node, -1); |
| |
| int alignment = this.options.alignment_for_compact_loop; |
| for (int i = this.tm.firstIndexIn(node, -1) + 1; i < whileIndex; i++) { |
| Token token = this.tm.get(i); |
| if (token.getLineBreaksBefore() > 0 || token.getLineBreaksAfter() > 0) |
| alignment |= Alignment.M_FORCE; |
| } |
| handleWrap(alignment, node); |
| } |
| } |
| |
| @Override |
| public boolean visit(TryStatement node) { |
| if (!node.resources().isEmpty()) { |
| int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN); |
| int rParen = this.tm.firstIndexBefore(node.getBody(), TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_try_clause); |
| } |
| 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 (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, -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 { |
| prepareElementsList(types, TokenNameOR, TokenNameLPAREN); |
| handleWrap(this.options.alignment_for_union_type_in_multicatch); |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean visit(LambdaExpression node) { |
| int lParen = this.tm.firstIndexIn(node, -1); |
| if (this.tm.get(lParen).tokenType == TokenNameLPAREN) { |
| int rParen = this.tm.firstIndexBefore(node.getBody(), TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_lambda_declaration); |
| } |
| if (node.getBody() instanceof Block) { |
| forceContinuousWrapping(node.getBody(), this.tm.firstIndexIn(node, -1)); |
| |
| List<Statement> statements = ((Block) node.getBody()).statements(); |
| if (!statements.isEmpty()) { |
| int openBraceIndex = this.tm.firstIndexBefore(statements.get(0), TokenNameLBRACE); |
| int closeBraceIndex = this.tm.firstIndexAfter(statements.get(statements.size() - 1), TokenNameRBRACE); |
| boolean areKeptOnOneLine = statements.stream() |
| .allMatch(n -> this.tm.firstTokenIn(n, -1).getLineBreaksBefore() == 0); |
| if (areKeptOnOneLine) { |
| for (Statement statement : statements) |
| this.wrapIndexes.add(this.tm.firstIndexIn(statement, -1)); |
| this.wrapParentIndex = openBraceIndex; |
| this.wrapGroupEnd = closeBraceIndex; |
| handleWrap(Alignment.M_ONE_PER_LINE_SPLIT, node); |
| this.tm.get(closeBraceIndex).setWrapPolicy(new WrapPolicy(WrapMode.TOP_PRIORITY, openBraceIndex, |
| closeBraceIndex, 0, this.currentDepth, 1, false, false)); |
| } |
| } |
| } |
| if (node.hasParentheses()) { |
| List<VariableDeclaration> parameters = node.parameters(); |
| // the legacy formatter didn't like wrapping lambda parameters, so neither do we |
| this.currentDepth++; |
| handleArguments(parameters, this.options.alignment_for_parameters_in_method_declaration); |
| this.currentDepth--; |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean visit(FieldDeclaration node) { |
| handleVariableDeclarations(node.fragments()); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(VariableDeclarationStatement node) { |
| handleVariableDeclarations(node.fragments()); |
| return true; |
| } |
| @Override |
| public boolean visit(ParameterizedType node) { |
| prepareElementsList(node.typeArguments(), TokenNameCOMMA, TokenNameLESS); |
| handleWrap(this.options.alignment_for_parameterized_type_references); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(TypeMethodReference node) { |
| handleTypeArguments(node.typeArguments()); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(ExpressionMethodReference node) { |
| handleTypeArguments(node.typeArguments()); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(SuperMethodReference node) { |
| handleTypeArguments(node.typeArguments()); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(CreationReference node) { |
| handleTypeArguments(node.typeArguments()); |
| return true; |
| } |
| |
| private void handleTypeArguments(List<Type> typeArguments) { |
| if (typeArguments.isEmpty()) |
| return; |
| prepareElementsList(typeArguments, TokenNameCOMMA, TokenNameLESS); |
| handleWrap(this.options.alignment_for_type_arguments); |
| } |
| |
| //{ObjectTeams: more visits: |
| @Override |
| public boolean visit(RoleTypeDeclaration node) { |
| this.visit((TypeDeclaration) node); |
| Type baseclassType = node.getBaseClassType(); |
| if (baseclassType != null) { |
| this.wrapParentIndex = this.tm.lastIndexIn(node.getName(), -1); |
| this.wrapGroupEnd = this.tm.lastIndexIn(baseclassType, -1); |
| this.wrapIndexes.add(this.tm.firstIndexBefore(baseclassType, TokenNameplayedBy)); |
| this.wrapIndexes.add(this.tm.firstIndexIn(baseclassType, -1)); |
| handleWrap(this.options.alignment_for_superclass_in_type_declaration, PREFERRED); |
| } |
| return true; |
| } |
| @Override |
| public boolean visit(MethodSpec node) { |
| List<SingleVariableDeclaration> parameters = node.parameters(); |
| if (!parameters.isEmpty()) { |
| int wrappingOption = this.options.alignment_for_parameters_in_method_declaration; |
| this.wrapGroupEnd = this.tm.lastIndexIn(parameters.get(parameters.size() - 1), -1); |
| handleArguments(parameters, wrappingOption); |
| } |
| return true; |
| } |
| @Override |
| public boolean visit(BaseCallMessageSend node) { |
| handleArguments(node.getArguments(), this.options.alignment_for_arguments_in_method_invocation); |
| return true; |
| } |
| // TODO(SH): smart wrapping of long method mappings |
| // SH} |
| |
| @Override |
| public boolean visit(ExportsDirective node) { |
| handleModuleStatement(node.modules(), TokenNameto); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(OpensDirective node) { |
| handleModuleStatement(node.modules(), TokenNameto); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(ProvidesDirective node) { |
| handleModuleStatement(node.implementations(), TokenNamewith); |
| return true; |
| } |
| |
| private void handleModuleStatement(List<Name> names, int joiningTokenType) { |
| if (names.isEmpty()) |
| return; |
| int joiningTokenIndex = this.tm.firstIndexBefore(names.get(0), joiningTokenType); |
| this.wrapParentIndex = this.tm.firstIndexBefore(names.get(0), TokenNameIdentifier); |
| this.wrapIndexes.add(joiningTokenIndex); |
| prepareElementsList(names, TokenNameCOMMA, -1); |
| handleWrap(this.options.alignment_for_module_statements, PREFERRED); |
| } |
| |
| @Override |
| public boolean visit(CatchClause node) { |
| int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN); |
| int rParen = this.tm.firstIndexBefore(node.getBody(), TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_catch_clause); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(SwitchStatement node) { |
| int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN); |
| int rParen = this.tm.firstIndexAfter(node.getExpression(), TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_switch_statement); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(SwitchExpression node) { |
| int lParen = this.tm.firstIndexIn(node, TokenNameLPAREN); |
| int rParen = this.tm.firstIndexAfter(node.getExpression(), TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_switch_statement); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(DoStatement node) { |
| int lParen = this.tm.firstIndexBefore(node.getExpression(), TokenNameLPAREN); |
| int rParen = this.tm.firstIndexAfter(node.getExpression(), TokenNameRPAREN); |
| handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_if_while_statement); |
| return true; |
| } |
| |
| /** |
| * Makes sure all new lines within given node will have wrap policy so that |
| * wrap executor will fix their indentation if necessary. |
| */ |
| 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); |
| 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 || token.getWrapPolicy().wrapMode == WrapMode.BLOCK_INDENT)) { |
| int extraIndent = token.getIndent() + indentChange; |
| token.setWrapPolicy(new WrapPolicy(WrapMode.BLOCK_INDENT, parentIndex, extraIndent)); |
| token.setIndent(parentIndent + extraIndent); |
| } |
| previous = token; |
| } |
| } |
| |
| private void handleVariableDeclarations(List<VariableDeclarationFragment> fragments) { |
| if (fragments.size() > 1) { |
| this.wrapParentIndex = this.tm.firstIndexIn(fragments.get(0), -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) { |
| 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)); |
| 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); |
| } |
| } |
| |
| private void handleWrap(int wrappingOption) { |
| handleWrap(wrappingOption, null); |
| } |
| |
| private void handleWrap(int wrappingOption, float firstPenaltyMultiplier) { |
| this.wrapPenalties.add(firstPenaltyMultiplier); |
| handleWrap(wrappingOption, null); |
| } |
| |
| private void handleWrap(int wrappingOption, ASTNode parentNode) { |
| handleWrap(wrappingOption, true, parentNode); |
| } |
| |
| private void handleWrap(int wrappingOption, boolean wrapPreceedingComments, ASTNode parentNode) { |
| doHandleWrap(wrappingOption, wrapPreceedingComments, parentNode); |
| this.wrapIndexes.clear(); |
| this.secondaryWrapIndexes.clear(); |
| this.wrapPenalties.clear(); |
| this.wrapParentIndex = this.wrapGroupEnd = -1; |
| } |
| |
| private void doHandleWrap(int wrappingOption, boolean wrapPreceedingComments, ASTNode parentNode) { |
| if (this.wrapIndexes.isEmpty()) |
| return; |
| assert this.wrapParentIndex >= 0 && this.wrapParentIndex < this.wrapIndexes.get(0); |
| assert this.wrapGroupEnd >= this.wrapIndexes.get(this.wrapIndexes.size() - 1); |
| |
| while (this.tm.get(this.wrapParentIndex).isComment() && this.wrapParentIndex > 0) |
| this.wrapParentIndex--; |
| |
| float penalty = this.wrapPenalties.isEmpty() ? 1 : this.wrapPenalties.get(0); |
| WrapPolicy policy = getWrapPolicy(wrappingOption, penalty, true, parentNode); |
| |
| WrapPolicy existing = this.tm.get(this.wrapIndexes.get(0)).getWrapPolicy(); |
| if (existing != null && existing.wrapMode == WrapMode.TOP_PRIORITY) { |
| // SEPARATE_LINES_IF_WRAPPED |
| assert existing.wrapParentIndex == this.wrapParentIndex; |
| this.wrapGroupEnd = existing.groupEndIndex; |
| policy = new WrapPolicy(WrapMode.TOP_PRIORITY, policy.wrapParentIndex, this.wrapGroupEnd, policy.extraIndent, |
| policy.structureDepth, policy.penaltyMultiplier, true, policy.indentOnColumn); |
| } |
| |
| setTokenWrapPolicy(0, policy, true); |
| |
| for (int i = 1; i < this.wrapIndexes.size(); i++) { |
| penalty = this.wrapPenalties.size() > i ? this.wrapPenalties.get(i) : 1; |
| if (penalty != policy.penaltyMultiplier || i == 1) |
| policy = getWrapPolicy(wrappingOption, penalty, false, parentNode); |
| setTokenWrapPolicy(i, policy, wrapPreceedingComments); |
| } |
| |
| 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 wrapIndexesIndex, WrapPolicy policy, boolean wrapPreceedingComments) { |
| int index = this.wrapIndexes.get(wrapIndexesIndex); |
| if (wrapPreceedingComments) { |
| for (int i = index - 1; i >= 0; i--) { |
| Token previous = this.tm.get(i); |
| if (!previous.isComment()) |
| break; |
| if (previous.getWrapPolicy() == WrapPolicy.FORCE_FIRST_COLUMN) |
| break; |
| if (previous.getLineBreaksAfter() == 0 && i == index - 1) |
| index = i; |
| if (previous.getLineBreaksBefore() > 0) |
| previous.setWrapPolicy(policy); |
| } |
| this.wrapIndexes.set(wrapIndexesIndex, index); |
| } |
| |
| Token token = this.tm.get(index); |
| if (token.getWrapPolicy() == WrapPolicy.DISABLE_WRAP) |
| return; |
| |
| token.setWrapPolicy(policy); |
| if (policy.wrapMode == WrapMode.FORCE) { |
| token.breakBefore(); |
| } else if (this.options.join_wrapped_lines && token.tokenType == TokenNameCOMMENT_BLOCK) { |
| // allow wrap preparator to decide if this comment should be wrapped |
| token.clearLineBreaksBefore(); |
| } |
| } |
| |
| private WrapPolicy getWrapPolicy(int wrappingOption, float penaltyMultiplier, boolean isFirst, ASTNode parentNode) { |
| assert this.wrapParentIndex >= 0 && this.wrapGroupEnd >= 0; |
| int extraIndent = this.options.continuation_indentation; |
| boolean indentOnColumn = (wrappingOption & Alignment.M_INDENT_ON_COLUMN) != 0; |
| boolean isForceWrap = (wrappingOption & Alignment.M_FORCE) != 0; |
| boolean isAlreadyWrapped = false; |
| if (indentOnColumn) { |
| extraIndent = 0; |
| } else if (parentNode instanceof EnumDeclaration) { |
| // special behavior for compatibility with legacy formatter |
| extraIndent = ((wrappingOption & Alignment.M_INDENT_BY_ONE) != 0) ? 2 : 1; |
| if (!this.options.indent_body_declarations_compare_to_enum_declaration_header) |
| extraIndent--; |
| isAlreadyWrapped = isFirst; |
| } else if (parentNode instanceof IfStatement || parentNode instanceof ForStatement |
| || parentNode instanceof EnhancedForStatement || parentNode instanceof WhileStatement) { |
| extraIndent = 1; |
| this.wrapParentIndex = this.tm.firstIndexIn(parentNode, -1); // only if !indoentOnColumn |
| } else if (parentNode instanceof DoStatement) { |
| extraIndent = 0; |
| this.wrapParentIndex = this.tm.firstIndexIn(parentNode, -1); // only if !indoentOnColumn |
| } else if (parentNode instanceof LambdaExpression) { |
| extraIndent = 1; |
| } else if ((wrappingOption & Alignment.M_INDENT_BY_ONE) != 0) { |
| extraIndent = 1; |
| } else if (parentNode instanceof ArrayInitializer) { |
| extraIndent = this.options.continuation_indentation_for_array_initializer; |
| isAlreadyWrapped = isFirst && this.options.insert_new_line_after_opening_brace_in_array_initializer; |
| } |
| |
| WrapMode wrapMode = WrapMode.WHERE_NECESSARY; |
| boolean isTopPriority = false; |
| switch (wrappingOption & Alignment.SPLIT_MASK) { |
| case Alignment.M_NO_ALIGNMENT: |
| wrapMode = WrapMode.DISABLED; |
| isForceWrap = false; |
| break; |
| case Alignment.M_COMPACT_FIRST_BREAK_SPLIT: |
| isTopPriority = isFirst; |
| isForceWrap &= isFirst; |
| break; |
| case Alignment.M_ONE_PER_LINE_SPLIT: |
| isTopPriority = true; |
| break; |
| case Alignment.M_NEXT_SHIFTED_SPLIT: |
| isTopPriority = true; |
| if (!isFirst) |
| extraIndent++; |
| break; |
| case Alignment.M_NEXT_PER_LINE_SPLIT: |
| isTopPriority = !isFirst; |
| isForceWrap &= !isFirst; |
| break; |
| } |
| |
| if (isForceWrap) { |
| wrapMode = WrapMode.FORCE; |
| } else if (isAlreadyWrapped) { |
| wrapMode = WrapMode.DISABLED; // to avoid triggering top priority wrapping |
| } else if (isTopPriority) { |
| wrapMode = WrapMode.TOP_PRIORITY; |
| } |
| extraIndent *= this.options.indentation_size; |
| return new WrapPolicy(wrapMode, this.wrapParentIndex, this.wrapGroupEnd, extraIndent, this.currentDepth, |
| penaltyMultiplier, isFirst, indentOnColumn); |
| } |
| |
| public void finishUp(ASTNode astRoot, List<IRegion> regions) { |
| preserveExistingLineBreaks(); |
| applyBreaksOutsideRegions(regions); |
| new WrapExecutor(this.tm, this.options).executeWraps(); |
| this.aligner.alignComments(); |
| wrapComments(); |
| fixEnumConstantIndents(astRoot); |
| } |
| |
| private void preserveExistingLineBreaks() { |
| // normally n empty lines = n+1 line breaks, but not at the file start and end |
| Token first = this.tm.get(0); |
| int startingBreaks = first.getLineBreaksBefore(); |
| first.clearLineBreaksBefore(); |
| first.putLineBreaksBefore(startingBreaks - 1); |
| |
| this.tm.traverse(0, new TokenTraverser() { |
| boolean join_wrapped_lines = WrapPreparator.this.options.join_wrapped_lines; |
| |
| @Override |
| protected boolean token(Token token, int index) { |
| int lineBreaks = getLineBreaksToPreserve(getPrevious(), token); |
| if (lineBreaks > 1 || (!this.join_wrapped_lines && token.isWrappable()) || index == 0) |
| token.putLineBreaksBefore(lineBreaks); |
| return true; |
| } |
| |
| }); |
| |
| Token last = this.tm.get(this.tm.size() - 1); |
| last.clearLineBreaksAfter(); |
| int endingBreaks = getLineBreaksToPreserve(last, null); |
| if (endingBreaks > 0) { |
| last.putLineBreaksAfter(endingBreaks); |
| } else if ((this.kind & (CodeFormatter.K_COMPILATION_UNIT | CodeFormatter.K_MODULE_INFO)) != 0 |
| && this.options.insert_new_line_at_end_of_file_if_missing) { |
| last.breakAfter(); |
| } |
| } |
| |
| int getLineBreaksToPreserve(Token token1, Token token2) { |
| if ((token1 != null && !token1.isPreserveLineBreaksAfter()) |
| || (token2 != null && !token2.isPreserveLineBreaksBefore())) { |
| return 0; |
| } |
| 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); |
| 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 |
| return Math.min(lineBreaks, toPreserve); |
| } |
| |
| private void applyBreaksOutsideRegions(List<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; |
| for (int i = 0; i < this.tm.size(); i++) { |
| Token token = this.tm.get(i); |
| if (token.getLineBreaksBefore() > 0 || token.getLineBreaksAfter() > 0) |
| isNLSTagInLine = false; |
| if (token.hasNLSTag()) { |
| assert token.tokenType == TokenNameStringLiteral; |
| isNLSTagInLine = true; |
| } |
| List<Token> structure = token.getInternalStructure(); |
| if (structure != null && !structure.isEmpty() && !isNLSTagInLine) { |
| int startPosition = this.tm.getPositionInLine(i); |
| if (token.tokenType == TokenNameCOMMENT_LINE) { |
| commentWrapper.wrapLineComment(token, startPosition); |
| } else { |
| assert token.tokenType == TokenNameCOMMENT_BLOCK || token.tokenType == TokenNameCOMMENT_JAVADOC; |
| commentWrapper.wrapMultiLineComment(token, startPosition, false, false); |
| } |
| } |
| } |
| } |
| |
| private void fixEnumConstantIndents(ASTNode astRoot) { |
| if (this.options.use_tabs_only_for_leading_indentations) { |
| // enum constants should be indented like other declarations, not like wrapped elements |
| astRoot.accept(new ASTVisitor() { |
| |
| @Override |
| public boolean visit(EnumConstantDeclaration node) { |
| WrapPreparator.this.tm.firstTokenIn(node, -1).setWrapPolicy(null); |
| return true; |
| } |
| }); |
| } |
| } |
| |
| private void handleParenthesesPositions(int openingParenIndex, int closingParenIndex, String positionsSetting) { |
| boolean isEmpty = openingParenIndex + 1 == closingParenIndex; |
| switch (positionsSetting) { |
| case DefaultCodeFormatterConstants.COMMON_LINES: |
| // nothing to do |
| break; |
| case DefaultCodeFormatterConstants.SEPARATE_LINES_IF_WRAPPED: |
| if (isEmpty) |
| break; |
| this.tm.get(openingParenIndex + 1).setWrapPolicy(new WrapPolicy(WrapMode.TOP_PRIORITY, |
| openingParenIndex, closingParenIndex, this.options.indentation_size, 1, 1, true, false)); |
| this.tm.get(closingParenIndex).setWrapPolicy(new WrapPolicy(WrapMode.TOP_PRIORITY, |
| openingParenIndex, closingParenIndex, 0, 1, 1, false, false)); |
| break; |
| case DefaultCodeFormatterConstants.SEPARATE_LINES_IF_NOT_EMPTY: |
| if (isEmpty) |
| break; |
| //$FALL-THROUGH$ |
| case DefaultCodeFormatterConstants.SEPARATE_LINES: |
| case DefaultCodeFormatterConstants.PRESERVE_POSITIONS: |
| boolean always = !positionsSetting.equals(DefaultCodeFormatterConstants.PRESERVE_POSITIONS); |
| Token afterOpening = this.tm.get(openingParenIndex + 1); |
| if (always || this.tm.countLineBreaksBetween(this.tm.get(openingParenIndex), afterOpening) > 0) { |
| afterOpening.setWrapPolicy( |
| new WrapPolicy(WrapMode.WHERE_NECESSARY, openingParenIndex, this.options.indentation_size)); |
| afterOpening.breakBefore(); |
| } |
| Token closingParen = this.tm.get(closingParenIndex); |
| if (always || this.tm.countLineBreaksBetween(this.tm.get(closingParenIndex - 1), closingParen) > 0) { |
| closingParen.setWrapPolicy(new WrapPolicy(WrapMode.WHERE_NECESSARY, openingParenIndex, 0)); |
| closingParen.breakBefore(); |
| } |
| break; |
| default: |
| throw new IllegalArgumentException("Unrecognized parentheses positions setting: " + positionsSetting); //$NON-NLS-1$ |
| } |
| } |
| } |