Skip to content

Commit a6c2976

Browse files
committed
Fix #652
1 parent 4e44508 commit a6c2976

File tree

8 files changed

+112
-22
lines changed

8 files changed

+112
-22
lines changed

release-notes/CREDITS-2.x

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,11 +258,15 @@ Jonathan Haber (jhaber@github)
258258
* Contributed #573: More customizable TokenFilter inclusion (using `Tokenfilter.Inclusion`)
259259
(2.12.0)
260260

261+
Greg Wittel (gwittel@github)
262+
* Reported #652: Misleading exception for input source when processing byte buffer
263+
with start offset
264+
(2.13.0)
265+
261266
Ferenc Csaky (ferenc-csaky@github)
262267
* Contributed #677: Introduce O(n^1.5) BigDecimal parser implementation
263268
(2.13.0)
264269

265270
Fabian Meumertzheim (fmeum@github)
266271
* Reported #692: UTF32Reader ArrayIndexOutOfBoundsException
267272
(2.13.0)
268-

release-notes/VERSION-2.x

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ JSON library.
1616

1717
2.13.0 (not yet released)
1818

19+
#652: Misleading exception for input source when processing byte buffer
20+
with start offset
21+
(reported by Greg W)
1922
#664: Add `StreamWriteException` type to eventually replace `JsonGenerationException`
2023
#671: Replace `getCurrentLocation()`/`getTokenLocation()` with
2124
`currentLocation()`/`currentTokenLocation()` in `JsonParser`

src/main/java/com/fasterxml/jackson/core/JsonLocation.java

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -238,35 +238,67 @@ protected StringBuilder _appendSourceDesc(StringBuilder sb)
238238
// and then, include (part of) contents for selected types
239239
// (never for binary-format data)
240240
if (_contentReference.hasTextualContent()) {
241-
int len;
242-
String charStr = " chars";
243-
241+
// First, retrieve declared offset+length for content; handle
242+
// negative markers (can't do more for general case)
243+
int offset, length;
244+
offset = _contentReference.contentOffset();
245+
if (offset < 0) {
246+
offset = 0;
247+
length = 0;
248+
} else {
249+
length = Math.max(0, _contentReference.contentLength());
250+
}
251+
252+
String unitStr = " chars";
253+
String trimmed;
254+
244255
if (srcRef instanceof CharSequence) {
245-
CharSequence cs = (CharSequence) srcRef;
246-
len = cs.length();
247-
len -= _append(sb, cs.subSequence(0, Math.min(len, MAX_CONTENT_SNIPPET)).toString());
256+
trimmed = _truncate((CharSequence) srcRef, offset, length);
248257
} else if (srcRef instanceof char[]) {
249-
char[] ch = (char[]) srcRef;
250-
len = ch.length;
251-
len -= _append(sb, new String(ch, 0, Math.min(len, MAX_CONTENT_SNIPPET)));
258+
trimmed = _truncate((char[]) srcRef, offset, length);
252259
} else if (srcRef instanceof byte[]) {
253-
byte[] b = (byte[]) srcRef;
254-
int maxLen = Math.min(b.length, MAX_CONTENT_SNIPPET);
255-
_append(sb, new String(b, 0, maxLen, Charset.forName("UTF-8")));
256-
len = b.length - maxLen;
257-
charStr = " bytes";
260+
trimmed = _truncate((byte[]) srcRef, offset, length);
261+
unitStr = " bytes";
258262
} else {
259-
len = 0;
263+
trimmed = null;
260264
}
261-
if (len > 0) {
262-
sb.append("[truncated ").append(len).append(charStr).append(']');
265+
if (trimmed != null) {
266+
_append(sb, trimmed);
267+
final int truncLen = length - trimmed.length();
268+
if (truncLen > 0) {
269+
sb.append("[truncated ").append(truncLen).append(unitStr).append(']');
270+
}
263271
}
264272
} else {
265-
273+
// What should we do with binary content?
266274
}
267275
return sb;
268276
}
269277

