Statistics
| Branch: | Revision:

root / trunk / Libraries / Json40r2 / Source / Src / Newtonsoft.Json / JsonReader.cs @ d78cbf09

History | View | Annotate | Download (12.7 kB)

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
}