• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.core.base;
2 
3 import java.io.IOException;
4 import java.math.BigDecimal;
5 import java.math.BigInteger;
6 
7 import com.fasterxml.jackson.core.*;
8 import com.fasterxml.jackson.core.exc.InputCoercionException;
9 import com.fasterxml.jackson.core.io.JsonEOFException;
10 import com.fasterxml.jackson.core.io.NumberInput;
11 import com.fasterxml.jackson.core.util.ByteArrayBuilder;
12 import com.fasterxml.jackson.core.util.VersionUtil;
13 
14 import static com.fasterxml.jackson.core.JsonTokenId.*;
15 
16 /**
17  * Intermediate base class used by all Jackson {@link JsonParser}
18  * implementations, but does not add any additional fields that depend
19  * on particular method of obtaining input.
20  *<p>
21  * Note that 'minimal' here mostly refers to minimal number of fields
22  * (size) and functionality that is specific to certain types
23  * of parser implementations; but not necessarily to number of methods.
24  */
25 public abstract class ParserMinimalBase extends JsonParser
26 {
27     // Control chars:
28     protected final static int INT_TAB = '\t';
29     protected final static int INT_LF = '\n';
30     protected final static int INT_CR = '\r';
31     protected final static int INT_SPACE = 0x0020;
32 
33     // Markup
34     protected final static int INT_LBRACKET = '[';
35     protected final static int INT_RBRACKET = ']';
36     protected final static int INT_LCURLY = '{';
37     protected final static int INT_RCURLY = '}';
38     protected final static int INT_QUOTE = '"';
39     protected final static int INT_APOS = '\'';
40     protected final static int INT_BACKSLASH = '\\';
41     protected final static int INT_SLASH = '/';
42     protected final static int INT_ASTERISK = '*';
43     protected final static int INT_COLON = ':';
44     protected final static int INT_COMMA = ',';
45     protected final static int INT_HASH = '#';
46 
47     // Number chars
48     protected final static int INT_0 = '0';
49     protected final static int INT_9 = '9';
50     protected final static int INT_MINUS = '-';
51     protected final static int INT_PLUS = '+';
52 
53     protected final static int INT_PERIOD = '.';
54     protected final static int INT_e = 'e';
55     protected final static int INT_E = 'E';
56 
57     protected final static char CHAR_NULL = '\0';
58 
59     /**
60      * @since 2.9
61      */
62     protected final static byte[] NO_BYTES = new byte[0];
63 
64     /**
65      * @since 2.9
66      */
67     protected final static int[] NO_INTS = new int[0];
68 
69     /*
70     /**********************************************************
71     /* Constants and fields of former 'JsonNumericParserBase'
72     /**********************************************************
73      */
74 
75     protected final static int NR_UNKNOWN = 0;
76 
77     // First, integer types
78 
79     protected final static int NR_INT = 0x0001;
80     protected final static int NR_LONG = 0x0002;
81     protected final static int NR_BIGINT = 0x0004;
82 
83     // And then floating point types
84 
85     protected final static int NR_DOUBLE = 0x008;
86     protected final static int NR_BIGDECIMAL = 0x0010;
87 
88     /**
89      * NOTE! Not used by JSON implementation but used by many of binary codecs
90      *
91      * @since 2.9
92      */
93     protected final static int NR_FLOAT = 0x020;
94 
95     // Also, we need some numeric constants
96 
97     protected final static BigInteger BI_MIN_INT = BigInteger.valueOf(Integer.MIN_VALUE);
98     protected final static BigInteger BI_MAX_INT = BigInteger.valueOf(Integer.MAX_VALUE);
99 
100     protected final static BigInteger BI_MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE);
101     protected final static BigInteger BI_MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
102 
103     protected final static BigDecimal BD_MIN_LONG = new BigDecimal(BI_MIN_LONG);
104     protected final static BigDecimal BD_MAX_LONG = new BigDecimal(BI_MAX_LONG);
105 
106     protected final static BigDecimal BD_MIN_INT = new BigDecimal(BI_MIN_INT);
107     protected final static BigDecimal BD_MAX_INT = new BigDecimal(BI_MAX_INT);
108 
109     protected final static long MIN_INT_L = (long) Integer.MIN_VALUE;
110     protected final static long MAX_INT_L = (long) Integer.MAX_VALUE;
111 
112     // These are not very accurate, but have to do... (for bounds checks)
113 
114     protected final static double MIN_LONG_D = (double) Long.MIN_VALUE;
115     protected final static double MAX_LONG_D = (double) Long.MAX_VALUE;
116 
117     protected final static double MIN_INT_D = (double) Integer.MIN_VALUE;
118     protected final static double MAX_INT_D = (double) Integer.MAX_VALUE;
119 
120     /*
121     /**********************************************************
122     /* Misc other constants
123     /**********************************************************
124      */
125 
126     /**
127      * Maximum number of characters to include in token reported
128      * as part of error messages.
129      *
130      * @since 2.9
131      */
132     protected final static int MAX_ERROR_TOKEN_LENGTH = 256;
133 
134     /*
135     /**********************************************************
136     /* Minimal generally useful state
137     /**********************************************************
138      */
139 
140     /**
141      * Last token retrieved via {@link #nextToken}, if any.
142      * Null before the first call to <code>nextToken()</code>,
143      * as well as if token has been explicitly cleared
144      */
145     protected JsonToken _currToken;
146 
147     /**
148      * Last cleared token, if any: that is, value that was in
149      * effect when {@link #clearCurrentToken} was called.
150      */
151     protected JsonToken _lastClearedToken;
152 
153     /*
154     /**********************************************************
155     /* Life-cycle
156     /**********************************************************
157      */
158 
ParserMinimalBase()159     protected ParserMinimalBase() { }
ParserMinimalBase(int features)160     protected ParserMinimalBase(int features) { super(features); }
161 
162     // NOTE: had base impl in 2.3 and before; but shouldn't
163     // public abstract Version version();
164 
165     /*
166     /**********************************************************
167     /* Configuration overrides if any
168     /**********************************************************
169      */
170 
171     // from base class:
172 
173     //public void enableFeature(Feature f)
174     //public void disableFeature(Feature f)
175     //public void setFeature(Feature f, boolean state)
176     //public boolean isFeatureEnabled(Feature f)
177 
178     /*
179     /**********************************************************
180     /* JsonParser impl
181     /**********************************************************
182      */
183 
nextToken()184     @Override public abstract JsonToken nextToken() throws IOException;
185 
currentToken()186     @Override public JsonToken currentToken() { return _currToken; }
currentTokenId()187     @Override public int currentTokenId() {
188         final JsonToken t = _currToken;
189         return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
190     }
191 
getCurrentToken()192     @Override public JsonToken getCurrentToken() { return _currToken; }
193 
194     @Deprecated
getCurrentTokenId()195     @Override public int getCurrentTokenId() {
196         final JsonToken t = _currToken;
197         return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
198     }
199 
hasCurrentToken()200     @Override public boolean hasCurrentToken() { return _currToken != null; }
hasTokenId(int id)201     @Override public boolean hasTokenId(int id) {
202         final JsonToken t = _currToken;
203         if (t == null) {
204             return (JsonTokenId.ID_NO_TOKEN == id);
205         }
206         return t.id() == id;
207     }
208 
hasToken(JsonToken t)209     @Override public boolean hasToken(JsonToken t) {
210         return (_currToken == t);
211     }
212 
isExpectedStartArrayToken()213     @Override public boolean isExpectedStartArrayToken() { return _currToken == JsonToken.START_ARRAY; }
isExpectedStartObjectToken()214     @Override public boolean isExpectedStartObjectToken() { return _currToken == JsonToken.START_OBJECT; }
isExpectedNumberIntToken()215     @Override public boolean isExpectedNumberIntToken() { return _currToken == JsonToken.VALUE_NUMBER_INT; }
216 
217     @Override
nextValue()218     public JsonToken nextValue() throws IOException {
219         // Implementation should be as trivial as follows; only needs to change if
220         // we are to skip other tokens (for example, if comments were exposed as tokens)
221         JsonToken t = nextToken();
222         if (t == JsonToken.FIELD_NAME) {
223             t = nextToken();
224         }
225         return t;
226     }
227 
228     @Override
skipChildren()229     public JsonParser skipChildren() throws IOException
230     {
231         if (_currToken != JsonToken.START_OBJECT
232             && _currToken != JsonToken.START_ARRAY) {
233             return this;
234         }
235         int open = 1;
236 
237         // Since proper matching of start/end markers is handled
238         // by nextToken(), we'll just count nesting levels here
239         while (true) {
240             JsonToken t = nextToken();
241             if (t == null) {
242                 _handleEOF();
243                 /* given constraints, above should never return;
244                  * however, FindBugs doesn't know about it and
245                  * complains... so let's add dummy break here
246                  */
247                 return this;
248             }
249             if (t.isStructStart()) {
250                 ++open;
251             } else if (t.isStructEnd()) {
252                 if (--open == 0) {
253                     return this;
254                 }
255                 // 23-May-2018, tatu: [core#463] Need to consider non-blocking case...
256             } else if (t == JsonToken.NOT_AVAILABLE) {
257                 // Nothing much we can do except to either return `null` (which seems wrong),
258                 // or, what we actually do, signal error
259                 _reportError("Not enough content available for `skipChildren()`: non-blocking parser? (%s)",
260                             getClass().getName());
261             }
262         }
263     }
264 
265     /**
266      * Method sub-classes need to implement
267      */
_handleEOF()268     protected abstract void _handleEOF() throws JsonParseException;
269 
270     //public JsonToken getCurrentToken()
271     //public boolean hasCurrentToken()
272 
getCurrentName()273     @Override public abstract String getCurrentName() throws IOException;
close()274     @Override public abstract void close() throws IOException;
isClosed()275     @Override public abstract boolean isClosed();
276 
getParsingContext()277     @Override public abstract JsonStreamContext getParsingContext();
278 
279 //    public abstract JsonLocation getTokenLocation();
280 
281 //   public abstract JsonLocation getCurrentLocation();
282 
283     /*
284     /**********************************************************
285     /* Public API, token state overrides
286     /**********************************************************
287      */
288 
clearCurrentToken()289     @Override public void clearCurrentToken() {
290         if (_currToken != null) {
291             _lastClearedToken = _currToken;
292             _currToken = null;
293         }
294     }
295 
getLastClearedToken()296     @Override public JsonToken getLastClearedToken() { return _lastClearedToken; }
297 
overrideCurrentName(String name)298     @Override public abstract void overrideCurrentName(String name);
299 
300     /*
301     /**********************************************************
302     /* Public API, access to token information, text
303     /**********************************************************
304      */
305 
getText()306     @Override public abstract String getText() throws IOException;
getTextCharacters()307     @Override public abstract char[] getTextCharacters() throws IOException;
hasTextCharacters()308     @Override public abstract boolean hasTextCharacters();
getTextLength()309     @Override public abstract int getTextLength() throws IOException;
getTextOffset()310     @Override public abstract int getTextOffset() throws IOException;
311 
312     /*
313     /**********************************************************
314     /* Public API, access to token information, binary
315     /**********************************************************
316      */
317 
getBinaryValue(Base64Variant b64variant)318     @Override public abstract byte[] getBinaryValue(Base64Variant b64variant) throws IOException;
319 
320     /*
321     /**********************************************************
322     /* Public API, access with conversion/coercion
323     /**********************************************************
324      */
325 
326     @Override
getValueAsBoolean(boolean defaultValue)327     public boolean getValueAsBoolean(boolean defaultValue) throws IOException
328     {
329         JsonToken t = _currToken;
330         if (t != null) {
331             switch (t.id()) {
332             case ID_STRING:
333                 String str = getText().trim();
334                 if ("true".equals(str)) {
335                     return true;
336                 }
337                 if ("false".equals(str)) {
338                     return false;
339                 }
340                 if (_hasTextualNull(str)) {
341                     return false;
342                 }
343                 break;
344             case ID_NUMBER_INT:
345                 return getIntValue() != 0;
346             case ID_TRUE:
347                 return true;
348             case ID_FALSE:
349             case ID_NULL:
350                 return false;
351             case ID_EMBEDDED_OBJECT:
352                 Object value = getEmbeddedObject();
353                 if (value instanceof Boolean) {
354                     return (Boolean) value;
355                 }
356                 break;
357             default:
358             }
359         }
360         return defaultValue;
361     }
362 
363     @Override
getValueAsInt()364     public int getValueAsInt() throws IOException
365     {
366         JsonToken t = _currToken;
367         if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) {
368             return getIntValue();
369         }
370         return getValueAsInt(0);
371     }
372 
373     @Override
getValueAsInt(int defaultValue)374     public int getValueAsInt(int defaultValue) throws IOException
375     {
376         JsonToken t = _currToken;
377         if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) {
378             return getIntValue();
379         }
380         if (t != null) {
381             switch (t.id()) {
382             case ID_STRING:
383                 String str = getText();
384                 if (_hasTextualNull(str)) {
385                     return 0;
386                 }
387                 return NumberInput.parseAsInt(str, defaultValue);
388             case ID_TRUE:
389                 return 1;
390             case ID_FALSE:
391                 return 0;
392             case ID_NULL:
393                 return 0;
394             case ID_EMBEDDED_OBJECT:
395                 Object value = getEmbeddedObject();
396                 if (value instanceof Number) {
397                     return ((Number) value).intValue();
398                 }
399             }
400         }
401         return defaultValue;
402     }
403 
404     @Override
getValueAsLong()405     public long getValueAsLong() throws IOException
406     {
407         JsonToken t = _currToken;
408         if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) {
409             return getLongValue();
410         }
411         return getValueAsLong(0L);
412     }
413 
414     @Override
getValueAsLong(long defaultValue)415     public long getValueAsLong(long defaultValue) throws IOException
416     {
417         JsonToken t = _currToken;
418         if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) {
419             return getLongValue();
420         }
421         if (t != null) {
422             switch (t.id()) {
423             case ID_STRING:
424                 String str = getText();
425                 if (_hasTextualNull(str)) {
426                     return 0L;
427                 }
428                 return NumberInput.parseAsLong(str, defaultValue);
429             case ID_TRUE:
430                 return 1L;
431             case ID_FALSE:
432             case ID_NULL:
433                 return 0L;
434             case ID_EMBEDDED_OBJECT:
435                 Object value = getEmbeddedObject();
436                 if (value instanceof Number) {
437                     return ((Number) value).longValue();
438                 }
439             }
440         }
441         return defaultValue;
442     }
443 
444     @Override
getValueAsDouble(double defaultValue)445     public double getValueAsDouble(double defaultValue) throws IOException
446     {
447         JsonToken t = _currToken;
448         if (t != null) {
449             switch (t.id()) {
450             case ID_STRING:
451                 String str = getText();
452                 if (_hasTextualNull(str)) {
453                     return 0L;
454                 }
455                 return NumberInput.parseAsDouble(str, defaultValue);
456             case ID_NUMBER_INT:
457             case ID_NUMBER_FLOAT:
458                 return getDoubleValue();
459             case ID_TRUE:
460                 return 1.0;
461             case ID_FALSE:
462             case ID_NULL:
463                 return 0.0;
464             case ID_EMBEDDED_OBJECT:
465                 Object value = this.getEmbeddedObject();
466                 if (value instanceof Number) {
467                     return ((Number) value).doubleValue();
468                 }
469             }
470         }
471         return defaultValue;
472     }
473 
474     @Override
getValueAsString()475     public String getValueAsString() throws IOException {
476         // sub-classes tend to override so...
477         return getValueAsString(null);
478     }
479 
480     @Override
getValueAsString(String defaultValue)481     public String getValueAsString(String defaultValue) throws IOException {
482         if (_currToken == JsonToken.VALUE_STRING) {
483             return getText();
484         }
485         if (_currToken == JsonToken.FIELD_NAME) {
486             return getCurrentName();
487         }
488         if (_currToken == null || _currToken == JsonToken.VALUE_NULL || !_currToken.isScalarValue()) {
489             return defaultValue;
490         }
491         return getText();
492     }
493 
494     /*
495     /**********************************************************
496     /* Base64 decoding
497     /**********************************************************
498      */
499 
500     /**
501      * Helper method that can be used for base64 decoding in cases where
502      * encoded content has already been read as a String.
503      */
_decodeBase64(String str, ByteArrayBuilder builder, Base64Variant b64variant)504     protected void _decodeBase64(String str, ByteArrayBuilder builder, Base64Variant b64variant) throws IOException
505     {
506         try {
507             b64variant.decode(str, builder);
508         } catch (IllegalArgumentException e) {
509             _reportError(e.getMessage());
510         }
511     }
512 
513     /*
514     /**********************************************************
515     /* Coercion helper methods (overridable)
516     /**********************************************************
517      */
518 
519     /**
520      * Helper method used to determine whether we are currently pointing to
521      * a String value of "null" (NOT a null token); and, if so, that parser
522      * is to recognize and return it similar to if it was real null token.
523      *
524      * @since 2.3
525      */
_hasTextualNull(String value)526     protected boolean _hasTextualNull(String value) { return "null".equals(value); }
527 
528     /*
529     /**********************************************************
530     /* Error reporting
531     /**********************************************************
532      */
533 
reportUnexpectedNumberChar(int ch, String comment)534     protected void reportUnexpectedNumberChar(int ch, String comment) throws JsonParseException {
535         String msg = String.format("Unexpected character (%s) in numeric value", _getCharDesc(ch));
536         if (comment != null) {
537             msg += ": "+comment;
538         }
539         _reportError(msg);
540     }
541 
542     /**
543      * Method called to throw an exception for input token that looks like a number
544      * based on first character(s), but is not valid according to rules of format.
545      * In case of JSON this also includes invalid forms like positive sign and
546      * leading zeroes.
547      */
reportInvalidNumber(String msg)548     protected void reportInvalidNumber(String msg) throws JsonParseException {
549         _reportError("Invalid numeric value: "+msg);
550     }
551 
552     /**
553      * Method called to throw an exception for integral (not floating point) input
554      * token with value outside of Java signed 32-bit range when requested as {@link int}.
555      * Result will be {@link InputCoercionException} being thrown.
556      */
reportOverflowInt()557     protected void reportOverflowInt() throws IOException {
558         reportOverflowInt(getText());
559     }
560 
561     // @since 2.10
reportOverflowInt(String numDesc)562     protected void reportOverflowInt(String numDesc) throws IOException {
563         reportOverflowInt(numDesc, currentToken());
564     }
565 
566     // @since 2.10
reportOverflowInt(String numDesc, JsonToken inputType)567     protected void reportOverflowInt(String numDesc, JsonToken inputType) throws IOException {
568         _reportInputCoercion(String.format("Numeric value (%s) out of range of int (%d - %s)",
569                 _longIntegerDesc(numDesc), Integer.MIN_VALUE, Integer.MAX_VALUE),
570                 inputType, Integer.TYPE);
571     }
572 
573     /**
574      * Method called to throw an exception for integral (not floating point) input
575      * token with value outside of Java signed 64-bit range when requested as {@link long}.
576      * Result will be {@link InputCoercionException} being thrown.
577      */
reportOverflowLong()578     protected void reportOverflowLong() throws IOException {
579         reportOverflowLong(getText());
580     }
581 
582     // @since 2.10
reportOverflowLong(String numDesc)583     protected void reportOverflowLong(String numDesc) throws IOException {
584         reportOverflowLong(numDesc, currentToken());
585     }
586 
587     // @since 2.10
reportOverflowLong(String numDesc, JsonToken inputType)588     protected void reportOverflowLong(String numDesc, JsonToken inputType) throws IOException {
589         _reportInputCoercion(String.format("Numeric value (%s) out of range of long (%d - %s)",
590                 _longIntegerDesc(numDesc), Long.MIN_VALUE, Long.MAX_VALUE),
591                 inputType, Long.TYPE);
592     }
593 
594     /**
595      * @since 2.10
596      */
_reportInputCoercion(String msg, JsonToken inputType, Class<?> targetType)597     protected void _reportInputCoercion(String msg, JsonToken inputType, Class<?> targetType)
598             throws InputCoercionException {
599         throw new InputCoercionException(this, msg, inputType, targetType);
600     }
601 
602     // @since 2.9.8
_longIntegerDesc(String rawNum)603     protected String _longIntegerDesc(String rawNum) {
604         int rawLen = rawNum.length();
605         if (rawLen < 1000) {
606             return rawNum;
607         }
608         if (rawNum.startsWith("-")) {
609             rawLen -= 1;
610         }
611         return String.format("[Integer with %d digits]", rawLen);
612     }
613 
614     // @since 2.9.8
_longNumberDesc(String rawNum)615     protected String _longNumberDesc(String rawNum) {
616         int rawLen = rawNum.length();
617         if (rawLen < 1000) {
618             return rawNum;
619         }
620         if (rawNum.startsWith("-")) {
621             rawLen -= 1;
622         }
623         return String.format("[number with %d characters]", rawLen);
624     }
625 
_reportUnexpectedChar(int ch, String comment)626     protected void _reportUnexpectedChar(int ch, String comment) throws JsonParseException
627     {
628         if (ch < 0) { // sanity check
629             _reportInvalidEOF();
630         }
631         String msg = String.format("Unexpected character (%s)", _getCharDesc(ch));
632         if (comment != null) {
633             msg += ": "+comment;
634         }
635         _reportError(msg);
636     }
637 
_reportInvalidEOF()638     protected void _reportInvalidEOF() throws JsonParseException {
639         _reportInvalidEOF(" in "+_currToken, _currToken);
640     }
641 
642     /**
643      * @since 2.8
644      */
_reportInvalidEOFInValue(JsonToken type)645     protected void _reportInvalidEOFInValue(JsonToken type) throws JsonParseException {
646         String msg;
647         if (type == JsonToken.VALUE_STRING) {
648             msg = " in a String value";
649         } else if ((type == JsonToken.VALUE_NUMBER_INT)
650                 || (type == JsonToken.VALUE_NUMBER_FLOAT)) {
651             msg = " in a Number value";
652         } else {
653             msg = " in a value";
654         }
655         _reportInvalidEOF(msg, type);
656     }
657 
658     /**
659      * @since 2.8
660      */
_reportInvalidEOF(String msg, JsonToken currToken)661     protected void _reportInvalidEOF(String msg, JsonToken currToken) throws JsonParseException {
662         throw new JsonEOFException(this, currToken, "Unexpected end-of-input"+msg);
663     }
664 
665     /**
666      * @deprecated Since 2.8 use {@link #_reportInvalidEOF(String, JsonToken)} instead
667      */
668     @Deprecated // since 2.8
_reportInvalidEOFInValue()669     protected void _reportInvalidEOFInValue() throws JsonParseException {
670         _reportInvalidEOF(" in a value");
671     }
672 
673     /**
674      * @deprecated Since 2.8 use {@link #_reportInvalidEOF(String, JsonToken)} instead
675      */
676     @Deprecated // since 2.8
_reportInvalidEOF(String msg)677     protected void _reportInvalidEOF(String msg) throws JsonParseException {
678         throw new JsonEOFException(this, null, "Unexpected end-of-input"+msg);
679     }
680 
_reportMissingRootWS(int ch)681     protected void _reportMissingRootWS(int ch) throws JsonParseException {
682         _reportUnexpectedChar(ch, "Expected space separating root-level values");
683     }
684 
_throwInvalidSpace(int i)685     protected void _throwInvalidSpace(int i) throws JsonParseException {
686         char c = (char) i;
687         String msg = "Illegal character ("+_getCharDesc(c)+"): only regular white space (\\r, \\n, \\t) is allowed between tokens";
688         _reportError(msg);
689     }
690 
691     /*
692     /**********************************************************
693     /* Error reporting, generic
694     /**********************************************************
695      */
696 
_getCharDesc(int ch)697     protected final static String _getCharDesc(int ch)
698     {
699         char c = (char) ch;
700         if (Character.isISOControl(c)) {
701             return "(CTRL-CHAR, code "+ch+")";
702         }
703         if (ch > 255) {
704             return "'"+c+"' (code "+ch+" / 0x"+Integer.toHexString(ch)+")";
705         }
706         return "'"+c+"' (code "+ch+")";
707     }
708 
_reportError(String msg)709     protected final void _reportError(String msg) throws JsonParseException {
710         throw _constructError(msg);
711     }
712 
713     // @since 2.9
_reportError(String msg, Object arg)714     protected final void _reportError(String msg, Object arg) throws JsonParseException {
715         throw _constructError(String.format(msg, arg));
716     }
717 
718     // @since 2.9
_reportError(String msg, Object arg1, Object arg2)719     protected final void _reportError(String msg, Object arg1, Object arg2) throws JsonParseException {
720         throw _constructError(String.format(msg, arg1, arg2));
721     }
722 
_wrapError(String msg, Throwable t)723     protected final void _wrapError(String msg, Throwable t) throws JsonParseException {
724         throw _constructError(msg, t);
725     }
726 
_throwInternal()727     protected final void _throwInternal() {
728         VersionUtil.throwInternal();
729     }
730 
_constructError(String msg, Throwable t)731     protected final JsonParseException _constructError(String msg, Throwable t) {
732         return new JsonParseException(this, msg, t);
733     }
734 
735     @Deprecated // since 2.11
_asciiBytes(String str)736     protected static byte[] _asciiBytes(String str) {
737         byte[] b = new byte[str.length()];
738         for (int i = 0, len = str.length(); i < len; ++i) {
739             b[i] = (byte) str.charAt(i);
740         }
741         return b;
742     }
743 
744     @Deprecated // since 2.11
_ascii(byte[] b)745     protected static String _ascii(byte[] b) {
746         try {
747             return new String(b, "US-ASCII");
748         } catch (IOException e) { // never occurs
749             throw new RuntimeException(e);
750         }
751     }
752 }
753