-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multi source reader for parsing graphql Documents #1411
Merged
bbakerman
merged 3 commits into
graphql-java:master
from
bbakerman:multi-source-parse-input
Feb 7, 2019
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
package graphql.parser; | ||
|
||
import graphql.Assert; | ||
import graphql.PublicApi; | ||
|
||
import java.io.IOException; | ||
import java.io.LineNumberReader; | ||
import java.io.Reader; | ||
import java.io.StringReader; | ||
import java.io.UncheckedIOException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
/** | ||
* This reader allows you to read N number readers and combine them as one logical reader | ||
* however you can then map back to the underlying readers in terms of their source name | ||
* and the relative lines numbers. | ||
* | ||
* It can also track all data in memory if you want to have all of the previous read data in | ||
* place at some point in time. | ||
*/ | ||
@PublicApi | ||
public class MultiSourceReader extends Reader { | ||
|
||
private final List<SourcePart> sourceParts; | ||
private final StringBuilder data = new StringBuilder(); | ||
private int currentIndex = 0; | ||
private int overallLineNumber = 0; | ||
private final boolean trackData; | ||
|
||
|
||
private MultiSourceReader(Builder builder) { | ||
this.sourceParts = builder.sourceParts; | ||
this.trackData = builder.trackData; | ||
} | ||
|
||
@Override | ||
public int read(char[] cbuf, int off, int len) throws IOException { | ||
while (true) { | ||
synchronized (this) { | ||
if (currentIndex >= sourceParts.size()) { | ||
return -1; | ||
} | ||
SourcePart sourcePart = sourceParts.get(currentIndex); | ||
int read = sourcePart.lineReader.read(cbuf, off, len); | ||
overallLineNumber = calcLineNumber(); | ||
if (read == -1) { | ||
currentIndex++; | ||
} else { | ||
trackData(cbuf, off, read); | ||
return read; | ||
} | ||
} | ||
} | ||
} | ||
|
||
private void trackData(char[] cbuf, int off, int len) { | ||
if (trackData) { | ||
data.append(cbuf, off, len); | ||
} | ||
} | ||
|
||
private int calcLineNumber() { | ||
int linenumber = 0; | ||
for (SourcePart sourcePart : sourceParts) { | ||
linenumber += sourcePart.lineReader.getLineNumber(); | ||
} | ||
return linenumber; | ||
} | ||
|
||
public static class SourceAndLine { | ||
private String sourceName = null; | ||
private int line = 0; | ||
|
||
public String getSourceName() { | ||
return sourceName; | ||
} | ||
|
||
public int getLine() { | ||
return line; | ||
} | ||
} | ||
|
||
/** | ||
* This returns the source name and line number given an overall line number | ||
* | ||
* This is zeroes based like {@link java.io.LineNumberReader#getLineNumber()} | ||
* | ||
* @param overallLineNumber the over all line number | ||
* | ||
* @return the source name and relative line number to that source | ||
*/ | ||
public SourceAndLine getSourceAndLineFromOverallLine(int overallLineNumber) { | ||
SourceAndLine sourceAndLine = new SourceAndLine(); | ||
if (sourceParts.isEmpty()) { | ||
return sourceAndLine; | ||
} | ||
SourcePart currentPart; | ||
if (currentIndex >= sourceParts.size()) { | ||
currentPart = sourceParts.get(sourceParts.size() - 1); | ||
} else { | ||
currentPart = sourceParts.get(currentIndex); | ||
} | ||
int page = 0; | ||
int previousPage; | ||
for (SourcePart sourcePart : sourceParts) { | ||
sourceAndLine.sourceName = sourcePart.sourceName; | ||
if (sourcePart == currentPart) { | ||
// we cant go any further | ||
int offset = currentPart.lineReader.getLineNumber(); | ||
previousPage = page; | ||
page += offset; | ||
if (page > overallLineNumber) { | ||
sourceAndLine.line = overallLineNumber - previousPage; | ||
} else { | ||
sourceAndLine.line = page; | ||
} | ||
return sourceAndLine; | ||
} else { | ||
previousPage = page; | ||
page += sourcePart.lineReader.getLineNumber(); | ||
if (page > overallLineNumber) { | ||
sourceAndLine.line = overallLineNumber - previousPage; | ||
return sourceAndLine; | ||
} | ||
} | ||
} | ||
sourceAndLine.line = overallLineNumber - page; | ||
return sourceAndLine; | ||
} | ||
|
||
/** | ||
* @return the line number of the current source. This is zeroes based like {@link java.io.LineNumberReader#getLineNumber()} | ||
*/ | ||
public int getLineNumber() { | ||
synchronized (this) { | ||
if (sourceParts.isEmpty()) { | ||
return 0; | ||
} | ||
if (currentIndex >= sourceParts.size()) { | ||
return sourceParts.get(sourceParts.size() - 1).lineReader.getLineNumber(); | ||
} | ||
return sourceParts.get(currentIndex).lineReader.getLineNumber(); | ||
} | ||
} | ||
|
||
/** | ||
* @return The name of the current source | ||
*/ | ||
public String getSourceName() { | ||
synchronized (this) { | ||
if (sourceParts.isEmpty()) { | ||
return null; | ||
} | ||
if (currentIndex >= sourceParts.size()) { | ||
return sourceParts.get(sourceParts.size() - 1).sourceName; | ||
} | ||
return sourceParts.get(currentIndex).sourceName; | ||
} | ||
} | ||
|
||
/** | ||
* @return the overall line number of the all the sources. This is zeroes based like {@link java.io.LineNumberReader#getLineNumber()} | ||
*/ | ||
public int getOverallLineNumber() { | ||
return overallLineNumber; | ||
} | ||
|
||
public List<String> getData() { | ||
LineNumberReader reader = new LineNumberReader(new StringReader(data.toString())); | ||
List<String> lines = new ArrayList<>(); | ||
while (true) { | ||
try { | ||
String line = reader.readLine(); | ||
if (line == null) { | ||
return lines; | ||
} | ||
lines.add(line); | ||
} catch (IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public void close() throws IOException { | ||
synchronized (this) { | ||
for (SourcePart sourcePart : sourceParts) { | ||
if (!sourcePart.closed) { | ||
sourcePart.lineReader.close(); | ||
sourcePart.closed = true; | ||
} | ||
} | ||
} | ||
} | ||
|
||
private static class SourcePart { | ||
String sourceName; | ||
LineNumberReader lineReader; | ||
boolean closed; | ||
} | ||
|
||
|
||
public static Builder newMultiSourceReader() { | ||
return new Builder(); | ||
} | ||
|
||
public static class Builder { | ||
List<SourcePart> sourceParts = new ArrayList<>(); | ||
boolean trackData = true; | ||
|
||
private Builder() { | ||
} | ||
|
||
public Builder reader(Reader reader, String sourceName) { | ||
SourcePart sourcePart = new SourcePart(); | ||
sourcePart.lineReader = new LineNumberReader(Assert.assertNotNull(reader)); | ||
sourcePart.sourceName = sourceName; | ||
sourcePart.closed = false; | ||
sourceParts.add(sourcePart); | ||
return this; | ||
} | ||
|
||
public Builder string(String input, String sourceName) { | ||
SourcePart sourcePart = new SourcePart(); | ||
sourcePart.lineReader = new LineNumberReader(new StringReader(input)); | ||
sourcePart.sourceName = sourceName; | ||
sourcePart.closed = false; | ||
sourceParts.add(sourcePart); | ||
return this; | ||
} | ||
|
||
public Builder trackData(boolean trackData) { | ||
this.trackData = trackData; | ||
return this; | ||
|
||
} | ||
|
||
public MultiSourceReader build() { | ||
return new MultiSourceReader(this); | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you want a private constructor here maybe