-
-
Notifications
You must be signed in to change notification settings - Fork 762
/
JsonLocation.java
225 lines (199 loc) · 7.1 KB
/
JsonLocation.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*/
package com.fasterxml.jackson.core;
import java.nio.charset.Charset;
/**
* Object that encapsulates Location information used for reporting
* parsing (or potentially generation) errors, as well as current location
* within input streams.
*/
public class JsonLocation
implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
/**
* Include at most first 500 characters/bytes from contents; should be enough
* to give context, but not cause unfortunate side effects in things like
* logs.
*
* @since 2.9
*/
public static final int MAX_CONTENT_SNIPPET = 500;
/**
* Shared immutable "N/A location" that can be returned to indicate
* that no location information is available.
*<p>
* NOTE: before 2.9, Location was given as String "N/A"; with 2.9 it was
* removed so that source should be indicated as "UNKNOWN".
*/
public final static JsonLocation NA = new JsonLocation(null, -1L, -1L, -1, -1);
protected final long _totalBytes;
protected final long _totalChars;
protected final int _lineNr;
protected final int _columnNr;
/**
* Displayable description for input source: file path, URL.
*<p>
* NOTE: <code>transient</code> since 2.2 so that Location itself is Serializable.
*/
final transient Object _sourceRef;
public JsonLocation(Object srcRef, long totalChars, int lineNr, int colNr)
{
/* Unfortunately, none of legal encodings are straight single-byte
* encodings. Could determine offset for UTF-16/UTF-32, but the
* most important one is UTF-8...
* so for now, we'll just not report any real byte count
*/
this(srcRef, -1L, totalChars, lineNr, colNr);
}
public JsonLocation(Object sourceRef, long totalBytes, long totalChars,
int lineNr, int columnNr)
{
_sourceRef = sourceRef;
_totalBytes = totalBytes;
_totalChars = totalChars;
_lineNr = lineNr;
_columnNr = columnNr;
}
/**
* Reference to the original resource being read, if one available.
* For example, when a parser has been constructed by passing
* a {@link java.io.File} instance, this method would return
* that File. Will return null if no such reference is available,
* for example when {@link java.io.InputStream} was used to
* construct the parser instance.
*
* @return Source reference this location was constructed with, if any; {@code null} if none
*/
public Object getSourceRef() { return _sourceRef; }
/**
* @return Line number of the location (1-based)
*/
public int getLineNr() { return _lineNr; }
/**
* @return Column number of the location (1-based)
*/
public int getColumnNr() { return _columnNr; }
/**
* @return Character offset within underlying stream, reader or writer,
* if available; -1 if not.
*/
public long getCharOffset() { return _totalChars; }
/**
* @return Byte offset within underlying stream, reader or writer,
* if available; -1 if not.
*/
public long getByteOffset()
{
return _totalBytes;
}
/**
* Accessor for getting a textual description of source reference
* (Object returned by {@link #getSourceRef()}), as included in
* description returned by {@link #toString()}.
*<p>
* NOTE: not added as a "getter" to prevent it from getting serialized.
*
* @return Description of the source reference (see {@link #getSourceRef()}
*
* @since 2.9
*/
public String sourceDescription() {
return _appendSourceDesc(new StringBuilder(100)).toString();
}
/*
/**********************************************************
/* Std method overrides
/**********************************************************
*/
@Override
public int hashCode()
{
int hash = (_sourceRef == null) ? 1 : _sourceRef.hashCode();
hash ^= _lineNr;
hash += _columnNr;
hash ^= (int) _totalChars;
hash += (int) _totalBytes;
return hash;
}
@Override
public boolean equals(Object other)
{
if (other == this) return true;
if (other == null) return false;
if (!(other instanceof JsonLocation)) return false;
JsonLocation otherLoc = (JsonLocation) other;
if (_sourceRef == null) {
if (otherLoc._sourceRef != null) return false;
} else if (!_sourceRef.equals(otherLoc._sourceRef)) return false;
return (_lineNr == otherLoc._lineNr)
&& (_columnNr == otherLoc._columnNr)
&& (_totalChars == otherLoc._totalChars)
&& (getByteOffset() == otherLoc.getByteOffset())
;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder(80);
sb.append("[Source: ");
_appendSourceDesc(sb);
sb.append("; line: ");
sb.append(_lineNr);
sb.append(", column: ");
sb.append(_columnNr);
sb.append(']');
return sb.toString();
}
protected StringBuilder _appendSourceDesc(StringBuilder sb)
{
final Object srcRef = _sourceRef;
if (srcRef == null) {
sb.append("UNKNOWN");
return sb;
}
// First, figure out what name to use as source type
Class<?> srcType = (srcRef instanceof Class<?>) ?
((Class<?>) srcRef) : srcRef.getClass();
String tn = srcType.getName();
// standard JDK types without package
if (tn.startsWith("java.")) {
tn = srcType.getSimpleName();
} else if (srcRef instanceof byte[]) { // then some other special cases
tn = "byte[]";
} else if (srcRef instanceof char[]) {
tn = "char[]";
}
sb.append('(').append(tn).append(')');
// and then, include (part of) contents for selected types:
int len;
String charStr = " chars";
if (srcRef instanceof CharSequence) {
CharSequence cs = (CharSequence) srcRef;
len = cs.length();
len -= _append(sb, cs.subSequence(0, Math.min(len, MAX_CONTENT_SNIPPET)).toString());
} else if (srcRef instanceof char[]) {
char[] ch = (char[]) srcRef;
len = ch.length;
len -= _append(sb, new String(ch, 0, Math.min(len, MAX_CONTENT_SNIPPET)));
} else if (srcRef instanceof byte[]) {
byte[] b = (byte[]) srcRef;
int maxLen = Math.min(b.length, MAX_CONTENT_SNIPPET);
_append(sb, new String(b, 0, maxLen, Charset.forName("UTF-8")));
len = b.length - maxLen;
charStr = " bytes";
} else {
len = 0;
}
if (len > 0) {
sb.append("[truncated ").append(len).append(charStr).append(']');
}
return sb;
}
private int _append(StringBuilder sb, String content) {
sb.append('"').append(content).append('"');
return content.length();
}
}