-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPositionalReaderImpl.java
105 lines (89 loc) · 2.66 KB
/
PositionalReaderImpl.java
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
package org.example.lexer;
import io.vavr.API;
import io.vavr.Function0;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.example.lexer.error.InconsistentNewLine;
import org.example.token.Position;
public class PositionalReaderImpl implements PositionalReader {
private static final int NEW_LINE_CODE = '\n';
private static final int CARRIAGE_RETURN_CODE = '\r';
private static final String NEW_LINE = "\n";
private static final String NEW_LINE_CARRIAGE_RETURN = "\n\r";
private static final String CARRIAGE_RETURN_NEW_LINE = "\r\n";
private final Map<Integer, Function0<Integer>> newLineHandlers = Map.of(
NEW_LINE_CODE, API.unchecked(this::handleNewLine),
CARRIAGE_RETURN_CODE, API.unchecked(this::handleCarriageReturn)
);
private final BufferedReader reader;
private String newLineCharacter;
private final Position position;
public PositionalReaderImpl(Reader reader) {
this.reader = toBufferedReader(reader);
this.position = new Position();
this.newLineCharacter = null;
}
public Position getPosition() {
return position.copy();
}
@Override
public int read() throws IOException {
var currentCharacter = reader.read();
advanceCharacter();
return newLineHandlers.getOrDefault(
currentCharacter,
() -> currentCharacter
).apply();
}
@Override
public int getLine() {
return position.getLine();
}
private int handleNewLine() throws IOException {
reader.mark(1);
var nextCharacter = reader.read();
if (nextCharacter == CARRIAGE_RETURN_CODE) {
validateNewLine(NEW_LINE_CARRIAGE_RETURN);
} else {
validateNewLine(NEW_LINE);
reader.reset();
}
advanceLine();
return NEW_LINE_CODE;
}
private int handleCarriageReturn() throws IOException {
reader.mark(1);
var nextCharacter = reader.read();
if (nextCharacter == NEW_LINE_CODE) {
advanceLine();
validateNewLine(CARRIAGE_RETURN_NEW_LINE);
return NEW_LINE_CODE;
} else {
reader.reset();
return CARRIAGE_RETURN_CODE;
}
}
private void validateNewLine(String detectedNewLine) {
if (newLineCharacter == null) {
newLineCharacter = detectedNewLine;
} else if (!StringUtils.equals(newLineCharacter, detectedNewLine)) {
throw new InconsistentNewLine(detectedNewLine, newLineCharacter, position);
}
}
private void advanceLine() {
position.nextLine();
}
private void advanceCharacter() {
position.nextCharacter();
}
private BufferedReader toBufferedReader(Reader abstractReader) {
if (abstractReader instanceof BufferedReader bufferedReader) {
return bufferedReader;
} else {
return new BufferedReader(abstractReader, 8);
}
}
}