All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jdt.internal.formatter.align.Alignment Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2000, 2013 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.formatter.align;

import org.eclipse.core.runtime.Assert;
import org.eclipse.jdt.internal.formatter.Location;
import org.eclipse.jdt.internal.formatter.Scribe;

/**
 * Alignment management
 *
 * @since 2.1
 */
public class Alignment {

	// Kind of alignment
	public int kind;
	public static final int ALLOCATION = 1;
	public static final int ANNOTATION_MEMBERS_VALUE_PAIRS = 2;
	public static final int ARRAY_INITIALIZER = 3;
	public static final int ASSIGNMENT = 4;
	public static final int BINARY_EXPRESSION = 5;
	public static final int CASCADING_MESSAGE_SEND = 6;
	public static final int COMPACT_IF = 7;
	public static final int COMPOUND_ASSIGNMENT = 8;
	public static final int CONDITIONAL_EXPRESSION = 9;
	public static final int ENUM_CONSTANTS = 10;
	public static final int ENUM_CONSTANTS_ARGUMENTS = 11;
	public static final int EXPLICIT_CONSTRUCTOR_CALL = 12;
	public static final int FIELD_DECLARATION_ASSIGNMENT = 13;
	public static final int LOCAL_DECLARATION_ASSIGNMENT = 14;
	public static final int MESSAGE_ARGUMENTS = 15;
	public static final int MESSAGE_SEND = 16;
	public static final int METHOD_ARGUMENTS = 17;
	public static final int METHOD_DECLARATION = 18;
	public static final int MULTIPLE_FIELD = 19;
	public static final int SUPER_CLASS = 20;
	public static final int SUPER_INTERFACES = 21;
	public static final int THROWS = 22;
	public static final int TYPE_MEMBERS = 23;
	public static final int STRING_CONCATENATION = 24;
	public static final int TRY_RESOURCES = 25;
	public static final int MULTI_CATCH = 26;

	// name of alignment
	public String name;
	public static final String[] NAMES = {
		"", //$NON-NLS-1$
		"allocation", //$NON-NLS-1$
		"annotationMemberValuePairs", //$NON-NLS-1$
		"array_initializer", //$NON-NLS-1$
		"assignmentAlignment", //$NON-NLS-1$
		"binaryExpressionAlignment", //$NON-NLS-1$
		"cascadingMessageSendAlignment", //$NON-NLS-1$
		"compactIf", //$NON-NLS-1$
		"compoundAssignmentAlignment", //$NON-NLS-1$
		"conditionalExpression", //$NON-NLS-1$
		"enumConstants", //$NON-NLS-1$
		"enumConstantArguments", //$NON-NLS-1$
		"explicit_constructor_call", //$NON-NLS-1$
		"fieldDeclarationAssignmentAlignment", //$NON-NLS-1$
		"localDeclarationAssignmentAlignment", //$NON-NLS-1$
		"messageArguments", //$NON-NLS-1$
		"messageAlignment", //$NON-NLS-1$
		"methodArguments", //$NON-NLS-1$
		"methodDeclaration", //$NON-NLS-1$
		"multiple_field", //$NON-NLS-1$
		"superclass", //$NON-NLS-1$
		"superInterfaces", //$NON-NLS-1$
		"throws", //$NON-NLS-1$
		"typeMembers", //$NON-NLS-1$
		"stringConcatenation", //$NON-NLS-1$
		"tryResources", //$NON-NLS-1$
		"unionTypeInMulticatch", //$NON-NLS-1$
	};

	// link to enclosing alignment
	public Alignment enclosing;

	// start location of this alignment
	public Location location;

	// indentation management
	public int fragmentIndex;
	public int fragmentCount;
	public int[] fragmentIndentations;
	public boolean needRedoColumnAlignment;

	// chunk management
	public int chunkStartIndex;
	public int chunkKind;

	// break management
	public int originalIndentationLevel;
	public int breakIndentationLevel;
	public int shiftBreakIndentationLevel;
	public int[] fragmentBreaks;
	public boolean wasSplit;
	public boolean blockAlign = false;
	public boolean tooLong = false;

	public Scribe scribe;

	// reset
	private boolean reset = false;

	/*
	 * Alignment modes
	 */
	public static final int M_FORCE = 1; // if bit set, then alignment will be non-optional (default is optional)
	public static final int M_INDENT_ON_COLUMN = 2; // if bit set, broken fragments will be aligned on current location column (default is to break at current indentation level)
	public static final int	M_INDENT_BY_ONE = 4; // if bit set, broken fragments will be indented one level below current (not using continuation indentation)

