e6de9317203a4823e1463f060c36983cfb06225d
[dmaap/datarouter.git] / datarouter-prov / src / main / java / org / json / JSONTokener.java
1 /*******************************************************************************\r
2  * ============LICENSE_START==================================================\r
3  * * org.onap.dmaap\r
4  * * ===========================================================================\r
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
6  * * ===========================================================================\r
7  * * Licensed under the Apache License, Version 2.0 (the "License");\r
8  * * you may not use this file except in compliance with the License.\r
9  * * You may obtain a copy of the License at\r
10  * * \r
11  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
12  * * \r
13  *  * Unless required by applicable law or agreed to in writing, software\r
14  * * distributed under the License is distributed on an "AS IS" BASIS,\r
15  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
16  * * See the License for the specific language governing permissions and\r
17  * * limitations under the License.\r
18  * * ============LICENSE_END====================================================\r
19  * *\r
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
21  * *\r
22  ******************************************************************************/\r
23 package org.json;\r
24 \r
25 import java.io.BufferedReader;\r
26 import java.io.IOException;\r
27 import java.io.InputStream;\r
28 import java.io.InputStreamReader;\r
29 import java.io.Reader;\r
30 import java.io.StringReader;\r
31 \r
32 \r
33 \r
34 public class JSONTokener {\r
35 \r
36     private long    character;\r
37     private boolean eof;\r
38     private long    index;\r
39     private long    line;\r
40     private char    previous;\r
41     private Reader  reader;\r
42     private boolean usePrevious;\r
43 \r
44 \r
45     /**\r
46      * Construct a JSONTokener from a Reader.\r
47      *\r
48      * @param reader     A reader.\r
49      */\r
50     public JSONTokener(Reader reader) {\r
51         this.reader = reader.markSupported()\r
52             ? reader\r
53             : new BufferedReader(reader);\r
54         this.eof = false;\r
55         this.usePrevious = false;\r
56         this.previous = 0;\r
57         this.index = 0;\r
58         this.character = 1;\r
59         this.line = 1;\r
60     }\r
61 \r
62 \r
63     /**\r
64      * Construct a JSONTokener from an InputStream.\r
65      */\r
66     public JSONTokener(InputStream inputStream) throws JSONException {\r
67         this(new InputStreamReader(inputStream));\r
68     }\r
69 \r
70 \r
71     /**\r
72      * Construct a JSONTokener from a string.\r
73      *\r
74      * @param s     A source string.\r
75      */\r
76     public JSONTokener(String s) {\r
77         this(new StringReader(s));\r
78     }\r
79 \r
80 \r
81     /**\r
82      * Back up one character. This provides a sort of lookahead capability,\r
83      * so that you can test for a digit or letter before attempting to parse\r
84      * the next number or identifier.\r
85      */\r
86     public void back() throws JSONException {\r
87         if (this.usePrevious || this.index <= 0) {\r
88             throw new JSONException("Stepping back two steps is not supported");\r
89         }\r
90         this.index -= 1;\r
91         this.character -= 1;\r
92         this.usePrevious = true;\r
93         this.eof = false;\r
94     }\r
95 \r
96 \r
97     /**\r
98      * Get the hex value of a character (base16).\r
99      * @param c A character between '0' and '9' or between 'A' and 'F' or\r
100      * between 'a' and 'f'.\r
101      * @return  An int between 0 and 15, or -1 if c was not a hex digit.\r
102      */\r
103     public static int dehexchar(char c) {\r
104         if (c >= '0' && c <= '9') {\r
105             return c - '0';\r
106         }\r
107         if (c >= 'A' && c <= 'F') {\r
108             return c - ('A' - 10);\r
109         }\r
110         if (c >= 'a' && c <= 'f') {\r
111             return c - ('a' - 10);\r
112         }\r
113         return -1;\r
114     }\r
115 \r
116     public boolean end() {\r
117         return this.eof && !this.usePrevious;\r
118     }\r
119 \r
120 \r
121     /**\r
122      * Determine if the source string still contains characters that next()\r
123      * can consume.\r
124      * @return true if not yet at the end of the source.\r
125      */\r
126     public boolean more() throws JSONException {\r
127         this.next();\r
128         if (this.end()) {\r
129             return false;\r
130         }\r
131         this.back();\r
132         return true;\r
133     }\r
134 \r
135 \r
136     /**\r
137      * Get the next character in the source string.\r
138      *\r
139      * @return The next character, or 0 if past the end of the source string.\r
140      */\r
141     public char next() throws JSONException {\r
142         int c;\r
143         if (this.usePrevious) {\r
144             this.usePrevious = false;\r
145             c = this.previous;\r
146         } else {\r
147             try {\r
148                 c = this.reader.read();\r
149             } catch (IOException exception) {\r
150                 throw new JSONException(exception);\r
151             }\r
152 \r
153             if (c <= 0) { // End of stream\r
154                 this.eof = true;\r
155                 c = 0;\r
156             }\r
157         }\r
158         this.index += 1;\r
159         if (this.previous == '\r') {\r
160             this.line += 1;\r
161             this.character = c == '\n' ? 0 : 1;\r
162         } else if (c == '\n') {\r
163             this.line += 1;\r
164             this.character = 0;\r
165         } else {\r
166             this.character += 1;\r
167         }\r
168         this.previous = (char) c;\r
169         return this.previous;\r
170     }\r
171 \r
172 \r
173     /**\r
174      * Consume the next character, and check that it matches a specified\r
175      * character.\r
176      * @param c The character to match.\r
177      * @return The character.\r
178      * @throws JSONException if the character does not match.\r
179      */\r
180     public char next(char c) throws JSONException {\r
181         char n = this.next();\r
182         if (n != c) {\r
183             throw this.syntaxError("Expected '" + c + "' and instead saw '" +\r
184                     n + "'");\r
185         }\r
186         return n;\r
187     }\r
188 \r
189 \r
190     /**\r
191      * Get the next n characters.\r
192      *\r
193      * @param n     The number of characters to take.\r
194      * @return      A string of n characters.\r
195      * @throws JSONException\r
196      *   Substring bounds error if there are not\r
197      *   n characters remaining in the source string.\r
198      */\r
199      public String next(int n) throws JSONException {\r
200          if (n == 0) {\r
201              return "";\r
202          }\r
203 \r
204          char[] chars = new char[n];\r
205          int pos = 0;\r
206 \r
207          while (pos < n) {\r
208              chars[pos] = this.next();\r
209              if (this.end()) {\r
210                  throw this.syntaxError("Substring bounds error");\r
211              }\r
212              pos += 1;\r
213          }\r
214          return new String(chars);\r
215      }\r
216 \r
217 \r
218     /**\r
219      * Get the next char in the string, skipping whitespace.\r
220      * @throws JSONException\r
221      * @return  A character, or 0 if there are no more characters.\r
222      */\r
223     public char nextClean() throws JSONException {\r
224         for (;;) {\r
225             char c = this.next();\r
226             if (c == 0 || c > ' ') {\r
227                 return c;\r
228             }\r
229         }\r
230     }\r
231 \r
232 \r
233     /**\r
234      * Return the characters up to the next close quote character.\r
235      * Backslash processing is done. The formal JSON format does not\r
236      * allow strings in single quotes, but an implementation is allowed to\r
237      * accept them.\r
238      * @param quote The quoting character, either\r
239      *      <code>"</code>&nbsp;<small>(double quote)</small> or\r
240      *      <code>'</code>&nbsp;<small>(single quote)</small>.\r
241      * @return      A String.\r
242      * @throws JSONException Unterminated string.\r
243      */\r
244     public String nextString(char quote) throws JSONException {\r
245         char c;\r
246         StringBuffer sb = new StringBuffer();\r
247         for (;;) {\r
248             c = this.next();\r
249             switch (c) {\r
250             case 0:\r
251             case '\n':\r
252             case '\r':\r
253                 throw this.syntaxError("Unterminated string");\r
254             case '\\':\r
255                 c = this.next();\r
256                 switch (c) {\r
257                 case 'b':\r
258                     sb.append('\b');\r
259                     break;\r
260                 case 't':\r
261                     sb.append('\t');\r
262                     break;\r
263                 case 'n':\r
264                     sb.append('\n');\r
265                     break;\r
266                 case 'f':\r
267                     sb.append('\f');\r
268                     break;\r
269                 case 'r':\r
270                     sb.append('\r');\r
271                     break;\r
272                 case 'u':\r
273                     sb.append((char)Integer.parseInt(this.next(4), 16));\r
274                     break;\r
275                 case '"':\r
276                 case '\'':\r
277                 case '\\':\r
278                 case '/':\r
279                     sb.append(c);\r
280                     break;\r
281                 default:\r
282                     throw this.syntaxError("Illegal escape.");\r
283                 }\r
284                 break;\r
285             default:\r
286                 if (c == quote) {\r
287                     return sb.toString();\r
288                 }\r
289                 sb.append(c);\r
290             }\r
291         }\r
292     }\r
293 \r
294 \r
295     /**\r
296      * Get the text up but not including the specified character or the\r
297      * end of line, whichever comes first.\r
298      * @param  delimiter A delimiter character.\r
299      * @return   A string.\r
300      */\r
301     public String nextTo(char delimiter) throws JSONException {\r
302         StringBuffer sb = new StringBuffer();\r
303         for (;;) {\r
304             char c = this.next();\r
305             if (c == delimiter || c == 0 || c == '\n' || c == '\r') {\r
306                 if (c != 0) {\r
307                     this.back();\r
308                 }\r
309                 return sb.toString().trim();\r
310             }\r
311             sb.append(c);\r
312         }\r
313     }\r
314 \r
315 \r
316     /**\r
317      * Get the text up but not including one of the specified delimiter\r
318      * characters or the end of line, whichever comes first.\r
319      * @param delimiters A set of delimiter characters.\r
320      * @return A string, trimmed.\r
321      */\r
322     public String nextTo(String delimiters) throws JSONException {\r
323         char c;\r
324         StringBuffer sb = new StringBuffer();\r
325         for (;;) {\r
326             c = this.next();\r
327             if (delimiters.indexOf(c) >= 0 || c == 0 ||\r
328                     c == '\n' || c == '\r') {\r
329                 if (c != 0) {\r
330                     this.back();\r
331                 }\r
332                 return sb.toString().trim();\r
333             }\r
334             sb.append(c);\r
335         }\r
336     }\r
337 \r
338 \r
339     /**\r
340      * Get the next value. The value can be a Boolean, Double, Integer,\r
341      * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.\r
342      * @throws JSONException If syntax error.\r
343      *\r
344      * @return An object.\r
345      */\r
346     public Object nextValue() throws JSONException {\r
347         char c = this.nextClean();\r
348         String string;\r
349 \r
350         switch (c) {\r
351             case '"':\r
352             case '\'':\r
353                 return this.nextString(c);\r
354             case '{':\r
355                 this.back();\r
356                 return new JSONObject(this);\r
357             case '[':\r
358                 this.back();\r
359                 return new JSONArray(this);\r
360         }\r
361 \r
362         /*\r
363          * Handle unquoted text. This could be the values true, false, or\r
364          * null, or it can be a number. An implementation (such as this one)\r
365          * is allowed to also accept non-standard forms.\r
366          *\r
367          * Accumulate characters until we reach the end of the text or a\r
368          * formatting character.\r
369          */\r
370 \r
371         StringBuffer sb = new StringBuffer();\r
372         while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {\r
373             sb.append(c);\r
374             c = this.next();\r
375         }\r
376         this.back();\r
377 \r
378         string = sb.toString().trim();\r
379         if ("".equals(string)) {\r
380             throw this.syntaxError("Missing value");\r
381         }\r
382         return JSONObject.stringToValue(string);\r
383     }\r
384 \r
385 \r
386     /**\r
387      * Skip characters until the next character is the requested character.\r
388      * If the requested character is not found, no characters are skipped.\r
389      * @param to A character to skip to.\r
390      * @return The requested character, or zero if the requested character\r
391      * is not found.\r
392      */\r
393     public char skipTo(char to) throws JSONException {\r
394         char c;\r
395         try {\r
396             long startIndex = this.index;\r
397             long startCharacter = this.character;\r
398             long startLine = this.line;\r
399             this.reader.mark(1000000);\r
400             do {\r
401                 c = this.next();\r
402                 if (c == 0) {\r
403                     this.reader.reset();\r
404                     this.index = startIndex;\r
405                     this.character = startCharacter;\r
406                     this.line = startLine;\r
407                     return c;\r
408                 }\r
409             } while (c != to);\r
410         } catch (IOException exc) {\r
411             throw new JSONException(exc);\r
412         }\r
413 \r
414         this.back();\r
415         return c;\r
416     }\r
417 \r
418 \r
419     /**\r
420      * Make a JSONException to signal a syntax error.\r
421      *\r
422      * @param message The error message.\r
423      * @return  A JSONException object, suitable for throwing\r
424      */\r
425     public JSONException syntaxError(String message) {\r
426         return new JSONException(message + this.toString());\r
427     }\r
428 \r
429 \r
430     /**\r
431      * Make a printable string of this JSONTokener.\r
432      *\r
433      * @return " at {index} [character {character} line {line}]"\r
434      */\r
435     public String toString() {\r
436         return " at " + this.index + " [character " + this.character + " line " +\r
437             this.line + "]";\r
438     }\r
439 }\r