Tatu Saloranta | 68bb83d | 2013-04-19 10:41:15 -0700 | [diff] [blame] | 1 | /* Jackson JSON-processor. |
| 2 | * |
| 3 | * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
| 4 | */ |
| 5 | |
Tatu Saloranta | f15531c | 2011-12-22 23:00:40 -0800 | [diff] [blame] | 6 | package com.fasterxml.jackson.core; |
| 7 | |
Tatu Saloranta | ce193ae | 2017-02-22 23:20:10 -0800 | [diff] [blame] | 8 | import java.nio.charset.Charset; |
| 9 | |
Tatu Saloranta | f15531c | 2011-12-22 23:00:40 -0800 | [diff] [blame] | 10 | /** |
| 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 | */ |
| 15 | public class JsonLocation |
Tatu Saloranta | e962fb9 | 2016-07-21 14:14:26 -0700 | [diff] [blame] | 16 | implements java.io.Serializable |
Tatu Saloranta | f15531c | 2011-12-22 23:00:40 -0800 | [diff] [blame] | 17 | { |
| 18 | private static final long serialVersionUID = 1L; |
| 19 | |
| 20 | /** |
Tatu Saloranta | ce193ae | 2017-02-22 23:20:10 -0800 | [diff] [blame] | 21 | * 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 Saloranta | f15531c | 2011-12-22 23:00:40 -0800 | [diff] [blame] | 26 | */ |
Tatu Saloranta | ce193ae | 2017-02-22 23:20:10 -0800 | [diff] [blame] | 27 | 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 Saloranta | f15531c | 2011-12-22 23:00:40 -0800 | [diff] [blame] | 37 | |
Tatu Saloranta | ce193ae | 2017-02-22 23:20:10 -0800 | [diff] [blame] | 38 | protected final long _totalBytes; |
| 39 | protected final long _totalChars; |
Tatu Saloranta | f15531c | 2011-12-22 23:00:40 -0800 | [diff] [blame] | 40 | |
Tatu Saloranta | ce193ae | 2017-02-22 23:20:10 -0800 | [diff] [blame] | 41 | protected final int _lineNr; |
| 42 | protected final int _columnNr; |
Tatu Saloranta | f15531c | 2011-12-22 23:00:40 -0800 | [diff] [blame] | 43 | |
| 44 | /** |
Tatu Saloranta | c3ef9f3 | 2013-02-04 20:35:22 -0800 | [diff] [blame] | 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. |
Tatu Saloranta | f15531c | 2011-12-22 23:00:40 -0800 | [diff] [blame] | 48 | */ |
Tatu Saloranta | c3ef9f3 | 2013-02-04 20:35:22 -0800 | [diff] [blame] | 49 | final transient Object _sourceRef; |
Tatu Saloranta | f15531c | 2011-12-22 23:00:40 -0800 | [diff] [blame] | 50 | |
| 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 Saloranta | 3c75618 | 2011-12-28 20:27:59 -0800 | [diff] [blame] | 61 | public JsonLocation(Object sourceRef, long totalBytes, long totalChars, |
| 62 | int lineNr, int columnNr) |
Tatu Saloranta | f15531c | 2011-12-22 23:00:40 -0800 | [diff] [blame] | 63 | { |
| 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 Saloranta | d3918a2 | 2017-02-23 10:15:05 -0800 | [diff] [blame] | 106 | /** |
| 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 Saloranta | f15531c | 2011-12-22 23:00:40 -0800 | [diff] [blame] | 155 | @Override |
| 156 | public String toString() |
| 157 | { |
| 158 | StringBuilder sb = new StringBuilder(80); |
| 159 | sb.append("[Source: "); |
Tatu Saloranta | ce193ae | 2017-02-22 23:20:10 -0800 | [diff] [blame] | 160 | _appendSourceDesc(sb); |
Tatu Saloranta | f15531c | 2011-12-22 23:00:40 -0800 | [diff] [blame] | 161 | 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 Saloranta | ce193ae | 2017-02-22 23:20:10 -0800 | [diff] [blame] | 169 | 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 Saloranta | f15531c | 2011-12-22 23:00:40 -0800 | [diff] [blame] | 221 | } |