2010-01-15 Atsushi Enomoto <atsushi@ximian.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                         if (value.Days > 0 || value.Hours > 0 || value.Minutes > 0 || value.Seconds > 0 || value.Milliseconds > 0) {
561                                 builder.Append('T');
562                                 if (value.Hours > 0)
563                                         builder.Append (value.Hours).Append ('H');
564                                 if (value.Minutes > 0) 
565                                         builder.Append (value.Minutes).Append ('M');
566                                 if (value.Seconds > 0 || value.Milliseconds > 0) {
567                                         builder.Append (value.Seconds);
568                                         long ticks = value.Ticks % TimeSpan.TicksPerMillisecond;
569                                         if (ticks > 0)
570                                                 builder.Append ('.').AppendFormat ("{0:0000000}", value.Ticks % TimeSpan.TicksPerSecond);
571                                         else if (value.Milliseconds > 0)
572                                                 builder.Append ('.').AppendFormat ("{0:000}", value.Milliseconds);
573
574                                         builder.Append ('S');
575                                 }
576                         }
577                         return builder.ToString ();
578                 }
579
580                 public static string ToString(double value)
581                 {
582                         if (Double.IsNegativeInfinity(value)) return "-INF";
583                         if (Double.IsPositiveInfinity(value)) return "INF";
584                         if (Double.IsNaN(value)) return "NaN";
585                         return value.ToString("R", CultureInfo.InvariantCulture);
586                 }
587
588                 public static string ToString(float value)
589                 {
590                         if (Single.IsNegativeInfinity(value)) return "-INF";
591                         if (Single.IsPositiveInfinity(value)) return "INF";
592                         if (Single.IsNaN(value)) return "NaN";
593                         return value.ToString("R", CultureInfo.InvariantCulture);
594                 }
595
596                 [CLSCompliant (false)]
597                 public static string ToString(UInt32 value)
598                 {
599                         return value.ToString(CultureInfo.InvariantCulture);
600                 }
601
602                 [CLSCompliant (false)]
603                 public static string ToString(UInt16 value)
604                 {
605                         return value.ToString(CultureInfo.InvariantCulture);
606                 }
607
608 #if NET_2_0
609                 [Obsolete]
610 #endif
611                 public static string ToString (DateTime value)
612                 {
613                         return value.ToString ("yyyy-MM-ddTHH:mm:ss.fffffffzzz", CultureInfo.InvariantCulture);
614                 }
615
616 #if NET_2_0
617                 public static string ToString (DateTime value, XmlDateTimeSerializationMode mode)
618                 {
619                         // Unlike usual DateTime formatting, it preserves
620                         // MaxValue/MinValue as is.
621                         switch (mode) {
622                         case XmlDateTimeSerializationMode.Local:
623                                 return (value == DateTime.MinValue ? DateTime.MinValue : value == DateTime.MaxValue ? value : value.ToLocalTime ()).ToString (
624                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
625                                         CultureInfo.InvariantCulture);
626                         case XmlDateTimeSerializationMode.RoundtripKind:
627                                 return value.ToString (
628                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
629                                         CultureInfo.InvariantCulture);
630                         default:
631                                 return value.ToString (
632                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
633                                         CultureInfo.InvariantCulture);
634                         case XmlDateTimeSerializationMode.Utc:
635                                 return (value == DateTime.MinValue ? DateTime.MinValue : value == DateTime.MaxValue ? value : value.ToUniversalTime ()).ToString (
636                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFZ",
637                                         CultureInfo.InvariantCulture);
638                         case XmlDateTimeSerializationMode.Unspecified:
639                                 return value.ToString (
640                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFF",
641                                         CultureInfo.InvariantCulture);
642                         }
643                 }
644 #endif
645
646                 public static string ToString(DateTime value, string format)
647                 {
648                         return value.ToString(format, CultureInfo.InvariantCulture);
649                 }
650
651                 public static TimeSpan ToTimeSpan(string s)
652                 {
653                         if (s.Length == 0)
654                                 throw new FormatException ("Invalid format string for duration schema datatype.");
655
656                         int start = 0;
657                         if (s [0] == '-')
658                                 start = 1;
659                         bool minusValue = (start == 1);
660
661                         if (s [start] != 'P')
662                                 throw new FormatException ("Invalid format string for duration schema datatype.");
663                         start++;
664
665                         int parseStep = 0;
666                         int days = 0;
667                         bool isTime = false;
668                         int hours = 0;
669                         int minutes = 0;
670                         int seconds = 0;
671                         long ticks = 0;
672                         int parsedDigits = 0;
673
674                         bool error = false;
675
676                         int i = start;
677                         while (i < s.Length) {
678                                 if (s [i] == 'T') {
679                                         isTime = true;
680                                         parseStep = 4;
681                                         i++;
682                                         start = i;
683                                         continue;
684                                 }
685                                 for (; i < s.Length; i++)
686                                         if (s [i] < '0' || '9' < s [i])
687                                                 break;
688                                 if (parseStep == 7)
689                                         parsedDigits = i - start;
690                                 int value = int.Parse (s.Substring (start, i - start), CultureInfo.InvariantCulture);
691                                 if (parseStep == 7) {
692                                         // adjust to 7 digits so that it makes sense as millisecond digits
693                                         for (; parsedDigits > 7; parsedDigits--)
694                                                 value /= 10;
695                                         for (; parsedDigits < 7; parsedDigits++)
696                                                 value *= 10;
697                                 }
698                                 switch (s [i]) {
699                                 case 'Y':
700                                         days += value * 365;
701                                         if (parseStep > 0)
702                                                 error = true;
703                                         else
704                                                 parseStep = 1;
705                                         break;
706                                 case 'M':
707                                         if (parseStep < 2) {
708                                                 days += 365 * (value / 12) + 30 * (value % 12);
709                                                 parseStep = 2;
710                                         } else if (isTime && parseStep < 6) {
711                                                 minutes = value;
712                                                 parseStep = 6;
713                                         }
714                                         else
715                                                 error = true;
716                                         break;
717                                 case 'D':
718                                         days += value;
719                                         if (parseStep > 2)
720                                                 error = true;
721                                         else
722                                                 parseStep = 3;
723                                         break;
724                                 case 'H':
725                                         hours = value;
726                                         if (!isTime || parseStep > 4)
727                                                 error = true;
728                                         else
729                                                 parseStep = 5;
730                                         break;
731                                 case 'S':
732                                         if (parseStep == 7)
733                                                 ticks = value;
734                                         else
735                                                 seconds = value;
736                                         if (!isTime || parseStep > 7)
737                                                 error = true;
738                                         else
739                                                 parseStep = 8;
740                                         break;
741                                 case '.':
742                                         if (parseStep > 7)
743                                                 error = true;
744                                         seconds = value;
745                                         parseStep = 7;
746                                         break;
747                                 default:
748                                         error = true;
749                                         break;
750                                 }
751                                 if (error)
752                                         break;
753                                 ++i;
754                                 start = i;
755                         }
756                         if (error)
757                                 throw new FormatException ("Invalid format string for duration schema datatype.");
758                         TimeSpan ts = new TimeSpan (days, hours, minutes, seconds);
759                         if (minusValue)
760                                 return TimeSpan.FromTicks (- (ts.Ticks + ticks));
761                         else
762                                 return TimeSpan.FromTicks (ts.Ticks + ticks);
763                 }
764
765                 [CLSCompliant (false)]
766                 public static UInt16 ToUInt16(string s)
767                 {
768                         return UInt16.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
769                 }
770
771                 [CLSCompliant (false)]
772                 public static UInt32 ToUInt32(string s)
773                 {
774                         return UInt32.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
775                 }
776
777                 [CLSCompliant (false)]
778                 public static UInt64 ToUInt64(string s)
779                 {
780                         return UInt64.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
781                 }
782
783                 public static string VerifyName (string name)
784                 {
785                         if (name == null || name.Length == 0)
786                                 throw new ArgumentNullException("name");
787
788                         if (!XmlChar.IsName (name))
789                                 throw new XmlException("'" + name + "' is not a valid XML Name");
790                         return name;
791                         
792                 }
793
794                 public static string VerifyNCName (string ncname)
795                 {
796                         if (ncname == null || ncname.Length == 0)
797                                 throw new ArgumentNullException("ncname");
798
799                         if (!XmlChar.IsNCName (ncname))
800                                 throw new XmlException ("'" + ncname + "' is not a valid XML NCName");
801                         return ncname;
802                 }
803
804 #if NET_2_0
805                 public static string VerifyTOKEN (string name)
806 #else
807                 internal static string VerifyTOKEN (string name)
808 #endif
809                 {
810                         if (name == null)
811                                 throw new ArgumentNullException("name");
812
813                         if (name.Length == 0)
814                                 return name;
815
816                         if (XmlChar.IsWhitespace (name [0]) ||
817                                 XmlChar.IsWhitespace (name [name.Length - 1]))
818                                 throw new XmlException ("Whitespace characters (#xA, #xD, #x9, #x20) are not allowed as leading or trailing whitespaces of xs:token.");
819
820                         for (int i = 0; i < name.Length; i++)
821                                 if (XmlChar.IsWhitespace (name [i]) && name [i] != ' ')
822                                 throw new XmlException ("Either #xA, #xD or #x9 are not allowed inside xs:token.");
823
824                         return name;
825                 }
826
827 #if NET_2_0
828                 public static string VerifyNMTOKEN (string name)
829 #else
830                 internal static string VerifyNMTOKEN (string name)
831 #endif
832                 {
833                         if (name == null)
834                                 throw new ArgumentNullException("name");
835
836                         if (!XmlChar.IsNmToken (name))
837                                 throw new XmlException("'" + name + "' is not a valid XML NMTOKEN");
838                         return name;
839                         
840                 }
841
842                 // It is documented as public method, but in fact it is not.
843                 internal static byte [] FromBinHexString (string s)
844                 {
845                         char [] chars = s.ToCharArray ();
846                         byte [] bytes = new byte [chars.Length / 2 + chars.Length % 2];
847                         FromBinHexString (chars, 0, chars.Length, bytes);
848                         return bytes;
849                 }
850
851                 internal static int FromBinHexString (char [] chars, int offset, int charLength, byte [] buffer)
852                 {
853                         int bufIndex = offset;
854                         for (int i = 0; i < charLength - 1; i += 2) {
855                                 buffer [bufIndex] = (chars [i] > '9' ?
856                                                 (byte) (chars [i] - 'A' + 10) :
857                                                 (byte) (chars [i] - '0'));
858                                 buffer [bufIndex] <<= 4;
859                                 buffer [bufIndex] += chars [i + 1] > '9' ?
860                                                 (byte) (chars [i + 1] - 'A' + 10) : 
861                                                 (byte) (chars [i + 1] - '0');
862                                 bufIndex++;
863                         }
864                         if (charLength %2 != 0)
865                                 buffer [bufIndex++] = (byte)
866                                         ((chars [charLength - 1] > '9' ?
867                                                 (byte) (chars [charLength - 1] - 'A' + 10) :
868                                                 (byte) (chars [charLength - 1] - '0'))
869                                         << 4);
870
871                         return bufIndex - offset;
872                 }
873
874 #if NET_2_0 // actually NET_3_5
875 #if !TARGET_JVM
876
877                 public static DateTimeOffset ToDateTimeOffset (string s)
878                 {
879                         return ToDateTimeOffset (s, datetimeFormats);
880                 }
881
882                 public static DateTimeOffset ToDateTimeOffset (string s, string format)
883                 {
884                         return DateTimeOffset.ParseExact (s, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
885                 }
886
887                 public static DateTimeOffset ToDateTimeOffset (string s, string [] formats)
888                 {
889                         DateTimeStyles style = DateTimeStyles.AllowLeadingWhite |
890                                                DateTimeStyles.AllowTrailingWhite |
891                                                DateTimeStyles.AssumeUniversal;
892                         return DateTimeOffset.ParseExact (s, formats, CultureInfo.InvariantCulture, style);
893                 }
894
895                 public static string ToString (DateTimeOffset value)
896                 {
897                         return ToString (value, "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz");
898                 }
899
900                 public static string ToString (DateTimeOffset value, string format)
901                 {
902                         return value.ToString (format, CultureInfo.InvariantCulture);
903                 }
904 #endif
905
906                 // it is used only from 2.1 System.Xml.Serialization.dll from
907                 // MS Silverlight SDK. We don't use it so far.
908                 internal static Uri ToUri (string s)
909                 {
910                         return new Uri (s, UriKind.RelativeOrAbsolute);
911                 }
912
913 #endif
914         }
915 }