	// split modes can be combined either with M_FORCE or M_INDENT_ON_COLUMN

	/** foobar(#fragment1, #fragment2, 
    *
  • #fragment3, #fragment4
  • *
*/ public static final int M_COMPACT_SPLIT = 16; // fill each line with all possible fragments /** foobar(
    *
  • #fragment1, #fragment2,
  • *
  • #fragment3, #fragment4,
  • *
*/ public static final int M_COMPACT_FIRST_BREAK_SPLIT = 32; // compact mode, but will first try to break before first fragment /** foobar(
    *
  • #fragment1,
  • *
  • #fragment2,
  • *
  • #fragment3
  • *
  • #fragment4,
  • *
*/ public static final int M_ONE_PER_LINE_SPLIT = 32+16; // one fragment per line /** * foobar(
    *
  • #fragment1,
  • *
  • #fragment2,
  • *
  • #fragment3
  • *
  • #fragment4,
  • *
*/ public static final int M_NEXT_SHIFTED_SPLIT = 64; // one fragment per line, subsequent are indented further /** foobar(#fragment1,
    *
  • #fragment2,
  • *
  • #fragment3
  • *
  • #fragment4,
  • *
*/ public static final int M_NEXT_PER_LINE_SPLIT = 64+16; // one per line, except first fragment (if possible) //64+32 //64+32+16 // mode controlling column alignments /** * * * * *
#fragment1A #fragment2A #fragment3A #very-long-fragment4A
#fragment1B #long-fragment2B #fragment3B #fragment4B
#very-long-fragment1C #fragment2C #fragment3C #fragment4C
*/ public static final int M_MULTICOLUMN = 256; // fragments are on same line, but multiple line of fragments will be aligned vertically public static final int M_NO_ALIGNMENT = 0; public int mode; public static final int SPLIT_MASK = M_ONE_PER_LINE_SPLIT | M_NEXT_SHIFTED_SPLIT | M_COMPACT_SPLIT | M_COMPACT_FIRST_BREAK_SPLIT | M_NEXT_PER_LINE_SPLIT; // alignment tie-break rules - when split is needed, will decide whether innermost/outermost alignment is to be chosen public static final int R_OUTERMOST = 1; public static final int R_INNERMOST = 2; public int tieBreakRule; public int startingColumn = -1; // alignment effects on a per fragment basis public static final int NONE = 0; public static final int BREAK = 1; // chunk kind public static final int CHUNK_FIELD = 1; public static final int CHUNK_METHOD = 2; public static final int CHUNK_TYPE = 3; public static final int CHUNK_ENUM = 4; // location to align and break on. public Alignment(int kind, int mode, int tieBreakRule, Scribe scribe, int fragmentCount, int sourceRestart, int continuationIndent){ Assert.isTrue(kind >=ALLOCATION && kind <=MULTI_CATCH); this.kind = kind; this.name = NAMES[kind]; this.location = new Location(scribe, sourceRestart); this.mode = mode; this.tieBreakRule = tieBreakRule; this.fragmentCount = fragmentCount; this.scribe = scribe; this.originalIndentationLevel = this.scribe.indentationLevel; this.wasSplit = false; // initialize the break indentation level, using modes and continuationIndentationLevel preference final int indentSize = this.scribe.indentationSize; int currentColumn = this.location.outputColumn; if (currentColumn == 1) { currentColumn = this.location.outputIndentationLevel + 1; } if ((mode & M_INDENT_ON_COLUMN) != 0) { // indent broken fragments at next indentation level, based on current column this.breakIndentationLevel = this.scribe.getNextIndentationLevel(currentColumn); if (this.breakIndentationLevel == this.location.outputIndentationLevel) { this.breakIndentationLevel += (continuationIndent * indentSize); } } else if ((mode & M_INDENT_BY_ONE) != 0) { // indent broken fragments exactly one level deeper than current indentation this.breakIndentationLevel = this.location.outputIndentationLevel + indentSize; } else { this.breakIndentationLevel = this.location.outputIndentationLevel + continuationIndent * indentSize; } this.shiftBreakIndentationLevel = this.breakIndentationLevel + indentSize; this.fragmentIndentations = new int[this.fragmentCount]; this.fragmentBreaks = new int[this.fragmentCount]; // check for forced alignments if ((this.mode & M_FORCE) != 0) { couldBreak(); } } public boolean checkChunkStart(int chunk, int startIndex, int sourceRestart) { if (this.chunkKind != chunk) { this.chunkKind = chunk; // when redoing same chunk alignment, must not reset if (startIndex != this.chunkStartIndex) { this.chunkStartIndex = startIndex; this.location.update(this.scribe, sourceRestart); reset(); } return true; } return false; } public void checkColumn() { if ((this.mode & M_MULTICOLUMN) != 0 && this.fragmentCount > 0) { int currentIndentation = this.scribe.getNextIndentationLevel(this.scribe.column+(this.scribe.needSpace ? 1 : 0)); int fragmentIndentation = this.fragmentIndentations[this.fragmentIndex]; if (currentIndentation > fragmentIndentation) { this.fragmentIndentations[this.fragmentIndex] = currentIndentation; if (fragmentIndentation != 0) { for (int i = this.fragmentIndex+1; i < this.fragmentCount; i++) { this.fragmentIndentations[i] = 0; } this.needRedoColumnAlignment = true; } } // backtrack only once all fragments got checked if (this.needRedoColumnAlignment && this.fragmentIndex == this.fragmentCount-1) { // alignment too small // if (CodeFormatterVisitor.DEBUG){ // System.out.println("ALIGNMENT TOO SMALL"); // System.out.println(this); // } this.needRedoColumnAlignment = false; int relativeDepth = 0; Alignment targetAlignment = this.scribe.memberAlignment; while (targetAlignment != null){ if (targetAlignment == this){ throw new AlignmentException(AlignmentException.ALIGN_TOO_SMALL, relativeDepth); } targetAlignment = targetAlignment.enclosing; relativeDepth++; } } } } public int depth() { int depth = 0; Alignment current = this.enclosing; while (current != null) { depth++; current = current.enclosing; } return depth; } /** * Returns whether the alignment can be aligned or not. * Only used for message send alignment, it currently blocks its alignment * when it's at the first nesting level of a message send. It allow to save * space on the argument broken line by reducing the number of indentations. */ public boolean canAlign() { if (this.tooLong) { return true; } boolean canAlign = true; Alignment enclosingAlignment = this.enclosing; while (enclosingAlignment != null) { switch (enclosingAlignment.kind) { case Alignment.ALLOCATION: case Alignment.MESSAGE_ARGUMENTS: // message send inside arguments, avoid to align if (enclosingAlignment.isWrapped() && (enclosingAlignment.fragmentIndex > 0 || enclosingAlignment.fragmentCount < 2)) { return !this.blockAlign; } if (enclosingAlignment.tooLong) { return true; } canAlign = false; break; case Alignment.MESSAGE_SEND: // multiple depth of message send, hence allow current to align switch (this.kind) { case Alignment.ALLOCATION: case Alignment.MESSAGE_ARGUMENTS: case Alignment.MESSAGE_SEND: Alignment superEnclosingAlignment = enclosingAlignment.enclosing; while (superEnclosingAlignment != null) { switch (superEnclosingAlignment.kind) { case Alignment.ALLOCATION: case Alignment.MESSAGE_ARGUMENTS: case Alignment.MESSAGE_SEND: // block the alignment of the intermediate message send if (this.scribe.nlsTagCounter == 0) { enclosingAlignment.blockAlign = true; } return !this.blockAlign; } superEnclosingAlignment = superEnclosingAlignment.enclosing; } break; } return !this.blockAlign; } enclosingAlignment = enclosingAlignment.enclosing; } return canAlign && !this.blockAlign; } public boolean couldBreak(){ if (this.fragmentCount == 0) return false; int i; switch(this.mode & SPLIT_MASK){ /* # aligned fragment * foo( * #AAAAA, #BBBBB, * #CCCC); */ case M_COMPACT_FIRST_BREAK_SPLIT : if (this.fragmentBreaks[0] == NONE) { this.fragmentBreaks[0] = BREAK; this.fragmentIndentations[0] = this.breakIndentationLevel; return this.wasSplit = true; } i = this.fragmentIndex; do { if (this.fragmentBreaks[i] == NONE) { this.fragmentBreaks[i] = BREAK; this.fragmentIndentations[i] = this.breakIndentationLevel; return this.wasSplit = true; } } while (--i >= 0); break; /* # aligned fragment * foo(#AAAAA, #BBBBB, * #CCCC); */ case M_COMPACT_SPLIT : i = this.fragmentIndex; do { if (this.fragmentBreaks[i] == NONE) { this.fragmentBreaks[i] = BREAK; this.fragmentIndentations[i] = this.breakIndentationLevel; return this.wasSplit = true; } } while (--i >= 0); break; /* # aligned fragment * foo( * #AAAAA, * #BBBBB, * #CCCC); */ case M_NEXT_SHIFTED_SPLIT : if (this.fragmentBreaks[0] == NONE) { this.fragmentBreaks[0] = BREAK; this.fragmentIndentations[0] = this.breakIndentationLevel; for (i = 1; i < this.fragmentCount; i++){ this.fragmentBreaks[i] = BREAK; this.fragmentIndentations[i] = this.shiftBreakIndentationLevel; } return this.wasSplit = true; } break; /* # aligned fragment * foo( * #AAAAA, * #BBBBB, * #CCCC); */ case M_ONE_PER_LINE_SPLIT : if (this.fragmentBreaks[0] == NONE) { for (i = 0; i < this.fragmentCount; i++){ this.fragmentBreaks[i] = BREAK; this.fragmentIndentations[i] = this.breakIndentationLevel; } return this.wasSplit = true; } break; /* # aligned fragment * foo(#AAAAA, * #BBBBB, * #CCCC); */ case M_NEXT_PER_LINE_SPLIT : if (this.fragmentBreaks[0] == NONE) { if (this.fragmentCount > 1 && this.fragmentBreaks[1] == NONE) { if ((this.mode & M_INDENT_ON_COLUMN) != 0) { this.fragmentIndentations[0] = this.breakIndentationLevel; } for (i = 1; i < this.fragmentCount; i++) { this.fragmentBreaks[i] = BREAK; this.fragmentIndentations[i] = this.breakIndentationLevel; } return this.wasSplit = true; } } break; } return false; // cannot split better } public boolean isWrapped() { if (this.fragmentCount == 0) return false; return this.fragmentBreaks[this.fragmentIndex] == BREAK; } public int wrappedIndex() { for (int i = 0, max = this.fragmentCount; i < max; i++) { if (this.fragmentBreaks[i] == BREAK) { return i; } } return -1; } // perform alignment effect for current fragment public void performFragmentEffect(){ if (this.fragmentCount == 0) return; if ((this.mode & M_MULTICOLUMN) == 0) { switch(this.mode & SPLIT_MASK) { case Alignment.M_COMPACT_SPLIT : case Alignment.M_COMPACT_FIRST_BREAK_SPLIT : case Alignment.M_NEXT_PER_LINE_SPLIT : case Alignment.M_NEXT_SHIFTED_SPLIT : case Alignment.M_ONE_PER_LINE_SPLIT : break; default: return; } } int fragmentIndentation = this.fragmentIndentations[this.fragmentIndex]; if (this.startingColumn < 0 || (fragmentIndentation+1) < this.startingColumn) { if (this.fragmentBreaks[this.fragmentIndex] == BREAK) { this.scribe.printNewLine(); } if (fragmentIndentation > 0) { this.scribe.indentationLevel = fragmentIndentation; } } } // reset fragment indentation/break status public void reset() { this.wasSplit = false; if (this.fragmentCount > 0){ this.fragmentIndentations = new int[this.fragmentCount]; this.fragmentBreaks = new int[this.fragmentCount]; } // check for forced alignments if ((this.mode & M_FORCE) != 0) { couldBreak(); } this.reset = true; } public void toFragmentsString(StringBuffer buffer){ // default implementation } public String toString() { StringBuffer buffer = new StringBuffer(10); return toString(buffer, -1); } public String toString(StringBuffer buffer, int level) { // Compute the indentation at the given level StringBuffer indentation = new StringBuffer(); for (int i=0; i "); //$NON-NLS-1$ buffer .append("\n"); //$NON-NLS-1$ // Line for depth and break indentation buffer.append(indentation); buffer .append("\n"); //$NON-NLS-1$ // Line to display the location buffer.append(indentation); buffer .append("\n"); //$NON-NLS-1$ // Lines for fragments buffer .append(indentation) .append(" 0 ? "YES" : "NO") //$NON-NLS-1$ //$NON-NLS-2$ .append(">") //$NON-NLS-1$ .append("\n"); //$NON-NLS-1$ } buffer .append(indentation) .append(">\n"); //$NON-NLS-1$ // Display enclosing if (this.enclosing != null && level >= 0) { buffer .append(indentation) .append("\n"); //$NON-NLS-1$ } // Return the result return buffer.toString(); } public void update() { for (int i = 1; i < this.fragmentCount; i++){ if (this.fragmentBreaks[i] == BREAK) { this.fragmentIndentations[i] = this.breakIndentationLevel; } } } public boolean wasReset() { return this.reset; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy