2010-01-20 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mcs / class / System.XML / System.Xml / XmlConvert.cs
1 //
2 // System.Xml.XmlConvert
3 //
4 // Authors:
5 //      Dwivedi, Ajay kumar (Adwiv@Yahoo.com)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //      Alan Tam Siu Lung (Tam@SiuLung.com)
8 //      Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
9 //
10 // (C) 2002 Ximian, Inc (http://www.ximian.com)
11 //
12
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 using System;
34 using System.IO;
35 using System.Text;
36 using System.Globalization;
37 using System.Xml.Schema;
38
39 namespace System.Xml {
40
41         public class XmlConvert {
42
43                 const string encodedColon = "_x003A_";
44                 const NumberStyles floatStyle = NumberStyles.AllowCurrencySymbol |
45                         NumberStyles.AllowExponent | 
46                         NumberStyles.AllowDecimalPoint |
47                         NumberStyles.AllowLeadingSign |
48                         NumberStyles.AllowLeadingWhite |
49                         NumberStyles.AllowTrailingWhite;
50                 
51                 const NumberStyles integerStyle = NumberStyles.Integer |
52                         NumberStyles.AllowLeadingWhite |
53                         NumberStyles.AllowTrailingWhite;
54
55                 static readonly string [] datetimeFormats = {
56                   // dateTime
57 #if NET_2_0
58                   "yyyy-MM-ddTHH:mm:sszzz",
59                   "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
60                   "yyyy-MM-ddTHH:mm:ssZ",
61                   "yyyy-MM-ddTHH:mm:ss.FFFFFFFZ",
62                   "yyyy-MM-ddTHH:mm:ss",
63                   "yyyy-MM-ddTHH:mm:ss.FFFFFFF",
64                   "HH:mm:ss",
65                   "HH:mm:ss.FFFFFFF",
66                   "HH:mm:sszzz",
67                   "HH:mm:ss.FFFFFFFzzz",
68                   "HH:mm:ssZ",
69                   "HH:mm:ss.FFFFFFFZ",
70 #else // it is not required in trunk but should make it easy to backport...
71                   "yyyy-MM-ddTHH:mm:sszzz",
72                   "yyyy-MM-ddTHH:mm:ss.fzzz",
73                   "yyyy-MM-ddTHH:mm:ss.ffzzz",
74                   "yyyy-MM-ddTHH:mm:ss.fffzzz",
75                   "yyyy-MM-ddTHH:mm:ss.ffffzzz",
76                   "yyyy-MM-ddTHH:mm:ss.fffffzzz",
77                   "yyyy-MM-ddTHH:mm:ss.ffffffzzz",
78                   "yyyy-MM-ddTHH:mm:ss.fffffffzzz",
79                   "yyyy-MM-ddTHH:mm:ssZ",
80                   "yyyy-MM-ddTHH:mm:ss.fZ",
81                   "yyyy-MM-ddTHH:mm:ss.ffZ",
82                   "yyyy-MM-ddTHH:mm:ss.fffZ",
83                   "yyyy-MM-ddTHH:mm:ss.ffffZ",
84                   "yyyy-MM-ddTHH:mm:ss.fffffZ",
85                   "yyyy-MM-ddTHH:mm:ss.ffffffZ",
86                   "yyyy-MM-ddTHH:mm:ss.fffffffZ",
87                   "yyyy-MM-ddTHH:mm:ss",
88                   "yyyy-MM-ddTHH:mm:ss.f",
89                   "yyyy-MM-ddTHH:mm:ss.ff",
90                   "yyyy-MM-ddTHH:mm:ss.fff",
91                   "yyyy-MM-ddTHH:mm:ss.ffff",
92                   "yyyy-MM-ddTHH:mm:ss.fffff",
93                   "yyyy-MM-ddTHH:mm:ss.ffffff",
94                   "yyyy-MM-ddTHH:mm:ss.fffffff",
95                   // time
96                   "HH:mm:ss",
97                   "HH:mm:ss.f",
98                   "HH:mm:ss.ff",
99                   "HH:mm:ss.fff",
100                   "HH:mm:ss.ffff",
101                   "HH:mm:ss.fffff",
102                   "HH:mm:ss.ffffff",
103                   "HH:mm:ss.fffffff",
104                   "HH:mm:sszzz",
105                   "HH:mm:ss.fzzz",
106                   "HH:mm:ss.ffzzz",
107                   "HH:mm:ss.fffzzz",
108                   "HH:mm:ss.ffffzzz",
109                   "HH:mm:ss.fffffzzz",
110                   "HH:mm:ss.ffffffzzz",
111                   "HH:mm:ss.fffffffzzz",
112                   "HH:mm:ssZ",
113                   "HH:mm:ss.fZ",
114                   "HH:mm:ss.ffZ",
115                   "HH:mm:ss.fffZ",
116                   "HH:mm:ss.ffffZ",
117                   "HH:mm:ss.fffffZ",
118                   "HH:mm:ss.ffffffZ",
119                   "HH:mm:ss.fffffffZ",
120 #endif
121                   // date
122                   "yyyy-MM-dd",
123                   "yyyy-MM-ddzzz",
124                   "yyyy-MM-ddZ",
125                   // gYearMonth
126                   "yyyy-MM",
127                   "yyyy-MMzzz",
128                   "yyyy-MMZ",
129                   // gYear
130                   "yyyy",
131                   "yyyyzzz",
132                   "yyyyZ",
133                   // gMonthDay
134                   "--MM-dd",
135                   "--MM-ddzzz",
136                   "--MM-ddZ",
137                   // gDay
138                   "---dd",
139                   "---ddzzz",
140                   "---ddZ",
141                 };
142
143 #if NET_2_0
144                 static readonly string [] defaultDateTimeFormats = new string [] {
145                         "yyyy-MM-ddTHH:mm:ss", // dateTime(1)
146                         "yyyy-MM-ddTHH:mm:ss.FFFFFFF", // dateTime(2)
147                         "yyyy-MM-dd", // date
148                         "HH:mm:ss", // time
149                         "yyyy-MM", // gYearMonth
150                         "yyyy", // gYear
151                         "--MM-dd", // gMonthDay
152                         "---dd", // gDay
153                         };
154
155                 static readonly string [] roundtripDateTimeFormats;
156                 static readonly string [] localDateTimeFormats;
157                 static readonly string [] utcDateTimeFormats;
158                 static readonly string [] unspecifiedDateTimeFormats;
159
160                 static XmlConvert ()
161                 {
162                         int l = defaultDateTimeFormats.Length;
163                         roundtripDateTimeFormats = new string [l];
164                         localDateTimeFormats = new string [l];
165                         utcDateTimeFormats = new string [l * 3];
166                         unspecifiedDateTimeFormats = new string [l * 4];
167                         for (int i = 0; i < l; i++) {
168                                 string s = defaultDateTimeFormats [i];
169                                 localDateTimeFormats [i] = s + "zzz";
170                                 roundtripDateTimeFormats [i] = s + 'K';
171                                 utcDateTimeFormats [i * 3] = s;
172                                 utcDateTimeFormats [i * 3 + 1] = s + 'Z';
173                                 utcDateTimeFormats [i * 3 + 2] = s + "zzz";
174                                 unspecifiedDateTimeFormats [i * 4] = s;
175                                 unspecifiedDateTimeFormats [i * 4 + 1] = localDateTimeFormats [i];
176                                 unspecifiedDateTimeFormats [i * 4 + 2] = roundtripDateTimeFormats [i];
177                                 unspecifiedDateTimeFormats [i * 4 + 3] = utcDateTimeFormats [i];
178                         }
179                 }
180 #endif
181                 static DateTimeStyles _defaultStyle = DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite;
182                 
183                 public XmlConvert()
184                 {}
185
186                 private static string TryDecoding (string s)
187                 {
188                         if (s == null || s.Length < 6)
189                                 return s;
190
191                         char c = '\uFFFF';
192                         try {
193                                 c = (char) Int32.Parse (s.Substring (1, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
194                         } catch {
195                                 return s [0] + DecodeName (s.Substring (1));
196                         }
197                         
198                         if (s.Length == 6)
199                                 return c.ToString ();
200                         return c + DecodeName (s.Substring (6));
201                 }
202                 
203                 public static string DecodeName (string name)
204                 {
205                         if (name == null || name.Length == 0)
206                                 return name;
207
208                         int pos = name.IndexOf ('_');
209                         if (pos == -1 || pos + 6 >= name.Length)
210                                 return name;
211
212                         if ((name [pos + 1] != 'X' && name [pos + 1] != 'x') || name [pos + 6] != '_')
213                                 return name [0] + DecodeName (name.Substring (1));
214
215                         return name.Substring (0, pos) + TryDecoding (name.Substring (pos + 1));
216                 }
217
218                 public static string EncodeLocalName (string name)
219                 {
220                         if (name == null)
221                                 return name;
222
223                         string encoded = EncodeName (name);
224                         int pos = encoded.IndexOf (':');
225                         if (pos == -1)
226                                 return encoded;
227                         return encoded.Replace (":", encodedColon);
228                 }
229
230                 internal static bool IsInvalid (char c, bool firstOnlyLetter)
231                 {
232                         if (c == ':') // Special case. allowed in EncodeName, but encoded in EncodeLocalName
233                                 return false;
234                         
235                         if (firstOnlyLetter)
236                                 return !XmlChar.IsFirstNameChar (c);
237                         else
238                                 return !XmlChar.IsNameChar (c);
239                 }
240
241                 private static string EncodeName (string name, bool nmtoken)
242                 {
243                         if (name == null || name.Length == 0)
244                                 return name;
245
246                         StringBuilder sb = new StringBuilder ();
247                         int length = name.Length;
248                         for (int i = 0; i < length; i++) {
249                                 char c = name [i];
250                                 if (IsInvalid (c, i == 0 && !nmtoken))
251                                         sb.AppendFormat ("_x{0:X4}_", (int) c);
252                                 else if (c == '_' && i + 6 < length && name [i+1] == 'x' && name [i + 6] == '_')
253                                         sb.Append ("_x005F_");
254                                 else
255                                         sb.Append (c);
256                         }
257                         return sb.ToString ();
258                 }
259
260                 public static string EncodeName (string name)
261                 {
262                         return EncodeName (name, false);
263                 }
264                 
265                 public static string EncodeNmToken (string name)
266                 {
267                         if (name == String.Empty)
268                                 throw new XmlException ("Invalid NmToken: ''");
269                         return EncodeName (name, true);
270                 }
271
272                 // {true, false, 1, 0}
273                 public static bool ToBoolean(string s)
274                 {
275                         s = s.Trim (XmlChar.WhitespaceChars);
276                         switch(s)
277                         {
278                                 case "1":
279                                         return true;
280                                 case "true":
281                                         return true;
282                                 case "0":
283                                         return false;
284                                 case "false":
285                                         return false;
286                                 default:
287                                         throw new FormatException(s + " is not a valid boolean value");
288                         }
289                 }
290
291                 // LAMESPEC: It has been documented as public, but is marked as internal.
292                 internal static string ToBinHexString (byte [] buffer)
293                 {
294                         StringWriter w = new StringWriter ();
295                         WriteBinHex (buffer, 0, buffer.Length, w);
296                         return w.ToString ();
297                 }
298
299                 internal static void WriteBinHex (byte [] buffer, int index, int count, TextWriter w)
300                 {
301                         if (buffer == null)
302                                 throw new ArgumentNullException ("buffer");
303                         if (index < 0) {
304                                 throw new ArgumentOutOfRangeException (
305 #if !NET_2_1
306                                         "index", index,
307 #endif
308                                         "index must be non negative integer.");
309                         }
310                         if (count < 0) {
311                                 throw new ArgumentOutOfRangeException (
312 #if !NET_2_1
313                                         "count", count,
314 #endif
315                                         "count must be non negative integer.");
316                         }
317                         if (buffer.Length < index + count)
318                                 throw new ArgumentOutOfRangeException ("index and count must be smaller than the length of the buffer.");
319
320                         // Copied from XmlTextWriter.WriteBinHex ()
321                         int end = index + count;
322                         for (int i = index; i < end; i++) {
323                                 int val = buffer [i];
324                                 int high = val >> 4;
325                                 int low = val & 15;
326                                 if (high > 9)
327                                         w.Write ((char) (high + 55));
328                                 else
329                                         w.Write ((char) (high + 0x30));
330                                 if (low > 9)
331                                         w.Write ((char) (low + 55));
332                                 else
333                                         w.Write ((char) (low + 0x30));
334                         }
335                 }
336
337                 public static byte ToByte(string s)
338                 {
339                         return Byte.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
340                 }
341
342                 public static char ToChar(string s)
343                 {
344 #if !NET_2_1
345                         return Char.Parse(s);
346 #else
347                         if (s == null)
348                                 throw new ArgumentNullException ("s");
349
350                         if (s.Length != 1)
351                                 throw new FormatException ("String contain more than one char");
352
353                         return s [0];
354 #endif
355                 }
356
357 #if NET_2_0
358                 [Obsolete]
359 #endif
360                 public static DateTime ToDateTime (string s)
361                 {
362                         return ToDateTime (s, datetimeFormats);
363                 }
364                 
365 #if NET_2_0
366                 public static DateTime ToDateTime (string value, XmlDateTimeSerializationMode mode)
367                 {
368                         DateTime dt;
369                         switch (mode) {
370                         case XmlDateTimeSerializationMode.Local:
371                                 dt = ToDateTime (value, localDateTimeFormats);
372                                 return dt == DateTime.MinValue || dt == DateTime.MaxValue ? dt : dt.ToLocalTime ();
373                         case XmlDateTimeSerializationMode.RoundtripKind:
374                                 return ToDateTime (value, roundtripDateTimeFormats, _defaultStyle | DateTimeStyles.RoundtripKind);
375                         case XmlDateTimeSerializationMode.Utc:
376                                 dt = ToDateTime (value, utcDateTimeFormats);
377                                 return dt == DateTime.MinValue || dt == DateTime.MaxValue ? dt : dt.ToUniversalTime ();
378                         case XmlDateTimeSerializationMode.Unspecified:
379                                 return ToDateTime (value, unspecifiedDateTimeFormats);
380                         default:
381                                 return ToDateTime (value, defaultDateTimeFormats);
382                         }
383                 }
384 #endif
385                 public static DateTime ToDateTime(string s, string format)
386                 {
387                         //DateTimeFormatInfo d = new DateTimeFormatInfo();
388                         //d.FullDateTimePattern = format;
389                         //return DateTime.Parse(s, d);
390                         DateTimeStyles style = DateTimeStyles.AllowLeadingWhite |
391                                                DateTimeStyles.AllowTrailingWhite;                       
392                         return DateTime.ParseExact (s, format, DateTimeFormatInfo.InvariantInfo, style);
393                 }
394
395                 public static DateTime ToDateTime(string s, string[] formats)
396                 {
397                         return ToDateTime (s, formats, _defaultStyle);                  
398                 }
399
400                 private static DateTime ToDateTime (string s, string [] formats, DateTimeStyles style) 
401                 {
402                         return DateTime.ParseExact (s, formats, DateTimeFormatInfo.InvariantInfo, style);
403                 }
404                 
405                 public static Decimal ToDecimal(string s)
406                 {
407                         return Decimal.Parse(s, CultureInfo.InvariantCulture);
408                 }
409
410                 public static double ToDouble(string s)
411                 {
412                         if (s == null)
413                                 throw new ArgumentNullException();
414
415                         float f = TryParseStringFloatConstants (s);
416                         if (f != 0)
417                                 return f;
418
419                         return Double.Parse (s, floatStyle, CultureInfo.InvariantCulture);
420                 }
421
422                 static float TryParseStringFloatConstants (string s)
423                 {
424                         int sidx = 0;
425                         while (sidx < s.Length && Char.IsWhiteSpace (s [sidx]))
426                                 sidx++;
427                         if (sidx == s.Length)
428                                 throw new FormatException ();
429                         int sEndPos = s.Length - 1;
430                         while (Char.IsWhiteSpace (s [sEndPos]))
431                                 sEndPos--;
432
433                         if (TryParseStringConstant ("NaN", s, sidx, sEndPos))
434                                 return Single.NaN;
435                         if (TryParseStringConstant ("INF", s, sidx, sEndPos))
436                                 return Single.PositiveInfinity;
437                         if (TryParseStringConstant ("-INF", s, sidx, sEndPos))
438                                 return Single.NegativeInfinity;
439                         // Handle these here because Single.Parse("Infinity") is invalid while XmlConvert.ToSingle("Infinity") is valid.
440                         if (TryParseStringConstant ("Infinity", s, sidx, sEndPos))
441                                 return Single.PositiveInfinity;
442                         if (TryParseStringConstant ("-Infinity", s, sidx, sEndPos))
443                                 return Single.NegativeInfinity;
444                         return 0;
445                 }
446
447                 static bool TryParseStringConstant (string format, string s, int start, int end)
448                 {
449                         return end - start + 1 == format.Length && String.CompareOrdinal (format, 0, s, start, format.Length) == 0;
450                 }
451
452                 public static Guid ToGuid (string s)
453                 {
454                         try {
455                                 return new Guid(s);
456                         } catch (FormatException ex) {
457                                 throw new FormatException (String.Format ("Invalid Guid input '{0}'", ex.InnerException));
458                         }
459                 }
460
461                 public static short ToInt16(string s)
462                 {
463                         return Int16.Parse (s, integerStyle, CultureInfo.InvariantCulture);
464                 }
465
466                 public static int ToInt32(string s)
467                 {
468                         return Int32.Parse (s, integerStyle, CultureInfo.InvariantCulture);
469                 }
470
471                 public static long ToInt64(string s)
472                 {
473                         return Int64.Parse (s, integerStyle, CultureInfo.InvariantCulture);
474                 }
475
476                 [CLSCompliant (false)]
477                 public static SByte ToSByte(string s)
478                 {
479                         return SByte.Parse(s, integerStyle, CultureInfo.InvariantCulture);
480                 }
481
482                 public static float ToSingle(string s)
483                 {
484                         if (s == null)
485                                 throw new ArgumentNullException();
486
487                         float f = TryParseStringFloatConstants (s);
488                         if (f != 0)
489                                 return f;
490
491                         return Single.Parse(s, floatStyle, CultureInfo.InvariantCulture);
492                 }
493
494                 public static string ToString(Guid value)
495                 {
496                         return value.ToString("D", CultureInfo.InvariantCulture);
497                 }
498
499                 public static string ToString(int value)
500                 {
501                         return value.ToString(CultureInfo.InvariantCulture);
502                 }
503
504                 public static string ToString(short value)
505                 {
506                         return value.ToString(CultureInfo.InvariantCulture);
507                 }
508
509                 public static string ToString(byte value)
510                 {
511                         return value.ToString(CultureInfo.InvariantCulture);
512                 }
513
514                 public static string ToString(long value)
515                 {
516                         return value.ToString(CultureInfo.InvariantCulture);
517                 }
518
519                 public static string ToString(char value)
520                 {
521                         return value.ToString(CultureInfo.InvariantCulture);
522                 }
523
524                 public static string ToString(bool value)
525                 {
526                         if (value) return "true";
527                         return "false";
528                 }
529
530                 [CLSCompliant (false)]
531                 public static string ToString(SByte value)
532                 {
533                         return value.ToString(CultureInfo.InvariantCulture);
534                 }
535
536                 public static string ToString(Decimal value)
537                 {
538                         return value.ToString (CultureInfo.InvariantCulture);
539                 }
540
541                 [CLSCompliant (false)]
542                 public static string ToString(UInt64 value)
543                 {
544                         return value.ToString(CultureInfo.InvariantCulture);
545                 }
546
547                 public static string ToString (TimeSpan value)
548                 {
549                         if (value == TimeSpan.Zero)
550                                 return "PT0S";
551
552                         StringBuilder builder = new StringBuilder ();
553                         if (value.Ticks < 0) {
554                                 builder.Append ('-');
555                                 value = value.Negate ();
556                         }
557                         builder.Append ('P');
558                         if (value.Days > 0)
559                                 builder.Append (value.Days).Append ('D');
560                         long ticks = value.Ticks % TimeSpan.TicksPerMillisecond;
561                         if (value.Days > 0 || value.Hours > 0 || value.Minutes > 0 || value.Seconds > 0 || value.Milliseconds > 0 || ticks > 0) {
562                                 builder.Append('T');
563                                 if (value.Hours > 0)
564                                         builder.Append (value.Hours).Append ('H');
565                                 if (value.Minutes > 0) 
566                                         builder.Append (value.Minutes).Append ('M');
567                                 if (value.Seconds > 0 || value.Milliseconds > 0 || ticks > 0) {
568                                         builder.Append (value.Seconds);
569                                         bool trimZero = true;
570                                         if (ticks > 0)
571                                                 builder.Append ('.').AppendFormat ("{0:0000000}", value.Ticks % TimeSpan.TicksPerSecond);
572                                         else if (value.Milliseconds > 0)
573                                                 builder.Append ('.').AppendFormat ("{0:000}", value.Milliseconds);
574                                         else
575                                                 trimZero = false;
576                                         if (trimZero)
577                                                 while (builder [builder.Length - 1] == '0')
578                                                         builder.Remove (builder.Length - 1, 1);
579
580                                         builder.Append ('S');
581                                 }
582                         }
583                         return builder.ToString ();
584                 }
585
586                 public static string ToString(double value)
587                 {
588                         if (Double.IsNegativeInfinity(value)) return "-INF";
589                         if (Double.IsPositiveInfinity(value)) return "INF";
590                         if (Double.IsNaN(value)) return "NaN";
591                         return value.ToString("R", CultureInfo.InvariantCulture);
592                 }
593
594                 public static string ToString(float value)
595                 {
596                         if (Single.IsNegativeInfinity(value)) return "-INF";
597                         if (Single.IsPositiveInfinity(value)) return "INF";
598                         if (Single.IsNaN(value)) return "NaN";
599                         return value.ToString("R", CultureInfo.InvariantCulture);
600                 }
601
602                 [CLSCompliant (false)]
603                 public static string ToString(UInt32 value)
604                 {
605                         return value.ToString(CultureInfo.InvariantCulture);
606                 }
607
608                 [CLSCompliant (false)]
609                 public static string ToString(UInt16 value)
610                 {
611                         return value.ToString(CultureInfo.InvariantCulture);
612                 }
613
614 #if NET_2_0
615                 [Obsolete]
616 #endif
617                 public static string ToString (DateTime value)
618                 {
619                         return value.ToString ("yyyy-MM-ddTHH:mm:ss.fffffffzzz", CultureInfo.InvariantCulture);
620                 }
621
622 #if NET_2_0
623                 public static string ToString (DateTime value, XmlDateTimeSerializationMode mode)
624                 {
625                         // Unlike usual DateTime formatting, it preserves
626                         // MaxValue/MinValue as is.
627                         switch (mode) {
628                         case XmlDateTimeSerializationMode.Local:
629                                 return (value == DateTime.MinValue ? DateTime.MinValue : value == DateTime.MaxValue ? value : value.ToLocalTime ()).ToString (
630                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
631                                         CultureInfo.InvariantCulture);
632                         case XmlDateTimeSerializationMode.RoundtripKind:
633                                 return value.ToString (
634                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
635                                         CultureInfo.InvariantCulture);
636                         default:
637                                 return value.ToString (
638                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
639                                         CultureInfo.InvariantCulture);
640                         case XmlDateTimeSerializationMode.Utc:
641                                 return (value == DateTime.MinValue ? DateTime.MinValue : value == DateTime.MaxValue ? value : value.ToUniversalTime ()).ToString (
642                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFZ",
643                                         CultureInfo.InvariantCulture);
644                         case XmlDateTimeSerializationMode.Unspecified:
645                                 return value.ToString (
646                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFF",
647                                         CultureInfo.InvariantCulture);
648                         }
649                 }
650 #endif
651
652                 public static string ToString(DateTime value, string format)
653                 {
654                         return value.ToString(format, CultureInfo.InvariantCulture);
655                 }
656
657                 public static TimeSpan ToTimeSpan(string s)
658                 {
659                         s = s.Trim (XmlChar.WhitespaceChars);
660                         if (s.Length == 0)
661                                 throw new FormatException ("Invalid format string for duration schema datatype.");
662
663                         int start = 0;
664                         if (s [0] == '-')
665                                 start = 1;
666                         bool minusValue = (start == 1);
667
668                         if (s [start] != 'P')
669                                 throw new FormatException ("Invalid format string for duration schema datatype.");
670                         start++;
671
672                         int parseStep = 0;
673                         int days = 0;
674                         bool isTime = false;
675                         int hours = 0;
676                         int minutes = 0;
677                         int seconds = 0;
678                         long ticks = 0;
679                         int parsedDigits = 0;
680
681                         bool error = false;
682
683                         int i = start;
684                         while (i < s.Length) {
685                                 if (s [i] == 'T') {
686                                         isTime = true;
687                                         parseStep = 4;
688                                         i++;
689                                         start = i;
690                                         continue;
691                                 }
692                                 for (; i < s.Length; i++)
693                                         if (s [i] < '0' || '9' < s [i])
694                                                 break;
695                                 if (parseStep == 7)
696                                         parsedDigits = i - start;
697                                 int value = int.Parse (s.Substring (start, i - start), CultureInfo.InvariantCulture);
698                                 if (parseStep == 7) {
699                                         // adjust to 7 digits so that it makes sense as millisecond digits
700                                         for (; parsedDigits > 7; parsedDigits--)
701                                                 value /= 10;
702                                         for (; parsedDigits < 7; parsedDigits++)
703                                                 value *= 10;
704                                 }
705                                 switch (s [i]) {
706                                 case 'Y':
707                                         days += value * 365;
708                                         if (parseStep > 0)
709                                                 error = true;
710                                         else
711                                                 parseStep = 1;
712                                         break;
713                                 case 'M':
714                                         if (parseStep < 2) {
715                                                 days += 365 * (value / 12) + 30 * (value % 12);
716                                                 parseStep = 2;
717                                         } else if (isTime && parseStep < 6) {
718                                                 minutes = value;
719                                                 parseStep = 6;
720                                         }
721                                         else
722                                                 error = true;
723                                         break;
724                                 case 'D':
725                                         days += value;
726                                         if (parseStep > 2)
727                                                 error = true;
728                                         else
729                                                 parseStep = 3;
730                                         break;
731                                 case 'H':
732                                         hours = value;
733                                         if (!isTime || parseStep > 4)
734                                                 error = true;
735                                         else
736                                                 parseStep = 5;
737                                         break;
738                                 case 'S':
739                                         if (parseStep == 7)
740                                                 ticks = value;
741                                         else
742                                                 seconds = value;
743                                         if (!isTime || parseStep > 7)
744                                                 error = true;
745                                         else
746                                                 parseStep = 8;
747                                         break;
748                                 case '.':
749                                         if (parseStep > 7)
750                                                 error = true;
751                                         seconds = value;
752                                         parseStep = 7;
753                                         break;
754                                 default:
755                                         error = true;
756                                         break;
757                                 }
758                                 if (error)
759                                         break;
760                                 ++i;
761                                 start = i;
762                         }
763                         if (error)
764                                 throw new FormatException ("Invalid format string for duration schema datatype.");
765                         TimeSpan ts = new TimeSpan (days, hours, minutes, seconds);
766                         if (minusValue)
767                                 return TimeSpan.FromTicks (- (ts.Ticks + ticks));
768                         else
769                                 return TimeSpan.FromTicks (ts.Ticks + ticks);
770                 }
771
772                 [CLSCompliant (false)]
773                 public static UInt16 ToUInt16(string s)
774                 {
775                         return UInt16.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
776                 }
777
778                 [CLSCompliant (false)]
779                 public static UInt32 ToUInt32(string s)
780                 {
781                         return UInt32.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
782                 }
783
784                 [CLSCompliant (false)]
785                 public static UInt64 ToUInt64(string s)
786                 {
787                         return UInt64.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
788                 }
789
790                 public static string VerifyName (string name)
791                 {
792                         if (name == null || name.Length == 0)
793                                 throw new ArgumentNullException("name");
794
795                         if (!XmlChar.IsName (name))
796                                 throw new XmlException("'" + name + "' is not a valid XML Name");
797                         return name;
798                         
799                 }
800
801                 public static string VerifyNCName (string ncname)
802                 {
803                         if (ncname == null || ncname.Length == 0)
804                                 throw new ArgumentNullException("ncname");
805
806                         if (!XmlChar.IsNCName (ncname))
807                                 throw new XmlException ("'" + ncname + "' is not a valid XML NCName");
808                         return ncname;
809                 }
810
811 #if NET_2_0
812                 public static string VerifyTOKEN (string name)
813 #else
814                 internal static string VerifyTOKEN (string name)
815 #endif
816                 {
817                         if (name == null)
818                                 throw new ArgumentNullException("name");
819
820                         if (name.Length == 0)
821                                 return name;
822
823                         if (XmlChar.IsWhitespace (name [0]) ||
824                                 XmlChar.IsWhitespace (name [name.Length - 1]))
825                                 throw new XmlException ("Whitespace characters (#xA, #xD, #x9, #x20) are not allowed as leading or trailing whitespaces of xs:token.");
826
827                         for (int i = 0; i < name.Length; i++)
828                                 if (XmlChar.IsWhitespace (name [i]) && name [i] != ' ')
829                                 throw new XmlException ("Either #xA, #xD or #x9 are not allowed inside xs:token.");
830
831                         return name;
832                 }
833
834 #if NET_2_0
835                 public static string VerifyNMTOKEN (string name)
836 #else
837                 internal static string VerifyNMTOKEN (string name)
838 #endif
839                 {
840                         if (name == null)
841                                 throw new ArgumentNullException("name");
842
843                         if (!XmlChar.IsNmToken (name))
844                                 throw new XmlException("'" + name + "' is not a valid XML NMTOKEN");
845                         return name;
846                         
847                 }
848
849                 // It is documented as public method, but in fact it is not.
850                 internal static byte [] FromBinHexString (string s)
851                 {
852                         char [] chars = s.ToCharArray ();
853                         byte [] bytes = new byte [chars.Length / 2 + chars.Length % 2];
854                         FromBinHexString (chars, 0, chars.Length, bytes);
855                         return bytes;
856                 }
857
858                 internal static int FromBinHexString (char [] chars, int offset, int charLength, byte [] buffer)
859                 {
860                         int bufIndex = offset;
861                         for (int i = 0; i < charLength - 1; i += 2) {
862                                 buffer [bufIndex] = (chars [i] > '9' ?
863                                                 (byte) (chars [i] - 'A' + 10) :
864                                                 (byte) (chars [i] - '0'));
865                                 buffer [bufIndex] <<= 4;
866                                 buffer [bufIndex] += chars [i + 1] > '9' ?
867                                                 (byte) (chars [i + 1] - 'A' + 10) : 
868                                                 (byte) (chars [i + 1] - '0');
869                                 bufIndex++;
870                         }
871                         if (charLength %2 != 0)
872                                 buffer [bufIndex++] = (byte)
873                                         ((chars [charLength - 1] > '9' ?
874                                                 (byte) (chars [charLength - 1] - 'A' + 10) :
875                                                 (byte) (chars [charLength - 1] - '0'))
876                                         << 4);
877
878                         return bufIndex - offset;
879                 }
880
881 #if NET_2_0 // actually NET_3_5
882 #if !TARGET_JVM
883
884                 public static DateTimeOffset ToDateTimeOffset (string s)
885                 {
886                         return ToDateTimeOffset (s, datetimeFormats);
887                 }
888
889                 public static DateTimeOffset ToDateTimeOffset (string s, string format)
890                 {
891                         return DateTimeOffset.ParseExact (s, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
892                 }
893
894                 public static DateTimeOffset ToDateTimeOffset (string s, string [] formats)
895                 {
896                         DateTimeStyles style = DateTimeStyles.AllowLeadingWhite |
897                                                DateTimeStyles.AllowTrailingWhite |
898                                                DateTimeStyles.AssumeUniversal;
899                         return DateTimeOffset.ParseExact (s, formats, CultureInfo.InvariantCulture, style);
900                 }
901
902                 public static string ToString (DateTimeOffset value)
903                 {
904                         return ToString (value, "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz");
905                 }
906
907                 public static string ToString (DateTimeOffset value, string format)
908                 {
909                         return value.ToString (format, CultureInfo.InvariantCulture);
910                 }
911 #endif
912
913                 // it is used only from 2.1 System.Xml.Serialization.dll from
914                 // MS Silverlight SDK. We don't use it so far.
915                 internal static Uri ToUri (string s)
916                 {
917                         return new Uri (s, UriKind.RelativeOrAbsolute);
918                 }
919
920 #endif
921         }
922 }