blob: f75c487b1762e8a070931a444c22b91e9aeb3119 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014, 2015 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
*******************************************************************************/
package org.eclipse.jdt.internal.formatter;
import java.util.List;
/**
* Helper class that can be subclassed every time an algorithm needs to swipe through all or part of the tokens and
* easily keep track or previous and future tokens and whitespace.
*/
public abstract class TokenTraverser {
/** General purpose field that can be used by subclasses to count things */
protected int counter = 0;
/** General purpose field that can be used by subclasses to store an integer value */
protected int value = 0;
private boolean spaceBefore, spaceAfter;
private int lineBreaksBefore, lineBreaksAfter;
private Token previous, current, next;
private boolean structureChanged = false;
protected abstract boolean token(Token token, int index);
/**
* Must be called every time tokens are added or removed from the list that is currently being traversed so that
* cached data can be refreshed.
*/
protected void structureChanged() {
this.structureChanged = true;
}
protected boolean isSpaceBefore() {
return this.spaceBefore;
}
protected boolean isSpaceAfter() {
return this.spaceAfter;
}
protected int getLineBreaksBefore() {
return this.lineBreaksBefore;
}
protected int getLineBreaksAfter() {
return this.lineBreaksAfter;
}
protected Token getPrevious() {
return this.previous;
}
protected Token getCurrent() {
return this.current;
}
protected Token getNext() {
return this.next;
}
private void initTraverse(List<Token> tokens, int startIndex) {
if (tokens.isEmpty())
return;
this.structureChanged = false;
this.previous = this.next = null;
if (startIndex > 0)
this.previous = tokens.get(startIndex - 1);
this.current = tokens.get(startIndex);
this.lineBreaksBefore = Math.max(this.previous != null ? this.previous.getLineBreaksAfter() : 0,
this.current.getLineBreaksBefore());
this.spaceBefore = this.current.isSpaceBefore();
if (this.lineBreaksBefore == 0) {
this.spaceBefore = this.spaceBefore || (this.previous != null && this.previous.isSpaceAfter());
}
}
public int traverse(List<Token> tokens, int startIndex) {
initTraverse(tokens, startIndex);
for (int i = startIndex; i < tokens.size(); i++) {
if (this.structureChanged)
initTraverse(tokens, i);
this.next = null;
if (i < tokens.size() - 1) {
this.next = tokens.get(i + 1);
}
this.lineBreaksAfter = Math.max(this.current.getLineBreaksAfter(),
this.next != null ? this.next.getLineBreaksBefore() : 0);
this.spaceAfter = this.current.isSpaceAfter();
if (this.lineBreaksAfter == 0) {
this.spaceAfter = this.spaceAfter || (this.next != null && this.next.isSpaceBefore());
}
if (!this.token(this.current, i))
return i;
if (this.next != null) {
this.previous = this.current;
this.current = this.next;
this.lineBreaksBefore = this.lineBreaksAfter;
this.spaceBefore = this.spaceAfter;
if (this.lineBreaksBefore > 0)
this.spaceBefore = this.current.isSpaceBefore();
}
}
return tokens.size() - 1;
}
}