Skip to main content
summaryrefslogtreecommitdiffstats
blob: f75c487b1762e8a070931a444c22b91e9aeb3119 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*******************************************************************************
 * 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;
	}
}

Back to the top