@@ -29,7 +29,7 @@ of this software and associated documentation files (the "Software"), to deal
2929LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3030OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3131SOFTWARE.
32- */
32+ */
3333
3434/**
3535 * 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
3939 * @version 2014-05-03
4040 */
4141public class JSONTokener {
42- /** current read character. */
42+ /** current read character position on the current line . */
4343 private long character ;
4444 /** flag to indicate if the end of the input has been found. */
4545 private boolean eof ;
4646 /** current read index of the input. */
4747 private long index ;
4848 /** current line of the input. */
4949 private long line ;
50- /** previous index of the input. */
50+ /** previous character read from the input. */
5151 private char previous ;
5252 /** Reader for the input. */
5353 private final Reader reader ;
5454 /** flag to indicate that a previous character was requested. */
5555 private boolean usePrevious ;
56+ /** the number of characters read in the previous line. */
57+ private long characterPreviousLine ;
5658
5759
5860 /**
59- * Construct a JSONTokener from a Reader.
61+ * Construct a JSONTokener from a Reader. The caller must close the Reader.
6062 *
6163 * @param reader A reader.
6264 */
6365 public JSONTokener (Reader reader ) {
6466 this .reader = reader .markSupported ()
65- ? reader
66- : new BufferedReader (reader );
67+ ? reader
68+ : new BufferedReader (reader );
6769 this .eof = false ;
6870 this .usePrevious = false ;
6971 this .previous = 0 ;
7072 this .index = 0 ;
7173 this .character = 1 ;
74+ this .characterPreviousLine = 0 ;
7275 this .line = 1 ;
7376 }
7477
7578
7679 /**
77- * Construct a JSONTokener from an InputStream.
80+ * Construct a JSONTokener from an InputStream. The caller must close the input stream.
7881 * @param inputStream The source.
7982 */
8083 public JSONTokener (InputStream inputStream ) {
@@ -103,12 +106,23 @@ public void back() throws JSONException {
103106 if (this .usePrevious || this .index <= 0 ) {
104107 throw new JSONException ("Stepping back two steps is not supported" );
105108 }
106- this .index -= 1 ;
107- this .character -= 1 ;
109+ this .decrementIndexes ();
108110 this .usePrevious = true ;
109111 this .eof = false ;
110112 }
111113
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+ }
112126
113127 /**
114128 * Get the hex value of a character (base16).
@@ -130,6 +144,8 @@ public static int dehexchar(char c) {
130144 }
131145
132146 /**
147+ * Checks if the end of the input has been reached.
148+ *
133149 * @return true if at the end of the file and we didn't step back
134150 */
135151 public boolean end () {
@@ -145,11 +161,24 @@ public boolean end() {
145161 * or backward while checking for more data.
146162 */
147163 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 );
151181 }
152- this .back ();
153182 return true ;
154183 }
155184
@@ -171,26 +200,39 @@ public char next() throws JSONException {
171200 } catch (IOException exception ) {
172201 throw new JSONException (exception );
173202 }
174-
175- if (c <= 0 ) { // End of stream
176- this .eof = true ;
177- c = 0 ;
178- }
179203 }
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 ;
189207 }
208+ this .incrementIndexes (c );
190209 this .previous = (char ) c ;
191210 return this .previous ;
192211 }
193212
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+ }
194236
195237 /**
196238 * Consume the next character, and check that it matches a specified
@@ -202,8 +244,11 @@ public char next() throws JSONException {
202244 public char next (char c ) throws JSONException {
203245 char n = this .next ();
204246 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 ''" );
207252 }
208253 return n ;
209254 }
@@ -218,23 +263,23 @@ public char next(char c) throws JSONException {
218263 * Substring bounds error if there are not
219264 * n characters remaining in the source string.
220265 */
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+ }
225270
226- char [] chars = new char [n ];
227- int pos = 0 ;
271+ char [] chars = new char [n ];
272+ int pos = 0 ;
228273
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+ }
238283
239284
240285 /**
@@ -378,15 +423,15 @@ public Object nextValue() throws JSONException {
378423 String string ;
379424
380425 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 );
390435 }
391436
392437 /*
@@ -432,21 +477,24 @@ public char skipTo(char to) throws JSONException {
432477 do {
433478 c = this .next ();
434479 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).
435483 this .reader .reset ();
436484 this .index = startIndex ;
437485 this .character = startCharacter ;
438486 this .line = startLine ;
439- return c ;
487+ return 0 ;
440488 }
441489 } while (c != to );
490+ this .reader .mark (1 );
442491 } catch (IOException exception ) {
443492 throw new JSONException (exception );
444493 }
445494 this .back ();
446495 return c ;
447496 }
448497
449-
450498 /**
451499 * Make a JSONException to signal a syntax error.
452500 *
@@ -476,6 +524,6 @@ public JSONException syntaxError(String message, Throwable causedBy) {
476524 @ Override
477525 public String toString () {
478526 return " at " + this .index + " [character " + this .character + " line " +
479- this .line + "]" ;
527+ this .line + "]" ;
480528 }
481529}
0 commit comments