All files
[pithos-ms-client] / trunk / Libraries / Json40r2 / Source / Src / Newtonsoft.Json / JsonWriter.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.Text;
29 using System.IO;
30 using System.Xml;
31 using Newtonsoft.Json.Utilities;
32 using Newtonsoft.Json.Linq;
33 using System.Globalization;
34
35 namespace Newtonsoft.Json
36 {
37   /// <summary>
38   /// Specifies the state of the <see cref="JsonWriter"/>.
39   /// </summary>
40   public enum WriteState
41   {
42     /// <summary>
43     /// An exception has been thrown, which has left the <see cref="JsonWriter"/> in an invalid state.
44     /// You may call the <see cref="JsonWriter.Close"/> method to put the <see cref="JsonWriter"/> in the <c>Closed</c> state.
45     /// Any other <see cref="JsonWriter"/> method calls results in an <see cref="InvalidOperationException"/> being thrown. 
46     /// </summary>
47     Error,
48     /// <summary>
49     /// The <see cref="JsonWriter.Close"/> method has been called. 
50     /// </summary>
51     Closed,
52     /// <summary>
53     /// An object is being written. 
54     /// </summary>
55     Object,
56     /// <summary>
57     /// A array is being written.
58     /// </summary>
59     Array,
60     /// <summary>
61     /// A constructor is being written.
62     /// </summary>
63     Constructor,
64     /// <summary>
65     /// A property is being written.
66     /// </summary>
67     Property,
68     /// <summary>
69     /// A write method has not been called.
70     /// </summary>
71     Start
72   }
73
74   /// <summary>
75   /// Specifies formatting options for the <see cref="JsonTextWriter"/>.
76   /// </summary>
77   public enum Formatting
78   {
79     /// <summary>
80     /// No special formatting is applied. This is the default.
81     /// </summary>
82     None,
83     /// <summary>
84     /// Causes child objects to be indented according to the <see cref="JsonTextWriter.Indentation"/> and <see cref="JsonTextWriter.IndentChar"/> settings.
85     /// </summary>
86     Indented
87   }
88
89   /// <summary>
90   /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data.
91   /// </summary>
92   public abstract class JsonWriter : IDisposable
93   {
94     private enum State
95     {
96       Start,
97       Property,
98       ObjectStart,
99       Object,
100       ArrayStart,
101       Array,
102       ConstructorStart,
103       Constructor,
104       Bytes,
105       Closed,
106       Error
107     }
108
109     // array that gives a new state based on the current state an the token being written
110     private static readonly State[][] stateArray = new[] {
111 //                      Start                   PropertyName            ObjectStart         Object            ArrayStart              Array                   ConstructorStart        Constructor             Closed          Error
112 //                        
113 /* None             */new[]{ State.Error,            State.Error,            State.Error,        State.Error,      State.Error,            State.Error,            State.Error,            State.Error,            State.Error,    State.Error },
114 /* StartObject      */new[]{ State.ObjectStart,      State.ObjectStart,      State.Error,        State.Error,      State.ObjectStart,      State.ObjectStart,      State.ObjectStart,      State.ObjectStart,      State.Error,    State.Error },
115 /* StartArray       */new[]{ State.ArrayStart,       State.ArrayStart,       State.Error,        State.Error,      State.ArrayStart,       State.ArrayStart,       State.ArrayStart,       State.ArrayStart,       State.Error,    State.Error },
116 /* StartConstructor */new[]{ State.ConstructorStart, State.ConstructorStart, State.Error,        State.Error,      State.ConstructorStart, State.ConstructorStart, State.ConstructorStart, State.ConstructorStart, State.Error,    State.Error },
117 /* StartProperty    */new[]{ State.Property,         State.Error,            State.Property,     State.Property,   State.Error,            State.Error,            State.Error,            State.Error,            State.Error,    State.Error },
118 /* Comment          */new[]{ State.Start,            State.Property,         State.ObjectStart,  State.Object,     State.ArrayStart,       State.Array,            State.Constructor,      State.Constructor,      State.Error,    State.Error },
119 /* Raw              */new[]{ State.Start,            State.Property,         State.ObjectStart,  State.Object,     State.ArrayStart,       State.Array,            State.Constructor,      State.Constructor,      State.Error,    State.Error },
120 /* Value            */new[]{ State.Start,            State.Object,           State.Error,        State.Error,      State.Array,            State.Array,            State.Constructor,      State.Constructor,      State.Error,    State.Error },
121                 };
122
123     private int _top;
124
125     private readonly List<JTokenType> _stack;
126     private State _currentState;
127     private Formatting _formatting;
128
129     /// <summary>
130     /// Gets or sets a value indicating whether the underlying stream or
131     /// <see cref="TextReader"/> should be closed when the writer is closed.
132     /// </summary>
133     /// <value>
134     /// true to close the underlying stream or <see cref="TextReader"/> when
135     /// the writer is closed; otherwise false. The default is true.
136     /// </value>
137     public bool CloseOutput { get; set; }
138
139     /// <summary>
140     /// Gets the top.
141     /// </summary>
142     /// <value>The top.</value>
143     protected internal int Top
144     {
145       get { return _top; }
146     }
147
148     /// <summary>
149     /// Gets the state of the writer.
150     /// </summary>
151     public WriteState WriteState
152     {
153       get
154       {
155         switch (_currentState)
156         {
157           case State.Error:
158             return WriteState.Error;
159           case State.Closed:
160             return WriteState.Closed;
161           case State.Object:
162           case State.ObjectStart:
163             return WriteState.Object;
164           case State.Array:
165           case State.ArrayStart:
166             return WriteState.Array;
167           case State.Constructor:
168           case State.ConstructorStart:
169             return WriteState.Constructor;
170           case State.Property:
171             return WriteState.Property;
172           case State.Start:
173             return WriteState.Start;
174           default:
175             throw new JsonWriterException("Invalid state: " + _currentState);
176         }
177       }
178     }
179
180     /// <summary>
181     /// Indicates how the output is formatted.
182     /// </summary>
183     public Formatting Formatting
184     {
185       get { return _formatting; }
186       set { _formatting = value; }
187     }
188
189     /// <summary>
190     /// Creates an instance of the <c>JsonWriter</c> class. 
191     /// </summary>
192     protected JsonWriter()
193     {
194       _stack = new List<JTokenType>(8);
195       _stack.Add(JTokenType.None);
196       _currentState = State.Start;
197       _formatting = Formatting.None;
198
199       CloseOutput = true;
200     }
201
202     private void Push(JTokenType value)
203     {
204       _top++;
205       if (_stack.Count <= _top)
206         _stack.Add(value);
207       else
208         _stack[_top] = value;
209     }
210
211     private JTokenType Pop()
212     {
213       JTokenType value = Peek();
214       _top--;
215
216       return value;
217     }
218
219     private JTokenType Peek()
220     {
221       return _stack[_top];
222     }
223
224     /// <summary>
225     /// Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream.
226     /// </summary>
227     public abstract void Flush();
228
229     /// <summary>
230     /// Closes this stream and the underlying stream.
231     /// </summary>
232     public virtual void Close()
233     {
234       AutoCompleteAll();
235     }
236
237     /// <summary>
238     /// Writes the beginning of a Json object.
239     /// </summary>
240     public virtual void WriteStartObject()
241     {
242       AutoComplete(JsonToken.StartObject);
243       Push(JTokenType.Object);
244     }
245
246     /// <summary>
247     /// Writes the end of a Json object.
248     /// </summary>
249     public void WriteEndObject()
250     {
251       AutoCompleteClose(JsonToken.EndObject);
252     }
253
254     /// <summary>
255     /// Writes the beginning of a Json array.
256     /// </summary>
257     public virtual void WriteStartArray()
258     {
259       AutoComplete(JsonToken.StartArray);
260       Push(JTokenType.Array);
261     }
262
263     /// <summary>
264     /// Writes the end of an array.
265     /// </summary>
266     public void WriteEndArray()
267     {
268       AutoCompleteClose(JsonToken.EndArray);
269     }
270
271     /// <summary>
272     /// Writes the start of a constructor with the given name.
273     /// </summary>
274     /// <param name="name">The name of the constructor.</param>
275     public virtual void WriteStartConstructor(string name)
276     {
277       AutoComplete(JsonToken.StartConstructor);
278       Push(JTokenType.Constructor);
279     }
280
281     /// <summary>
282     /// Writes the end constructor.
283     /// </summary>
284     public void WriteEndConstructor()
285     {
286       AutoCompleteClose(JsonToken.EndConstructor);
287     }
288
289     /// <summary>
290     /// Writes the property name of a name/value pair on a Json object.
291     /// </summary>
292     /// <param name="name">The name of the property.</param>
293     public virtual void WritePropertyName(string name)
294     {
295       AutoComplete(JsonToken.PropertyName);
296     }
297
298     /// <summary>
299     /// Writes the end of the current Json object or array.
300     /// </summary>
301     public void WriteEnd()
302     {
303       WriteEnd(Peek());
304     }
305
306     /// <summary>
307     /// Writes the current <see cref="JsonReader"/> token.
308     /// </summary>
309     /// <param name="reader">The <see cref="JsonReader"/> to read the token from.</param>
310     public void WriteToken(JsonReader reader)
311     {
312       ValidationUtils.ArgumentNotNull(reader, "reader");
313
314       int initialDepth;
315
316       if (reader.TokenType == JsonToken.None)
317         initialDepth = -1;
318       else if (!IsStartToken(reader.TokenType))
319         initialDepth = reader.Depth + 1;
320       else
321         initialDepth = reader.Depth;
322
323       WriteToken(reader, initialDepth);
324     }
325
326     internal void WriteToken(JsonReader reader, int initialDepth)
327     {
328       do
329       {
330         switch (reader.TokenType)
331         {
332           case JsonToken.None:
333             // read to next
334             break;
335           case JsonToken.StartObject:
336             WriteStartObject();
337             break;
338           case JsonToken.StartArray:
339             WriteStartArray();
340             break;
341           case JsonToken.StartConstructor:
342             string constructorName = reader.Value.ToString();
343             // write a JValue date when the constructor is for a date
344             if (string.Compare(constructorName, "Date", StringComparison.Ordinal) == 0)
345               WriteConstructorDate(reader);
346             else
347               WriteStartConstructor(reader.Value.ToString());
348             break;
349           case JsonToken.PropertyName:
350             WritePropertyName(reader.Value.ToString());
351             break;
352           case JsonToken.Comment:
353             WriteComment(reader.Value.ToString());
354             break;
355           case JsonToken.Integer:
356             WriteValue((long)reader.Value);
357             break;
358           case JsonToken.Float:
359             WriteValue((double)reader.Value);
360             break;
361           case JsonToken.String:
362             WriteValue(reader.Value.ToString());
363             break;
364           case JsonToken.Boolean:
365             WriteValue((bool)reader.Value);
366             break;
367           case JsonToken.Null:
368             WriteNull();
369             break;
370           case JsonToken.Undefined:
371             WriteUndefined();
372             break;
373           case JsonToken.EndObject:
374             WriteEndObject();
375             break;
376           case JsonToken.EndArray:
377             WriteEndArray();
378             break;
379           case JsonToken.EndConstructor:
380             WriteEndConstructor();
381             break;
382           case JsonToken.Date:
383             WriteValue((DateTime)reader.Value);
384             break;
385           case JsonToken.Raw:
386             WriteRawValue((string)reader.Value);
387             break;
388           case JsonToken.Bytes:
389             WriteValue((byte[])reader.Value);
390             break;
391           default:
392             throw MiscellaneousUtils.CreateArgumentOutOfRangeException("TokenType", reader.TokenType, "Unexpected token type.");
393         }
394       }
395       while (
396         // stop if we have reached the end of the token being read
397         initialDepth - 1 < reader.Depth - (IsEndToken(reader.TokenType) ? 1 : 0)
398         && reader.Read());
399     }
400
401     private void WriteConstructorDate(JsonReader reader)
402     {
403       if (!reader.Read())
404         throw new Exception("Unexpected end while reading date constructor.");
405       if (reader.TokenType != JsonToken.Integer)
406         throw new Exception("Unexpected token while reading date constructor. Expected Integer, got " + reader.TokenType);
407
408       long ticks = (long)reader.Value;
409       DateTime date = JsonConvert.ConvertJavaScriptTicksToDateTime(ticks);
410
411       if (!reader.Read())
412         throw new Exception("Unexpected end while reading date constructor.");
413       if (reader.TokenType != JsonToken.EndConstructor)
414         throw new Exception("Unexpected token while reading date constructor. Expected EndConstructor, got " + reader.TokenType);
415
416       WriteValue(date);
417     }
418
419     private bool IsEndToken(JsonToken token)
420     {
421       switch (token)
422       {
423         case JsonToken.EndObject:
424         case JsonToken.EndArray:
425         case JsonToken.EndConstructor:
426           return true;
427         default:
428           return false;
429       }
430     }
431
432     private bool IsStartToken(JsonToken token)
433     {
434       switch (token)
435       {
436         case JsonToken.StartObject:
437         case JsonToken.StartArray:
438         case JsonToken.StartConstructor:
439           return true;
440         default:
441           return false;
442       }
443     }
444
445     private void WriteEnd(JTokenType type)
446     {
447       switch (type)
448       {
449         case JTokenType.Object:
450           WriteEndObject();
451           break;
452         case JTokenType.Array:
453           WriteEndArray();
454           break;
455         case JTokenType.Constructor:
456           WriteEndConstructor();
457           break;
458         default:
459           throw new JsonWriterException("Unexpected type when writing end: " + type);
460       }
461     }
462
463     private void AutoCompleteAll()
464     {
465       while (_top > 0)
466       {
467         WriteEnd();
468       }
469     }
470
471     private JTokenType GetTypeForCloseToken(JsonToken token)
472     {
473       switch (token)
474       {
475         case JsonToken.EndObject:
476           return JTokenType.Object;
477         case JsonToken.EndArray:
478           return JTokenType.Array;
479         case JsonToken.EndConstructor:
480           return JTokenType.Constructor;
481         default:
482           throw new JsonWriterException("No type for token: " + token);
483       }
484     }
485
486     private JsonToken GetCloseTokenForType(JTokenType type)
487     {
488       switch (type)
489       {
490         case JTokenType.Object:
491           return JsonToken.EndObject;
492         case JTokenType.Array:
493           return JsonToken.EndArray;
494         case JTokenType.Constructor:
495           return JsonToken.EndConstructor;
496         default:
497           throw new JsonWriterException("No close token for type: " + type);
498       }
499     }
500
501     private void AutoCompleteClose(JsonToken tokenBeingClosed)
502     {
503       // write closing symbol and calculate new state
504
505       int levelsToComplete = 0;
506
507       for (int i = 0; i < _top; i++)
508       {
509         int currentLevel = _top - i;
510
511         if (_stack[currentLevel] == GetTypeForCloseToken(tokenBeingClosed))
512         {
513           levelsToComplete = i + 1;
514           break;
515         }
516       }
517
518       if (levelsToComplete == 0)
519         throw new JsonWriterException("No token to close.");
520
521       for (int i = 0; i < levelsToComplete; i++)
522       {
523         JsonToken token = GetCloseTokenForType(Pop());
524
525         if (_currentState != State.ObjectStart && _currentState != State.ArrayStart)
526           WriteIndent();
527
528         WriteEnd(token);
529       }
530
531       JTokenType currentLevelType = Peek();
532
533       switch (currentLevelType)
534       {
535         case JTokenType.Object:
536           _currentState = State.Object;
537           break;
538         case JTokenType.Array:
539           _currentState = State.Array;
540           break;
541         case JTokenType.Constructor:
542           _currentState = State.Array;
543           break;
544         case JTokenType.None:
545           _currentState = State.Start;
546           break;
547         default:
548           throw new JsonWriterException("Unknown JsonType: " + currentLevelType);
549       }
550     }
551
552     /// <summary>
553     /// Writes the specified end token.
554     /// </summary>
555     /// <param name="token">The end token to write.</param>
556     protected virtual void WriteEnd(JsonToken token)
557     {
558     }
559
560     /// <summary>
561     /// Writes indent characters.
562     /// </summary>
563     protected virtual void WriteIndent()
564     {
565     }
566
567     /// <summary>
568     /// Writes the JSON value delimiter.
569     /// </summary>
570     protected virtual void WriteValueDelimiter()
571     {
572     }
573
574     /// <summary>
575     /// Writes an indent space.
576     /// </summary>
577     protected virtual void WriteIndentSpace()
578     {
579     }
580
581     internal void AutoComplete(JsonToken tokenBeingWritten)
582     {
583       int token;
584
585       switch (tokenBeingWritten)
586       {
587         default:
588           token = (int)tokenBeingWritten;
589           break;
590         case JsonToken.Integer:
591         case JsonToken.Float:
592         case JsonToken.String:
593         case JsonToken.Boolean:
594         case JsonToken.Null:
595         case JsonToken.Undefined:
596         case JsonToken.Date:
597         case JsonToken.Bytes:
598           // a value is being written
599           token = 7;
600           break;
601       }
602
603       // gets new state based on the current state and what is being written
604       State newState = stateArray[token][(int)_currentState];
605
606       if (newState == State.Error)
607         throw new JsonWriterException("Token {0} in state {1} would result in an invalid JavaScript object.".FormatWith(CultureInfo.InvariantCulture, tokenBeingWritten.ToString(), _currentState.ToString()));
608
609       if ((_currentState == State.Object || _currentState == State.Array || _currentState == State.Constructor) && tokenBeingWritten != JsonToken.Comment)
610       {
611         WriteValueDelimiter();
612       }
613       else if (_currentState == State.Property)
614       {
615         if (_formatting == Formatting.Indented)
616           WriteIndentSpace();
617       }
618
619       WriteState writeState = WriteState;
620
621       // don't indent a property when it is the first token to be written (i.e. at the start)
622       if ((tokenBeingWritten == JsonToken.PropertyName && writeState != WriteState.Start) ||
623         writeState == WriteState.Array || writeState == WriteState.Constructor)
624       {
625         WriteIndent();
626       }
627
628       _currentState = newState;
629     }
630
631     #region WriteValue methods
632     /// <summary>
633     /// Writes a null value.
634     /// </summary>
635     public virtual void WriteNull()
636     {
637       AutoComplete(JsonToken.Null);
638     }
639
640     /// <summary>
641     /// Writes an undefined value.
642     /// </summary>
643     public virtual void WriteUndefined()
644     {
645       AutoComplete(JsonToken.Undefined);
646     }
647
648     /// <summary>
649     /// Writes raw JSON without changing the writer's state.
650     /// </summary>
651     /// <param name="json">The raw JSON to write.</param>
652     public virtual void WriteRaw(string json)
653     {
654     }
655
656     /// <summary>
657     /// Writes raw JSON where a value is expected and updates the writer's state.
658     /// </summary>
659     /// <param name="json">The raw JSON to write.</param>
660     public virtual void WriteRawValue(string json)
661     {
662       // hack. want writer to change state as if a value had been written
663       AutoComplete(JsonToken.Undefined);
664       WriteRaw(json);
665     }
666
667     /// <summary>
668     /// Writes a <see cref="String"/> value.
669     /// </summary>
670     /// <param name="value">The <see cref="String"/> value to write.</param>
671     public virtual void WriteValue(string value)
672     {
673       AutoComplete(JsonToken.String);
674     }
675
676     /// <summary>
677     /// Writes a <see cref="Int32"/> value.
678     /// </summary>
679     /// <param name="value">The <see cref="Int32"/> value to write.</param>
680     public virtual void WriteValue(int value)
681     {
682       AutoComplete(JsonToken.Integer);
683     }
684
685     /// <summary>
686     /// Writes a <see cref="UInt32"/> value.
687     /// </summary>
688     /// <param name="value">The <see cref="UInt32"/> value to write.</param>
689     [CLSCompliant(false)]
690     public virtual void WriteValue(uint value)
691     {
692       AutoComplete(JsonToken.Integer);
693     }
694
695     /// <summary>
696     /// Writes a <see cref="Int64"/> value.
697     /// </summary>
698     /// <param name="value">The <see cref="Int64"/> value to write.</param>
699     public virtual void WriteValue(long value)
700     {
701       AutoComplete(JsonToken.Integer);
702     }
703
704     /// <summary>
705     /// Writes a <see cref="UInt64"/> value.
706     /// </summary>
707     /// <param name="value">The <see cref="UInt64"/> value to write.</param>
708     [CLSCompliant(false)]
709     public virtual void WriteValue(ulong value)
710     {
711       AutoComplete(JsonToken.Integer);
712     }
713
714     /// <summary>
715     /// Writes a <see cref="Single"/> value.
716     /// </summary>
717     /// <param name="value">The <see cref="Single"/> value to write.</param>
718     public virtual void WriteValue(float value)
719     {
720       AutoComplete(JsonToken.Float);
721     }
722
723     /// <summary>
724     /// Writes a <see cref="Double"/> value.
725     /// </summary>
726     /// <param name="value">The <see cref="Double"/> value to write.</param>
727     public virtual void WriteValue(double value)
728     {
729       AutoComplete(JsonToken.Float);
730     }
731
732     /// <summary>
733     /// Writes a <see cref="Boolean"/> value.
734     /// </summary>
735     /// <param name="value">The <see cref="Boolean"/> value to write.</param>
736     public virtual void WriteValue(bool value)
737     {
738       AutoComplete(JsonToken.Boolean);
739     }
740
741     /// <summary>
742     /// Writes a <see cref="Int16"/> value.
743     /// </summary>
744     /// <param name="value">The <see cref="Int16"/> value to write.</param>
745     public virtual void WriteValue(short value)
746     {
747       AutoComplete(JsonToken.Integer);
748     }
749
750     /// <summary>
751     /// Writes a <see cref="UInt16"/> value.
752     /// </summary>
753     /// <param name="value">The <see cref="UInt16"/> value to write.</param>
754     [CLSCompliant(false)]
755     public virtual void WriteValue(ushort value)
756     {
757       AutoComplete(JsonToken.Integer);
758     }
759
760     /// <summary>
761     /// Writes a <see cref="Char"/> value.
762     /// </summary>
763     /// <param name="value">The <see cref="Char"/> value to write.</param>
764     public virtual void WriteValue(char value)
765     {
766       AutoComplete(JsonToken.String);
767     }
768
769     /// <summary>
770     /// Writes a <see cref="Byte"/> value.
771     /// </summary>
772     /// <param name="value">The <see cref="Byte"/> value to write.</param>
773     public virtual void WriteValue(byte value)
774     {
775       AutoComplete(JsonToken.Integer);
776     }
777
778     /// <summary>
779     /// Writes a <see cref="SByte"/> value.
780     /// </summary>
781     /// <param name="value">The <see cref="SByte"/> value to write.</param>
782     [CLSCompliant(false)]
783     public virtual void WriteValue(sbyte value)
784     {
785       AutoComplete(JsonToken.Integer);
786     }
787
788     /// <summary>
789     /// Writes a <see cref="Decimal"/> value.
790     /// </summary>
791     /// <param name="value">The <see cref="Decimal"/> value to write.</param>
792     public virtual void WriteValue(decimal value)
793     {
794       AutoComplete(JsonToken.Float);
795     }
796
797     /// <summary>
798     /// Writes a <see cref="DateTime"/> value.
799     /// </summary>
800     /// <param name="value">The <see cref="DateTime"/> value to write.</param>
801     public virtual void WriteValue(DateTime value)
802     {
803       AutoComplete(JsonToken.Date);
804     }
805
806 #if !PocketPC && !NET20
807     /// <summary>
808     /// Writes a <see cref="DateTimeOffset"/> value.
809     /// </summary>
810     /// <param name="value">The <see cref="DateTimeOffset"/> value to write.</param>
811     public virtual void WriteValue(DateTimeOffset value)
812     {
813       AutoComplete(JsonToken.Date);
814     }
815 #endif
816
817     /// <summary>
818     /// Writes a <see cref="Nullable{Int32}"/> value.
819     /// </summary>
820     /// <param name="value">The <see cref="Nullable{Int32}"/> value to write.</param>
821     public virtual void WriteValue(int? value)
822     {
823       if (value == null)
824         WriteNull();
825       else
826         WriteValue(value.Value);
827     }
828
829     /// <summary>
830     /// Writes a <see cref="Nullable{UInt32}"/> value.
831     /// </summary>
832     /// <param name="value">The <see cref="Nullable{UInt32}"/> value to write.</param>
833     [CLSCompliant(false)]
834     public virtual void WriteValue(uint? value)
835     {
836       if (value == null)
837         WriteNull();
838       else
839         WriteValue(value.Value);
840     }
841
842     /// <summary>
843     /// Writes a <see cref="Nullable{Int64}"/> value.
844     /// </summary>
845     /// <param name="value">The <see cref="Nullable{Int64}"/> value to write.</param>
846     public virtual void WriteValue(long? value)
847     {
848       if (value == null)
849         WriteNull();
850       else
851         WriteValue(value.Value);
852     }
853
854     /// <summary>
855     /// Writes a <see cref="Nullable{UInt64}"/> value.
856     /// </summary>
857     /// <param name="value">The <see cref="Nullable{UInt64}"/> value to write.</param>
858     [CLSCompliant(false)]
859     public virtual void WriteValue(ulong? value)
860     {
861       if (value == null)
862         WriteNull();
863       else
864         WriteValue(value.Value);
865     }
866
867     /// <summary>
868     /// Writes a <see cref="Nullable{Single}"/> value.
869     /// </summary>
870     /// <param name="value">The <see cref="Nullable{Single}"/> value to write.</param>
871     public virtual void WriteValue(float? value)
872     {
873       if (value == null)
874         WriteNull();
875       else
876         WriteValue(value.Value);
877     }
878
879     /// <summary>
880     /// Writes a <see cref="Nullable{Double}"/> value.
881     /// </summary>
882     /// <param name="value">The <see cref="Nullable{Double}"/> value to write.</param>
883     public virtual void WriteValue(double? value)
884     {
885       if (value == null)
886         WriteNull();
887       else
888         WriteValue(value.Value);
889     }
890
891     /// <summary>
892     /// Writes a <see cref="Nullable{Boolean}"/> value.
893     /// </summary>
894     /// <param name="value">The <see cref="Nullable{Boolean}"/> value to write.</param>
895     public virtual void WriteValue(bool? value)
896     {
897       if (value == null)
898         WriteNull();
899       else
900         WriteValue(value.Value);
901     }
902
903     /// <summary>
904     /// Writes a <see cref="Nullable{Int16}"/> value.
905     /// </summary>
906     /// <param name="value">The <see cref="Nullable{Int16}"/> value to write.</param>
907     public virtual void WriteValue(short? value)
908     {
909       if (value == null)
910         WriteNull();
911       else
912         WriteValue(value.Value);
913     }
914
915     /// <summary>
916     /// Writes a <see cref="Nullable{UInt16}"/> value.
917     /// </summary>
918     /// <param name="value">The <see cref="Nullable{UInt16}"/> value to write.</param>
919     [CLSCompliant(false)]
920     public virtual void WriteValue(ushort? value)
921     {
922       if (value == null)
923         WriteNull();
924       else
925         WriteValue(value.Value);
926     }
927
928     /// <summary>
929     /// Writes a <see cref="Nullable{Char}"/> value.
930     /// </summary>
931     /// <param name="value">The <see cref="Nullable{Char}"/> value to write.</param>
932     public virtual void WriteValue(char? value)
933     {
934       if (value == null)
935         WriteNull();
936       else
937         WriteValue(value.Value);
938     }
939
940     /// <summary>
941     /// Writes a <see cref="Nullable{Byte}"/> value.
942     /// </summary>
943     /// <param name="value">The <see cref="Nullable{Byte}"/> value to write.</param>
944     public virtual void WriteValue(byte? value)
945     {
946       if (value == null)
947         WriteNull();
948       else
949         WriteValue(value.Value);
950     }
951
952     /// <summary>
953     /// Writes a <see cref="Nullable{SByte}"/> value.
954     /// </summary>
955     /// <param name="value">The <see cref="Nullable{SByte}"/> value to write.</param>
956     [CLSCompliant(false)]
957     public virtual void WriteValue(sbyte? value)
958     {
959       if (value == null)
960         WriteNull();
961       else
962         WriteValue(value.Value);
963     }
964
965     /// <summary>
966     /// Writes a <see cref="Nullable{Decimal}"/> value.
967     /// </summary>
968     /// <param name="value">The <see cref="Nullable{Decimal}"/> value to write.</param>
969     public virtual void WriteValue(decimal? value)
970     {
971       if (value == null)
972         WriteNull();
973       else
974         WriteValue(value.Value);
975     }
976
977     /// <summary>
978     /// Writes a <see cref="Nullable{DateTime}"/> value.
979     /// </summary>
980     /// <param name="value">The <see cref="Nullable{DateTime}"/> value to write.</param>
981     public virtual void WriteValue(DateTime? value)
982     {
983       if (value == null)
984         WriteNull();
985       else
986         WriteValue(value.Value);
987     }
988
989 #if !PocketPC && !NET20
990     /// <summary>
991     /// Writes a <see cref="Nullable{DateTimeOffset}"/> value.
992     /// </summary>
993     /// <param name="value">The <see cref="Nullable{DateTimeOffset}"/> value to write.</param>
994     public virtual void WriteValue(DateTimeOffset? value)
995     {
996       if (value == null)
997         WriteNull();
998       else
999         WriteValue(value.Value);
1000     }
1001 #endif
1002
1003     /// <summary>
1004     /// Writes a <see cref="T:Byte[]"/> value.
1005     /// </summary>
1006     /// <param name="value">The <see cref="T:Byte[]"/> value to write.</param>
1007     public virtual void WriteValue(byte[] value)
1008     {
1009       if (value == null)
1010         WriteNull();
1011       else
1012         AutoComplete(JsonToken.Bytes);
1013     }
1014
1015     /// <summary>
1016     /// Writes a <see cref="Object"/> value.
1017     /// An error will raised if the value cannot be written as a single JSON token.
1018     /// </summary>
1019     /// <param name="value">The <see cref="Object"/> value to write.</param>
1020     public virtual void WriteValue(object value)
1021     {
1022       if (value == null)
1023       {
1024         WriteNull();
1025         return;
1026       }
1027       else if (value is IConvertible)
1028       {
1029         IConvertible convertible = value as IConvertible;
1030
1031         switch (convertible.GetTypeCode())
1032         {
1033           case TypeCode.String:
1034             WriteValue(convertible.ToString(CultureInfo.InvariantCulture));
1035             return;
1036           case TypeCode.Char:
1037             WriteValue(convertible.ToChar(CultureInfo.InvariantCulture));
1038             return;
1039           case TypeCode.Boolean:
1040             WriteValue(convertible.ToBoolean(CultureInfo.InvariantCulture));
1041             return;
1042           case TypeCode.SByte:
1043             WriteValue(convertible.ToSByte(CultureInfo.InvariantCulture));
1044             return;
1045           case TypeCode.Int16:
1046             WriteValue(convertible.ToInt16(CultureInfo.InvariantCulture));
1047             return;
1048           case TypeCode.UInt16:
1049             WriteValue(convertible.ToUInt16(CultureInfo.InvariantCulture));
1050             return;
1051           case TypeCode.Int32:
1052             WriteValue(convertible.ToInt32(CultureInfo.InvariantCulture));
1053             return;
1054           case TypeCode.Byte:
1055             WriteValue(convertible.ToByte(CultureInfo.InvariantCulture));
1056             return;
1057           case TypeCode.UInt32:
1058             WriteValue(convertible.ToUInt32(CultureInfo.InvariantCulture));
1059             return;
1060           case TypeCode.Int64:
1061             WriteValue(convertible.ToInt64(CultureInfo.InvariantCulture));
1062             return;
1063           case TypeCode.UInt64:
1064             WriteValue(convertible.ToUInt64(CultureInfo.InvariantCulture));
1065             return;
1066           case TypeCode.Single:
1067             WriteValue(convertible.ToSingle(CultureInfo.InvariantCulture));
1068             return;
1069           case TypeCode.Double:
1070             WriteValue(convertible.ToDouble(CultureInfo.InvariantCulture));
1071             return;
1072           case TypeCode.DateTime:
1073             WriteValue(convertible.ToDateTime(CultureInfo.InvariantCulture));
1074             return;
1075           case TypeCode.Decimal:
1076             WriteValue(convertible.ToDecimal(CultureInfo.InvariantCulture));
1077             return;
1078           case TypeCode.DBNull:
1079             WriteNull();
1080             return;
1081         }
1082       }
1083 #if !PocketPC && !NET20
1084       else if (value is DateTimeOffset)
1085       {
1086         WriteValue((DateTimeOffset)value);
1087         return;
1088       }
1089 #endif
1090       else if (value is byte[])
1091       {
1092         WriteValue((byte[])value);
1093         return;
1094       }
1095
1096       throw new ArgumentException("Unsupported type: {0}. Use the JsonSerializer class to get the object's JSON representation.".FormatWith(CultureInfo.InvariantCulture, value.GetType()));
1097     }
1098     #endregion
1099
1100     /// <summary>
1101     /// Writes out a comment <code>/*...*/</code> containing the specified text. 
1102     /// </summary>
1103     /// <param name="text">Text to place inside the comment.</param>
1104     public virtual void WriteComment(string text)
1105     {
1106       AutoComplete(JsonToken.Comment);
1107     }
1108
1109     /// <summary>
1110     /// Writes out the given white space.
1111     /// </summary>
1112     /// <param name="ws">The string of white space characters.</param>
1113     public virtual void WriteWhitespace(string ws)
1114     {
1115       if (ws != null)
1116       {
1117         if (!StringUtils.IsWhiteSpace(ws))
1118           throw new JsonWriterException("Only white space characters should be used.");
1119       }
1120     }
1121
1122
1123     void IDisposable.Dispose()
1124     {
1125       Dispose(true);
1126     }
1127
1128     private void Dispose(bool disposing)
1129     {
1130       if (WriteState != WriteState.Closed)
1131         Close();
1132     }
1133   }
1134 }