2008-09-20 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web.Extensions / System.Web.Script.Serialization / JsonDeserializer.cs
1 //
2 // JsonDeserializer.cs
3 //
4 // Author:
5 //   Marek Habersack <mhabersack@novell.com>
6 //
7 // (C) 2008 Novell, Inc.  http://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 // Code is based on JSON_checker (http://www.json.org/JSON_checker/) and JSON_parser
29 // (http://fara.cs.uni-potsdam.de/~jsg/json_parser/) C sources. License for the original code
30 // follows:
31
32 #region Original License
33 /*
34 Copyright (c) 2005 JSON.org
35
36 Permission is hereby granted, free of charge, to any person obtaining a copy
37 of this software and associated documentation files (the "Software"), to deal
38 in the Software without restriction, including without limitation the rights
39 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
40 copies of the Software, and to permit persons to whom the Software is
41 furnished to do so, subject to the following conditions:
42
43 The above copyright notice and this permission notice shall be included in all
44 copies or substantial portions of the Software.
45
46 The Software shall be used for Good, not Evil.
47
48 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
51 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
52 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
53 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
54 SOFTWARE.
55 */
56 #endregion
57
58 using System;
59 using System.Collections;
60 using System.Collections.Generic;
61 using System.Data;
62 using System.Globalization;
63 using System.IO;
64 using System.Reflection;
65 using System.Text;
66
67 namespace System.Web.Script.Serialization
68 {
69         internal sealed class JsonDeserializer
70         {
71                 /* Universal error constant */
72                 const int __ = -1;
73                 const int UNIVERSAL_ERROR = __;
74                 
75                 /*
76                   Characters are mapped into these 31 character classes. This allows for
77                   a significant reduction in the size of the state transition table.
78                 */
79                 const int C_SPACE = 0x00;  /* space */
80                 const int C_WHITE = 0x01;  /* other whitespace */
81                 const int C_LCURB = 0x02;  /* {  */
82                 const int C_RCURB = 0x03;  /* } */
83                 const int C_LSQRB = 0x04;  /* [ */
84                 const int C_RSQRB = 0x05;  /* ] */
85                 const int C_COLON = 0x06;  /* : */
86                 const int C_COMMA = 0x07;  /* , */
87                 const int C_QUOTE = 0x08;  /* " */
88                 const int C_BACKS = 0x09;  /* \ */
89                 const int C_SLASH = 0x0A;  /* / */
90                 const int C_PLUS  = 0x0B;  /* + */
91                 const int C_MINUS = 0x0C;  /* - */
92                 const int C_POINT = 0x0D;  /* . */
93                 const int C_ZERO  = 0x0E;  /* 0 */
94                 const int C_DIGIT = 0x0F;  /* 123456789 */
95                 const int C_LOW_A = 0x10;  /* a */
96                 const int C_LOW_B = 0x11;  /* b */
97                 const int C_LOW_C = 0x12;  /* c */
98                 const int C_LOW_D = 0x13;  /* d */
99                 const int C_LOW_E = 0x14;  /* e */
100                 const int C_LOW_F = 0x15;  /* f */
101                 const int C_LOW_L = 0x16;  /* l */
102                 const int C_LOW_N = 0x17;  /* n */
103                 const int C_LOW_R = 0x18;  /* r */
104                 const int C_LOW_S = 0x19;  /* s */
105                 const int C_LOW_T = 0x1A;  /* t */
106                 const int C_LOW_U = 0x1B;  /* u */
107                 const int C_ABCDF = 0x1C;  /* ABCDF */
108                 const int C_E     = 0x1D;  /* E */
109                 const int C_ETC   = 0x1E;  /* everything else */
110                 const int C_STAR  = 0x1F;  /* * */
111                 const int C_I     = 0x20;  /* I */
112                 const int C_LOW_I = 0x21;  /* i */
113                 const int C_LOW_Y = 0x22;  /* y */
114                 const int C_N     = 0x23;  /* N */
115                 
116                 /* The state codes. */
117                 const int GO = 0x00;  /* start    */
118                 const int OK = 0x01;  /* ok       */
119                 const int OB = 0x02;  /* object   */
120                 const int KE = 0x03;  /* key      */
121                 const int CO = 0x04;  /* colon    */
122                 const int VA = 0x05;  /* value    */
123                 const int AR = 0x06;  /* array    */
124                 const int ST = 0x07;  /* string   */
125                 const int ES = 0x08;  /* escape   */
126                 const int U1 = 0x09;  /* u1       */
127                 const int U2 = 0x0A;  /* u2       */
128                 const int U3 = 0x0B;  /* u3       */
129                 const int U4 = 0x0C;  /* u4       */
130                 const int MI = 0x0D;  /* minus    */
131                 const int ZE = 0x0E;  /* zero     */
132                 const int IN = 0x0F;  /* integer  */
133                 const int FR = 0x10;  /* fraction */
134                 const int E1 = 0x11;  /* e        */
135                 const int E2 = 0x12;  /* ex       */
136                 const int E3 = 0x13;  /* exp      */
137                 const int T1 = 0x14;  /* tr       */
138                 const int T2 = 0x15;  /* tru      */
139                 const int T3 = 0x16;  /* true     */
140                 const int F1 = 0x17;  /* fa       */
141                 const int F2 = 0x18;  /* fal      */
142                 const int F3 = 0x19;  /* fals     */
143                 const int F4 = 0x1A;  /* false    */
144                 const int N1 = 0x1B;  /* nu       */
145                 const int N2 = 0x1C;  /* nul      */
146                 const int N3 = 0x1D;  /* null     */
147                 const int FX = 0x1E;  /* *.* *eE* */
148                 const int IV = 0x1F;  /* invalid input */
149                 const int UK = 0x20;  /* unquoted key name */
150                 const int UI = 0x21; /* ignore during unquoted key name construction */
151                 const int I1 = 0x22;  /* In */
152                 const int I2 = 0x23;  /* Inf */
153                 const int I3 = 0x24;  /* Infi */
154                 const int I4 = 0x25;  /* Infin */
155                 const int I5 = 0x26;  /* Infini */
156                 const int I6 = 0x27;  /* Infinit */
157                 const int I7 = 0x28;  /* Infinity */
158                 const int V1 = 0x29;  /* Na */
159                 const int V2 = 0x2A;  /* NaN */
160                 
161                 /* Actions */
162                 const int FA = -10; /* false */
163                 const int TR = -11; /* false */
164                 const int NU = -12; /* null */
165                 const int DE = -13; /* double detected by exponent e E */
166                 const int DF = -14; /* double detected by fraction . */
167                 const int SB = -15; /* string begin */
168                 const int MX = -16; /* integer detected by minus */
169                 const int ZX = -17; /* integer detected by zero */
170                 const int IX = -18; /* integer detected by 1-9 */
171                 const int EX = -19; /* next char is escaped */
172                 const int UC = -20; /* Unicode character read */
173                 const int SE =  -4; /* string end */
174                 const int AB =  -5; /* array begin */
175                 const int AE =  -7; /* array end */
176                 const int OS =  -6; /* object start */
177                 const int OE =  -8; /* object end */
178                 const int EO =  -9; /* empty object */
179                 const int CM =  -3; /* comma */
180                 const int CA =  -2; /* colon action */
181                 const int PX = -21; /* integer detected by plus */
182                 const int KB = -22; /* unquoted key name begin */
183                 const int UE = -23; /* unquoted key name end */
184                 const int IF = -25; /* Infinity */
185                 const int NN = -26; /* NaN */
186                 
187                 
188                 enum JsonMode {
189                         NONE,
190                         ARRAY,
191                         DONE,
192                         KEY,
193                         OBJECT
194                 };
195
196                 enum JsonType {
197                         NONE = 0,
198                         ARRAY_BEGIN,
199                         ARRAY_END,
200                         OBJECT_BEGIN,
201                         OBJECT_END,
202                         INTEGER,
203                         FLOAT,
204                         NULL,
205                         TRUE,
206                         FALSE,
207                         STRING,
208                         KEY,
209                         MAX
210                 };
211                 
212                 /*
213                   This array maps the 128 ASCII characters into character classes.
214                   The remaining Unicode characters should be mapped to C_ETC.
215                   Non-whitespace control characters are errors.
216                 */
217                 static readonly int[] ascii_class = {
218                         __,      __,      __,      __,      __,      __,      __,      __,
219                         __,      C_WHITE, C_WHITE, __,      __,      C_WHITE, __,      __,
220                         __,      __,      __,      __,      __,      __,      __,      __,
221                         __,      __,      __,      __,      __,      __,      __,      __,
222
223                         C_SPACE, C_ETC,   C_QUOTE, C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_QUOTE,
224                         C_ETC,   C_ETC,   C_STAR,   C_PLUS,  C_COMMA, C_MINUS, C_POINT, C_SLASH,
225                         C_ZERO,  C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT,
226                         C_DIGIT, C_DIGIT, C_COLON, C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,
227
228                         C_ETC,   C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E,     C_ABCDF, C_ETC,
229                         C_ETC,   C_I,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_N,   C_ETC,
230                         C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,
231                         C_ETC,   C_ETC,   C_ETC,   C_LSQRB, C_BACKS, C_RSQRB, C_ETC,   C_ETC,
232
233                         C_ETC,   C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC,
234                         C_ETC,   C_LOW_I, C_ETC,   C_ETC,   C_LOW_L, C_ETC,   C_LOW_N, C_ETC,
235                         C_ETC,   C_ETC,   C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC,   C_ETC,
236                         C_ETC,   C_LOW_Y,   C_ETC,   C_LCURB, C_ETC,   C_RCURB, C_ETC,   C_ETC
237                 };
238
239                 static readonly int[,] state_transition_table = {
240                 /*
241                   The state transition table takes the current state and the current symbol,
242                   and returns either a new state or an action. An action is represented as a
243                   negative number. A JSON text is accepted if at the end of the text the
244                   state is OK and if the mode is MODE_DONE.
245
246                                            white                  '                   1-9                                   ABCDF  etc
247                                        space |  {  }  [  ]  :  ,  "  \  /  +  -  .  0  |  a  b  c  d  e  f  l  n  r  s  t  u  |  E  |  *  I  i  y  N */
248                         /*start    GO*/ {GO,GO,OS,__,AB,__,__,__,SB,__,__,PX,MX,__,ZX,IX,__,__,__,__,__,FA,__,__,__,__,TR,__,__,__,__,__,I1,__,__,V1},
249                         /*ok       OK*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
250                         /*object   OB*/ {OB,OB,__,EO,__,__,__,__,SB,__,__,__,KB,__,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,__,KB,KB,KB,KB},
251                         /*key      KE*/ {KE,KE,__,__,__,__,__,__,SB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
252                         /*colon    CO*/ {CO,CO,__,__,__,__,CA,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
253                         /*value    VA*/ {VA,VA,OS,__,AB,__,__,__,SB,__,__,PX,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__,I1,__,__,V1},
254                         /*array    AR*/ {AR,AR,OS,__,AB,AE,__,__,SB,__,__,PX,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__,I1,__,__,V1},
255                         /*string   ST*/ {ST,__,ST,ST,ST,ST,ST,ST,SE,EX,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST},
256                         /*escape   ES*/ {__,__,__,__,__,__,__,__,ST,ST,ST,__,__,__,__,__,__,ST,__,__,__,ST,__,ST,ST,__,ST,U1,__,__,__,__,__,__,__,__},
257                         /*u1       U1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U2,U2,U2,U2,U2,U2,U2,U2,__,__,__,__,__,__,U2,U2,__,__,__,__,__,__},
258                         /*u2       U2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U3,U3,U3,U3,U3,U3,U3,U3,__,__,__,__,__,__,U3,U3,__,__,__,__,__,__},
259                         /*u3       U3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U4,U4,U4,U4,U4,U4,U4,U4,__,__,__,__,__,__,U4,U4,__,__,__,__,__,__},
260                         /*u4       U4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,UC,UC,UC,UC,UC,UC,UC,UC,__,__,__,__,__,__,UC,UC,__,__,__,__,__,__},
261                         /*minus    MI*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,ZE,IN,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I1,__,__,__},
262                         /*zero     ZE*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,DF,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
263                         /*int      IN*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,DF,IN,IN,__,__,__,__,DE,__,__,__,__,__,__,__,__,DE,__,__,__,__,__,__},
264                         /*frac     FR*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__,__,__,__,__},
265                         /*e        E1*/ {__,__,__,__,__,__,__,__,__,__,__,E2,E2,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
266                         /*ex       E2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
267                         /*exp      E3*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
268                         /*tr       T1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T2,__,__,__,__,__,__,__,__,__,__,__},
269                         /*tru      T2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T3,__,__,__,__,__,__,__,__},
270                         /*true     T3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
271                         /*fa       F1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
272                         /*fal      F2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F3,__,__,__,__,__,__,__,__,__,__,__,__,__},
273                         /*fals     F3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F4,__,__,__,__,__,__,__,__,__,__},
274                         /*false    F4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
275                         /*nu       N1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N2,__,__,__,__,__,__,__,__},
276                         /*nul      N2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N3,__,__,__,__,__,__,__,__,__,__,__,__,__},
277                         /*null     N3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__,__,__},
278                         /*_.       FX*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__,__,__,__,__},
279                         /*inval.   IV*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
280                         /*unq.key  UK*/ {__,UI,__,__,__,__,UE,__,__,__,__,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,__,UK,UK,UK,UK},
281                         /*unq.ign. UI*/ {__,UI,__,__,__,__,UE,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
282                         /*i1       I1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I2,__,__,__,__,__,__,__,__,__,__,__,__},
283                         /*i2       I2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I3,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
284                         /*i3       I3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I4,__,__},
285                         /*i4       I4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I5,__,__,__,__,__,__,__,__,__,__,__,__},
286                         /*i5       I5*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I6,__,__},
287                         /*i6       I6*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I7,__,__,__,__,__,__,__,__,__},
288                         /*i7       I7*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,IF,__},
289                         /*v1       V1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,V2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
290                         /*v2       V2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,NN},
291                 };
292
293                 JavaScriptSerializer serializer;
294                 JavaScriptTypeResolver typeResolver;
295                 int maxJsonLength;
296                 int currentPosition;
297                 int recursionLimit;
298                 int recursionDepth;
299                 
300                 Stack <JsonMode> modes;
301                 Stack <object> returnValue;
302                 JsonType jsonType;
303                 
304                 bool escaped;
305                 bool negated;
306                 int state;
307                 Stack <string> currentKey;
308                 StringBuilder buffer;
309                 char quoteChar;
310                 
311                 public JsonDeserializer (JavaScriptSerializer serializer)
312                 {
313                         this.serializer = serializer;
314                         this.maxJsonLength = serializer.MaxJsonLength;
315                         this.recursionLimit = serializer.RecursionLimit;
316                         this.typeResolver = serializer.TypeResolver;
317                         this.modes = new Stack <JsonMode> ();
318                         this.currentKey = new Stack <string> ();
319                         this.returnValue = new Stack <object> ();
320                         this.state = GO;
321                         this.currentPosition = 0;
322                         this.recursionDepth = 0;
323                 }
324
325                 public object Deserialize (string input)
326                 {
327                         if (input == null)
328                                 throw new ArgumentNullException ("input");
329                         
330                         return Deserialize (new StringReader (input));
331                 }
332                 
333                 public object Deserialize (TextReader input)
334                 {
335                         if (input == null)
336                                 throw new ArgumentNullException ("input");
337                         
338                         int value;
339                         buffer = new StringBuilder ();
340                         
341                         while (true) {
342                                 value = input.Read ();
343                                 if (value < 0)
344                                         break;
345
346                                 currentPosition++;
347                                 if (currentPosition > maxJsonLength)
348                                         throw new ArgumentException ("Maximum JSON input length has been exceeded.");
349
350                                 if (!ProcessCharacter ((char) value))
351                                         throw new InvalidOperationException ("JSON syntax error.");
352                         }
353
354                         object topObject = PeekObject ();
355                         if (buffer.Length > 0) {
356                                 object result;
357
358                                 if (ParseBuffer (out result)) {
359                                         if (topObject != null)
360                                                 StoreValue (result);
361                                         else
362                                                 PushObject (result);
363                                 }
364                         }
365
366                         if (returnValue.Count > 1)
367                                 throw new InvalidOperationException ("JSON syntax error.");
368
369                         object ret = PopObject ();
370 //                      Console.WriteLine ("Returning: {0}", ret);
371 //                      DumpObject (String.Empty, ret);
372                         return ret;
373                 }
374
375                 void DumpObject (string indent, object obj)
376                 {
377                         if (obj is Dictionary <string, object>) {
378                                 Console.WriteLine (indent + "{");
379                                 foreach (KeyValuePair <string, object> kvp in (Dictionary <string, object>)obj) {
380                                         Console.WriteLine (indent + "\t\"{0}\": ", kvp.Key);
381                                         DumpObject (indent + "\t\t", kvp.Value);
382                                 }
383                                 Console.WriteLine (indent + "}");
384                         } else if (obj is object[]) {
385                                 Console.WriteLine (indent + "[");
386                                 foreach (object o in (object[])obj)
387                                         DumpObject (indent + "\t", o);
388                                 Console.WriteLine (indent + "]");
389                         } else if (obj != null)
390                                 Console.WriteLine (indent + obj.ToString ());
391                         else
392                                 Console.WriteLine ("null");
393                 }
394                 
395                 void DecodeUnicodeChar ()
396                 {
397                         int len = buffer.Length;
398                         if (len < 6)
399                                 throw new ArgumentException ("Invalid escaped unicode character specification (" + currentPosition + ")");
400
401                         int code = Int32.Parse (buffer.ToString ().Substring (len - 4), NumberStyles.HexNumber);
402                         buffer.Length = len - 6;
403                         buffer.Append ((char)code);
404                 }
405
406                 string GetModeMessage (JsonMode expectedMode)
407                 {
408 //                      Console.WriteLine ("{0}.GetModeMessage ({1})", this, expectedMode);
409                         switch (expectedMode) {
410                                 case JsonMode.ARRAY:
411                                         return "Invalid array passed in, ',' or ']' expected (" + currentPosition + ")";
412
413                                 case JsonMode.KEY:
414                                         return "Invalid object passed in, key name or ':' expected (" + currentPosition + ")";
415
416                                 case JsonMode.OBJECT:
417                                         return "Invalid object passed in, key value expected (" + currentPosition + ")";
418                                         
419                                 default:
420                                         return "Invalid JSON string";
421                         }
422                 }
423                 
424                 void PopMode (JsonMode expectedMode)
425                 {
426                         JsonMode mode = PeekMode ();
427                         if (mode != expectedMode)
428                                 throw new ArgumentException (GetModeMessage (mode));
429
430                         modes.Pop ();
431                 }
432
433                 void PushMode (JsonMode newMode)
434                 {
435                         modes.Push (newMode);
436                 }
437
438                 JsonMode PeekMode ()
439                 {
440                         if (modes.Count == 0)
441                                 return JsonMode.NONE;
442
443                         return modes.Peek ();
444                 }
445
446                 void PushObject (object o)
447                 {
448                         returnValue.Push (o);
449                 }
450
451                 object PopObject (bool notIfLast)
452                 {
453                         int count = returnValue.Count;
454                         if (count == 0)
455                                 return null;
456
457                         if (notIfLast && count == 1)
458                                 return null;
459                         
460                         return returnValue.Pop ();
461                 }
462
463                 object PopObject ()
464                 {
465                         return PopObject (false);
466                 }
467                 
468                 object PeekObject ()
469                 {
470                         if (returnValue.Count == 0)
471                                 return null;
472
473                         return returnValue.Peek ();
474                 }
475                 
476                 void RemoveLastCharFromBuffer ()
477                 {
478                         int len = buffer.Length;
479                         if (len == 0)
480                                 return;
481                         buffer.Length = len - 1;
482                 }
483
484                 bool ParseBuffer (out object result)
485                 {
486 //                      Console.WriteLine ("{0}.ParseBuffer ()", this);
487 //                      Console.WriteLine ("\ttype: {0}", jsonType);
488                         result = null;
489                         
490                         if (jsonType == JsonType.NONE) {
491                                 buffer.Length = 0;
492                                 return false;
493                         }
494
495                         string s = buffer.ToString ();
496                         bool converted = true;
497                         int intValue;
498                         long longValue;
499                         decimal decimalValue;
500                         double doubleValue;
501                         
502                         switch (jsonType) {
503                                 case JsonType.INTEGER:
504                                         /* MS AJAX.NET JSON parser promotes big integers to double */
505                                         
506                                         if (Int32.TryParse (s, out intValue))
507                                                 result = intValue;
508                                         else if (Int64.TryParse (s, out longValue))
509                                                 result = longValue;
510                                         else if (Decimal.TryParse (s, out decimalValue))
511                                                 result = decimalValue;
512                                         else if (Double.TryParse (s, out doubleValue))
513                                                 result = doubleValue;
514                                         else
515                                                 converted = false;
516                                         break;
517
518                                 case JsonType.FLOAT:
519                                         if (Decimal.TryParse (s, out decimalValue))
520                                                 result = decimalValue;
521                                         else if (Double.TryParse (s, out doubleValue))
522                                                 result = doubleValue;
523                                         else
524                                                 converted = false;
525                                         break;
526
527                                 case JsonType.TRUE:
528                                         if (String.Compare (s, "true", StringComparison.Ordinal) == 0)
529                                                 result = true;
530                                         else
531                                                 converted = false;
532                                         break;
533
534                                 case JsonType.FALSE:
535                                         if (String.Compare (s, "false", StringComparison.Ordinal) == 0)
536                                                 result = false;
537                                         else
538                                                 converted = false;
539                                         break;
540
541                                 case JsonType.NULL:
542                                         if (String.Compare (s, "null", StringComparison.Ordinal) != 0)
543                                                 converted = false;
544                                         break;
545
546                                 case JsonType.STRING:
547                                         if (s.StartsWith ("/Date(", StringComparison.Ordinal) && s.EndsWith (")/", StringComparison.Ordinal)) {
548                                                 long javaScriptTicks = Convert.ToInt64 (s.Substring (6, s.Length - 8));
549                                                 result = new DateTime ((javaScriptTicks * 10000) + JsonSerializer.InitialJavaScriptDateTicks, DateTimeKind.Utc);
550                                         } else
551                                                 result = s;
552                                         break;
553
554                                 default:
555                                         throw new InvalidOperationException (String.Format ("Internal error: unexpected JsonType ({0})", jsonType));
556                                         
557                         }
558
559                         if (!converted)
560                                 throw new ArgumentException ("Invalid JSON primitive: " + s);
561
562                         buffer.Length = 0;
563                         return true;
564                 }
565                 
566                 bool ProcessCharacter (char ch)
567                 {
568 //                      Console.WriteLine ("{0}.ProcessCharacter ('{1}')", this, ch);
569                         int next_class, next_state;
570
571                         if (ch >= 128)
572                                 next_class = C_ETC;
573                         else {
574                                 next_class = ascii_class [ch];
575                                 if (next_class <= UNIVERSAL_ERROR)
576                                         return false;
577                         }
578
579 //                      Console.WriteLine ("\tnext_class == {0:x} ({0})", next_class);
580                         if (escaped) {
581                                 escaped = false;
582                                 RemoveLastCharFromBuffer ();
583                                 
584                                 switch (ch) {
585                                         case 'b':
586                                                 buffer.Append ('\b');
587                                                 break;
588                                         case 'f':
589                                                 buffer.Append ('\f');
590                                                 break;
591                                         case 'n':
592                                                 buffer.Append ('\n');
593                                                 break;
594                                         case 'r':
595                                                 buffer.Append ('\r');
596                                                 break;
597                                         case 't':
598                                                 buffer.Append ('\t');
599                                                 break;
600                                         case '"':
601                                         case '\\':
602                                         case '/':
603                                                 buffer.Append (ch);
604                                                 break;
605                                         case 'u':
606                                                 buffer.Append ("\\u");
607                                                 break;
608                                         default:
609                                                 return false;
610                                 }
611                         } else if (jsonType != JsonType.NONE || !(next_class == C_SPACE || next_class == C_WHITE))
612                                 buffer.Append (ch);
613
614 //                      Console.WriteLine ("\tstate == {0:x} ({0})", state);
615                         next_state = state_transition_table [state, next_class];
616 //                      Console.WriteLine ("\tnext_state == {0:x} ({0})", next_state);
617                         if (next_state >= 0) {
618                                 state = next_state;
619                                 return true;
620                         }
621
622                         object result;
623                         /* An action to perform */
624                         switch (next_state) {
625                                 case UC: /* Unicode character */
626                                         DecodeUnicodeChar ();
627                                         state = ST;
628                                         break;
629                                         
630                                 case EX: /* Escaped character */
631                                         escaped = true;
632                                         state = ES;
633                                         break;
634
635                                 case MX: /* integer detected by minus */
636                                         jsonType = JsonType.INTEGER;
637                                         state = MI;
638                                         break;
639
640                                 case PX: /* integer detected by plus */
641                                         jsonType = JsonType.INTEGER;
642                                         state = MI;
643                                         break;
644                                         
645                                 case ZX: /* integer detected by zero */
646                                         jsonType = JsonType.INTEGER;
647                                         state = ZE;
648                                         break;
649
650                                 case IX: /* integer detected by 1-9 */
651                                         jsonType = JsonType.INTEGER;
652                                         state = IN;
653                                         break;
654
655                                 case DE: /* floating point number detected by exponent*/
656                                         jsonType = JsonType.FLOAT;
657                                         state = E1;
658                                         break;
659
660                                 case DF: /* floating point number detected by fraction */
661                                         jsonType = JsonType.FLOAT;
662                                         state = FX;
663                                         break;
664
665                                 case SB: /* string begin " or ' */
666                                         buffer.Length = 0;
667                                         quoteChar = ch;
668                                         jsonType = JsonType.STRING;
669                                         state = ST;
670                                         break;
671
672                                 case KB: /* unquoted key name begin */
673                                         jsonType = JsonType.STRING;
674                                         state = UK;
675                                         break;
676
677                                 case UE: /* unquoted key name end ':' */
678                                         RemoveLastCharFromBuffer ();
679                                         if (ParseBuffer (out result))
680                                                 StoreKey (result);
681                                         jsonType = JsonType.NONE;
682                                         
683                                         PopMode (JsonMode.KEY);
684                                         PushMode (JsonMode.OBJECT);
685                                         state = VA;
686                                         buffer.Length = 0;
687                                         break;
688                                         
689                                 case NU: /* n */
690                                         jsonType = JsonType.NULL;
691                                         state = N1;
692                                         break;
693
694                                 case FA: /* f */
695                                         jsonType = JsonType.FALSE;
696                                         state = F1;
697                                         break;
698
699                                 case TR: /* t */
700                                         jsonType = JsonType.TRUE;
701                                         state = T1;
702                                         break;
703
704                                 case EO: /* empty } */
705                                         result = PopObject (true);
706                                         if (result != null)
707                                                 StoreValue (result);
708                                         PopMode (JsonMode.KEY);
709                                         state = OK;
710                                         break;
711
712                                 case OE: /* } */
713                                         RemoveLastCharFromBuffer ();
714
715                                         if (ParseBuffer (out result))
716                                                 StoreValue (result);
717
718                                         result = PopObject (true);
719                                         if (result != null)
720                                                 StoreValue (result);
721
722                                         PopMode (JsonMode.OBJECT);
723
724                                         jsonType = JsonType.NONE;
725                                         state = OK;
726                                         break;
727
728                                 case AE: /* ] */
729                                         RemoveLastCharFromBuffer ();
730                                         if (ParseBuffer (out result))
731                                                 StoreValue (result);
732                                         PopMode (JsonMode.ARRAY);
733                                         result = PopObject (true);
734                                         if (result != null) {
735 //                                              Console.WriteLine ("result: {0}", result);
736                                                 StoreValue (result);
737                                         }
738                                         
739                                         jsonType = JsonType.NONE;
740                                         state = OK;
741                                         break;
742
743                                 case OS: /* { */
744                                         RemoveLastCharFromBuffer ();
745                                         CreateObject ();
746                                         PushMode (JsonMode.KEY);
747                                         
748                                         state = OB;
749                                         break;
750
751                                 case AB: /* [ */
752                                         RemoveLastCharFromBuffer ();
753                                         CreateArray ();
754                                         PushMode (JsonMode.ARRAY);
755
756                                         state = AR;
757                                         break;
758
759                                 case SE: /* string end " or ' */
760                                         if (ch == quoteChar) {
761                                                 RemoveLastCharFromBuffer ();
762
763                                                 switch (PeekMode ()) {
764                                                         case JsonMode.KEY:
765                                                                 if (ParseBuffer (out result))
766                                                                         StoreKey (result);
767
768                                                                 jsonType = JsonType.NONE;
769                                                                 state = CO;
770                                                                 buffer.Length = 0;
771                                                                 break;
772
773                                                         case JsonMode.ARRAY:
774                                                         case JsonMode.OBJECT:
775                                                                 if (ParseBuffer (out result))
776                                                                         StoreValue (result);
777                                                         
778                                                                 jsonType = JsonType.NONE;
779                                                                 state = OK;
780                                                                 break;
781
782                                                         case JsonMode.NONE: /* A stand-alone string */
783                                                                 jsonType = JsonType.STRING;
784                                                                 state = IV; /* the rest of input is invalid */
785                                                                 if (ParseBuffer (out result))
786                                                                         PushObject (result);
787                                                                 break;
788                                                         
789                                                         default:
790                                                                 throw new ArgumentException ("Syntax error: string in unexpected place.");
791                                                 }
792                                         }
793                                         break;
794
795                                 case CM: /* , */
796                                         RemoveLastCharFromBuffer ();
797
798                                         // With MS.AJAX, a comma resets the recursion depth
799                                         recursionDepth = 0;
800                                         
801                                         bool doStore = ParseBuffer (out result);
802                                         switch (PeekMode ()) {
803                                                 case JsonMode.OBJECT:
804                                                         if (doStore)
805                                                                 StoreValue (result);
806                                                         PopMode (JsonMode.OBJECT);
807                                                         PushMode (JsonMode.KEY);
808                                                         jsonType = JsonType.NONE;
809                                                         state = KE;
810                                                         break;
811
812                                                 case JsonMode.ARRAY:
813                                                         jsonType = JsonType.NONE;
814                                                         state = VA;
815                                                         if (doStore)
816                                                                 StoreValue (result);
817                                                         break;
818
819                                                 default:
820                                                         throw new ArgumentException ("Syntax error: unexpected comma.");
821                                         }
822                                         
823                                         break;
824
825                                 case CA: /* : */
826                                         RemoveLastCharFromBuffer ();
827
828                                         // With MS.AJAX a colon increases recursion depth
829                                         if (++recursionDepth >= recursionLimit)
830                                                 throw new ArgumentException ("Recursion limit has been reached on parsing input.");
831                                         
832                                         PopMode (JsonMode.KEY);
833                                         PushMode (JsonMode.OBJECT);
834                                         state = VA;
835                                         break;
836
837                                 case IF: /* Infinity */
838                                 case NN: /* NaN */
839                                         jsonType = JsonType.FLOAT;
840                                         switch (PeekMode ()) {
841                                                 case JsonMode.ARRAY:
842                                                 case JsonMode.OBJECT:
843                                                         if (ParseBuffer (out result))
844                                                                 StoreValue (result);
845                                                         
846                                                         jsonType = JsonType.NONE;
847                                                         state = OK;
848                                                         break;
849
850                                                 case JsonMode.NONE: /* A stand-alone NaN/Infinity */
851                                                         jsonType = JsonType.FLOAT;
852                                                         state = IV; /* the rest of input is invalid */
853                                                         if (ParseBuffer (out result))
854                                                                 PushObject (result);
855                                                         break;
856                                                         
857                                                 default:
858                                                         throw new ArgumentException ("Syntax error: misplaced NaN/Infinity.");
859                                         }
860                                         buffer.Length = 0;
861                                         break;
862                                         
863                                 default:
864                                         throw new ArgumentException (GetModeMessage (PeekMode ()));
865                         }
866                         
867                         return true;
868                 }
869
870                 void CreateArray ()
871                 {
872                         var arr = new ArrayList ();
873                         PushObject (arr);
874                 }
875                 
876                 void CreateObject ()
877                 {
878                         var dict = new Dictionary <string, object> ();
879                         PushObject (dict);
880                 }
881
882                 void StoreKey (object o)
883                 {
884 //                      Console.WriteLine ("{0}.StoreKey ({1})", this, o);
885                         string key = o as string;
886                         if (key != null)
887                                 key = key.Trim ();
888                         
889                         if (String.IsNullOrEmpty (key))
890                                 throw new InvalidOperationException ("Internal error: key is null, empty or not a string.");
891                         
892                         currentKey.Push (key);
893                         Dictionary <string, object> dict = PeekObject () as Dictionary <string, object>;
894                         if (dict == null)
895                                 throw new InvalidOperationException ("Internal error: current object is not a dictionary.");
896
897                         /* MS AJAX.NET silently overwrites existing currentKey value */
898                         dict [key] = null;
899                 }
900
901                 void StoreValue (object o)
902                 {
903 //                      Console.WriteLine ("{0}.StoreValue ({1})", this, o);
904                         Dictionary <string, object> dict = PeekObject () as Dictionary <string, object>;
905                         if (dict == null) {
906                                 ArrayList arr = PeekObject () as ArrayList;
907                                 if (arr == null)
908                                         throw new InvalidOperationException ("Internal error: current object is not a dictionary or an array.");
909                                 arr.Add (o);
910                                 return;
911                         }
912
913                         string key;
914                         if (currentKey.Count == 0)
915                                 key = null;
916                         else
917                                 key = currentKey.Pop ();
918
919 //                      Console.WriteLine ("\tkey == {0}", key);
920                         if (String.IsNullOrEmpty (key))
921                                 throw new InvalidOperationException ("Internal error: object is a dictionary, but no key present.");
922                         
923                         dict [key] = o;
924                 }
925         }
926 }