@@ -29,7 +29,7 @@ of this software and associated documentation files (the "Software"), to deal
29
29
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
30
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31
31
SOFTWARE.
32
- */
32
+ */
33
33
34
34
/**
35
35
* A JSONTokener takes a source string and extracts characters and tokens from
@@ -39,42 +39,45 @@ of this software and associated documentation files (the "Software"), to deal
39
39
* @version 2014-05-03
40
40
*/
41
41
public class JSONTokener {
42
- /** current read character. */
42
+ /** current read character position on the current line . */
43
43
private long character ;
44
44
/** flag to indicate if the end of the input has been found. */
45
45
private boolean eof ;
46
46
/** current read index of the input. */
47
47
private long index ;
48
48
/** current line of the input. */
49
49
private long line ;
50
- /** previous index of the input. */
50
+ /** previous character read from the input. */
51
51
private char previous ;
52
52
/** Reader for the input. */
53
53
private final Reader reader ;
54
54
/** flag to indicate that a previous character was requested. */
55
55
private boolean usePrevious ;
56
+ /** the number of characters read in the previous line. */
57
+ private long characterPreviousLine ;
56
58
57
59
58
60
/**
59
- * Construct a JSONTokener from a Reader.
61
+ * Construct a JSONTokener from a Reader. The caller must close the Reader.
60
62
*
61
63
* @param reader A reader.
62
64
*/
63
65
public JSONTokener (Reader reader ) {
64
66
this .reader = reader .markSupported ()
65
- ? reader
66
- : new BufferedReader (reader );
67
+ ? reader
68
+ : new BufferedReader (reader );
67
69
this .eof = false ;
68
70
this .usePrevious = false ;
69
71
this .previous = 0 ;
70
72
this .index = 0 ;
71
73
this .character = 1 ;
74
+ this .characterPreviousLine = 0 ;
72
75
this .line = 1 ;
73
76
}
74
77
75
78
76
79
/**
77
- * Construct a JSONTokener from an InputStream.
80
+ * Construct a JSONTokener from an InputStream. The caller must close the input stream.
78
81
* @param inputStream The source.
79
82
*/
80
83
public JSONTokener (InputStream inputStream ) {
@@ -103,12 +106,23 @@ public void back() throws JSONException {
103
106
if (this .usePrevious || this .index <= 0 ) {
104
107
throw new JSONException ("Stepping back two steps is not supported" );
105
108
}
106
- this .index -= 1 ;
107
- this .character -= 1 ;
109
+ this .decrementIndexes ();
108
110
this .usePrevious = true ;
109
111
this .eof = false ;
110
112
}
111
113
114
+ /**
115
+ * Decrements the indexes for the {@link #back()} method based on the previous character read.
116
+ */
117
+ private void decrementIndexes () {
118
+ this .index --;
119
+ if (this .previous =='\r' || this .previous == '\n' ) {
120
+ this .line --;
121
+ this .character =this .characterPreviousLine ;
122
+ } else if (this .character > 0 ){
123
+ this .character --;
124
+ }
125
+ }
112
126
113
127
/**
114
128
* Get the hex value of a character (base16).
@@ -130,6 +144,8 @@ public static int dehexchar(char c) {
130
144
}
131
145
132
146
/**
147
+ * Checks if the end of the input has been reached.
148
+ *
133
149
* @return true if at the end of the file and we didn't step back
134
150
*/
135
151
public boolean end () {
@@ -145,11 +161,24 @@ public boolean end() {
145
161
* or backward while checking for more data.
146
162
*/
147
163
public boolean more () throws JSONException {
148
- this .next ();
149
- if (this .end ()) {
150
- return false ;
164
+ if (this .usePrevious ) {
165
+ return true ;
166
+ }
167
+ try {
168
+ this .reader .mark (1 );
169
+ } catch (IOException e ) {
170
+ throw new JSONException ("Unable to preserve stream position" , e );
171
+ }
172
+ try {
173
+ // -1 is EOF, but next() can not consume the null character '\0'
174
+ if (this .reader .read () <= 0 ) {
175
+ this .eof = true ;
176
+ return false ;
177
+ }
178
+ this .reader .reset ();
179
+ } catch (IOException e ) {
180
+ throw new JSONException ("Unable to read the next character from the stream" , e );
151
181
}
152
- this .back ();
153
182
return true ;
154
183
}
155
184
@@ -171,26 +200,39 @@ public char next() throws JSONException {
171
200
} catch (IOException exception ) {
172
201
throw new JSONException (exception );
173
202
}
174
-
175
- if (c <= 0 ) { // End of stream
176
- this .eof = true ;
177
- c = 0 ;
178
- }
179
203
}
180
- this .index += 1 ;
181
- if (this .previous == '\r' ) {
182
- this .line += 1 ;
183
- this .character = c == '\n' ? 0 : 1 ;
184
- } else if (c == '\n' ) {
185
- this .line += 1 ;
186
- this .character = 0 ;
187
- } else {
188
- this .character += 1 ;
204
+ if (c <= 0 ) { // End of stream
205
+ this .eof = true ;
206
+ return 0 ;
189
207
}
208
+ this .incrementIndexes (c );
190
209
this .previous = (char ) c ;
191
210
return this .previous ;
192
211
}
193
212
213
+ /**
214
+ * Increments the internal indexes according to the previous character
215
+ * read and the character passed as the current character.
216
+ * @param c the current character read.
217
+ */
218
+ private void incrementIndexes (int c ) {
219
+ if (c > 0 ) {
220
+ this .index ++;
221
+ if (c =='\r' ) {
222
+ this .line ++;
223
+ this .characterPreviousLine = this .character ;
224
+ this .character =0 ;
225
+ }else if (c =='\n' ) {
226
+ if (this .previous != '\r' ) {
227
+ this .line ++;
228
+ this .characterPreviousLine = this .character ;
229
+ }
230
+ this .character =0 ;
231
+ } else {
232
+ this .character ++;
233
+ }
234
+ }
235
+ }
194
236
195
237
/**
196
238
* Consume the next character, and check that it matches a specified
@@ -202,8 +244,11 @@ public char next() throws JSONException {
202
244
public char next (char c ) throws JSONException {
203
245
char n = this .next ();
204
246
if (n != c ) {
205
- throw this .syntaxError ("Expected '" + c + "' and instead saw '" +
206
- n + "'" );
247
+ if (n > 0 ) {
248
+ throw this .syntaxError ("Expected '" + c + "' and instead saw '" +
249
+ n + "'" );
250
+ }
251
+ throw this .syntaxError ("Expected '" + c + "' and instead saw ''" );
207
252
}
208
253
return n ;
209
254
}
@@ -218,23 +263,23 @@ public char next(char c) throws JSONException {
218
263
* Substring bounds error if there are not
219
264
* n characters remaining in the source string.
220
265
*/
221
- public String next (int n ) throws JSONException {
222
- if (n == 0 ) {
223
- return "" ;
224
- }
266
+ public String next (int n ) throws JSONException {
267
+ if (n == 0 ) {
268
+ return "" ;
269
+ }
225
270
226
- char [] chars = new char [n ];
227
- int pos = 0 ;
271
+ char [] chars = new char [n ];
272
+ int pos = 0 ;
228
273
229
- while (pos < n ) {
230
- chars [pos ] = this .next ();
231
- if (this .end ()) {
232
- throw this .syntaxError ("Substring bounds error" );
233
- }
234
- pos += 1 ;
235
- }
236
- return new String (chars );
237
- }
274
+ while (pos < n ) {
275
+ chars [pos ] = this .next ();
276
+ if (this .end ()) {
277
+ throw this .syntaxError ("Substring bounds error" );
278
+ }
279
+ pos += 1 ;
280
+ }
281
+ return new String (chars );
282
+ }
238
283
239
284
240
285
/**
@@ -378,15 +423,15 @@ public Object nextValue() throws JSONException {
378
423
String string ;
379
424
380
425
switch (c ) {
381
- case '"' :
382
- case '\'' :
383
- return this .nextString (c );
384
- case '{' :
385
- this .back ();
386
- return new JSONObject (this );
387
- case '[' :
388
- this .back ();
389
- return new JSONArray (this );
426
+ case '"' :
427
+ case '\'' :
428
+ return this .nextString (c );
429
+ case '{' :
430
+ this .back ();
431
+ return new JSONObject (this );
432
+ case '[' :
433
+ this .back ();
434
+ return new JSONArray (this );
390
435
}
391
436
392
437
/*
@@ -432,21 +477,24 @@ public char skipTo(char to) throws JSONException {
432
477
do {
433
478
c = this .next ();
434
479
if (c == 0 ) {
480
+ // in some readers, reset() may throw an exception if
481
+ // the remaining portion of the input is greater than
482
+ // the mark size (1,000,000 above).
435
483
this .reader .reset ();
436
484
this .index = startIndex ;
437
485
this .character = startCharacter ;
438
486
this .line = startLine ;
439
- return c ;
487
+ return 0 ;
440
488
}
441
489
} while (c != to );
490
+ this .reader .mark (1 );
442
491
} catch (IOException exception ) {
443
492
throw new JSONException (exception );
444
493
}
445
494
this .back ();
446
495
return c ;
447
496
}
448
497
449
-
450
498
/**
451
499
* Make a JSONException to signal a syntax error.
452
500
*
@@ -476,6 +524,6 @@ public JSONException syntaxError(String message, Throwable causedBy) {
476
524
@ Override
477
525
public String toString () {
478
526
return " at " + this .index + " [character " + this .character + " line " +
479
- this .line + "]" ;
527
+ this .line + "]" ;
480
528
}
481
529
}
0 commit comments