278+
private String _truncate(CharSequence cs, int start, int length) {
279+
final int fullLength = cs.length();
280+
start = Math.min(start, fullLength);
281+
length = Math.min(Math.min(length, fullLength - start),
282+
MAX_CONTENT_SNIPPET);
283+
return cs.subSequence(start, start+length).toString();
284+
}
285+
286+
private String _truncate(char[] cs, int start, int length) {
287+
final int fullLength = cs.length;
288+
start = Math.min(start, fullLength);
289+
length = Math.min(Math.min(length, fullLength - start),
290+
MAX_CONTENT_SNIPPET);
291+
return new String(cs, start, length);
292+
}
293+
294+
private String _truncate(byte[] b, int start, int length) {
295+
final int fullLength = b.length;
296+
start = Math.min(start, fullLength);
297+
length = Math.min(Math.min(length, fullLength - start),
298+
MAX_CONTENT_SNIPPET);
299+
return new String(b, start, length, Charset.forName("UTF-8"));
300+
}
301+
270302
private int _append(StringBuilder sb, String content) {
271303
sb.append('"').append(content).append('"');
272304
return content.length();

src/main/java/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public abstract class JsonGeneratorImpl extends GeneratorBase
4646
/**********************************************************
4747
*/
4848

49-
final protected IOContext _ioContext;
49+
protected final IOContext _ioContext;
5050

5151
/*
5252
/**********************************************************

src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,13 @@ public ReaderBasedJsonParser(IOContext ctxt, int features, Reader r,
144144
{
145145
super(ctxt, features);
146146
_reader = r;
147+
_objectCodec = codec;
147148
_inputBuffer = inputBuffer;
148149
_inputPtr = start;
149150
_inputEnd = end;
150-
_objectCodec = codec;
151+
_currInputRowStart = start;
152+
// If we have offset, need to omit that from byte offset, so:
153+
_currInputProcessed = -start;
151154
_symbols = st;
152155
_hashSeed = st.hashSeed();
153156
_bufferRecyclable = bufferRecyclable;

src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3619,7 +3619,10 @@ protected void _reportInvalidToken(String matchedPart, String msg) throws IOExce
36193619
char c = (char) _decodeCharForError(i);
36203620
if (!Character.isJavaIdentifierPart(c)) {
36213621
// 11-Jan-2016, tatu: note: we will fully consume the character,
3622-
// included or not, so if recovery was possible, it'd be off-by-one...
3622+
// included or not, so if recovery was possible, it'd be off-by-one...
3623+
// 04-Apr-2021, tatu: ... and the reason we can't do much about it is
3624+
// because it may be multi-byte UTF-8 character (and even if saved
3625+
// offset, on buffer boundary it would not work, still)
36233626
break;
36243627
}
36253628
sb.append(c);

src/test/java/com/fasterxml/jackson/core/BaseTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,10 @@ protected static String quote(String str) {
536536
}
537537

538538
protected static String aposToQuotes(String json) {
539+
return a2q(json);
540+
}
541+
542+
protected static String a2q(String json) {
539543
return json.replace("'", "\"");
540544
}
541545

src/test/java/com/fasterxml/jackson/core/TestExceptions.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.io.StringWriter;
44

5+
import com.fasterxml.jackson.core.exc.StreamReadException;
56
import com.fasterxml.jackson.core.io.JsonEOFException;
67

78
public class TestExceptions extends BaseTest
@@ -119,4 +120,44 @@ private void _testEofExceptions(int mode) throws Exception
119120

120121
// any other cases we'd like to test?
121122
}
123+
124+
public void testContentSnippetWithOffset() throws Exception
125+
{
126+
JsonParser p;
127+
final String json = a2q("{'k1':'v1'}\n[broken]\n");
128+
final byte[] jsonB = utf8Bytes(json);
129+
final int lfIndex = json.indexOf("\n");
130+
final int start = lfIndex+1;
131+
final int len = json.length() - start;
132+
133+
p = JSON_F.createParser(jsonB, start, len);
134+
// for byte-based, will be after character that follows token:
135+
// (and alas cannot be easily fixed)
136+
_testContentSnippetWithOffset(p, 9, "(byte[])\"[broken]\n\"");
137+
p.close();
138+
139+
final char[] jsonC = json.toCharArray();
140+
p = JSON_F.createParser(jsonC, start, len);
141+
// for char-based we get true offset at end of token
142+
_testContentSnippetWithOffset(p, 8, "(char[])\"[broken]\n\"");
143+
p.close();
144+
}
145+
146+
private void _testContentSnippetWithOffset(final JsonParser p,
147+
int expColumn, String expContent) throws Exception
148+
{
149+
assertToken(JsonToken.START_ARRAY, p.nextToken());
150+
try {
151+
p.nextToken();
152+
fail("Should not pass");
153+
} catch (StreamReadException e) {
154+
verifyException(e, "Unrecognized token 'broken'");
155+
JsonLocation loc = e.getLocation();
156+
assertEquals(1, loc.getLineNr());
157+
assertEquals(expColumn, loc.getColumnNr());
158+
final String srcDesc = loc.sourceDescription();
159+
160+
assertEquals(expContent, srcDesc);
161+
}
162+
}
122163
}

0 commit comments

Comments
 (0)