Skip to content

Commit b89098e

Browse files
committed
Start work on solving #652, #658
1 parent 722556b commit b89098e

File tree

5 files changed

+243
-31
lines changed

5 files changed

+243
-31
lines changed

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

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
import java.nio.charset.Charset;
99

10+
import com.fasterxml.jackson.core.io.InputSourceReference;
11+
1012
/**
1113
* Object that encapsulates Location information used for reporting
1214
* parsing (or potentially generation) errors, as well as current location
@@ -15,7 +17,7 @@
1517
public class JsonLocation
1618
implements java.io.Serializable
1719
{
18-
private static final long serialVersionUID = 1L;
20+
private static final long serialVersionUID = 2L; // in 2.13
1921

2022
/**
2123
* Include at most first 500 characters/bytes from contents; should be enough
@@ -33,7 +35,8 @@ public class JsonLocation
3335
* NOTE: before 2.9, Location was given as String "N/A"; with 2.9 it was
3436
* removed so that source should be indicated as "UNKNOWN".
3537
*/
36-
public final static JsonLocation NA = new JsonLocation(null, -1L, -1L, -1, -1);
38+
public final static JsonLocation NA = new JsonLocation(InputSourceReference.unknown(),
39+
-1L, -1L, -1, -1);
3740

3841
protected final long _totalBytes;
3942
protected final long _totalChars;
@@ -42,32 +45,67 @@ public class JsonLocation
4245
protected final int _columnNr;
4346

4447
/**
45-
* Displayable description for input source: file path, URL.
46-
*<p>
47-
* NOTE: <code>transient</code> since 2.2 so that Location itself is Serializable.
48+
* Reference to input source; never null (but may be that of
49+
* {@link InputSourceReference#unknown()}).
50+
*
51+
* @since 2.13 (before we have {@code _sourceRef} (Object-valued)
4852
*/
49-
final transient Object _sourceRef;
53+
protected final InputSourceReference _inputSource;
5054

51-
public JsonLocation(Object srcRef, long totalChars, int lineNr, int colNr)
55+
public JsonLocation(InputSourceReference inputSource, long totalChars,
56+
int lineNr, int colNr)
5257
{
53-
/* Unfortunately, none of legal encodings are straight single-byte
54-
* encodings. Could determine offset for UTF-16/UTF-32, but the
55-
* most important one is UTF-8...
56-
* so for now, we'll just not report any real byte count
57-
*/
58-
this(srcRef, -1L, totalChars, lineNr, colNr);
58+
this(inputSource, -1L, totalChars, lineNr, colNr);
5959
}
6060

