• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Jackson JSON-processor.
2  *
3  * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
4  */
5 
6 package com.fasterxml.jackson.core;
7 
8 import java.nio.charset.Charset;
9 
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
16     implements java.io.Serializable
17 {
18     private static final long serialVersionUID = 1L;
19 
20     /**
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
26      */
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);
37 
38     protected final long _totalBytes;
39     protected final long _totalChars;
40 
41     protected final int _lineNr;
42     protected final int _columnNr;
43 
44     /**
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      */
49     final transient Object _sourceRef;
50 
JsonLocation(Object srcRef, long totalChars, int lineNr, int colNr)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 
JsonLocation(Object sourceRef, long totalBytes, long totalChars, int lineNr, int columnNr)61     public JsonLocation(Object sourceRef, long totalBytes, long totalChars,
62             int lineNr, int columnNr)
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      */
getSourceRef()79     public Object getSourceRef() { return _sourceRef; }
80 
81     /**
82      * @return Line number of the location (1-based)
83      */
getLineNr()84     public int getLineNr() { return _lineNr; }
85 
86     /**
87      * @return Column number of the location (1-based)
88      */
getColumnNr()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      */
getCharOffset()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      */
getByteOffset()101     public long getByteOffset()
102     {
103         return _totalBytes;
104     }
105 
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      */
sourceDescription()115     public String sourceDescription() {
116         return _appendSourceDesc(new StringBuilder(100)).toString();
117     }
118 
119     /*
120     /**********************************************************
121     /* Std method overrides
122     /**********************************************************
123      */
124 
125     @Override
hashCode()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
equals(Object other)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 
155     @Override
toString()156     public String toString()
157     {
158         StringBuilder sb = new StringBuilder(80);
159         sb.append("[Source: ");
160         _appendSourceDesc(sb);
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 
_appendSourceDesc(StringBuilder sb)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 
_append(StringBuilder sb, String content)217     private int _append(StringBuilder sb, String content) {
218         sb.append('"').append(content).append('"');
219         return content.length();
220     }
221 }
222