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