/******************************************************************************* * Copyright (c) 2014, 2015 Mateusz Matela and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * 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; } }