All files
[pithos-ms-client] / trunk / Libraries / Json40r2 / Source / Src / Newtonsoft.Json / JsonReader.cs
1 #region License
2 // Copyright (c) 2007 James Newton-King
3 //
4 // Permission is hereby granted, free of charge, to any person
5 // obtaining a copy of this software and associated documentation
6 // files (the "Software"), to deal in the Software without
7 // restriction, including without limitation the rights to use,
8 // copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the
10 // Software is furnished to do so, subject to the following
11 // conditions:
12 //
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 // OTHER DEALINGS IN THE SOFTWARE.
24 #endregion
25
26 using System;
27 using System.Collections.Generic;
28 using System.IO;
29 using System.Globalization;
30 using Newtonsoft.Json.Linq;
31 using Newtonsoft.Json.Utilities;
32
33 namespace Newtonsoft.Json
34 {
35   /// <summary>
36   /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data.
37   /// </summary>
38   public abstract class JsonReader : IDisposable
39   {
40     /// <summary>
41     /// Specifies the state of the reader.
42     /// </summary>
43     protected enum State
44     {
45       /// <summary>
46       /// The Read method has not been called.
47       /// </summary>
48       Start,
49       /// <summary>
50       /// The end of the file has been reached successfully.
51       /// </summary>
52       Complete,
53       /// <summary>
54       /// Reader is at a property.
55       /// </summary>
56       Property,
57       /// <summary>
58       /// Reader is at the start of an object.
59       /// </summary>
60       ObjectStart,
61       /// <summary>
62       /// Reader is in an object.
63       /// </summary>
64       Object,
65       /// <summary>
66       /// Reader is at the start of an array.
67       /// </summary>
68       ArrayStart,
69       /// <summary>
70       /// Reader is in an array.
71       /// </summary>
72       Array,
73       /// <summary>
74       /// The Close method has been called.
75       /// </summary>
76       Closed,
77       /// <summary>
78       /// Reader has just read a value.
79       /// </summary>
80       PostValue,
81       /// <summary>
82       /// Reader is at the start of a constructor.
83       /// </summary>
84       ConstructorStart,
85       /// <summary>
86       /// Reader in a constructor.
87       /// </summary>
88       Constructor,
89       /// <summary>
90       /// An error occurred that prevents the read operation from continuing.
91       /// </summary>
92       Error,
93       /// <summary>
94       /// The end of the file has been reached successfully.
95       /// </summary>
96       Finished
97     }
98
99     // current Token data
100     private JsonToken _token;
101     private object _value;
102     private Type _valueType;
103     private char _quoteChar;
104     private State _currentState;
105     private JTokenType _currentTypeContext;
106
107     /// <summary>
108     /// Gets the current reader state.
109     /// </summary>
110     /// <value>The current reader state.</value>
111     protected State CurrentState
112     {
113       get { return _currentState; }
114     }
115
116     private int _top;
117
118     private readonly List<JTokenType> _stack;
119
120     /// <summary>
121     /// Gets or sets a value indicating whether the underlying stream or
122     /// <see cref="TextReader"/> should be closed when the reader is closed.
123     /// </summary>
124     /// <value>
125     /// true to close the underlying stream or <see cref="TextReader"/> when
126     /// the reader is closed; otherwise false. The default is true.
127     /// </value>
128     public bool CloseInput { get; set; }
129
130     /// <summary>
131     /// Gets the quotation mark character used to enclose the value of a string.
132     /// </summary>
133     public virtual char QuoteChar
134     {
135       get { return _quoteChar; }
136       protected internal set { _quoteChar = value; }
137     }
138
139     /// <summary>
140     /// Gets the type of the current Json token. 
141     /// </summary>
142     public virtual JsonToken TokenType
143     {
144       get { return _token; }
145     }
146
147     /// <summary>
148     /// Gets the text value of the current Json token.
149     /// </summary>
150     public virtual object Value
151     {
152       get { return _value; }
153     }
154
155     /// <summary>
156     /// Gets The Common Language Runtime (CLR) type for the current Json token.
157     /// </summary>
158     public virtual Type ValueType
159     {
160       get { return _valueType; }
161     }
162
163     /// <summary>
164     /// Gets the depth of the current token in the JSON document.
165     /// </summary>
166     /// <value>The depth of the current token in the JSON document.</value>
167     public virtual int Depth
168     {
169       get
170       {
171         int depth = _top - 1;
172         if (IsStartToken(TokenType))
173           return depth - 1;
174         else
175           return depth;
176       }
177     }
178
179     /// <summary>
180     /// Initializes a new instance of the <see cref="JsonReader"/> class with the specified <see cref="TextReader"/>.
181     /// </summary>
182     protected JsonReader()
183     {
184       _currentState = State.Start;
185       _stack = new List<JTokenType>();
186
187       CloseInput = true;
188       
189       Push(JTokenType.None);
190     }
191
192     private void Push(JTokenType value)
193     {
194       _stack.Add(value);
195       _top++;
196       _currentTypeContext = value;
197     }
198
199     private JTokenType Pop()
200     {
201       JTokenType value = Peek();
202       _stack.RemoveAt(_stack.Count - 1);
203       _top--;
204       _currentTypeContext = _stack[_top - 1];
205
206       return value;
207     }
208
209     private JTokenType Peek()
210     {
211       return _currentTypeContext;
212     }
213
214     /// <summary>
215     /// Reads the next JSON token from the stream.
216     /// </summary>
217     /// <returns>true if the next token was read successfully; false if there are no more tokens to read.</returns>
218     public abstract bool Read();
219
220     /// <summary>
221     /// Reads the next JSON token from the stream as a <see cref="T:Byte[]"/>.
222     /// </summary>
223     /// <returns>A <see cref="T:Byte[]"/> or a null reference if the next JSON token is null.</returns>
224     public abstract byte[] ReadAsBytes();
225
226     /// <summary>
227     /// Reads the next JSON token from the stream as a <see cref="Nullable{Decimal}"/>.
228     /// </summary>
229     /// <returns>A <see cref="Nullable{Decimal}"/>.</returns>
230     public abstract decimal? ReadAsDecimal();
231
232 #if !NET20
233     /// <summary>
234     /// Reads the next JSON token from the stream as a <see cref="Nullable{DateTimeOffset}"/>.
235     /// </summary>
236     /// <returns>A <see cref="Nullable{DateTimeOffset}"/>.</returns>
237     public abstract DateTimeOffset? ReadAsDateTimeOffset();
238 #endif
239
240     /// <summary>
241     /// Skips the children of the current token.
242     /// </summary>
243     public void Skip()
244     {
245       if (IsStartToken(TokenType))
246       {
247         int depth = Depth;
248
249         while (Read() && (depth < Depth))
250         {
251         }
252       }
253     }
254
255     /// <summary>
256     /// Sets the current token.
257     /// </summary>
258     /// <param name="newToken">The new token.</param>
259     protected void SetToken(JsonToken newToken)
260     {
261       SetToken(newToken, null);
262     }
263
264     /// <summary>
265     /// Sets the current token and value.
266     /// </summary>
267     /// <param name="newToken">The new token.</param>
268     /// <param name="value">The value.</param>
269     protected virtual void SetToken(JsonToken newToken, object value)
270     {
271       _token = newToken;
272
273       switch (newToken)
274       {
275         case JsonToken.StartObject:
276           _currentState = State.ObjectStart;
277           Push(JTokenType.Object);
278           break;
279         case JsonToken.StartArray:
280           _currentState = State.ArrayStart;
281           Push(JTokenType.Array);
282           break;
283         case JsonToken.StartConstructor:
284           _currentState = State.ConstructorStart;
285           Push(JTokenType.Constructor);
286           break;
287         case JsonToken.EndObject:
288           ValidateEnd(JsonToken.EndObject);
289           _currentState = State.PostValue;
290           break;
291         case JsonToken.EndArray:
292           ValidateEnd(JsonToken.EndArray);
293           _currentState = State.PostValue;
294           break;
295         case JsonToken.EndConstructor:
296           ValidateEnd(JsonToken.EndConstructor);
297           _currentState = State.PostValue;
298           break;
299         case JsonToken.PropertyName:
300           _currentState = State.Property;
301           Push(JTokenType.Property);
302           break;
303         case JsonToken.Undefined:
304         case JsonToken.Integer:
305         case JsonToken.Float:
306         case JsonToken.Boolean:
307         case JsonToken.Null:
308         case JsonToken.Date:
309         case JsonToken.String:
310         case JsonToken.Raw:
311         case JsonToken.Bytes:
312           _currentState = State.PostValue;
313           break;
314       }
315
316       JTokenType current = Peek();
317       if (current == JTokenType.Property && _currentState == State.PostValue)
318         Pop();
319
320       if (value != null)
321       {
322         _value = value;
323         _valueType = value.GetType();
324       }
325       else
326       {
327         _value = null;
328         _valueType = null;
329       }
330     }
331
332     private void ValidateEnd(JsonToken endToken)
333     {
334       JTokenType currentObject = Pop();
335
336       if (GetTypeForCloseToken(endToken) != currentObject)
337         throw new JsonReaderException("JsonToken {0} is not valid for closing JsonType {1}.".FormatWith(CultureInfo.InvariantCulture, endToken, currentObject));
338     }
339
340     /// <summary>
341     /// Sets the state based on current token type.
342     /// </summary>
343     protected void SetStateBasedOnCurrent()
344     {
345       JTokenType currentObject = Peek();
346
347       switch (currentObject)
348       {
349         case JTokenType.Object:
350           _currentState = State.Object;
351           break;
352         case JTokenType.Array:
353           _currentState = State.Array;
354           break;
355         case JTokenType.Constructor:
356           _currentState = State.Constructor;
357           break;
358         case JTokenType.None:
359           _currentState = State.Finished;
360           break;
361         default:
362           throw new JsonReaderException("While setting the reader state back to current object an unexpected JsonType was encountered: {0}".FormatWith(CultureInfo.InvariantCulture, currentObject));
363       }
364     }
365
366     internal static bool IsPrimitiveToken(JsonToken token)
367     {
368       switch (token)
369       {
370         case JsonToken.Integer:
371         case JsonToken.Float:
372         case JsonToken.String:
373         case JsonToken.Boolean:
374         case JsonToken.Undefined:
375         case JsonToken.Null:
376         case JsonToken.Date:
377         case JsonToken.Bytes:
378           return true;
379         default:
380           return false;
381       }
382     }
383
384     internal static bool IsStartToken(JsonToken token)
385     {
386       switch (token)
387       {
388         case JsonToken.StartObject:
389         case JsonToken.StartArray:
390         case JsonToken.StartConstructor:
391         case JsonToken.PropertyName:
392           return true;
393         case JsonToken.None:
394         case JsonToken.Comment:
395         case JsonToken.Integer:
396         case JsonToken.Float:
397         case JsonToken.String:
398         case JsonToken.Boolean:
399         case JsonToken.Null:
400         case JsonToken.Undefined:
401         case JsonToken.EndObject:
402         case JsonToken.EndArray:
403         case JsonToken.EndConstructor:
404         case JsonToken.Date:
405         case JsonToken.Raw:
406         case JsonToken.Bytes:
407           return false;
408         default:
409           throw MiscellaneousUtils.CreateArgumentOutOfRangeException("token", token, "Unexpected JsonToken value.");
410       }
411     }
412
413     private JTokenType GetTypeForCloseToken(JsonToken token)
414     {
415       switch (token)
416       {
417         case JsonToken.EndObject:
418           return JTokenType.Object;
419         case JsonToken.EndArray:
420           return JTokenType.Array;
421         case JsonToken.EndConstructor:
422           return JTokenType.Constructor;
423         default:
424           throw new JsonReaderException("Not a valid close JsonToken: {0}".FormatWith(CultureInfo.InvariantCulture, token));
425       }
426     }
427
428     /// <summary>
429     /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
430     /// </summary>
431     void IDisposable.Dispose()
432     {
433       Dispose(true);
434     }
435
436     /// <summary>
437     /// Releases unmanaged and - optionally - managed resources
438     /// </summary>
439     /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
440     protected virtual void Dispose(bool disposing)
441     {
442       if (_currentState != State.Closed && disposing)
443         Close();
444     }
445
446     /// <summary>
447     /// Changes the <see cref="State"/> to Closed. 
448     /// </summary>
449     public virtual void Close()
450     {
451       _currentState = State.Closed;
452       _token = JsonToken.None;
453       _value = null;
454       _valueType = null;
455     }
456   }
457 }