61-
public JsonLocation(Object sourceRef, long totalBytes, long totalChars,
61+
public JsonLocation(InputSourceReference inputSource, long totalBytes, long totalChars,
6262
int lineNr, int columnNr)
6363
{
64-
_sourceRef = sourceRef;
64+
// 14-Mar-2021, tatu: Defensive programming, but also for convenience...
65+
if (inputSource == null) {
66+
inputSource = InputSourceReference.unknown();
67+
}
68+
_inputSource = inputSource;
6569
_totalBytes = totalBytes;
6670
_totalChars = totalChars;
6771
_lineNr = lineNr;
6872
_columnNr = columnNr;
6973
}
7074

75+
@Deprecated
76+
public JsonLocation(Object srcRef, long totalChars, int lineNr, int columnNr) {
77+
this(_wrap(srcRef), totalChars, lineNr, columnNr);
78+
}
79+
80+
@Deprecated
81+
public JsonLocation(Object srcRef, long totalBytes, long totalChars,
82+
int lineNr, int columnNr) {
83+
this(_wrap(srcRef), totalBytes, totalChars, lineNr, columnNr);
84+
}
85+
86+
protected static InputSourceReference _wrap(Object srcRef) {
87+
if (srcRef instanceof InputSourceReference) {
88+
return (InputSourceReference) srcRef;
89+
}
90+
return new InputSourceReference(false, srcRef);
91+
}
92+
93+
/**
94+
* Accessor for information about the original input source content is being
95+
* read from. Returned reference is never {@code null} but may not contain
96+
* useful information.
97+
*<p>
98+
* NOTE: not getter, on purpose, to avoid inlusion if serialized using
99+
* default Jackson serializer.
100+
*
101+
* @return Object with information about input source.
102+
*
103+
* @since 2.13 (to replace {@code getSourceRef})
104+
*/
105+
public InputSourceReference inputSource() {
106+
return _inputSource;
107+
}
108+
71109
/**
72110
* Reference to the original resource being read, if one available.
73111
* For example, when a parser has been constructed by passing
@@ -77,8 +115,13 @@ public JsonLocation(Object sourceRef, long totalBytes, long totalChars,
77115
* construct the parser instance.
78116
*
79117
* @return Source reference this location was constructed with, if any; {@code null} if none
118+
*
119+
* @deprecated Since 2.13 Use {@link #inputSource} instead
80120
*/
81-
public Object getSourceRef() { return _sourceRef; }
121+
@Deprecated
122+
public Object getSourceRef() {
123+
return _inputSource.getSource();
124+
}
82125

83126
/**
84127
* @return Line number of the location (1-based)
@@ -129,7 +172,7 @@ public String sourceDescription() {
129172
@Override
130173
public int hashCode()
131174
{
132-
int hash = (_sourceRef == null) ? 1 : _sourceRef.hashCode();
175+
int hash = (_inputSource == null) ? 1 : 2;
133176
hash ^= _lineNr;
134177
hash += _columnNr;
135178
hash ^= (int) _totalChars;
@@ -145,9 +188,9 @@ public boolean equals(Object other)
145188
if (!(other instanceof JsonLocation)) return false;
146189
JsonLocation otherLoc = (JsonLocation) other;
147190

148-
if (_sourceRef == null) {
149-
if (otherLoc._sourceRef != null) return false;
150-
} else if (!_sourceRef.equals(otherLoc._sourceRef)) return false;
191+
if (_inputSource == null) {
192+
if (otherLoc._inputSource != null) return false;
193+
} else if (!_inputSource.equals(otherLoc._inputSource)) return false;
151194

152195
return (_lineNr == otherLoc._lineNr)
153196
&& (_columnNr == otherLoc._columnNr)
@@ -172,7 +215,7 @@ public String toString()
172215

173216
protected StringBuilder _appendSourceDesc(StringBuilder sb)
174217
{
175-
final Object srcRef = _sourceRef;
218+
final Object srcRef = _inputSource.getSource();
176219

177220
if (srcRef == null) {
178221
sb.append("UNKNOWN");

src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public abstract class GeneratorBase extends JsonGenerator
5353
* @since 2.7.7
5454
*/
5555
protected final static int MAX_BIG_DECIMAL_SCALE = 9999;
56-
56+
5757
/*
5858
/**********************************************************
5959
/* Configuration
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package com.fasterxml.jackson.core.io;
2+
3+
import java.io.IOException;
4+
import java.io.ObjectInputStream;
5+
import java.io.ObjectOutputStream;
6+
7+
/**
8+
* Abstraction that encloses information about input source (streaming or
9+
* not) for the purpose of including pertinent information in
10+
* location (see {@link com.fasterxml.jackson.core.JsonLocation})
11+
* objections, most commonly to be printed out as part of {@code Exception}
12+
* messages.
13+
*
14+
* @since 2.13
15+
*/
16+
public class InputSourceReference
17+
// sort of: we will read back as "UNKNOWN_INPUT"
18+
implements java.io.Serializable
19+
{
20+
private static final long serialVersionUID = 1L;
21+
22+
protected final static InputSourceReference UNKNOWN_INPUT =
23+
new InputSourceReference(false, null);
24+
25+
/**
26+
* Reference to the actual underlying source.
27+
*/
28+
protected final transient Object _rawSource;
29+
30+
/**
31+
* For static input sources, indicates offset from the beginning
32+
* of static array.
33+
* {@code -1} if not in use.
34+
*/
35+
protected final int _offset;
36+
37+
/**
38+
* For static input sources, indicates length of content in
39+
* the static array.
40+
* {@code -1} if not in use.
41+
*/
42+
protected final int _length;
43+
44+
/**
45+
* Marker flag to indicate whether included content is textual or not:
46+
* this is taken to mean, by default, that a snippet of content may be
47+
* displayed for exception messages.
48+
*/
49+
protected final boolean _textualContent;
50+
51+
/*
52+
/**********************************************************************
53+
/* Life-cycle
54+
/**********************************************************************
55+
*/
56+
57+
public InputSourceReference(boolean textualContent, Object rawSource) {
58+
this(textualContent, rawSource, -1, -1);
59+
}
60+
61+
public InputSourceReference(boolean textualContent, Object rawSource,
62+
int offset, int length)
63+
{
64+
_textualContent = textualContent;
65+
_rawSource = rawSource;
66+
_offset = offset;
67+
_length = length;
68+
}
69+
70+
/**
71+
* Accessor for getting a placeholder for cases where actual input source
72+
* is not known (or is not something that system wants to expose).
73+
*
74+
* @return Placeholder "unknown" (or "empty") instance to use instead of
75+
* {@code null} reference
76+
*/
77+
public static InputSourceReference unknown() {
78+
return UNKNOWN_INPUT;
79+
}
80+
81+
/**
82+
* Factory method for legacy code to use for constructing instances to
83+
* input sources for which only minimal amount of information is available.
84+
* Assumed not to contain textual content (no snippet displayed).
85+
*
86+
* @param rawSource Underlying raw input source
87+
*
88+
* @return Instance with minimal information about source (basically just
89+
* raw source without offsets;
90+
*/
91+
public static InputSourceReference rawSource(Object rawSource) {
92+
return new InputSourceReference(false, rawSource);
93+
}
94+
95+
/*
96+
/**********************************************************************
97+
/* Serializable overrides
98+
/**********************************************************************
99+
*/
100+
101+
// For JDK serialization: can/should not retain raw source, so need
102+
// not read or write anything
103+
104+
private void readObject(ObjectInputStream in) throws IOException {
105+
// nop: but must override the method
106+
}
107+
108+
private void writeObject(ObjectOutputStream out) throws IOException {
109+
// nop: but must override the method
110+
}
111+
112+
protected Object readResolve() {
113+
return UNKNOWN_INPUT;
114+
}
115+
116+
/*
117+
/**********************************************************************
118+
/* Basic accessors
119+
/**********************************************************************
120+
*/
121+
122+
public boolean hasTextualContent() {
123+
return _textualContent;
124+
}
125+
126+
public Object getSource() {
127+
return _rawSource;
128+
}
129+
130+
/*
131+
/**********************************************************************
132+
/* Standard method overrides
133+
/**********************************************************************
134+
*/
135+
136+
// Just needed for JsonLocation#equals(): although it'd seem we only need
137+
// to care about identity, for backwards compatibility better compare
138+
// bit more
139+
@Override
140+
public boolean equals(Object other)
141+
{
142+
if (other == this) return true;
143+
if (other == null) return false;
144+
if (!(other instanceof InputSourceReference)) return false;
145+
InputSourceReference otherSrc = (InputSourceReference) other;
146+
147+
return _rawSource == otherSrc._rawSource;
148+
}
149+
}

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

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

33
import java.io.*;
44

5+
import com.fasterxml.jackson.core.io.InputSourceReference;
56
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
67

78
/**
@@ -81,6 +82,16 @@ public void testLocation() throws Exception
8182
jp.close();
8283
}
8384

85+
public void testSourceReference() throws Exception
86+
{
87+
InputSourceReference ref = new InputSourceReference(true, "text");
88+
89+
byte[] stuff = jdkSerialize(ref);
90+
InputSourceReference ref2 = jdkDeserialize(stuff);
91+
assertNotNull(ref2);
92+
assertSame(ref2, InputSourceReference.unknown());
93+
}
94+
8495
public void testParseException() throws Exception
8596
{
8697
JsonFactory jf = new JsonFactory();

0 commit comments

Comments
 (0)