Merge pull request #856 from madewokherd/textcontrol
[mono.git] / mcs / class / System.ServiceModel.Web / System.Runtime.Serialization.Json / JsonReader.cs
1 //
2 // JsonReader.cs
3 //
4 // Author:
5 //      Atsushi Enomoto  <atsushi@ximian.com>
6 //
7 // Copyright (C) 2007-2011 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.Collections.Generic;
30 using System.Globalization;
31 using System.IO;
32 using System.Text;
33 using System.Xml;
34
35 namespace System.Runtime.Serialization.Json
36 {
37         // It is a subset of XmlInputStream from System.XML.
38         class EncodingDetecingInputStream : Stream
39         {
40                 internal static readonly Encoding StrictUTF8, Strict1234UTF32, StrictBigEndianUTF16, StrictUTF16;
41
42                 static EncodingDetecingInputStream ()
43                 {
44                         StrictUTF8 = new UTF8Encoding (false, true);
45                         Strict1234UTF32 = new UTF32Encoding (true, false, true);
46                         StrictBigEndianUTF16 = new UnicodeEncoding (true, false, true);
47                         StrictUTF16 = new UnicodeEncoding (false, false, true);
48                 }
49
50                 Encoding enc;
51                 Stream stream;
52                 byte[] buffer;
53                 int bufLength;
54                 int bufPos;
55
56                 static XmlException encodingException = new XmlException ("invalid encoding specification.");
57
58                 public EncodingDetecingInputStream (Stream stream)
59                 {
60                         if (stream == null)
61                                 throw new ArgumentNullException ("stream");
62                         Initialize (stream);
63                 }
64
65                 private void Initialize (Stream stream)
66                 {
67                         buffer = new byte [6];
68                         this.stream = stream;
69                         enc = StrictUTF8; // Default to UTF8 if we can't guess it
70                         bufLength = stream.Read (buffer, 0, buffer.Length);
71                         if (bufLength == -1 || bufLength == 0) {
72                                 return;
73                         }
74
75                         int c = ReadByteSpecial ();
76                         switch (c) {
77                         case 0xFF:
78                                 c = ReadByteSpecial ();
79                                 if (c == 0xFE) {
80                                         // BOM-ed little endian utf-16
81                                         enc = Encoding.Unicode;
82                                 } else {
83                                         // It doesn't start from "<?xml" then its encoding is utf-8
84                                         bufPos = 0;
85                                 }
86                                 break;
87                         case 0xFE:
88                                 c = ReadByteSpecial ();
89                                 if (c == 0xFF) {
90                                         // BOM-ed big endian utf-16
91                                         enc = Encoding.BigEndianUnicode;
92                                         return;
93                                 } else {
94                                         // It doesn't start from "<?xml" then its encoding is utf-8
95                                         bufPos = 0;
96                                 }
97                                 break;
98                         case 0xEF:
99                                 c = ReadByteSpecial ();
100                                 if (c == 0xBB) {
101                                         c = ReadByteSpecial ();
102                                         if (c != 0xBF) {
103                                                 bufPos = 0;
104                                         }
105                                 } else {
106                                         buffer [--bufPos] = 0xEF;
107                                 }
108                                 break;
109                         case 0:
110                                 // It could still be 1234/2143/3412 variants of UTF32, but only 1234 version is available on .NET.
111                                 c = ReadByteSpecial ();
112                                 if (c == 0)
113                                         enc = Strict1234UTF32;
114                                 else
115                                         enc = StrictBigEndianUTF16;
116                                 break;
117                         default:
118                                 c = ReadByteSpecial ();
119                                 if (c == 0)
120                                         enc = StrictUTF16;
121                                 bufPos = 0;
122                                 break;
123                         }
124                 }
125
126                 // Just like readbyte, but grows the buffer too.
127                 int ReadByteSpecial ()
128                 {
129                         if (bufLength > bufPos)
130                                 return buffer [bufPos++];
131
132                         byte [] newbuf = new byte [buffer.Length * 2];
133                         Buffer.BlockCopy (buffer, 0, newbuf, 0, bufLength);
134                         int nbytes = stream.Read (newbuf, bufLength, buffer.Length);
135                         if (nbytes == -1 || nbytes == 0)
136                                 return -1;
137                                 
138                         bufLength += nbytes;
139                         buffer = newbuf;
140                         return buffer [bufPos++];
141                 }
142
143                 public Encoding ActualEncoding {
144                         get { return enc; }
145                 }
146
147                 #region Public Overrides
148                 public override bool CanRead {
149                         get {
150                                 if (bufLength > bufPos)
151                                         return true;
152                                 else
153                                         return stream.CanRead; 
154                         }
155                 }
156
157                 // FIXME: It should support base stream's CanSeek.
158                 public override bool CanSeek {
159                         get { return false; } // stream.CanSeek; }
160                 }
161
162                 public override bool CanWrite {
163                         get { return false; }
164                 }
165
166                 public override long Length {
167                         get {
168                                 return stream.Length;
169                         }
170                 }
171
172                 public override long Position {
173                         get {
174                                 return stream.Position - bufLength + bufPos;
175                         }
176                         set {
177                                 if(value < bufLength)
178                                         bufPos = (int)value;
179                                 else
180                                         stream.Position = value - bufLength;
181                         }
182                 }
183
184                 public override void Close ()
185                 {
186                         stream.Close ();
187                 }
188
189                 public override void Flush ()
190                 {
191                         stream.Flush ();
192                 }
193
194                 public override int Read (byte[] buffer, int offset, int count)
195                 {
196                         int ret;
197                         if (count <= bufLength - bufPos)        {       // all from buffer
198                                 Buffer.BlockCopy (this.buffer, bufPos, buffer, offset, count);
199                                 bufPos += count;
200                                 ret = count;
201                         } else {
202                                 int bufRest = bufLength - bufPos;
203                                 if (bufLength > bufPos) {
204                                         Buffer.BlockCopy (this.buffer, bufPos, buffer, offset, bufRest);
205                                         bufPos += bufRest;
206                                 }
207                                 ret = bufRest +
208                                         stream.Read (buffer, offset + bufRest, count - bufRest);
209                         }
210                         return ret;
211                 }
212
213                 public override int ReadByte ()
214                 {
215                         if (bufLength > bufPos) {
216                                 return buffer [bufPos++];
217                         }
218                         return stream.ReadByte ();
219                 }
220
221                 public override long Seek (long offset, System.IO.SeekOrigin origin)
222                 {
223                         int bufRest = bufLength - bufPos;
224                         if (origin == SeekOrigin.Current)
225                                 if (offset < bufRest)
226                                         return buffer [bufPos + offset];
227                                 else
228                                         return stream.Seek (offset - bufRest, origin);
229                         else
230                                 return stream.Seek (offset, origin);
231                 }
232
233                 public override void SetLength (long value)
234                 {
235                         stream.SetLength (value);
236                 }
237
238                 public override void Write (byte[] buffer, int offset, int count)
239                 {
240                         throw new NotSupportedException ();
241                 }
242                 #endregion
243         }
244
245         class PushbackReader : StreamReader
246         {
247                 Stack<int> pushback;
248
249                 public PushbackReader (Stream stream, Encoding encoding) : base (stream, encoding)
250                 {
251                         pushback = new Stack<int>();
252                 }
253
254                 public PushbackReader (Stream stream) : this (new EncodingDetecingInputStream (stream))
255                 {
256                 }
257
258                 public PushbackReader (EncodingDetecingInputStream stream) : this (stream, stream.ActualEncoding)
259                 {
260                 }
261
262                 public override void Close ()
263                 {
264                         pushback.Clear ();
265                 }
266
267                 public override int Peek ()
268                 {
269                         if (pushback.Count > 0) {
270                                 return pushback.Peek ();
271                         }
272                         else {
273                                 return base.Peek ();
274                         }
275                 }
276
277                 public override int Read ()
278                 {
279                         if (pushback.Count > 0) {
280                                 return pushback.Pop ();
281                         }
282                         else {
283                                 return base.Read ();
284                         }
285                 }
286
287                 public void Pushback (int ch)
288                 {
289                         pushback.Push (ch);
290                 }
291         }
292
293         // FIXME: quotas check
294         class JsonReader : XmlDictionaryReader, IXmlJsonReaderInitializer, IXmlLineInfo
295         {
296                 class ElementInfo
297                 {
298                         public readonly string Name;
299                         public readonly string Type;
300                         public bool HasContent;
301
302                         public ElementInfo (string name, string type)
303                         {
304                                 this.Name = name;
305                                 this.Type = type;
306                         }
307                 }
308
309                 enum AttributeState
310                 {
311                         None,
312                         Type,
313                         TypeValue,
314                         RuntimeType,
315                         RuntimeTypeValue
316                 }
317
318                 PushbackReader reader;
319                 XmlDictionaryReaderQuotas quotas;
320                 OnXmlDictionaryReaderClose on_close;
321                 XmlNameTable name_table = new NameTable ();
322
323                 XmlNodeType current_node;
324                 AttributeState attr_state;
325                 string simple_value;
326                 string next_element;
327                 string current_runtime_type, next_object_content_name;
328                 ReadState read_state = ReadState.Initial;
329                 bool content_stored;
330                 bool finished;
331                 Stack<ElementInfo> elements = new Stack<ElementInfo> ();
332
333                 int line = 1, column = 0;
334
335                 // Constructors
336
337                 public JsonReader (byte [] buffer, int offset, int count, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
338                 {
339                         SetInput (buffer, offset, count, encoding, quotas, onClose);
340                 }
341
342                 public JsonReader (Stream stream, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
343                 {
344                         SetInput (stream, encoding, quotas, onClose);
345                 }
346
347                 internal bool LameSilverlightLiteralParser { get; set; }
348
349                 // IXmlLineInfo
350
351                 public bool HasLineInfo ()
352                 {
353                         return true;
354                 }
355
356                 public int LineNumber {
357                         get { return line; }
358                 }
359
360                 public int LinePosition {
361                         get { return column; }
362                 }
363
364                 // IXmlJsonReaderInitializer
365
366                 public void SetInput (byte [] buffer, int offset, int count, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
367                 {
368                         SetInput (new MemoryStream (buffer, offset, count), encoding, quotas, onClose);
369                 }
370
371                 public void SetInput (Stream stream, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
372                 {
373                         if (encoding != null)
374                                 reader = new PushbackReader (stream, encoding);
375                         else
376                                 reader = new PushbackReader (stream);
377                         if (quotas == null)
378                                 throw new ArgumentNullException ("quotas");
379                         this.quotas = quotas;
380                         this.on_close = onClose;
381                 }
382
383                 // XmlDictionaryReader
384
385                 public override int AttributeCount {
386                         get { return current_node != XmlNodeType.Element ? 0 : current_runtime_type != null ? 2 : 1; }
387                 }
388
389                 public override string BaseURI {
390                         get { return String.Empty; }
391                 }
392
393                 public override int Depth {
394                         get {
395                                 int mod = 0;
396                                 switch (attr_state) {
397                                 case AttributeState.Type:
398                                 case AttributeState.RuntimeType:
399                                         mod++;
400                                         break;
401                                 case AttributeState.TypeValue:
402                                 case AttributeState.RuntimeTypeValue:
403                                         mod += 2;
404                                         break;
405                                 case AttributeState.None:
406                                         if (NodeType == XmlNodeType.Text)
407                                                 mod++;
408                                         break;
409                                 }
410                                 return read_state != ReadState.Interactive ? 0 : elements.Count - 1 + mod;
411                         }
412                 }
413
414                 public override bool EOF {
415                         get {
416                                 switch (read_state) {
417                                 case ReadState.Closed:
418                                 case ReadState.EndOfFile:
419                                         return true;
420                                 default:
421                                         return false;
422                                 }
423                         }
424                 }
425
426                 public override bool HasValue {
427                         get {
428                                 switch (NodeType) {
429                                 case XmlNodeType.Attribute:
430                                 case XmlNodeType.Text:
431                                         return true;
432                                 default:
433                                         return false;
434                                 }
435                         }
436                 }
437
438                 public override bool IsEmptyElement {
439                         get { return false; }
440                 }
441
442                 public override string LocalName {
443                         get {
444                                 switch (attr_state) {
445                                 case AttributeState.Type:
446                                         return "type";
447                                 case AttributeState.RuntimeType:
448                                         return "__type";
449                                 }
450                                 switch (NodeType) {
451                                 case XmlNodeType.Element:
452                                 case XmlNodeType.EndElement:
453                                         return elements.Peek ().Name;
454                                 default:
455                                         return String.Empty;
456                                 }
457                         }
458                 }
459
460                 public override string NamespaceURI {
461                         get { return String.Empty; }
462                 }
463
464                 public override XmlNameTable NameTable {
465                         get { return name_table; }
466                 }
467
468                 public override XmlNodeType NodeType {
469                         get {
470                                 switch (attr_state) {
471                                 case AttributeState.Type:
472                                 case AttributeState.RuntimeType:
473                                         return XmlNodeType.Attribute;
474                                 case AttributeState.TypeValue:
475                                 case AttributeState.RuntimeTypeValue:
476                                         return XmlNodeType.Text;
477                                 default:
478                                         return current_node;
479                                 }
480                         }
481                 }
482
483                 public override string Prefix {
484                         get { return String.Empty; }
485                 }
486
487                 public override ReadState ReadState {
488                         get { return read_state; }
489                 }
490
491                 public override string Value {
492                         get {
493                                 switch (attr_state) {
494                                 case AttributeState.Type:
495                                 case AttributeState.TypeValue:
496                                         return elements.Peek ().Type;
497                                 case AttributeState.RuntimeType:
498                                 case AttributeState.RuntimeTypeValue:
499                                         return current_runtime_type;
500                                 default:
501                                         return current_node == XmlNodeType.Text ? simple_value : String.Empty;
502                                 }
503                         }
504                 }
505
506                 public override void Close ()
507                 {
508                         if (on_close != null) {
509                                 on_close (this);
510                                 on_close = null;
511                         }
512                         read_state = ReadState.Closed;
513                 }
514
515                 public override string GetAttribute (int index)
516                 {
517                         if (index == 0 && current_node == XmlNodeType.Element)
518                                 return elements.Peek ().Type;
519                         else if (index == 1 && current_runtime_type != null)
520                                 return current_runtime_type;
521                         throw new ArgumentOutOfRangeException ("index", "Index is must be either 0 or 1 when there is an explicit __type in the object, and only valid on an element on this XmlDictionaryReader");
522                 }
523
524                 public override string GetAttribute (string name)
525                 {
526                         if (current_node != XmlNodeType.Element)
527                                 return null;
528                         switch (name) {
529                         case "type":
530                                 return elements.Peek ().Type;
531                         case "__type":
532                                 return current_runtime_type;
533                         default:
534                                 return null;
535                         }
536                 }
537
538                 public override string GetAttribute (string localName, string ns)
539                 {
540                         if (ns == String.Empty)
541                                 return GetAttribute (localName);
542                         else
543                                 return null;
544                 }
545
546                 public override string LookupNamespace (string prefix)
547                 {
548                         if (prefix == null)
549                                 throw new ArgumentNullException ("prefix");
550                         else if (prefix.Length == 0)
551                                 return String.Empty;
552                         return null;
553                 }
554
555                 public override bool MoveToAttribute (string name)
556                 {
557                         if (current_node != XmlNodeType.Element)
558                                 return false;
559                         switch (name) {
560                         case "type":
561                                 attr_state = AttributeState.Type;
562                                 return true;
563                         case "__type":
564                                 if (current_runtime_type == null)
565                                         return false;
566                                 attr_state = AttributeState.RuntimeType;
567                                 return true;
568                         default:
569                                 return false;
570                         }
571                 }
572
573                 public override bool MoveToAttribute (string localName, string ns)
574                 {
575                         if (ns != String.Empty)
576                                 return false;
577                         return MoveToAttribute (localName);
578                 }
579
580                 public override bool MoveToElement ()
581                 {
582                         if (attr_state == AttributeState.None)
583                                 return false;
584                         attr_state = AttributeState.None;
585                         return true;
586                 }
587
588                 public override bool MoveToFirstAttribute ()
589                 {
590                         if (current_node != XmlNodeType.Element)
591                                 return false;
592                         attr_state = AttributeState.Type;
593                         return true;
594                 }
595
596                 public override bool MoveToNextAttribute ()
597                 {
598                         if (attr_state == AttributeState.None)
599                                 return MoveToFirstAttribute ();
600                         else
601                                 return MoveToAttribute ("__type");
602                 }
603
604                 public override bool ReadAttributeValue ()
605                 {
606                         switch (attr_state) {
607                         case AttributeState.Type:
608                                 attr_state = AttributeState.TypeValue;
609                                 return true;
610                         case AttributeState.RuntimeType:
611                                 attr_state = AttributeState.RuntimeTypeValue;
612                                 return true;
613                         }
614                         return false;
615                 }
616
617                 public override void ResolveEntity ()
618                 {
619                         throw new NotSupportedException ();
620                 }
621
622                 public override bool Read ()
623                 {
624                         switch (read_state) {
625                         case ReadState.EndOfFile:
626                         case ReadState.Closed:
627                         case ReadState.Error:
628                                 return false;
629                         case ReadState.Initial:
630                                 read_state = ReadState.Interactive;
631                                 next_element = "root";
632                                 current_node = XmlNodeType.Element;
633                                 break;
634                         }
635
636                         MoveToElement ();
637
638                         if (content_stored) {
639                                 if (current_node == XmlNodeType.Element) {
640                                         if (elements.Peek ().Type == "null") {
641                                                 // since null is not consumed as text content, it skips Text state.
642                                                 current_node = XmlNodeType.EndElement;
643                                                 content_stored = false;
644                                         }
645                                         else
646                                                 current_node = XmlNodeType.Text;
647                                         return true;
648                                 } else if (current_node == XmlNodeType.Text) {
649                                         current_node = XmlNodeType.EndElement;
650                                         content_stored = false;
651                                         return true;
652                                 }
653                         }
654                         else if (current_node == XmlNodeType.EndElement) {
655                                 // clear EndElement state
656                                 elements.Pop ();
657                                 if (elements.Count > 0)
658                                         elements.Peek ().HasContent = true;
659                                 else
660                                         finished = true;
661                         }
662
663                         SkipWhitespaces ();
664
665                         attr_state = AttributeState.None;
666                         // Default. May be overriden only as EndElement or None.
667                         current_node = XmlNodeType.Element;
668
669                         if (!ReadContent (false))
670                                 return false;
671                         if (finished)
672                                 throw XmlError ("Multiple top-level content is not allowed");
673                         return true;
674                 }
675
676                 bool TryReadString (string str)
677                 {
678                         for (int i = 0; i < str.Length; i ++) {
679                                 int ch = ReadChar ();
680                                 if (ch != str[i]) {
681                                         for (int j = i; j >= 0; j--)
682                                                 PushbackChar (j);
683                                         return false;
684                                 }
685                         }
686
687                         return true;
688                 }
689
690                 bool ReadContent (bool objectValue)
691                 {
692                         int ch = ReadChar ();
693                         if (ch < 0) {
694                                 ReadEndOfStream ();
695                                 return false;
696                         }
697
698                         bool itemMustFollow = false;
699
700                         if (!objectValue && elements.Count > 0 && elements.Peek ().HasContent) {
701                                 if (ch == ',') {
702                                         switch (elements.Peek ().Type) {
703                                         case "object":
704                                         case "array":
705                                                 SkipWhitespaces ();
706                                                 ch = ReadChar ();
707                                                 itemMustFollow = true;
708                                                 break;
709                                         }
710                                 }
711                                 else if (ch != '}' && ch != ']')
712                                         throw XmlError ("Comma is required unless an array or object is at the end");
713                         }
714
715                         if (elements.Count > 0 && elements.Peek ().Type == "array")
716                                 next_element = "item";
717                         else if (next_object_content_name != null) {
718                                 next_element = next_object_content_name;
719                                 next_object_content_name = null;
720                                 if (ch != ':')
721                                         throw XmlError ("':' is expected after a name of an object content");
722                                 SkipWhitespaces ();
723                                 ReadContent (true);
724                                 return true;
725                         }
726
727                         switch (ch) {
728                         case '{':
729                                 ReadStartObject ();
730                                 return true;
731                         case '[':
732                                 ReadStartArray ();
733                                 return true;
734                         case '}':
735                                 if (itemMustFollow)
736                                         throw XmlError ("Invalid comma before an end of object");
737                                 if (objectValue)
738                                         throw XmlError ("Invalid end of object as an object content");
739                                 ReadEndObject ();
740                                 return true;
741                         case ']':
742                                 if (itemMustFollow)
743                                         throw XmlError ("Invalid comma before an end of array");
744                                 if (objectValue)
745                                         throw XmlError ("Invalid end of array as an object content");
746                                 ReadEndArray ();
747                                 return true;
748                         case '"':
749                                 bool lame = LameSilverlightLiteralParser && ch != '"';
750                                 string s = ReadStringLiteral (lame);
751                                 if (!objectValue && elements.Count > 0 && elements.Peek ().Type == "object") {
752                                         next_element = s;
753                                         SkipWhitespaces ();
754                                         if (!lame)
755                                                 Expect (':');
756                                         SkipWhitespaces ();
757                                         ReadContent (true);
758                                 }
759                                 else
760                                         ReadAsSimpleContent ("string", s);
761                                 return true;
762                         case '-':
763                                 ReadNumber (ch);
764                                 return true;
765                         case 'n':
766                                 if (TryReadString("ull")) {
767                                         ReadAsSimpleContent ("null", "null");
768                                         return true;
769                                 }
770                                 else {
771                                         // the pushback for 'n' is taken care of by the
772                                         // default case if we're in lame silverlight literal
773                                         // mode
774                                         goto default;
775                                 }
776                         case 't':
777                                 if (TryReadString ("rue")) {
778                                         ReadAsSimpleContent ("boolean", "true");
779                                         return true;
780                                 }
781                                 else {
782                                         // the pushback for 't' is taken care of by the
783                                         // default case if we're in lame silverlight literal
784                                         // mode
785                                         goto default;
786                                 }
787                         case 'f':
788                                 if (TryReadString ("alse")) {
789                                         ReadAsSimpleContent ("boolean", "false");
790                                         return true;
791                                 }
792                                 else {
793                                         // the pushback for 'f' is taken care of by the
794                                         // default case if we're in lame silverlight literal
795                                         // mode
796                                         goto default;
797                                 }
798                         default:
799                                 if ('0' <= ch && ch <= '9') {
800                                         ReadNumber (ch);
801                                         return true;
802                                 }
803                                 if (LameSilverlightLiteralParser) {
804                                         PushbackChar (ch);
805                                         goto case '"';
806                                 }
807                                 throw XmlError (String.Format ("Unexpected token: '{0}' ({1:X04})", (char) ch, (int) ch));
808                         }
809                 }
810
811                 void ReadStartObject ()
812                 {
813                         ElementInfo ei = new ElementInfo (next_element, "object");
814                         elements.Push (ei);
815
816                         SkipWhitespaces ();
817                         if (PeekChar () == '"') { // it isn't premise: the object might be empty
818                                 ReadChar ();
819                                 string s = ReadStringLiteral ();
820                                 if (s == "__type") {
821                                         SkipWhitespaces ();
822                                         Expect (':');
823                                         SkipWhitespaces ();
824                                         Expect ('"');
825                                         current_runtime_type = ReadStringLiteral ();
826                                         SkipWhitespaces ();
827                                         ei.HasContent = true;
828                                 }
829                                 else
830                                         next_object_content_name = s;
831                         }
832                 }
833
834                 void ReadStartArray ()
835                 {
836                         elements.Push (new ElementInfo (next_element, "array"));
837                 }
838
839                 void ReadEndObject ()
840                 {
841                         if (elements.Count == 0 || elements.Peek ().Type != "object")
842                                 throw XmlError ("Unexpected end of object");
843                         current_node = XmlNodeType.EndElement;
844                 }
845
846                 void ReadEndArray ()
847                 {
848                         if (elements.Count == 0 || elements.Peek ().Type != "array")
849                                 throw XmlError ("Unexpected end of array");
850                         current_node = XmlNodeType.EndElement;
851                 }
852
853                 void ReadEndOfStream ()
854                 {
855                         if (elements.Count > 0)
856                                 throw XmlError (String.Format ("{0} missing end of arrays or objects", elements.Count));
857                         read_state = ReadState.EndOfFile;
858                         current_node = XmlNodeType.None;
859                 }
860
861                 void ReadAsSimpleContent (string type, string value)
862                 {
863                         elements.Push (new ElementInfo (next_element, type));
864                         simple_value = value;
865                         content_stored = true;
866                 }
867
868                 void ReadNumber (int ch)
869                 {
870                         elements.Push (new ElementInfo (next_element, "number"));
871                         content_stored = true;
872
873                         int init = ch;
874                         int prev;
875                         bool floating = false, exp = false;
876
877                         StringBuilder sb = new StringBuilder ();
878                         bool cont = true;
879                         do {
880                                 sb.Append ((char) ch);
881                                 prev = ch;
882                                 ch = ReadChar ();
883
884                                 if (prev == '-' && !IsNumber (ch)) // neither '.', '-' or '+' nor anything else is valid
885                                         throw XmlError ("Invalid JSON number");
886
887                                 switch (ch) {
888                                 case 'e':
889                                 case 'E':
890                                         if (exp)
891                                                 throw XmlError ("Invalid JSON number token. Either 'E' or 'e' must not occur more than once");
892                                         if (!IsNumber (prev))
893                                                 throw XmlError ("Invalid JSON number token. only a number is valid before 'E' or 'e'");
894                                         exp = true;
895                                         break;
896                                 case '.':
897                                         if (floating)
898                                                 throw XmlError ("Invalid JSON number token. '.' must not occur twice");
899                                         if (exp)
900                                                 throw XmlError ("Invalid JSON number token. '.' must not occur after 'E' or 'e'");
901                                         floating = true;
902                                         break;
903                                 case '+':
904                                 case '-':
905                                         if (prev == 'E' || prev == 'e')
906                                                 break;
907                                         goto default;
908                                 default:
909                                         if (!IsNumber (ch)) {
910                                                 PushbackChar (ch);
911                                                 cont = false;
912                                         }
913                                         break;
914                                 }
915                         } while (cont);
916
917                         if (!IsNumber (prev)) // only number is valid at the end
918                                 throw XmlError ("Invalid JSON number");
919
920                         simple_value = sb.ToString ();
921
922                         if (init == '0' && !floating && !exp && simple_value != "0")
923                                 throw XmlError ("Invalid JSON number");
924                 }
925
926                 bool IsNumber (int c)
927                 {
928                         return '0' <= c && c <= '9';
929                 }
930
931                 StringBuilder vb = new StringBuilder ();
932
933                 string ReadStringLiteral ()
934                 {
935                         return ReadStringLiteral (false);
936                 }
937
938                 string ReadStringLiteral (bool endWithColon)
939                 {
940                         vb.Length = 0;
941                         while (true) {
942                                 int c = ReadChar ();
943                                 if (c < 0)
944                                         throw XmlError ("JSON string is not closed");
945                                 if (c == '"' && !endWithColon)
946                                         return vb.ToString ();
947                                 else if (c == ':' && endWithColon)
948                                         return vb.ToString ();
949                                 else if (c != '\\') {
950                                         vb.Append ((char) c);
951                                         continue;
952                                 }
953
954                                 // escaped expression
955                                 c = ReadChar ();
956                                 if (c < 0)
957                                         throw XmlError ("Invalid JSON string literal; incomplete escape sequence");
958                                 switch (c) {
959                                 case '"':
960                                 case '\\':
961                                 case '/':
962                                         vb.Append ((char) c);
963                                         break;
964                                 case 'b':
965                                         vb.Append ('\x8');
966                                         break;
967                                 case 'f':
968                                         vb.Append ('\f');
969                                         break;
970                                 case 'n':
971                                         vb.Append ('\n');
972                                         break;
973                                 case 'r':
974                                         vb.Append ('\r');
975                                         break;
976                                 case 't':
977                                         vb.Append ('\t');
978                                         break;
979                                 case 'u':
980                                         ushort cp = 0;
981                                         for (int i = 0; i < 4; i++) {
982                                                 if ((c = ReadChar ()) < 0)
983                                                         throw XmlError ("Incomplete unicode character escape literal");
984                                                 cp *= 16;
985                                                 if ('0' <= c && c <= '9')
986                                                         cp += (ushort) (c - '0');
987                                                 if ('A' <= c && c <= 'F')
988                                                         cp += (ushort) (c - 'A' + 10);
989                                                 if ('a' <= c && c <= 'f')
990                                                         cp += (ushort) (c - 'a' + 10);
991                                         }
992                                         vb.Append ((char) cp);
993                                         break;
994                                 default:
995                                         throw XmlError ("Invalid JSON string literal; unexpected escape character");
996                                 }
997                         }
998                 }
999
1000                 int PeekChar ()
1001                 {
1002                         return reader.Peek ();
1003                 }
1004
1005                 int ReadChar ()
1006                 {
1007                         int v = reader.Read ();
1008                         if (v == '\n') {
1009                                 line++;
1010                                 column = 0;
1011                         }
1012                         else
1013                                 column++;
1014                         return v;
1015                 }
1016
1017                 void PushbackChar (int ch)
1018                 {
1019                         // FIXME handle lines (and columns?  ugh, how?)
1020                         reader.Pushback (ch);
1021                 }
1022
1023                 void SkipWhitespaces ()
1024                 {
1025                         do {
1026                                 switch (PeekChar ()) {
1027                                 case ' ':
1028                                 case '\t':
1029                                 case '\r':
1030                                 case '\n':
1031                                         ReadChar ();
1032                                         continue;
1033                                 default:
1034                                         return;
1035                                 }
1036                         } while (true);
1037                 }
1038
1039                 void Expect (char c)
1040                 {
1041                         int v = ReadChar ();
1042                         if (v < 0)
1043                                 throw XmlError (String.Format ("Expected '{0}' but got EOF", c));
1044                         if (v != c)
1045                                 throw XmlError (String.Format ("Expected '{0}' but got '{1}'", c, (char) v));
1046                 }
1047
1048                 Exception XmlError (string s)
1049                 {
1050                         return new XmlException (String.Format ("{0} ({1},{2})", s, line, column));
1051                 }
1052
1053                 // This reads the current element and all its content as a string,
1054                 // with no processing done except for advancing the reader.
1055                 public override string ReadInnerXml ()
1056                 {
1057
1058                         if (NodeType != XmlNodeType.Element)
1059                                 return base.ReadInnerXml ();
1060
1061                         StringBuilder sb = new StringBuilder ();
1062                         bool isobject = elements.Peek ().Type == "object";
1063                         char end = isobject ? '}' : ']';
1064                         char start = isobject ? '{' : '[';
1065                         int count = 1;
1066
1067                         sb.Append (start);
1068
1069                         // add the first child manually, it's already been read
1070                         // but hasn't been processed yet
1071                         if (isobject && !String.IsNullOrEmpty (next_object_content_name))
1072                                         sb.Append ("\"" + next_object_content_name + "\"");
1073
1074                         // keep reading until we hit the end marker, no processing is
1075                         // done on anything
1076                         do {
1077                                 char c = (char)ReadChar ();
1078                                 sb.Append (c);
1079                                 if (c == start)
1080                                         ++count;
1081                                 else if (c == end)
1082                                         --count;
1083                         } while (count > 0);
1084
1085                         // Replace the content we've read with an empty object so it gets
1086                         // skipped on the following Read
1087                         reader.Pushback (end);
1088                         if (isobject) {
1089                                 reader.Pushback ('"');
1090                                 reader.Pushback ('"');
1091                                 reader.Pushback (':');
1092                         }
1093
1094                         // Skip the element
1095                         Read ();
1096                         return sb.ToString ();
1097                 }
1098         }
1099 }