Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 46 additions & 7 deletions src/main/java/graphql/parser/MultiSourceReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,28 @@
@PublicApi
public class MultiSourceReader extends Reader {

// In Java version 16+, LineNumberReader.read considers end-of-stream to be a line terminator
// and will increment the line number, whereas in previous versions it doesn't.
private static final boolean LINE_NUMBER_READER_EOS_IS_TERMINATOR;

private final List<SourcePart> sourceParts;
private final StringBuilder data = new StringBuilder();
private int currentIndex = 0;
private int overallLineNumber = 0;
private final boolean trackData;
private final LockKit.ReentrantLock readerLock = new LockKit.ReentrantLock();

static {
LineNumberReader reader = new LineNumberReader(new StringReader("a"));
try {
reader.read();
reader.read();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
LINE_NUMBER_READER_EOS_IS_TERMINATOR = reader.getLineNumber() > 0;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor pick - I would encapsulate this into a static method

So you can do

private static final boolean LINE_NUMBER_READER_EOS_IS_TERMINATOR = determineStuff();


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clever!


private MultiSourceReader(Builder builder) {
this.sourceParts = builder.sourceParts;
Expand All @@ -46,10 +61,16 @@ public int read(char[] cbuf, int off, int len) throws IOException {
}
SourcePart sourcePart = sourceParts.get(currentIndex);
int read = sourcePart.lineReader.read(cbuf, off, len);
overallLineNumber = calcLineNumber();
if (read == -1) {
currentIndex++;
} else {
sourcePart.reachedEndOfStream = true;
} else if (read > 0) {
sourcePart.lastRead = cbuf[off + read - 1];
}
// note: calcLineNumber() must be called after updating sourcePart.reachedEndOfStream
// and sourcePart.lastRead
overallLineNumber = calcLineNumber();
if (read != -1) {
trackData(cbuf, off, read);
return read;
}
Expand All @@ -68,7 +89,7 @@ private void trackData(char[] cbuf, int off, int len) {
private int calcLineNumber() {
int linenumber = 0;
for (SourcePart sourcePart : sourceParts) {
linenumber += sourcePart.lineReader.getLineNumber();
linenumber += sourcePart.getLineNumber();
}
return linenumber;
}
Expand Down Expand Up @@ -125,7 +146,7 @@ public SourceAndLine getSourceAndLineFromOverallLine(int overallLineNumber) {
sourceAndLine.sourceName = sourcePart.sourceName;
if (sourcePart == currentPart) {
// we cant go any further
int partLineNumber = currentPart.lineReader.getLineNumber();
int partLineNumber = currentPart.getLineNumber();
previousPage = page;
page += partLineNumber;
if (page > overallLineNumber) {
Expand All @@ -136,7 +157,7 @@ public SourceAndLine getSourceAndLineFromOverallLine(int overallLineNumber) {
return sourceAndLine;
} else {
previousPage = page;
int partLineNumber = sourcePart.lineReader.getLineNumber();
int partLineNumber = sourcePart.getLineNumber();
page += partLineNumber;
if (page > overallLineNumber) {
sourceAndLine.line = overallLineNumber - previousPage;
Expand All @@ -157,9 +178,9 @@ public int getLineNumber() {
return 0;
}
if (currentIndex >= sourceParts.size()) {
return sourceParts.get(sourceParts.size() - 1).lineReader.getLineNumber();
return sourceParts.get(sourceParts.size() - 1).getLineNumber();
}
return sourceParts.get(currentIndex).lineReader.getLineNumber();
return sourceParts.get(currentIndex).getLineNumber();
});
}

Expand Down Expand Up @@ -220,6 +241,24 @@ private static class SourcePart {
String sourceName;
LineNumberReader lineReader;
boolean closed;
char lastRead;
boolean reachedEndOfStream = false;

/**
* This handles the discrepancy between LineNumberReader.getLineNumber() for Java versions
* 16+ vs below. Use this instead of lineReader.getLineNumber() directly.
* @return The current line number. EOS is not considered a line terminator.
*/
int getLineNumber() {
int lineNumber = lineReader.getLineNumber();
if (reachedEndOfStream
&& LINE_NUMBER_READER_EOS_IS_TERMINATOR
&& lastRead != '\r'
&& lastRead != '\n') {
return Math.max(lineNumber - 1, 0);
}
return lineNumber;
}
}


Expand Down