Skip to content
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

Issue #455 getCurrentLocation() incorrect at end of input #557

Merged
merged 4 commits into from
Oct 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -217,19 +217,20 @@ protected boolean _loadMore() throws IOException
{
final int bufSize = _inputEnd;

_currInputProcessed += bufSize;
_currInputRowStart -= bufSize;

// 26-Nov-2015, tatu: Since name-offset requires it too, must offset
// this increase to avoid "moving" name-offset, resulting most likely
// in negative value, which is fine as combine value remains unchanged.
_nameStartOffset -= bufSize;

if (_reader != null) {
int count = _reader.read(_inputBuffer, 0, _inputBuffer.length);
if (count > 0) {
_inputPtr = 0;
_inputEnd = count;

_currInputProcessed += bufSize;
_currInputRowStart -= bufSize;

// 26-Nov-2015, tatu: Since name-offset requires it too, must offset
// this increase to avoid "moving" name-offset, resulting most likely
// in negative value, which is fine as combine value remains unchanged.
_nameStartOffset -= bufSize;

return true;
}
// End of input
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,14 +185,6 @@ protected final boolean _loadMore() throws IOException
{
final int bufSize = _inputEnd;

_currInputProcessed += _inputEnd;
_currInputRowStart -= _inputEnd;

// 26-Nov-2015, tatu: Since name-offset requires it too, must offset
// this increase to avoid "moving" name-offset, resulting most likely
// in negative value, which is fine as combine value remains unchanged.
_nameStartOffset -= bufSize;

if (_inputStream != null) {
int space = _inputBuffer.length;
if (space == 0) { // only occurs when we've been closed
Expand All @@ -203,6 +195,15 @@ protected final boolean _loadMore() throws IOException
if (count > 0) {
_inputPtr = 0;
_inputEnd = count;

_currInputProcessed += _inputEnd;
_currInputRowStart -= _inputEnd;

// 26-Nov-2015, tatu: Since name-offset requires it too, must offset
// this increase to avoid "moving" name-offset, resulting most likely
// in negative value, which is fine as combine value remains unchanged.
_nameStartOffset -= bufSize;

return true;
}
// End of input
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
package com.fasterxml.jackson.core.read;

import java.util.Arrays;
import java.util.List;

import com.fasterxml.jackson.core.BaseTest;
import com.fasterxml.jackson.core.JsonLocation;
import com.fasterxml.jackson.core.JsonParser;

/**
* Set of tests that checks getCurrentLocation() and getTokenLocation() are as expected during
* parsing.
*/
public class LocationDuringReaderParsingTest extends BaseTest
{
public void testLocationAtEndOfParse() throws Exception
{
for (LocationTestCase test : LocationTestCase.values()) {
//System.out.println(test.name());
testLocationAtEndOfParse(test);
}
}

public void testInitialLocation() throws Exception
{
for (LocationTestCase test : LocationTestCase.values()) {
//System.out.println(test.name());
testInitialLocation(test);
}
}

public void testTokenLocations() throws Exception
{
for (LocationTestCase test : LocationTestCase.values()) {
//System.out.println(test.name());
testTokenLocations(test);
}
}

private void testLocationAtEndOfParse(LocationTestCase test) throws Exception
{
JsonParser p = createParserUsingReader(test.json);
while (p.nextToken() != null) {
p.nextToken();
}
assertCurrentLocation(p, test.getFinalLocation());
p.close();
}

private void testInitialLocation(LocationTestCase test) throws Exception
{
JsonParser p = createParserUsingReader(test.json);
JsonLocation loc = p.getCurrentLocation();
p.close();

assertLocation(loc, at(1, 1, 0));
}

private void testTokenLocations(LocationTestCase test) throws Exception
{
JsonParser p = createParserUsingReader(test.json);
int i = 0;
while (p.nextToken() != null) {
assertTokenLocation(p, test.locations.get(i));
i++;
}
assertEquals(test.locations.size(), i + 1); // last LocData is end of stream
p.close();
}

private void assertCurrentLocation(JsonParser p, LocData loc)
{
assertLocation(p.getCurrentLocation(), loc);
}

private void assertTokenLocation(JsonParser p, LocData loc)
{
assertLocation(p.getTokenLocation(), loc);
}

private void assertLocation(JsonLocation pLoc, LocData loc)
{
String expected = String.format("(%d, %d, %d)",
loc.lineNumber, loc.columnNumber, loc.offset);
String actual = String.format("(%d, %d, %d)", pLoc.getLineNr(), pLoc.getColumnNr(),
pLoc.getByteOffset() == -1 ? pLoc.getCharOffset() : pLoc.getByteOffset());
assertEquals(expected, actual);
}

private static class LocData
{
private long lineNumber;
private long columnNumber;
private long offset;

LocData(long lineNumber, long columnNumber, long offset)
{
this.lineNumber = lineNumber;
this.columnNumber = columnNumber;
this.offset = offset;
}
}

private static LocData at(long lineNumber, long columnNumber, long offset)
{
return new LocData(lineNumber, columnNumber, offset);
}

/**
* Adapted liberally from https://github.com/leadpony/jsonp-test-suite, also
* released under the Apache License v2.
*/
enum LocationTestCase
{
SIMPLE_VALUE("42", at(1, 1, 0), at(1, 3, 2)),

SIMPLE_VALUE_WITH_PADDING(" 1337 ", at(1, 4, 3), at(1, 10, 9)),

SIMPLE_VALUE_WITH_MULTIBYTE_CHARS("\"Правда\"",
at(1, 1, 0),
at(1, 9, 8)
),

SIMPLE_VALUE_INCLUDING_SURROGATE_PAIR_CHARS("\"a П \uD83D\uDE01\"",
at(1, 1, 0),
at(1, 9, 8) // reader counts surrogate pairs as two chars
),

ARRAY_IN_ONE_LINE("[\"hello\",42,true]",
at(1, 1, 0), // [
at(1, 2, 1), // "hello"
at(1, 10, 9), // 42
at(1, 13, 12), // true
at(1, 17, 16), // ]
at(1, 18, 17) // end of input
),

ARRAY_IN_ONE_LINE_WITH_PADDING(" [ \"hello\" , 42 , true ] ",
at(1, 3, 2), // [
at(1, 5, 4), // "hello"
at(1, 17, 16), // 42
at(1, 26, 25), // true
at(1, 33, 32), // ]
at(1, 37, 36) // end of input
),

ARRAY_IN_MULTIPLE_LINES("[\n" + " \"hello\",\n" + " 42,\n" + " true\n" + "]",
at(1, 1, 0), // [
at(2, 5, 6), // "hello"
at(3, 5, 19), // 42
at(4, 5, 27), // true
at(5, 1, 32), // ]
at(5, 2, 33) // end of input
),

ARRAY_IN_MULTIPLE_LINES_WITH_WEIRD_SPACING(" [\n" + " \"hello\" , \n" + " 42 ,\n" + " true\n" + " ]",
at(1, 2, 1), // [
at(2, 3, 5), // "hello"
at(3, 2, 18), // 42
at(4, 7, 31), // true
at(5, 2, 37), // ]
at(5, 3, 38) // end of input
),

ARRAY_IN_MULTIPLE_LINES_CRLF("[\r\n" + " \"hello\",\r\n" + " 42,\r\n" + " true\r\n" + "]",
at(1, 1, 0), // [
at(2, 5, 7), // "hello"
at(3, 5, 21), // 42
at(4, 5, 30), // true
at(5, 1, 36), // ]
at(5, 2, 37) // end of input
),

OBJECT_IN_ONE_LINE("{\"first\":\"hello\",\"second\":42}",
at(1, 1, 0), // {
at(1, 2, 1), // "first"
at(1, 10, 9), // "hello"
at(1, 18, 17), // "second"
at(1, 27, 26), // 42
at(1, 29, 28), // }
at(1, 30, 29) // end of input
),

OBJECT_IN_MULTIPLE_LINES("{\n" + " \"first\":\"hello\",\n" + " \"second\":42\n" + "}",
at(1, 1, 0), // {
at(2, 5, 6), // "first"
at(2, 13, 14), // "hello"
at(3, 5, 27), // "second"
at(3, 14, 36), // 42
at(4, 1, 39), // }
at(4, 2, 40) // end of input
),

OBJECT_IN_MULTIPLE_LINES_CRLF("{\r\n" + " \"first\":\"hello\",\r\n" + " \"second\":42\r\n" + "}",
at(1, 1, 0), // {
at(2, 5, 7), // "first"
at(2, 13, 15), // "hello"
at(3, 5, 29), // "second"
at(3, 14, 38), // 42
at(4, 1, 42), // }
at(4, 2, 43) // end of input
),
;

final String json;
final List<LocData> locations;

LocationTestCase(String json, LocData... locations)
{
this.json = json;
this.locations = Arrays.asList(locations);
}

LocData getFinalLocation()
{
return locations.get(locations.size() - 1);
}
}
}