blob: 48e84ccc4e283a8b0c2a0583c1554f5c3a1718bf [file] [log] [blame]
csharptestafe844b2011-06-10 16:03:22 -05001using System;
2using System.Collections.Generic;
csharptest74c5e0c2011-07-14 13:06:22 -05003using System.Diagnostics;
csharptestafe844b2011-06-10 16:03:22 -05004using System.Globalization;
5using System.IO;
csharptestafe844b2011-06-10 16:03:22 -05006
7namespace Google.ProtocolBuffers.Serialization
8{
9 /// <summary>
10 /// JSon Tokenizer used by JsonFormatReader
11 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -050012 internal abstract class JsonCursor
csharptestafe844b2011-06-10 16:03:22 -050013 {
csharptest74c5e0c2011-07-14 13:06:22 -050014 public enum JsType
15 {
16 String,
17 Number,
18 Object,
19 Array,
20 True,
21 False,
22 Null
23 }
csharptestafe844b2011-06-10 16:03:22 -050024
csharptest7fc785c2011-06-10 23:54:53 -050025 #region Buffering implementations
csharptest74c5e0c2011-07-14 13:06:22 -050026
27 private class JsonStreamCursor : JsonCursor
csharptest7fc785c2011-06-10 23:54:53 -050028 {
29 private readonly byte[] _buffer;
30 private int _bufferPos;
31 private readonly Stream _input;
32
33 public JsonStreamCursor(Stream input)
34 {
35 _input = input;
36 _next = _input.ReadByte();
37 }
csharptest74c5e0c2011-07-14 13:06:22 -050038
csharptest7fc785c2011-06-10 23:54:53 -050039 public JsonStreamCursor(byte[] input)
40 {
41 _input = null;
42 _buffer = input;
43 _next = _buffer[_bufferPos];
44 }
45
46 protected override int Peek()
47 {
48 if (_input != null)
csharptest74c5e0c2011-07-14 13:06:22 -050049 {
csharptest7fc785c2011-06-10 23:54:53 -050050 return _next;
csharptest74c5e0c2011-07-14 13:06:22 -050051 }
csharptest7fc785c2011-06-10 23:54:53 -050052 else if (_bufferPos < _buffer.Length)
csharptest74c5e0c2011-07-14 13:06:22 -050053 {
csharptest7fc785c2011-06-10 23:54:53 -050054 return _buffer[_bufferPos];
csharptest74c5e0c2011-07-14 13:06:22 -050055 }
csharptest7fc785c2011-06-10 23:54:53 -050056 else
csharptest74c5e0c2011-07-14 13:06:22 -050057 {
csharptest7fc785c2011-06-10 23:54:53 -050058 return -1;
csharptest74c5e0c2011-07-14 13:06:22 -050059 }
csharptest7fc785c2011-06-10 23:54:53 -050060 }
61
62 protected override int Read()
63 {
64 if (_input != null)
65 {
66 int result = _next;
67 _next = _input.ReadByte();
68 return result;
69 }
70 else if (_bufferPos < _buffer.Length)
csharptest74c5e0c2011-07-14 13:06:22 -050071 {
csharptest7fc785c2011-06-10 23:54:53 -050072 return _buffer[_bufferPos++];
csharptest74c5e0c2011-07-14 13:06:22 -050073 }
csharptest7fc785c2011-06-10 23:54:53 -050074 else
csharptest74c5e0c2011-07-14 13:06:22 -050075 {
csharptest7fc785c2011-06-10 23:54:53 -050076 return -1;
csharptest74c5e0c2011-07-14 13:06:22 -050077 }
csharptest7fc785c2011-06-10 23:54:53 -050078 }
79 }
80
csharptest74c5e0c2011-07-14 13:06:22 -050081 private class JsonTextCursor : JsonCursor
csharptest7fc785c2011-06-10 23:54:53 -050082 {
83 private readonly char[] _buffer;
84 private int _bufferPos;
85 private readonly TextReader _input;
86
87 public JsonTextCursor(char[] input)
88 {
89 _input = null;
90 _buffer = input;
91 _bufferPos = 0;
92 _next = Peek();
93 }
94
95 public JsonTextCursor(TextReader input)
96 {
97 _input = input;
98 _next = Peek();
99 }
100
101 protected override int Peek()
102 {
103 if (_input != null)
csharptest74c5e0c2011-07-14 13:06:22 -0500104 {
csharptest7fc785c2011-06-10 23:54:53 -0500105 return _input.Peek();
csharptest74c5e0c2011-07-14 13:06:22 -0500106 }
csharptest7fc785c2011-06-10 23:54:53 -0500107 else if (_bufferPos < _buffer.Length)
csharptest74c5e0c2011-07-14 13:06:22 -0500108 {
csharptest7fc785c2011-06-10 23:54:53 -0500109 return _buffer[_bufferPos];
csharptest74c5e0c2011-07-14 13:06:22 -0500110 }
csharptest7fc785c2011-06-10 23:54:53 -0500111 else
csharptest74c5e0c2011-07-14 13:06:22 -0500112 {
csharptest7fc785c2011-06-10 23:54:53 -0500113 return -1;
csharptest74c5e0c2011-07-14 13:06:22 -0500114 }
csharptest7fc785c2011-06-10 23:54:53 -0500115 }
116
117 protected override int Read()
118 {
119 if (_input != null)
csharptest74c5e0c2011-07-14 13:06:22 -0500120 {
csharptest7fc785c2011-06-10 23:54:53 -0500121 return _input.Read();
csharptest74c5e0c2011-07-14 13:06:22 -0500122 }
csharptest7fc785c2011-06-10 23:54:53 -0500123 else if (_bufferPos < _buffer.Length)
csharptest74c5e0c2011-07-14 13:06:22 -0500124 {
csharptest7fc785c2011-06-10 23:54:53 -0500125 return _buffer[_bufferPos++];
csharptest74c5e0c2011-07-14 13:06:22 -0500126 }
csharptest7fc785c2011-06-10 23:54:53 -0500127 else
csharptest74c5e0c2011-07-14 13:06:22 -0500128 {
csharptest7fc785c2011-06-10 23:54:53 -0500129 return -1;
csharptest74c5e0c2011-07-14 13:06:22 -0500130 }
csharptest7fc785c2011-06-10 23:54:53 -0500131 }
132 }
csharptest74c5e0c2011-07-14 13:06:22 -0500133
csharptest7fc785c2011-06-10 23:54:53 -0500134 #endregion
135
136 protected int _next;
csharptestafe844b2011-06-10 16:03:22 -0500137 private int _lineNo, _linePos;
138
csharptest74c5e0c2011-07-14 13:06:22 -0500139 public static JsonCursor CreateInstance(byte[] input)
140 {
141 return new JsonStreamCursor(input);
142 }
143
144 public static JsonCursor CreateInstance(Stream input)
145 {
146 return new JsonStreamCursor(input);
147 }
148
149 public static JsonCursor CreateInstance(string input)
150 {
151 return new JsonTextCursor(input.ToCharArray());
152 }
153
154 public static JsonCursor CreateInstance(TextReader input)
155 {
156 return new JsonTextCursor(input);
157 }
csharptest7fc785c2011-06-10 23:54:53 -0500158
159 protected JsonCursor()
csharptestafe844b2011-06-10 16:03:22 -0500160 {
csharptestafe844b2011-06-10 16:03:22 -0500161 _lineNo = 1;
csharptest7fc785c2011-06-10 23:54:53 -0500162 _linePos = 0;
csharptestafe844b2011-06-10 16:03:22 -0500163 }
csharptest74c5e0c2011-07-14 13:06:22 -0500164
csharptest7fc785c2011-06-10 23:54:53 -0500165 /// <summary>Returns the next character without actually 'reading' it</summary>
166 protected abstract int Peek();
csharptest74c5e0c2011-07-14 13:06:22 -0500167
csharptest7fc785c2011-06-10 23:54:53 -0500168 /// <summary>Reads the next character in the input</summary>
169 protected abstract int Read();
csharptestafe844b2011-06-10 16:03:22 -0500170
csharptest74c5e0c2011-07-14 13:06:22 -0500171 public Char NextChar
172 {
173 get
174 {
175 SkipWhitespace();
176 return (char) _next;
177 }
178 }
csharptestafe844b2011-06-10 16:03:22 -0500179
180 #region Assert(...)
csharptest74c5e0c2011-07-14 13:06:22 -0500181
182 [DebuggerNonUserCode]
csharptestafe844b2011-06-10 16:03:22 -0500183 private string CharDisplay(int ch)
184 {
csharptest74c5e0c2011-07-14 13:06:22 -0500185 return ch == -1
186 ? "EOF"
187 : (ch > 32 && ch < 127)
188 ? String.Format("'{0}'", (char) ch)
189 : String.Format("'\\u{0:x4}'", ch);
csharptestafe844b2011-06-10 16:03:22 -0500190 }
csharptest74c5e0c2011-07-14 13:06:22 -0500191
192 [DebuggerNonUserCode]
csharptestafe844b2011-06-10 16:03:22 -0500193 private void Assert(bool cond, char expected)
194 {
195 if (!cond)
196 {
197 throw new FormatException(
198 String.Format(CultureInfo.InvariantCulture,
199 "({0}:{1}) error: Unexpected token {2}, expected: {3}.",
csharptest74c5e0c2011-07-14 13:06:22 -0500200 _lineNo, _linePos,
csharptestafe844b2011-06-10 16:03:22 -0500201 CharDisplay(_next),
202 CharDisplay(expected)
203 ));
204 }
205 }
csharptest74c5e0c2011-07-14 13:06:22 -0500206
207 [DebuggerNonUserCode]
csharptestafe844b2011-06-10 16:03:22 -0500208 public void Assert(bool cond, string message)
209 {
210 if (!cond)
211 {
212 throw new FormatException(
213 String.Format(CultureInfo.InvariantCulture,
214 "({0},{1}) error: {2}", _lineNo, _linePos, message));
215 }
216 }
csharptest74c5e0c2011-07-14 13:06:22 -0500217
218 [DebuggerNonUserCode]
csharptestafe844b2011-06-10 16:03:22 -0500219 public void Assert(bool cond, string format, params object[] args)
220 {
221 if (!cond)
222 {
223 if (args != null && args.Length > 0)
csharptest74c5e0c2011-07-14 13:06:22 -0500224 {
csharptestafe844b2011-06-10 16:03:22 -0500225 format = String.Format(format, args);
csharptest74c5e0c2011-07-14 13:06:22 -0500226 }
csharptestafe844b2011-06-10 16:03:22 -0500227 throw new FormatException(
228 String.Format(CultureInfo.InvariantCulture,
229 "({0},{1}) error: {2}", _lineNo, _linePos, format));
230 }
231 }
csharptest74c5e0c2011-07-14 13:06:22 -0500232
csharptestafe844b2011-06-10 16:03:22 -0500233 #endregion
234
235 private char ReadChar()
236 {
237 int ch = Read();
238 Assert(ch != -1, "Unexpected end of file.");
239 if (ch == '\n')
240 {
241 _lineNo++;
242 _linePos = 0;
243 }
244 else if (ch != '\r')
245 {
246 _linePos++;
247 }
248 _next = Peek();
csharptest74c5e0c2011-07-14 13:06:22 -0500249 return (char) ch;
csharptestafe844b2011-06-10 16:03:22 -0500250 }
251
csharptest74c5e0c2011-07-14 13:06:22 -0500252 public void Consume(char ch)
253 {
254 Assert(TryConsume(ch), ch);
255 }
256
csharptestafe844b2011-06-10 16:03:22 -0500257 public bool TryConsume(char ch)
258 {
259 SkipWhitespace();
260 if (_next == ch)
261 {
262 ReadChar();
263 return true;
264 }
265 return false;
266 }
267
268 public void Consume(string sequence)
269 {
270 SkipWhitespace();
271
272 foreach (char ch in sequence)
csharptest74c5e0c2011-07-14 13:06:22 -0500273 {
csharptestafe844b2011-06-10 16:03:22 -0500274 Assert(ch == ReadChar(), "Expected token '{0}'.", sequence);
csharptest74c5e0c2011-07-14 13:06:22 -0500275 }
csharptestafe844b2011-06-10 16:03:22 -0500276 }
277
278 public void SkipWhitespace()
279 {
280 int chnext = _next;
281 while (chnext != -1)
282 {
csharptest74c5e0c2011-07-14 13:06:22 -0500283 if (!Char.IsWhiteSpace((char) chnext))
284 {
csharptestafe844b2011-06-10 16:03:22 -0500285 break;
csharptest74c5e0c2011-07-14 13:06:22 -0500286 }
csharptestafe844b2011-06-10 16:03:22 -0500287 ReadChar();
288 chnext = _next;
289 }
290 }
291
292 public string ReadString()
293 {
294 SkipWhitespace();
295 Consume('"');
csharptestddb74eb2011-06-10 16:14:51 -0500296 List<Char> sb = new List<char>(100);
csharptestafe844b2011-06-10 16:03:22 -0500297 while (_next != '"')
298 {
299 if (_next == '\\')
300 {
csharptest74c5e0c2011-07-14 13:06:22 -0500301 Consume('\\'); //skip the escape
csharptestafe844b2011-06-10 16:03:22 -0500302 char ch = ReadChar();
303 switch (ch)
304 {
csharptest74c5e0c2011-07-14 13:06:22 -0500305 case 'b':
306 sb.Add('\b');
307 break;
308 case 'f':
309 sb.Add('\f');
310 break;
311 case 'n':
312 sb.Add('\n');
313 break;
314 case 'r':
315 sb.Add('\r');
316 break;
317 case 't':
318 sb.Add('\t');
319 break;
csharptestafe844b2011-06-10 16:03:22 -0500320 case 'u':
321 {
csharptest74c5e0c2011-07-14 13:06:22 -0500322 string hex = new string(new char[] {ReadChar(), ReadChar(), ReadChar(), ReadChar()});
csharptestafe844b2011-06-10 16:03:22 -0500323 int result;
csharptest74c5e0c2011-07-14 13:06:22 -0500324 Assert(
325 int.TryParse(hex, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture,
326 out result),
327 "Expected a 4-character hex specifier.");
328 sb.Add((char) result);
csharptestafe844b2011-06-10 16:03:22 -0500329 break;
330 }
331 default:
csharptest74c5e0c2011-07-14 13:06:22 -0500332 sb.Add(ch);
333 break;
csharptestafe844b2011-06-10 16:03:22 -0500334 }
335 }
336 else
337 {
338 Assert(_next != '\n' && _next != '\r' && _next != '\f' && _next != -1, '"');
csharptestddb74eb2011-06-10 16:14:51 -0500339 sb.Add(ReadChar());
csharptestafe844b2011-06-10 16:03:22 -0500340 }
341 }
342 Consume('"');
csharptestddb74eb2011-06-10 16:14:51 -0500343 return new String(sb.ToArray());
csharptestafe844b2011-06-10 16:03:22 -0500344 }
345
346 public string ReadNumber()
347 {
348 SkipWhitespace();
csharptestddb74eb2011-06-10 16:14:51 -0500349 List<Char> sb = new List<char>(24);
csharptestafe844b2011-06-10 16:03:22 -0500350 if (_next == '-')
csharptest74c5e0c2011-07-14 13:06:22 -0500351 {
csharptestddb74eb2011-06-10 16:14:51 -0500352 sb.Add(ReadChar());
csharptest74c5e0c2011-07-14 13:06:22 -0500353 }
csharptestafe844b2011-06-10 16:03:22 -0500354 Assert(_next >= '0' && _next <= '9', "Expected a numeric type.");
355 while ((_next >= '0' && _next <= '9') || _next == '.')
csharptest74c5e0c2011-07-14 13:06:22 -0500356 {
csharptestddb74eb2011-06-10 16:14:51 -0500357 sb.Add(ReadChar());
csharptest74c5e0c2011-07-14 13:06:22 -0500358 }
csharptestafe844b2011-06-10 16:03:22 -0500359 if (_next == 'e' || _next == 'E')
360 {
csharptestddb74eb2011-06-10 16:14:51 -0500361 sb.Add(ReadChar());
csharptestafe844b2011-06-10 16:03:22 -0500362 if (_next == '-' || _next == '+')
csharptest74c5e0c2011-07-14 13:06:22 -0500363 {
csharptestddb74eb2011-06-10 16:14:51 -0500364 sb.Add(ReadChar());
csharptest74c5e0c2011-07-14 13:06:22 -0500365 }
csharptestafe844b2011-06-10 16:03:22 -0500366 Assert(_next >= '0' && _next <= '9', "Expected a numeric type.");
367 while (_next >= '0' && _next <= '9')
csharptest74c5e0c2011-07-14 13:06:22 -0500368 {
csharptestddb74eb2011-06-10 16:14:51 -0500369 sb.Add(ReadChar());
csharptest74c5e0c2011-07-14 13:06:22 -0500370 }
csharptestafe844b2011-06-10 16:03:22 -0500371 }
csharptestddb74eb2011-06-10 16:14:51 -0500372 return new String(sb.ToArray());
csharptestafe844b2011-06-10 16:03:22 -0500373 }
374
375 public JsType ReadVariant(out object value)
376 {
377 SkipWhitespace();
378 switch (_next)
379 {
csharptest74c5e0c2011-07-14 13:06:22 -0500380 case 'n':
381 Consume("null");
382 value = null;
383 return JsType.Null;
384 case 't':
385 Consume("true");
386 value = true;
387 return JsType.True;
388 case 'f':
389 Consume("false");
390 value = false;
391 return JsType.False;
392 case '"':
393 value = ReadString();
394 return JsType.String;
csharptestafe844b2011-06-10 16:03:22 -0500395 case '{':
396 {
397 Consume('{');
398 while (NextChar != '}')
399 {
400 ReadString();
401 Consume(':');
402 object tmp;
403 ReadVariant(out tmp);
404 if (!TryConsume(','))
csharptest74c5e0c2011-07-14 13:06:22 -0500405 {
csharptestafe844b2011-06-10 16:03:22 -0500406 break;
csharptest74c5e0c2011-07-14 13:06:22 -0500407 }
csharptestafe844b2011-06-10 16:03:22 -0500408 }
409 Consume('}');
410 value = null;
411 return JsType.Object;
412 }
413 case '[':
414 {
415 Consume('[');
416 List<object> values = new List<object>();
417 while (NextChar != ']')
418 {
419 object tmp;
420 ReadVariant(out tmp);
421 values.Add(tmp);
422 if (!TryConsume(','))
csharptest74c5e0c2011-07-14 13:06:22 -0500423 {
csharptestafe844b2011-06-10 16:03:22 -0500424 break;
csharptest74c5e0c2011-07-14 13:06:22 -0500425 }
csharptestafe844b2011-06-10 16:03:22 -0500426 }
427 Consume(']');
428 value = values.ToArray();
429 return JsType.Array;
430 }
431 default:
432 if ((_next >= '0' && _next <= '9') || _next == '-')
433 {
434 value = ReadNumber();
435 return JsType.Number;
436 }
437 Assert(false, "Expected a value.");
438 throw new FormatException();
439 }
440 }
441 }
442}