Code Coverage Statistics for Source File
Newtonsoft.Json\JsonTextReader.cs
Symbol Coverage: 90.29% (344 of 381)
Branch Coverage: 92.62% (251 of 271)
Cyclomatic Complexity Avg: 6.21 Max:31
Code Lines: 381
Symbol Coverage Trend
View:
L | V | Source |
---|---|---|
1 |
#region License |
|
2 |
// Copyright (c) 2007 James Newton-King |
|
3 |
// |
|
4 |
// Permission is hereby granted, free of charge, to any person |
|
5 |
// obtaining a copy of this software and associated documentation |
|
6 |
// files (the "Software"), to deal in the Software without |
|
7 |
// restriction, including without limitation the rights to use, |
|
8 |
// copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
9 |
// copies of the Software, and to permit persons to whom the |
|
10 |
// Software is furnished to do so, subject to the following |
|
11 |
// conditions: |
|
12 |
// |
|
13 |
// The above copyright notice and this permission notice shall be |
|
14 |
// included in all copies or substantial portions of the Software. |
|
15 |
// |
|
16 |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
17 |
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
|
18 |
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
19 |
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
|
20 |
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
|
21 |
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
22 |
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
|
23 |
// OTHER DEALINGS IN THE SOFTWARE. |
|
24 |
#endregion |
|
25 |
||
26 |
using System; |
|
27 |
using System.Collections.Generic; |
|
28 |
using System.Text; |
|
29 |
using System.IO; |
|
30 |
using System.Xml; |
|
31 |
using System.Globalization; |
|
32 |
using Newtonsoft.Json.Utilities; |
|
33 |
||
34 |
namespace Newtonsoft.Json |
|
35 |
{ |
|
36 |
/// <summary> |
|
37 |
/// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. |
|
38 |
/// </summary> |
|
39 |
public class JsonTextReader : JsonReader, IJsonLineInfo |
|
40 |
{ |
|
41 |
private readonly TextReader _reader; |
|
42 |
private readonly StringBuffer _buffer; |
|
43 |
private char? _lastChar; |
|
44 |
private int _currentLinePosition; |
|
45 |
private int _currentLineNumber; |
|
46 |
private bool _end; |
|
47 |
||
48 |
/// <summary> |
|
49 |
/// Initializes a new instance of the <see cref="JsonReader"/> class with the specified <see cref="TextReader"/>. |
|
50 |
/// </summary> |
|
51 |
/// <param name="reader">The <c>TextReader</c> containing the XML data to read.</param> |
|
52 |
5439 |
public JsonTextReader(TextReader reader)
|
53 |
{ |
|
54 |
5439 |
if (reader == null)
|
55 |
1 |
throw new ArgumentNullException("reader");
|
56 |
||
57 |
5438 |
_reader = reader;
|
58 |
5438 |
_buffer = new StringBuffer(4096);
|
59 |
5438 |
_currentLineNumber = 1;
|
60 |
5438 |
}
|
61 |
||
62 |
private void ParseString(char quote) |
|
63 |
{ |
|
64 |
61309 |
ReadStringIntoBuffer(quote);
|
65 |
||
66 |
61305 |
string text = _buffer.ToString();
|
67 |
61305 |
_buffer.Position = 0;
|
68 |
||
69 |
61305 |
if (text.StartsWith("/Date(", StringComparison.Ordinal) && text.EndsWith(")/", StringComparison.Ordinal))
|
70 |
{ |
|
71 |
20056 |
ParseDate(text);
|
72 |
} |
|
73 |
else |
|
74 |
{ |
|
75 |
41249 |
SetToken(JsonToken.String, text);
|
76 |
41249 |
QuoteChar = quote;
|
77 |
} |
|
78 |
61305 |
}
|
79 |
||
80 |
private void ReadStringIntoBuffer(char quote) |
|
81 |
{ |
|
82 |
1956661 |
while (true)
|
83 |
{ |
|
84 |
1956661 |
char currentChar = MoveNext();
|
85 |
||
86 |
1956661 |
switch (currentChar)
|
87 |
{ |
|
88 |
case '\0': |
|
89 |
3 |
if (_end)
|
90 |
2 |
throw CreateJsonReaderException("Unterminated string. Expected delimiter: {0}. Line {1}, position {2}.", quote, _currentLineNumber, _currentLinePosition);
|
91 |
||
92 |
1 |
_buffer.Append('\0');
|
93 |
1 |
break;
|
94 |
case '\\': |
|
95 |
45186 |
if ((currentChar = MoveNext()) != '\0' || !_end)
|
96 |
{ |
|
97 |
45185 |
switch (currentChar)
|
98 |
{ |
|
99 |
case 'b': |
|
100 |
6 |
_buffer.Append('\b');
|
101 |
6 |
break;
|
102 |
case 't': |
|
103 |
8 |
_buffer.Append('\t');
|
104 |
8 |
break;
|
105 |
case 'n': |
|
106 |
9 |
_buffer.Append('\n');
|
107 |
9 |
break;
|
108 |
case 'f': |
|
109 |
6 |
_buffer.Append('\f');
|
110 |
6 |
break;
|
111 |
case 'r': |
|
112 |
9 |
_buffer.Append('\r');
|
113 |
9 |
break;
|
114 |
case '\\': |
|
115 |
17 |
_buffer.Append('\\');
|
116 |
17 |
break;
|
117 |
case '"': |
|
118 |
case '\'': |
|
119 |
case '/': |
|
120 |
40121 |
_buffer.Append(currentChar);
|
121 |
40121 |
break;
|
122 |
case 'u': |
|
123 |
5008 |
char[] hexValues = new char[4];
|
124 |
5008 |
for (int i = 0; i < hexValues.Length; i++) |
125 |
{ |
|
126 |
20032 |
if ((currentChar = MoveNext()) != '\0' || !_end)
|
127 |
20031 |
hexValues[i] = currentChar;
|
128 |
else |
|
129 |
1 |
throw CreateJsonReaderException("Unexpected end while parsing unicode character. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
|
130 |
} |
|
131 |
||
132 |
5007 |
char hexChar = Convert.ToChar(int.Parse(new string(hexValues), NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo));
|
133 |
5007 |
_buffer.Append(hexChar);
|
134 |
5007 |
break;
|
135 |
default: |
|
136 |
1 |
throw CreateJsonReaderException("Bad JSON escape sequence: {0}. Line {1}, position {2}.", @"\" + currentChar, _currentLineNumber, _currentLinePosition);
|
137 |
} |
|
138 |
} |
|
139 |
else |
|
140 |
{ |
|
141 |
1 |
throw CreateJsonReaderException("Unterminated string. Expected delimiter: {0}. Line {1}, position {2}.", quote, _currentLineNumber, _currentLinePosition);
|
142 |
} |
|
143 |
45183 |
break;
|
144 |
case '"': |
|
145 |
case '\'': |
|
146 |
158121 |
if (currentChar == quote)
|
147 |
{ |
|
148 |
158105 |
return;
|
149 |
} |
|
150 |
else |
|
151 |
{ |
|
152 |
16 |
_buffer.Append(currentChar);
|
153 |
} |
|
154 |
16 |
break;
|
155 |
default: |
|
156 |
1753351 |
_buffer.Append(currentChar);
|
157 |
1753351 |
break;
|
158 |
} |
|
159 |
} |
|
160 |
158105 |
}
|
161 |
||
162 |
private JsonReaderException CreateJsonReaderException(string format, params object[] args) |
|
163 |
{ |
|
164 |
11 |
string message = format.FormatWith(CultureInfo.InvariantCulture, args);
|
165 |
11 |
return new JsonReaderException(message, null, _currentLineNumber, _currentLinePosition);
|
166 |
11 |
}
|
167 |
||
168 |
private void ParseDate(string text) |
|
169 |
{ |
|
170 |
20056 |
string value = text.Substring(6, text.Length - 8);
|
171 |
20056 |
DateTimeKind kind = DateTimeKind.Utc;
|
172 |
||
173 |
20056 |
int index = value.IndexOf('+', 1);
|
174 |
||
175 |
20056 |
if (index == -1)
|
176 |
47 |
index = value.IndexOf('-', 1);
|
177 |
||
178 |
20056 |
if (index != -1)
|
179 |
{ |
|
180 |
20015 |
kind = DateTimeKind.Local;
|
181 |
20015 |
value = value.Substring(0, index);
|
182 |
} |
|
183 |
||
184 |
20056 |
long javaScriptTicks = long.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
|
185 |
20056 |
DateTime utcDateTime = JsonConvert.ConvertJavaScriptTicksToDateTime(javaScriptTicks);
|
186 |
DateTime dateTime; |
|
187 |
||
188 |
20056 |
switch (kind)
|
189 |
{ |
|
190 |
case DateTimeKind.Unspecified: |
|
191 |
0 |
dateTime = DateTime.SpecifyKind(utcDateTime.ToLocalTime(), DateTimeKind.Unspecified);
|
192 |
0 |
break;
|
193 |
case DateTimeKind.Local: |
|
194 |
20015 |
dateTime = utcDateTime.ToLocalTime();
|
195 |
20015 |
break;
|
196 |
default: |
|
197 |
41 |
dateTime = utcDateTime;
|
198 |
41 |
break;
|
199 |
} |
|
200 |
||
201 |
20056 |
SetToken(JsonToken.Date, dateTime);
|
202 |
20056 |
}
|
203 |
||
204 |
private const int LineFeedValue = StringUtils.LineFeed; |
|
205 |
private const int CarriageReturnValue = StringUtils.CarriageReturn; |
|
206 |
||
207 |
private char MoveNext() |
|
208 |
{ |
|
209 |
2586278 |
int value = _reader.Read();
|
210 |
||
211 |
2586278 |
switch (value)
|
212 |
{ |
|
213 |
case -1: |
|
214 |
217 |
_end = true;
|
215 |
217 |
return '\0';
|
216 |
case CarriageReturnValue: |
|
217 |
2345 |
if (_reader.Peek() == LineFeedValue)
|
218 |
2345 |
_reader.Read();
|
219 |
||
220 |
2345 |
_currentLineNumber++;
|
221 |
2345 |
_currentLinePosition = 0;
|
222 |
2345 |
break;
|
223 |
case LineFeedValue: |
|
224 |
1 |
_currentLineNumber++;
|
225 |
1 |
_currentLinePosition = 0;
|
226 |
1 |
break;
|
227 |
default: |
|
228 |
2583715 |
_currentLinePosition++;
|
229 |
2583715 |
break;
|
230 |
} |
|
231 |
||
232 |
2586061 |
return (char)value;
|
233 |
2586278 |
}
|
234 |
||
235 |
private bool HasNext() |
|
236 |
{ |
|
237 |
10131 |
return (_reader.Peek() != -1);
|
238 |
10131 |
}
|
239 |
||
240 |
private int PeekNext() |
|
241 |
{ |
|
242 |
20340 |
return _reader.Peek();
|
243 |
20340 |
}
|
244 |
||
245 |
/// <summary> |
|
246 |
/// Reads the next JSON token from the stream. |
|
247 |
/// </summary> |
|
248 |
/// <returns> |
|
249 |
/// true if the next token was read successfully; false if there are no more tokens to read. |
|
250 |
/// </returns> |
|
251 |
public override bool Read() |
|
252 |
{ |
|
253 |
347775 |
while (true)
|
254 |
{ |
|
255 |
char currentChar; |
|
256 |
347775 |
if (_lastChar != null)
|
257 |
{ |
|
258 |
20490 |
currentChar = _lastChar.Value;
|
259 |
20490 |
_lastChar = null;
|
260 |
} |
|
261 |
else |
|
262 |
{ |
|
263 |
327285 |
currentChar = MoveNext();
|
264 |
} |
|
265 |
||
266 |
347775 |
if (currentChar == '\0' && _end)
|
267 |
194 |
return false;
|
268 |
||
269 |
347581 |
switch (CurrentState)
|
270 |
{ |
|
271 |
case State.Start: |
|
272 |
case State.Property: |
|
273 |
case State.Array: |
|
274 |
case State.ArrayStart: |
|
275 |
case State.Constructor: |
|
276 |
case State.ConstructorStart: |
|
277 |
128030 |
return ParseValue(currentChar);
|
278 |
case State.Complete: |
|
279 |
0 |
break;
|
280 |
case State.Object: |
|
281 |
case State.ObjectStart: |
|
282 |
96964 |
return ParseObject(currentChar);
|
283 |
case State.PostValue: |
|
284 |
// returns true if it hits |
|
285 |
// end of object or array |
|
286 |
122587 |
if (ParsePostValue(currentChar))
|
287 |
35967 |
return true;
|
288 |
86619 |
break;
|
289 |
case State.Closed: |
|
290 |
0 |
break;
|
291 |
case State.Error: |
|
292 |
0 |
break;
|
293 |
default: |
|
294 |
0 |
throw CreateJsonReaderException("Unexpected state: {0}. Line {1}, position {2}.", CurrentState, _currentLineNumber, _currentLinePosition);
|
295 |
} |
|
296 |
} |
|
297 |
261148 |
}
|
298 |
||
299 |
/// <summary> |
|
300 |
/// Reads the next JSON token from the stream as a <see cref="T:Byte[]"/>. |
|
301 |
/// </summary> |
|
302 |
/// <returns> |
|
303 |
/// A <see cref="T:Byte[]"/> or a null reference if the next JSON token is null. |
|
304 |
/// </returns> |
|
305 |
public override byte[] ReadAsBytes() |
|
306 |
{ |
|
307 |
11 |
while (true)
|
308 |
{ |
|
309 |
char currentChar; |
|
310 |
11 |
if (_lastChar != null)
|
311 |
{ |
|
312 |
1 |
currentChar = _lastChar.Value;
|
313 |
1 |
_lastChar = null;
|
314 |
} |
|
315 |
else |
|
316 |
{ |
|
317 |
10 |
currentChar = MoveNext();
|
318 |
} |
|
319 |
||
320 |
11 |
if (currentChar == '\0' && _end)
|
321 |
1 |
throw CreateJsonReaderException("Unexpected end when reading bytes: Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
|
322 |
||
323 |
10 |
switch (CurrentState)
|
324 |
{ |
|
325 |
case State.PostValue: |
|
326 |
case State.Start: |
|
327 |
case State.Property: |
|
328 |
case State.Array: |
|
329 |
case State.ArrayStart: |
|
330 |
case State.Constructor: |
|
331 |
case State.ConstructorStart: |
|
332 |
do |
|
333 |
{ |
|
334 |
14 |
switch (currentChar)
|
335 |
{ |
|
336 |
case '"': |
|
337 |
case '\'': |
|
338 |
7 |
ReadStringIntoBuffer(currentChar);
|
339 |
||
340 |
byte[] data; |
|
341 |
6 |
if (_buffer.Position == 0)
|
342 |
{ |
|
343 |
2 |
data = new byte[0];
|
344 |
} |
|
345 |
else |
|
346 |
{ |
|
347 |
4 |
data = Convert.FromBase64CharArray(_buffer.GetInternalBuffer(), 0, _buffer.Position);
|
348 |
4 |
_buffer.Position = 0;
|
349 |
} |
|
350 |
||
351 |
6 |
SetToken(JsonToken.Bytes, data);
|
352 |
||
353 |
6 |
return data;
|
354 |
case 'n': |
|
355 |
1 |
ParseNull();
|
356 |
1 |
return null;
|
357 |
case ' ': |
|
358 |
case StringUtils.Tab: |
|
359 |
case StringUtils.LineFeed: |
|
360 |
case StringUtils.CarriageReturn: |
|
361 |
// eat |
|
362 |
4 |
break;
|
363 |
case ',': |
|
364 |
1 |
SetStateBasedOnCurrent();
|
365 |
1 |
break;
|
366 |
default: |
|
367 |
1 |
if (char.IsWhiteSpace(currentChar))
|
368 |
{ |
|
369 |
// eat |
|
370 |
} |
|
371 |
else |
|
372 |
{ |
|
373 |
1 |
throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
|
374 |
} |
|
375 |
0 |
break;
|
376 |
} |
|
377 |
5 |
} while ((currentChar = MoveNext()) != '\0' || !_end);
|
378 |
1 |
break;
|
379 |
default: |
|
380 |
0 |
throw CreateJsonReaderException("Unexpected state: {0}. Line {1}, position {2}.", CurrentState, _currentLineNumber, _currentLinePosition);
|
381 |
} |
|
382 |
} |
|
383 |
7 |
}
|
384 |
||
385 |
private bool ParsePostValue(char currentChar) |
|
386 |
{ |
|
387 |
do |
|
388 |
{ |
|
389 |
125738 |
switch (currentChar)
|
390 |
{ |
|
391 |
case '}': |
|
392 |
25709 |
SetToken(JsonToken.EndObject);
|
393 |
25709 |
return true;
|
394 |
case ']': |
|
395 |
10238 |
SetToken(JsonToken.EndArray);
|
396 |
10238 |
return true;
|
397 |
case ')': |
|
398 |
13 |
SetToken(JsonToken.EndConstructor);
|
399 |
13 |
return true;
|
400 |
case '/': |
|
401 |
7 |
ParseComment();
|
402 |
7 |
return true;
|
403 |
case ',': |
|
404 |
// finished parsing |
|
405 |
86614 |
SetStateBasedOnCurrent();
|
406 |
86614 |
return false;
|
407 |
case ' ': |
|
408 |
case StringUtils.Tab: |
|
409 |
case StringUtils.LineFeed: |
|
410 |
case StringUtils.CarriageReturn: |
|
411 |
// eat |
|
412 |
3156 |
break;
|
413 |
default: |
|
414 |
1 |
if (char.IsWhiteSpace(currentChar))
|
415 |
{ |
|
416 |
// eat |
|
417 |
} |
|
418 |
else |
|
419 |
{ |
|
420 |
1 |
throw CreateJsonReaderException("After parsing a value an unexpected character was encountered: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
|
421 |
} |
|
422 |
0 |
break;
|
423 |
} |
|
424 |
3156 |
} while ((currentChar = MoveNext()) != '\0' || !_end);
|
425 |
||
426 |
5 |
return false;
|
427 |
122586 |
}
|
428 |
||
429 |
private bool ParseObject(char currentChar) |
|
430 |
{ |
|
431 |
do |
|
432 |
{ |
|
433 |
106918 |
switch (currentChar)
|
434 |
{ |
|
435 |
case '}': |
|
436 |
28 |
SetToken(JsonToken.EndObject);
|
437 |
28 |
return true;
|
438 |
case '/': |
|
439 |
2 |
ParseComment();
|
440 |
2 |
return true;
|
441 |
case ' ': |
|
442 |
case StringUtils.Tab: |
|
443 |
case StringUtils.LineFeed: |
|
444 |
case StringUtils.CarriageReturn: |
|
445 |
// eat |
|
446 |
9954 |
break;
|
447 |
default: |
|
448 |
96934 |
if (char.IsWhiteSpace(currentChar))
|
449 |
{ |
|
450 |
// eat |
|
451 |
} |
|
452 |
else |
|
453 |
{ |
|
454 |
96934 |
return ParseProperty(currentChar);
|
455 |
} |
|
456 |
0 |
break;
|
457 |
} |
|
458 |
9954 |
} while ((currentChar = MoveNext()) != '\0' || !_end);
|
459 |
||
460 |
0 |
return false;
|
461 |
96961 |
}
|
462 |
||
463 |
private bool ParseProperty(char firstChar) |
|
464 |
{ |
|
465 |
96934 |
char currentChar = firstChar;
|
466 |
char quoteChar; |
|
467 |
||
468 |
96934 |
if (ValidIdentifierChar(currentChar))
|
469 |
{ |
|
470 |
139 |
quoteChar = '\0';
|
471 |
139 |
currentChar = ParseUnquotedProperty(currentChar);
|
472 |
} |
|
473 |
96795 |
else if (currentChar == '"' || currentChar == '\'')
|
474 |
{ |
|
475 |
96794 |
quoteChar = currentChar;
|
476 |
96794 |
ReadStringIntoBuffer(quoteChar);
|
477 |
96794 |
currentChar = MoveNext();
|
478 |
} |
|
479 |
else |
|
480 |
{ |
|
481 |
1 |
throw CreateJsonReaderException("Invalid property identifier character: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
|
482 |
} |
|
483 |
||
484 |
96932 |
if (currentChar != ':')
|
485 |
{ |
|
486 |
88 |
currentChar = MoveNext();
|
487 |
||
488 |
// finished property. skip any whitespace and move to colon |
|
489 |
88 |
EatWhitespace(currentChar, false, out currentChar);
|
490 |
||
491 |
88 |
if (currentChar != ':')
|
492 |
1 |
throw CreateJsonReaderException("Invalid character after parsing property name. Expected ':' but got: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
|
493 |
} |
|
494 |
||
495 |
96931 |
SetToken(JsonToken.PropertyName, _buffer.ToString());
|
496 |
96931 |
QuoteChar = quoteChar;
|
497 |
96931 |
_buffer.Position = 0;
|
498 |
||
499 |
96931 |
return true;
|
500 |
96931 |
}
|
501 |
||
502 |
private bool ValidIdentifierChar(char value) |
|
503 |
{ |
|
504 |
97885 |
return (char.IsLetterOrDigit(value) || value == '_' || value == '$');
|
505 |
97885 |
}
|
506 |
||
507 |
private char ParseUnquotedProperty(char firstChar) |
|
508 |
{ |
|
509 |
// parse unquoted property name until whitespace or colon |
|
510 |
139 |
_buffer.Append(firstChar);
|
511 |
||
512 |
char currentChar; |
|
513 |
||
514 |
1090 |
while ((currentChar = MoveNext()) != '\0' || !_end)
|
515 |
{ |
|
516 |
1089 |
if (char.IsWhiteSpace(currentChar) || currentChar == ':')
|
517 |
{ |
|
518 |
138 |
return currentChar;
|
519 |
} |
|
520 |
951 |
else if (ValidIdentifierChar(currentChar))
|
521 |
{ |
|
522 |
951 |
_buffer.Append(currentChar);
|
523 |
} |
|
524 |
else |
|
525 |
{ |
|
526 |
0 |
throw CreateJsonReaderException("Invalid JavaScript property identifier character: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
|
527 |
} |
|
528 |
} |
|
529 |
||
530 |
1 |
throw CreateJsonReaderException("Unexpected end when parsing unquoted property name. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
|
531 |
138 |
}
|
532 |
||
533 |
private bool ParseValue(char currentChar) |
|
534 |
{ |
|
535 |
do |
|
536 |
{ |
|
537 |
131743 |
switch (currentChar)
|
538 |
{ |
|
539 |
case '"': |
|
540 |
case '\'': |
|
541 |
61309 |
ParseString(currentChar);
|
542 |
61305 |
return true;
|
543 |
case 't': |
|
544 |
27 |
ParseTrue();
|
545 |
27 |
return true;
|
546 |
case 'f': |
|
547 |
16 |
ParseFalse();
|
548 |
16 |
return true;
|
549 |
case 'n': |
|
550 |
10131 |
if (HasNext())
|
551 |
{ |
|
552 |
10131 |
char next = (char)PeekNext();
|
553 |
||
554 |
10131 |
if (next == 'u')
|
555 |
10118 |
ParseNull();
|
556 |
13 |
else if (next == 'e')
|
557 |
13 |
ParseConstructor();
|
558 |
else |
|
559 |
0 |
throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
|
560 |
} |
|
561 |
else |
|
562 |
{ |
|
563 |
0 |
throw CreateJsonReaderException("Unexpected end. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
|
564 |
} |
|
565 |
10131 |
return true;
|
566 |
case 'N': |
|
567 |
3 |
ParseNumberNaN();
|
568 |
3 |
return true;
|
569 |
case 'I': |
|
570 |
3 |
ParseNumberPositiveInfinity();
|
571 |
3 |
return true;
|
572 |
case '-': |
|
573 |
23 |
if (PeekNext() == 'I')
|
574 |
3 |
ParseNumberNegativeInfinity();
|
575 |
else |
|
576 |
20 |
ParseNumber(currentChar);
|
577 |
23 |
return true;
|
578 |
case '/': |
|
579 |
7 |
ParseComment();
|
580 |
7 |
return true;
|
581 |
case 'u': |
|
582 |
2 |
ParseUndefined();
|
583 |
2 |
return true;
|
584 |
case '{': |
|
585 |
25767 |
SetToken(JsonToken.StartObject);
|
586 |
25767 |
return true;
|
587 |
case '[': |
|
588 |
10253 |
SetToken(JsonToken.StartArray);
|
589 |
10253 |
return true;
|
590 |
case '}': |
|
591 |
0 |
SetToken(JsonToken.EndObject);
|
592 |
0 |
return true;
|
593 |
case ']': |
|
594 |
4 |
SetToken(JsonToken.EndArray);
|
595 |
4 |
return true;
|
596 |
case ',': |
|
597 |
0 |
SetToken(JsonToken.Undefined);
|
598 |
0 |
return true;
|
599 |
case ')': |
|
600 |
0 |
SetToken(JsonToken.EndConstructor);
|
601 |
0 |
return true;
|
602 |
case ' ': |
|
603 |
case StringUtils.Tab: |
|
604 |
case StringUtils.LineFeed: |
|
605 |
case StringUtils.CarriageReturn: |
|
606 |
// eat |
|
607 |
3713 |
break;
|
608 |
default: |
|
609 |
20485 |
if (char.IsWhiteSpace(currentChar))
|
610 |
{ |
|
611 |
// eat |
|
612 |
} |
|
613 |
20485 |
else if (char.IsNumber(currentChar) || currentChar == '-' || currentChar == '.')
|
614 |
{ |
|
615 |
20485 |
ParseNumber(currentChar);
|
616 |
20485 |
return true;
|
617 |
} |
|
618 |
else |
|
619 |
{ |
|
620 |
0 |
throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
|
621 |
} |
|
622 |
0 |
break;
|
623 |
} |
|
624 |
3713 |
} while ((currentChar = MoveNext()) != '\0' || !_end);
|
625 |
||
626 |
0 |
return false;
|
627 |
128026 |
}
|
628 |
||
629 |
private bool EatWhitespace(char initialChar, bool oneOrMore, out char finalChar) |
|
630 |
{ |
|
631 |
114 |
bool whitespace = false;
|
632 |
114 |
char currentChar = initialChar;
|
633 |
127 |
while (currentChar == ' ' || char.IsWhiteSpace(currentChar))
|
634 |
{ |
|
635 |
13 |
whitespace = true;
|
636 |
13 |
currentChar = MoveNext();
|
637 |
} |
|
638 |
||
639 |
114 |
finalChar = currentChar;
|
640 |
||
641 |
114 |
return (!oneOrMore || whitespace);
|
642 |
114 |
}
|
643 |
||
644 |
private void ParseConstructor() |
|
645 |
{ |
|
646 |
13 |
if (MatchValue('n', "new", true))
|
647 |
{ |
|
648 |
13 |
char currentChar = MoveNext();
|
649 |
||
650 |
13 |
if (EatWhitespace(currentChar, true, out currentChar))
|
651 |
{ |
|
652 |
65 |
while (char.IsLetter(currentChar))
|
653 |
{ |
|
654 |
52 |
_buffer.Append(currentChar);
|
655 |
52 |
currentChar = MoveNext();
|
656 |
} |
|
657 |
||
658 |
13 |
EatWhitespace(currentChar, false, out currentChar);
|
659 |
||
660 |
13 |
if (currentChar != '(')
|
661 |
0 |
throw CreateJsonReaderException("Unexpected character while parsing constructor: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
|
662 |
||
663 |
13 |
string constructorName = _buffer.ToString();
|
664 |
13 |
_buffer.Position = 0;
|
665 |
||
666 |
13 |
SetToken(JsonToken.StartConstructor, constructorName);
|
667 |
} |
|
668 |
} |
|
669 |
13 |
}
|
670 |
||
671 |
private void ParseNumber(char firstChar) |
|
672 |
{ |
|
673 |
20505 |
char currentChar = firstChar;
|
674 |
20505 |
bool nonBase10 = (firstChar == '0');
|
675 |
||
676 |
// parse until seperator character or end |
|
677 |
20505 |
bool end = false;
|
678 |
do |
|
679 |
{ |
|
680 |
111966 |
if (IsSeperator(currentChar))
|
681 |
{ |
|
682 |
20494 |
end = true;
|
683 |
20494 |
_lastChar = currentChar;
|
684 |
} |
|
685 |
else |
|
686 |
{ |
|
687 |
91472 |
_buffer.Append(currentChar);
|
688 |
} |
|
689 |
||
690 |
111966 |
} while (!end && ((currentChar = MoveNext()) != '\0' || !_end));
|
691 |
||
692 |
20505 |
string number = _buffer.ToString();
|
693 |
object numberValue; |
|
694 |
JsonToken numberType; |
|
695 |
||
696 |
20505 |
if (number.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1
|
697 |
20505 |
|| number.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1)
|
698 |
{ |
|
699 |
5093 |
numberValue = Convert.ToDouble(number, CultureInfo.InvariantCulture);
|
700 |
5093 |
numberType = JsonToken.Float;
|
701 |
} |
|
702 |
15412 |
else if (nonBase10)
|
703 |
{ |
|
704 |
34 |
numberValue = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
|
705 |
34 |
? Convert.ToInt64(number, 16)
|
706 |
34 |
: Convert.ToInt64(number, 8);
|
707 |
34 |
numberType = JsonToken.Integer;
|
708 |
} |
|
709 |
else |
|
710 |
{ |
|
711 |
15378 |
numberValue = Convert.ToInt64(number, CultureInfo.InvariantCulture);
|
712 |
15378 |
numberType = JsonToken.Integer;
|
713 |
} |
|
714 |
||
715 |
20505 |
_buffer.Position = 0;
|
716 |
||
717 |
20505 |
SetToken(numberType, numberValue);
|
718 |
20505 |
}
|
719 |
||
720 |
private void ParseComment() |
|
721 |
{ |
|
722 |
// should have already parsed / character before reaching this method |
|
723 |
||
724 |
16 |
char currentChar = MoveNext();
|
725 |
||
726 |
16 |
if (currentChar == '*')
|
727 |
{ |
|
728 |
126 |
while ((currentChar = MoveNext()) != '\0' || !_end)
|
729 |
{ |
|
730 |
126 |
if (currentChar == '*')
|
731 |
{ |
|
732 |
17 |
if ((currentChar = MoveNext()) != '\0' || !_end)
|
733 |
{ |
|
734 |
17 |
if (currentChar == '/')
|
735 |
{ |
|
736 |
16 |
break;
|
737 |
} |
|
738 |
else |
|
739 |
{ |
|
740 |
1 |
_buffer.Append('*');
|
741 |
1 |
_buffer.Append(currentChar);
|
742 |
} |
|
743 |
} |
|
744 |
} |
|
745 |
else |
|
746 |
{ |
|
747 |
109 |
_buffer.Append(currentChar);
|
748 |
} |
|
749 |
} |
|
750 |
} |
|
751 |
else |
|
752 |
{ |
|
753 |
0 |
throw CreateJsonReaderException("Error parsing comment. Expected: *. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
|
754 |
} |
|
755 |
||
756 |
16 |
SetToken(JsonToken.Comment, _buffer.ToString());
|
757 |
||
758 |
16 |
_buffer.Position = 0;
|
759 |
16 |
}
|
760 |
||
761 |
private bool MatchValue(char firstChar, string value) |
|
762 |
{ |
|
763 |
10186 |
char currentChar = firstChar;
|
764 |
||
765 |
10186 |
int i = 0;
|
766 |
do |
|
767 |
{ |
|
768 |
40781 |
if (currentChar != value[i])
|
769 |
{ |
|
770 |
0 |
break;
|
771 |
} |
|
772 |
40781 |
i++;
|
773 |
} |
|
774 |
40781 |
while (i < value.Length && ((currentChar = MoveNext()) != '\0' || !_end));
|
775 |
||
776 |
10186 |
return (i == value.Length);
|
777 |
10186 |
}
|
778 |
||
779 |
private bool MatchValue(char firstChar, string value, bool noTrailingNonSeperatorCharacters) |
|
780 |
{ |
|
781 |
// will match value and then move to the next character, checking that it is a seperator character |
|
782 |
10186 |
bool match = MatchValue(firstChar, value);
|
783 |
||
784 |
10186 |
if (!noTrailingNonSeperatorCharacters)
|
785 |
{ |
|
786 |
0 |
return match;
|
787 |
} |
|
788 |
else |
|
789 |
{ |
|
790 |
10186 |
int c = PeekNext();
|
791 |
10186 |
char next = (c != -1) ? (char) c : '\0';
|
792 |
10186 |
bool matchAndNoTrainingNonSeperatorCharacters = (match && (next == '\0' || IsSeperator(next)));
|
793 |
||
794 |
10186 |
return matchAndNoTrainingNonSeperatorCharacters;
|
795 |
} |
|
796 |
10186 |
}
|
797 |
||
798 |
private bool IsSeperator(char c) |
|
799 |
{ |
|
800 |
122148 |
switch (c)
|
801 |
{ |
|
802 |
case '}': |
|
803 |
case ']': |
|
804 |
case ',': |
|
805 |
30530 |
return true;
|
806 |
case '/': |
|
807 |
// check next character to see if start of a comment |
|
808 |
0 |
return (HasNext() && PeekNext() == '*');
|
809 |
case ')': |
|
810 |
13 |
if (CurrentState == State.Constructor || CurrentState == State.ConstructorStart)
|
811 |
13 |
return true;
|
812 |
0 |
break;
|
813 |
case ' ': |
|
814 |
case StringUtils.Tab: |
|
815 |
case StringUtils.LineFeed: |
|
816 |
case StringUtils.CarriageReturn: |
|
817 |
133 |
return true;
|
818 |
default: |
|
819 |
91472 |
if (char.IsWhiteSpace(c))
|
820 |
0 |
return true;
|
821 |
91472 |
break;
|
822 |
} |
|
823 |
||
824 |
91472 |
return false;
|
825 |
122148 |
}
|
826 |
||
827 |
private void ParseTrue() |
|
828 |
{ |
|
829 |
// check characters equal 'true' |
|
830 |
// and that it is followed by either a seperator character |
|
831 |
// or the text ends |
|
832 |
27 |
if (MatchValue('t', JsonConvert.True, true))
|
833 |
{ |
|
834 |
27 |
SetToken(JsonToken.Boolean, true);
|
835 |
} |
|
836 |
else |
|
837 |
{ |
|
838 |
0 |
throw CreateJsonReaderException("Error parsing boolean value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
|
839 |
} |
|
840 |
27 |
}
|
841 |
||
842 |
private void ParseNull() |
|
843 |
{ |
|
844 |
10119 |
if (MatchValue('n', JsonConvert.Null, true))
|
845 |
{ |
|
846 |
10119 |
SetToken(JsonToken.Null);
|
847 |
} |
|
848 |
else |
|
849 |
{ |
|
850 |
0 |
throw CreateJsonReaderException("Error parsing null value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
|
851 |
} |
|
852 |
10119 |
}
|
853 |
||
854 |
private void ParseUndefined() |
|
855 |
{ |
|
856 |
2 |
if (MatchValue('u', JsonConvert.Undefined, true))
|
857 |
{ |
|
858 |
2 |
SetToken(JsonToken.Undefined);
|
859 |
} |
|
860 |
else |
|
861 |
{ |
|
862 |
0 |
throw CreateJsonReaderException("Error parsing undefined value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
|
863 |
} |
|
864 |
2 |
}
|
865 |
||
866 |
private void ParseFalse() |
|
867 |
{ |
|
868 |
16 |
if (MatchValue('f', JsonConvert.False, true))
|
869 |
{ |
|
870 |
16 |
SetToken(JsonToken.Boolean, false);
|
871 |
} |
|
872 |
else |
|
873 |
{ |
|
874 |
0 |
throw CreateJsonReaderException("Error parsing boolean value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
|
875 |
} |
|
876 |
16 |
}
|
877 |
||
878 |
private void ParseNumberNegativeInfinity() |
|
879 |
{ |
|
880 |
3 |
if (MatchValue('-', JsonConvert.NegativeInfinity, true))
|
881 |
{ |
|
882 |
3 |
SetToken(JsonToken.Float, double.NegativeInfinity);
|
883 |
} |
|
884 |
else |
|
885 |
{ |
|
886 |
0 |
throw CreateJsonReaderException("Error parsing negative infinity value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
|
887 |
} |
|
888 |
3 |
}
|
889 |
||
890 |
private void ParseNumberPositiveInfinity() |
|
891 |
{ |
|
892 |
3 |
if (MatchValue('I', JsonConvert.PositiveInfinity, true))
|
893 |
{ |
|
894 |
3 |
SetToken(JsonToken.Float, double.PositiveInfinity);
|
895 |
} |
|
896 |
else |
|
897 |
{ |
|
898 |
0 |
throw CreateJsonReaderException("Error parsing positive infinity value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
|
899 |
} |
|
900 |
3 |
}
|
901 |
||
902 |
private void ParseNumberNaN() |
|
903 |
{ |
|
904 |
3 |
if (MatchValue('N', JsonConvert.NaN, true))
|
905 |
{ |
|
906 |
3 |
SetToken(JsonToken.Float, double.NaN);
|
907 |
} |
|
908 |
else |
|
909 |
{ |
|
910 |
0 |
throw CreateJsonReaderException("Error parsing NaN value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
|
911 |
} |
|
912 |
3 |
}
|
913 |
||
914 |
/// <summary> |
|
915 |
/// Changes the state to closed. |
|
916 |
/// </summary> |
|
917 |
public override void Close() |
|
918 |
{ |
|
919 |
215 |
base.Close();
|
920 |
||
921 |
215 |
if (_reader != null)
|
922 |
215 |
_reader.Close();
|
923 |
||
924 |
215 |
if (_buffer != null)
|
925 |
215 |
_buffer.Clear();
|
926 |
215 |
}
|
927 |
||
928 |
/// <summary> |
|
929 |
/// Gets a value indicating whether the class can return line information. |
|
930 |
/// </summary> |
|
931 |
/// <returns> |
|
932 |
/// <c>true</c> if LineNumber and LinePosition can be provided; otherwise, <c>false</c>. |
|
933 |
/// </returns> |
|
934 |
public bool HasLineInfo() |
|
935 |
{ |
|
936 |
867 |
return true;
|
937 |
867 |
}
|
938 |
||
939 |
/// <summary> |
|
940 |
/// Gets the current line number. |
|
941 |
/// </summary> |
|
942 |
/// <value> |
|
943 |
/// The current line number or 0 if no line information is available (for example, HasLineInfo returns false). |
|
944 |
/// </value> |
|
945 |
public int LineNumber |
|
946 |
{ |
|
947 |
get |
|
948 |
{ |
|
949 |
912 |
if (CurrentState == State.Start)
|
950 |
1 |
return 0;
|
951 |
||
952 |
911 |
return _currentLineNumber;
|
953 |
912 |
}
|
954 |
} |
|
955 |
||
956 |
/// <summary> |
|
957 |
/// Gets the current line position. |
|
958 |
/// </summary> |
|
959 |
/// <value> |
|
960 |
/// The current line position or 0 if no line information is available (for example, HasLineInfo returns false). |
|
961 |
/// </value> |
|
962 |
public int LinePosition |
|
963 |
{ |
|
964 |
912 |
get { return _currentLinePosition; }
|
965 |
} |
|
966 |
} |
|
967 |
} |