Skip to content

Commit e8513d4

Browse files
committed
java: Remove redundant ITokenScanner interface and constructors
The parser always reads the entire Gherkin document as a String so there is no need for intermediate steps involving a reader. However, the `BufferedReader` is still much more efficient than the `Scanner` so we do use that.
1 parent 0003000 commit e8513d4

File tree

7 files changed

+42
-68
lines changed

7 files changed

+42
-68
lines changed

java/gherkin-java.razor

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,12 @@ class @Model.ParserClassName<T> {
7070
private final Builder<T> builder;
7171

7272
static class ParserContext {
73-
final ITokenScanner tokenScanner;
74-
final ITokenMatcher tokenMatcher;
73+
final TokenScanner tokenScanner;
74+
final TokenMatcher tokenMatcher;
7575
final Queue<Token> tokenQueue;
7676
final List<ParserException> errors;
7777

78-
ParserContext(ITokenScanner tokenScanner, ITokenMatcher tokenMatcher, Queue<Token> tokenQueue, List<ParserException> errors) {
78+
ParserContext(TokenScanner tokenScanner, TokenMatcher tokenMatcher, Queue<Token> tokenQueue, List<ParserException> errors) {
7979
this.tokenScanner = tokenScanner;
8080
this.tokenMatcher = tokenMatcher;
8181
this.tokenQueue = tokenQueue;
@@ -88,26 +88,18 @@ class @Model.ParserClassName<T> {
8888
}
8989

9090
T parse(String source, String uri) {
91-
return parse(new StringReader(source), uri);
92-
}
93-
94-
T parse(Reader source, String uri) {
9591
return parse(new TokenScanner(source), uri);
9692
}
9793

98-
T parse(ITokenScanner tokenScanner, String uri) {
94+
T parse(TokenScanner tokenScanner, String uri) {
9995
return parse(tokenScanner, new TokenMatcher(), uri);
10096
}
10197

102-
T parse(String source, ITokenMatcher tokenMatcher, String uri) {
103-
return parse(new StringReader(source), tokenMatcher, uri);
104-
}
105-
106-
T parse(Reader source, ITokenMatcher tokenMatcher, String uri) {
98+
T parse(String source, TokenMatcher tokenMatcher, String uri) {
10799
return parse(new TokenScanner(source), tokenMatcher, uri);
108100
}
109101

110-
T parse(ITokenScanner tokenScanner, ITokenMatcher tokenMatcher, String uri) {
102+
T parse(TokenScanner tokenScanner, TokenMatcher tokenMatcher, String uri) {
111103
builder.reset(uri);
112104
tokenMatcher.reset();
113105

@@ -288,11 +280,7 @@ class @Model.ParserClassName<T> {
288280
void reset(String uri);
289281
}
290282

291-
interface ITokenScanner {
292-
Token read();
293-
}
294-
295-
interface ITokenMatcher {
283+
interface TokenMatcher {
296284
@foreach(var rule in Model.RuleSet.TokenRules)
297285
{
298286
@:boolean match_@(rule.Name.Replace("#", ""))(Token token);

java/src/main/java/io/cucumber/gherkin/TokenMatcher.java renamed to java/src/main/java/io/cucumber/gherkin/GherkinTokenMatcher.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.regex.Matcher;
66
import java.util.regex.Pattern;
77

8+
import io.cucumber.gherkin.Parser.TokenMatcher;
89
import io.cucumber.messages.types.StepKeywordType;
910

1011
import static io.cucumber.gherkin.GherkinLanguageConstants.COMMENT_PREFIX;
@@ -15,27 +16,26 @@
1516
import static io.cucumber.gherkin.GherkinLanguageConstants.TITLE_KEYWORD_SEPARATOR;
1617
import static io.cucumber.gherkin.Locations.COLUMN_OFFSET;
1718
import static io.cucumber.gherkin.Locations.atColumn;
18-
import static io.cucumber.gherkin.Parser.ITokenMatcher;
1919
import static io.cucumber.gherkin.Parser.TokenType;
2020

21-
class TokenMatcher implements ITokenMatcher {
21+
class GherkinTokenMatcher implements TokenMatcher {
2222

2323
private static final Pattern LANGUAGE_PATTERN = Pattern.compile("^#\\s*language\\s*:\\s*([a-zA-Z\\-_]+)\\s*$");
2424
private final GherkinDialectProvider dialectProvider;
2525
private GherkinDialect currentDialect;
2626
private String activeDocStringSeparator = null;
2727
private int indentToRemove = 0;
2828

29-
TokenMatcher(GherkinDialectProvider dialectProvider) {
29+
GherkinTokenMatcher(GherkinDialectProvider dialectProvider) {
3030
this.dialectProvider = dialectProvider;
3131
reset();
3232
}
3333

34-
TokenMatcher() {
34+
GherkinTokenMatcher() {
3535
this(new GherkinDialectProvider());
3636
}
3737

38-
TokenMatcher(String defaultDialectName) {
38+
GherkinTokenMatcher(String defaultDialectName) {
3939
this(new GherkinDialectProvider(defaultDialectName));
4040
}
4141

java/src/main/java/io/cucumber/gherkin/Parser.java

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,12 @@ enum RuleType {
8484
private final Builder<T> builder;
8585

8686
static class ParserContext {
87-
final ITokenScanner tokenScanner;
88-
final ITokenMatcher tokenMatcher;
87+
final TokenScanner tokenScanner;
88+
final TokenMatcher tokenMatcher;
8989
final Queue<Token> tokenQueue;
9090
final List<ParserException> errors;
9191

92-
ParserContext(ITokenScanner tokenScanner, ITokenMatcher tokenMatcher, Queue<Token> tokenQueue, List<ParserException> errors) {
92+
ParserContext(TokenScanner tokenScanner, TokenMatcher tokenMatcher, Queue<Token> tokenQueue, List<ParserException> errors) {
9393
this.tokenScanner = tokenScanner;
9494
this.tokenMatcher = tokenMatcher;
9595
this.tokenQueue = tokenQueue;
@@ -102,26 +102,18 @@ static class ParserContext {
102102
}
103103

104104
T parse(String source, String uri) {
105-
return parse(new StringReader(source), uri);
106-
}
107-
108-
T parse(Reader source, String uri) {
109105
return parse(new TokenScanner(source), uri);
110106
}
111107

112-
T parse(ITokenScanner tokenScanner, String uri) {
113-
return parse(tokenScanner, new TokenMatcher(), uri);
114-
}
115-
116-
T parse(String source, ITokenMatcher tokenMatcher, String uri) {
117-
return parse(new StringReader(source), tokenMatcher, uri);
108+
T parse(TokenScanner tokenScanner, String uri) {
109+
return parse(tokenScanner, new GherkinTokenMatcher(), uri);
118110
}
119111

120-
T parse(Reader source, ITokenMatcher tokenMatcher, String uri) {
112+
T parse(String source, TokenMatcher tokenMatcher, String uri) {
121113
return parse(new TokenScanner(source), tokenMatcher, uri);
122114
}
123115

124-
T parse(ITokenScanner tokenScanner, ITokenMatcher tokenMatcher, String uri) {
116+
T parse(TokenScanner tokenScanner, TokenMatcher tokenMatcher, String uri) {
125117
builder.reset(uri);
126118
tokenMatcher.reset();
127119

@@ -3831,11 +3823,7 @@ interface Builder<T> {
38313823
void reset(String uri);
38323824
}
38333825

3834-
interface ITokenScanner {
3835-
Token read();
3836-
}
3837-
3838-
interface ITokenMatcher {
3826+
interface TokenMatcher {
38393827
boolean match_EOF(Token token);
38403828
boolean match_Empty(Token token);
38413829
boolean match_Comment(Token token);

java/src/main/java/io/cucumber/gherkin/TokenScanner.java

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,34 @@
44

55
import java.io.BufferedReader;
66
import java.io.IOException;
7-
import java.io.Reader;
7+
import java.io.StringReader;
88

99
import static io.cucumber.gherkin.Token.createEOF;
1010
import static io.cucumber.gherkin.Token.createGherkinLine;
1111

1212
/**
13-
* <p>
14-
* The scanner reads a gherkin doc (typically read from a .feature file) and creates a token
15-
* for each line. The tokens are passed to the parser, which outputs an AST (Abstract Syntax Tree).</p>
16-
* <p>
17-
* If the scanner sees a # language header, it will reconfigure itself dynamically to look for
18-
* Gherkin keywords for the associated language. The keywords are defined in gherkin-languages.json.</p>
13+
* The scanner reads a gherkin doc (typically read from a .feature file) and
14+
* creates a token for each line. The tokens are passed to the parser, which
15+
* outputs an AST (Abstract Syntax Tree).</p>
1916
*/
20-
class TokenScanner implements Parser.ITokenScanner {
17+
class TokenScanner {
2118

2219
private final BufferedReader reader;
2320
private int lineNumber;
2421

25-
TokenScanner(Reader source) {
26-
this.reader = new BufferedReader(source);
22+
TokenScanner(String source) {
23+
this.reader = new BufferedReader(new StringReader(source));
2724
}
2825

29-
@Override
30-
public Token read() {
26+
Token read() {
3127
try {
3228
String rawText = reader.readLine();
33-
Location location = Locations.atLine(++lineNumber);
3429
if (rawText == null) {
30+
// Don't optimistically increment the line number
31+
Location location = Locations.atLine(++lineNumber);
3532
return createEOF(location);
3633
}
34+
Location location = Locations.atLine(++lineNumber);
3735
return createGherkinLine(rawText, location);
3836
} catch (IOException e) {
3937
throw new RuntimeException(e);

java/src/test/java/io/cucumber/gherkin/GenerateTokens.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
package io.cucumber.gherkin;
22

3-
import java.io.FileInputStream;
4-
import java.io.FileNotFoundException;
5-
import java.io.InputStreamReader;
6-
import java.io.Reader;
7-
import java.nio.charset.StandardCharsets;
3+
import java.io.IOException;
4+
import java.nio.file.Paths;
5+
6+
import static java.nio.charset.StandardCharsets.UTF_8;
7+
import static java.nio.file.Files.readAllBytes;
88

99
public final class GenerateTokens {
10-
public static void main(String[] args) throws FileNotFoundException {
10+
public static void main(String[] args) throws IOException {
1111
TokenFormatterBuilder builder = new TokenFormatterBuilder();
1212
Parser<String> parser = new Parser<>(builder);
13-
TokenMatcher matcher = new TokenMatcher();
13+
GherkinTokenMatcher matcher = new GherkinTokenMatcher();
1414
for (String fileName : args) {
15-
Reader in = new InputStreamReader(new FileInputStream(fileName), StandardCharsets.UTF_8);
16-
String result = parser.parse(in, matcher, fileName);
15+
byte[] bytes = readAllBytes(Paths.get(fileName));
16+
String result = parser.parse(new String(bytes, UTF_8), matcher, fileName);
1717
Stdio.out.print(result);
1818
Stdio.out.flush(); // print doesn't autoflush
1919
}

java/src/test/java/io/cucumber/gherkin/GherkinDocumentBuilderTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class GherkinDocumentBuilderTest {
2121
@Test
2222
void is_reusable() {
2323
Parser<GherkinDocument> parser = new Parser<>(new GherkinDocumentBuilder(idGenerator, "test.feature"));
24-
TokenMatcher matcher = new TokenMatcher();
24+
GherkinTokenMatcher matcher = new GherkinTokenMatcher();
2525

2626
GherkinDocument d1 = parser.parse("Feature: 1", matcher, "1.feature");
2727
GherkinDocument d2 = parser.parse("Feature: 2", matcher, "2.feature");

java/src/test/java/io/cucumber/gherkin/ParserTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class ParserTest {
1010

1111
@Test
1212
void change_default_language() {
13-
TokenMatcher matcher = new TokenMatcher("no");
13+
GherkinTokenMatcher matcher = new GherkinTokenMatcher("no");
1414
IdGenerator idGenerator = new IncrementingIdGenerator();
1515
Parser<GherkinDocument> parser = new Parser<>(new GherkinDocumentBuilder(idGenerator, "test.feature"));
1616

0 commit comments

Comments
 (0)