2008-06-05 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                 
49                 static readonly string [] datetimeFormats = {
50                   // dateTime
51                   "yyyy-MM-ddTHH:mm:ss",
52                   "yyyy-MM-ddTHH:mm:ss.f",
53                   "yyyy-MM-ddTHH:mm:ss.ff",
54                   "yyyy-MM-ddTHH:mm:ss.fff",
55                   "yyyy-MM-ddTHH:mm:ss.ffff",
56                   "yyyy-MM-ddTHH:mm:ss.fffff",
57                   "yyyy-MM-ddTHH:mm:ss.ffffff",
58                   "yyyy-MM-ddTHH:mm:ss.fffffff",
59                   "yyyy-MM-ddTHH:mm:sszzz",
60                   "yyyy-MM-ddTHH:mm:ss.fzzz",
61                   "yyyy-MM-ddTHH:mm:ss.ffzzz",
62                   "yyyy-MM-ddTHH:mm:ss.fffzzz",
63                   "yyyy-MM-ddTHH:mm:ss.ffffzzz",
64                   "yyyy-MM-ddTHH:mm:ss.fffffzzz",
65                   "yyyy-MM-ddTHH:mm:ss.ffffffzzz",
66                   "yyyy-MM-ddTHH:mm:ss.fffffffzzz",
67                   "yyyy-MM-ddTHH:mm:ssZ",
68                   "yyyy-MM-ddTHH:mm:ss.fZ",
69                   "yyyy-MM-ddTHH:mm:ss.ffZ",
70                   "yyyy-MM-ddTHH:mm:ss.fffZ",
71                   "yyyy-MM-ddTHH:mm:ss.ffffZ",
72                   "yyyy-MM-ddTHH:mm:ss.fffffZ",
73                   "yyyy-MM-ddTHH:mm:ss.ffffffZ",
74                   "yyyy-MM-ddTHH:mm:ss.fffffffZ",
75                   // time
76                   "HH:mm:ss",
77                   "HH:mm:ss.f",
78                   "HH:mm:ss.ff",
79                   "HH:mm:ss.fff",
80                   "HH:mm:ss.ffff",
81                   "HH:mm:ss.fffff",
82                   "HH:mm:ss.ffffff",
83                   "HH:mm:ss.fffffff",
84                   "HH:mm:sszzz",
85                   "HH:mm:ss.fzzz",
86                   "HH:mm:ss.ffzzz",
87                   "HH:mm:ss.fffzzz",
88                   "HH:mm:ss.ffffzzz",
89                   "HH:mm:ss.fffffzzz",
90                   "HH:mm:ss.ffffffzzz",
91                   "HH:mm:ss.fffffffzzz",
92                   "HH:mm:ssZ",
93                   "HH:mm:ss.fZ",
94                   "HH:mm:ss.ffZ",
95                   "HH:mm:ss.fffZ",
96                   "HH:mm:ss.ffffZ",
97                   "HH:mm:ss.fffffZ",
98                   "HH:mm:ss.ffffffZ",
99                   "HH:mm:ss.fffffffZ",
100                   // date
101                   "yyyy-MM-dd",
102                   "yyyy-MM-ddzzz",
103                   "yyyy-MM-ddZ",
104                   // gYearMonth
105                   "yyyy-MM",
106                   "yyyy-MMzzz",
107                   "yyyy-MMZ",
108                   // gYear
109                   "yyyy",
110                   "yyyyzzz",
111                   "yyyyZ",
112                   // gMonthDay
113                   "--MM-dd",
114                   "--MM-ddzzz",
115                   "--MM-ddZ",
116                   // gDay
117                   "---dd",
118                   "---ddzzz",
119                   "---ddZ",
120                 };
121
122 #if NET_2_0
123                 static readonly string [] defaultDateTimeFormats = new string [] {
124                         "yyyy-MM-ddTHH:mm:ss", // dateTime(1)
125                         "yyyy-MM-ddTHH:mm:ss.FFFFFFF", // dateTime(2)
126                         "yyyy-MM-dd", // date
127                         "HH:mm:ss", // time
128                         "yyyy-MM", // gYearMonth
129                         "yyyy", // gYear
130                         "--MM-dd", // gMonthDay
131                         "---dd", // gDay
132                         };
133
134                 static readonly string [] roundtripDateTimeFormats;
135                 static readonly string [] localDateTimeFormats;
136                 static readonly string [] utcDateTimeFormats;
137 #if TARGET_JVM
138                 static readonly string [] unspecifiedDateTimeFormats;
139 #endif
140
141                 static XmlConvert ()
142                 {
143                         int l = defaultDateTimeFormats.Length;
144                         roundtripDateTimeFormats = new string [l];
145                         localDateTimeFormats = new string [l];
146                         utcDateTimeFormats = new string [l];
147 #if TARGET_JVM
148                         unspecifiedDateTimeFormats = new string [l * 4];
149 #endif
150                         for (int i = 0; i < l; i++) {
151                                 string s = defaultDateTimeFormats [i];
152                                 localDateTimeFormats [i] = s + "zzz";
153                                 roundtripDateTimeFormats [i] = s + 'K';
154                                 utcDateTimeFormats [i] = s + 'Z';
155 #if TARGET_JVM
156                                 unspecifiedDateTimeFormats [i * 4] = s;
157                                 unspecifiedDateTimeFormats [i * 4 + 1] = localDateTimeFormats [i];
158                                 unspecifiedDateTimeFormats [i * 4 + 2] = roundtripDateTimeFormats [i];
159                                 unspecifiedDateTimeFormats [i * 4 + 3] = utcDateTimeFormats [i];
160 #endif
161                         }
162                 }
163 #endif
164                 static DateTimeStyles _defaultStyle = DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite;
165                 
166                 public XmlConvert()
167                 {}
168
169                 private static string TryDecoding (string s)
170                 {
171                         if (s == null || s.Length < 6)
172                                 return s;
173
174                         char c = '\uFFFF';
175                         try {
176                                 c = (char) Int32.Parse (s.Substring (1, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
177                         } catch {
178                                 return s [0] + DecodeName (s.Substring (1));
179                         }
180                         
181                         if (s.Length == 6)
182                                 return c.ToString ();
183                         return c + DecodeName (s.Substring (6));
184                 }
185                 
186                 public static string DecodeName (string name)
187                 {
188                         if (name == null || name.Length == 0)
189                                 return name;
190
191                         int pos = name.IndexOf ('_');
192                         if (pos == -1 || pos + 6 >= name.Length)
193                                 return name;
194
195                         if ((name [pos + 1] != 'X' && name [pos + 1] != 'x') || name [pos + 6] != '_')
196                                 return name [0] + DecodeName (name.Substring (1));
197
198                         return name.Substring (0, pos) + TryDecoding (name.Substring (pos + 1));
199                 }
200
201                 public static string EncodeLocalName (string name)
202                 {
203                         if (name == null)
204                                 return name;
205
206                         string encoded = EncodeName (name);
207                         int pos = encoded.IndexOf (':');
208                         if (pos == -1)
209                                 return encoded;
210                         return encoded.Replace (":", encodedColon);
211                 }
212
213                 internal static bool IsInvalid (char c, bool firstOnlyLetter)
214                 {
215                         if (c == ':') // Special case. allowed in EncodeName, but encoded in EncodeLocalName
216                                 return false;
217                         
218                         if (firstOnlyLetter)
219                                 return !XmlChar.IsFirstNameChar (c);
220                         else
221                                 return !XmlChar.IsNameChar (c);
222                 }
223
224                 private static string EncodeName (string name, bool nmtoken)
225                 {
226                         if (name == null || name.Length == 0)
227                                 return name;
228
229                         StringBuilder sb = new StringBuilder ();
230                         int length = name.Length;
231                         for (int i = 0; i < length; i++) {
232                                 char c = name [i];
233                                 if (IsInvalid (c, i == 0 && !nmtoken))
234                                         sb.AppendFormat ("_x{0:X4}_", (int) c);
235                                 else if (c == '_' && i + 6 < length && name [i+1] == 'x' && name [i + 6] == '_')
236                                         sb.Append ("_x005F_");
237                                 else
238                                         sb.Append (c);
239                         }
240                         return sb.ToString ();
241                 }
242
243                 public static string EncodeName (string name)
244                 {
245                         return EncodeName (name, false);
246                 }
247                 
248                 public static string EncodeNmToken (string name)
249                 {
250                         if (name == String.Empty)
251                                 throw new XmlException ("Invalid NmToken: ''");
252                         return EncodeName (name, true);
253                 }
254
255                 // {true, false, 1, 0}
256                 public static bool ToBoolean(string s)
257                 {
258                         s = s.Trim (XmlChar.WhitespaceChars);
259                         switch(s)
260                         {
261                                 case "1":
262                                         return true;
263                                 case "true":
264                                         return true;
265                                 case "0":
266                                         return false;
267                                 case "false":
268                                         return false;
269                                 default:
270                                         throw new FormatException(s + " is not a valid boolean value");
271                         }
272                 }
273
274                 // LAMESPEC: It has been documented as public, but is marked as internal.
275                 internal static string ToBinHexString (byte [] buffer)
276                 {
277                         StringWriter w = new StringWriter ();
278                         WriteBinHex (buffer, 0, buffer.Length, w);
279                         return w.ToString ();
280                 }
281
282                 internal static void WriteBinHex (byte [] buffer, int index, int count, TextWriter w)
283                 {
284                         if (buffer == null)
285                                 throw new ArgumentNullException ("buffer");
286                         if (index < 0) {
287                                 throw new ArgumentOutOfRangeException (
288 #if !NET_2_1
289                                         "index", index,
290 #endif
291                                         "index must be non negative integer.");
292                         }
293                         if (count < 0) {
294                                 throw new ArgumentOutOfRangeException (
295 #if !NET_2_1
296                                         "count", count,
297 #endif
298                                         "count must be non negative integer.");
299                         }
300                         if (buffer.Length < index + count)
301                                 throw new ArgumentOutOfRangeException ("index and count must be smaller than the length of the buffer.");
302
303                         // Copied from XmlTextWriter.WriteBinHex ()
304                         int end = index + count;
305                         for (int i = index; i < end; i++) {
306                                 int val = buffer [i];
307                                 int high = val >> 4;
308                                 int low = val & 15;
309                                 if (high > 9)
310                                         w.Write ((char) (high + 55));
311                                 else
312                                         w.Write ((char) (high + 0x30));
313                                 if (low > 9)
314                                         w.Write ((char) (low + 55));
315                                 else
316                                         w.Write ((char) (low + 0x30));
317                         }
318                 }
319
320                 public static byte ToByte(string s)
321                 {
322                         return Byte.Parse(s, CultureInfo.InvariantCulture);
323                 }
324
325                 public static char ToChar(string s)
326                 {
327                         return Char.Parse(s);
328                 }
329
330 #if NET_2_0
331                 [Obsolete]
332 #endif
333                 public static DateTime ToDateTime (string s)
334                 {
335                         return ToDateTime (s, datetimeFormats);
336                 }
337                 
338 #if NET_2_0
339                 public static DateTime ToDateTime (string value, XmlDateTimeSerializationMode mode)
340                 {
341                         string modestr = null;
342                         DateTime dt;
343                         switch (mode) {
344                         case XmlDateTimeSerializationMode.Local:
345                                 dt = ToDateTime (value, localDateTimeFormats);
346                                 return dt == DateTime.MinValue || dt == DateTime.MaxValue ? dt : dt.ToLocalTime ();
347                         case XmlDateTimeSerializationMode.RoundtripKind:
348                                 return ToDateTime (value, roundtripDateTimeFormats, _defaultStyle | DateTimeStyles.RoundtripKind);
349                         case XmlDateTimeSerializationMode.Utc:
350                                 dt = ToDateTime (value, utcDateTimeFormats);
351                                 return dt == DateTime.MinValue || dt == DateTime.MaxValue ? dt : dt.ToUniversalTime ();
352                         case XmlDateTimeSerializationMode.Unspecified:
353 #if TARGET_JVM
354                                 return ToDateTime (value, unspecifiedDateTimeFormats);
355 #endif
356                         default:
357                                 return ToDateTime (value, defaultDateTimeFormats);
358                         }
359                 }
360 #endif
361                 public static DateTime ToDateTime(string s, string format)
362                 {
363                         //DateTimeFormatInfo d = new DateTimeFormatInfo();
364                         //d.FullDateTimePattern = format;
365                         //return DateTime.Parse(s, d);
366                         DateTimeStyles style = DateTimeStyles.AllowLeadingWhite |
367                                                DateTimeStyles.AllowTrailingWhite;                       
368                         return DateTime.ParseExact (s, format, DateTimeFormatInfo.InvariantInfo, style);
369                 }
370
371                 public static DateTime ToDateTime(string s, string[] formats)
372                 {
373                         return ToDateTime (s, formats, _defaultStyle);                  
374                 }
375
376                 private static DateTime ToDateTime (string s, string [] formats, DateTimeStyles style) 
377                 {
378                         return DateTime.ParseExact (s, formats, DateTimeFormatInfo.InvariantInfo, style);
379                 }
380                 
381                 public static Decimal ToDecimal(string s)
382                 {
383                         return Decimal.Parse(s, CultureInfo.InvariantCulture);
384                 }
385
386                 public static double ToDouble(string s)
387                 {
388                         if (s == null)
389                                 throw new ArgumentNullException();
390                         if (s == "INF")
391                                 return Double.PositiveInfinity;
392                         if (s == "-INF")
393                                 return Double.NegativeInfinity;
394                         if (s == "NaN")
395                                 return Double.NaN;
396                         return Double.Parse (s, floatStyle, CultureInfo.InvariantCulture);
397                 }
398
399                 public static Guid ToGuid(string s)
400                 {
401                         return new Guid(s);
402                 }
403
404                 public static short ToInt16(string s)
405                 {
406                         return Int16.Parse (s, NumberStyles.Integer, CultureInfo.InvariantCulture);
407                 }
408
409                 public static int ToInt32(string s)
410                 {
411                         return Int32.Parse (s, NumberStyles.Integer, CultureInfo.InvariantCulture);
412                 }
413
414                 public static long ToInt64(string s)
415                 {
416                         return Int64.Parse (s, NumberStyles.Integer, CultureInfo.InvariantCulture);
417                 }
418
419                 [CLSCompliant (false)]
420                 public static SByte ToSByte(string s)
421                 {
422                         return SByte.Parse(s, CultureInfo.InvariantCulture);
423                 }
424
425                 public static float ToSingle(string s)
426                 {
427                         if (s == null)
428                                 throw new ArgumentNullException();
429                         if (s == "INF")
430                                 return Single.PositiveInfinity;
431                         if (s == "-INF")
432                                 return Single.NegativeInfinity;
433                         if (s == "NaN")
434                                 return Single.NaN;
435                         return Single.Parse(s, floatStyle, CultureInfo.InvariantCulture);
436                 }
437
438                 public static string ToString(Guid value)
439                 {
440                         return value.ToString("D", CultureInfo.InvariantCulture);
441                 }
442
443                 public static string ToString(int value)
444                 {
445                         return value.ToString(CultureInfo.InvariantCulture);
446                 }
447
448                 public static string ToString(short value)
449                 {
450                         return value.ToString(CultureInfo.InvariantCulture);
451                 }
452
453                 public static string ToString(byte value)
454                 {
455                         return value.ToString(CultureInfo.InvariantCulture);
456                 }
457
458                 public static string ToString(long value)
459                 {
460                         return value.ToString(CultureInfo.InvariantCulture);
461                 }
462
463                 public static string ToString(char value)
464                 {
465                         return value.ToString(CultureInfo.InvariantCulture);
466                 }
467
468                 public static string ToString(bool value)
469                 {
470                         if (value) return "true";
471                         return "false";
472                 }
473
474                 [CLSCompliant (false)]
475                 public static string ToString(SByte value)
476                 {
477                         return value.ToString(CultureInfo.InvariantCulture);
478                 }
479
480                 public static string ToString(Decimal value)
481                 {
482                         return value.ToString (CultureInfo.InvariantCulture);
483                 }
484
485                 [CLSCompliant (false)]
486                 public static string ToString(UInt64 value)
487                 {
488                         return value.ToString(CultureInfo.InvariantCulture);
489                 }
490
491                 public static string ToString (TimeSpan value)
492                 {
493                         if (value == TimeSpan.Zero)
494                                 return "PT0S";
495
496                         StringBuilder builder = new StringBuilder ();
497                         if (value.Ticks < 0) {
498                                 builder.Append ('-');
499                                 value = value.Negate ();
500                         }
501                         builder.Append ('P');
502                         if (value.Days > 0)
503                                 builder.Append (value.Days).Append ('D');
504                         if (value.Days > 0 || value.Hours > 0 || value.Minutes > 0 || value.Seconds > 0 || value.Milliseconds > 0) {
505                                 builder.Append('T');
506                                 if (value.Hours > 0)
507                                         builder.Append (value.Hours).Append ('H');
508                                 if (value.Minutes > 0) 
509                                         builder.Append (value.Minutes).Append ('M');
510                                 if (value.Seconds > 0 || value.Milliseconds > 0) {
511                                         builder.Append (value.Seconds);
512                                         long ticks = value.Ticks % TimeSpan.TicksPerMillisecond;
513                                         if (ticks > 0)
514                                                 builder.Append ('.').AppendFormat ("{0:0000000}", value.Ticks % TimeSpan.TicksPerSecond);
515                                         else if (value.Milliseconds > 0)
516                                                 builder.Append ('.').AppendFormat ("{0:000}", value.Milliseconds);
517
518                                         builder.Append ('S');
519                                 }
520                         }
521                         return builder.ToString ();
522                 }
523
524                 public static string ToString(double value)
525                 {
526                         if (Double.IsNegativeInfinity(value)) return "-INF";
527                         if (Double.IsPositiveInfinity(value)) return "INF";
528                         if (Double.IsNaN(value)) return "NaN";
529                         return value.ToString("R", CultureInfo.InvariantCulture);
530                 }
531
532                 public static string ToString(float value)
533                 {
534                         if (Single.IsNegativeInfinity(value)) return "-INF";
535                         if (Single.IsPositiveInfinity(value)) return "INF";
536                         if (Single.IsNaN(value)) return "NaN";
537                         return value.ToString("R", CultureInfo.InvariantCulture);
538                 }
539
540                 [CLSCompliant (false)]
541                 public static string ToString(UInt32 value)
542                 {
543                         return value.ToString(CultureInfo.InvariantCulture);
544                 }
545
546                 [CLSCompliant (false)]
547                 public static string ToString(UInt16 value)
548                 {
549                         return value.ToString(CultureInfo.InvariantCulture);
550                 }
551
552 #if NET_2_0
553                 [Obsolete]
554 #endif
555                 public static string ToString (DateTime value)
556                 {
557                         return value.ToString ("yyyy-MM-ddTHH:mm:ss.fffffffzzz", CultureInfo.InvariantCulture);
558                 }
559
560 #if NET_2_0
561                 public static string ToString (DateTime value, XmlDateTimeSerializationMode mode)
562                 {
563                         // Unlike usual DateTime formatting, it preserves
564                         // MaxValue/MinValue as is.
565                         string modestr = null;
566                         switch (mode) {
567                         case XmlDateTimeSerializationMode.Local:
568                                 return (value == DateTime.MinValue ? DateTime.MinValue : value == DateTime.MaxValue ? value : value.ToLocalTime ()).ToString (
569                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
570                                         CultureInfo.InvariantCulture);
571                                 break;
572                         case XmlDateTimeSerializationMode.RoundtripKind:
573                                 return value.ToString (
574                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
575                                         CultureInfo.InvariantCulture);
576                                 break;
577                         default:
578                                 return value.ToString (
579                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
580                                         CultureInfo.InvariantCulture);
581                                 break;
582                         case XmlDateTimeSerializationMode.Utc:
583                                 return (value == DateTime.MinValue ? DateTime.MinValue : value == DateTime.MaxValue ? value : value.ToUniversalTime ()).ToString (
584                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFZ",
585                                         CultureInfo.InvariantCulture);
586                                 break;
587                         case XmlDateTimeSerializationMode.Unspecified:
588                                 return value.ToString (
589                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFF",
590                                         CultureInfo.InvariantCulture);
591                                 break;
592                         }
593                 }
594 #endif
595
596                 public static string ToString(DateTime value, string format)
597                 {
598                         return value.ToString(format, CultureInfo.InvariantCulture);
599                 }
600
601                 public static TimeSpan ToTimeSpan(string s)
602                 {
603                         if (s.Length == 0)
604                                 throw new ArgumentException ("Invalid format string for duration schema datatype.");
605
606                         int start = 0;
607                         if (s [0] == '-')
608                                 start = 1;
609                         bool minusValue = (start == 1);
610
611                         if (s [start] != 'P')
612                                 throw new ArgumentException ("Invalid format string for duration schema datatype.");
613                         start++;
614
615                         int parseStep = 0;
616                         int days = 0;
617                         bool isTime = false;
618                         int hours = 0;
619                         int minutes = 0;
620                         int seconds = 0;
621                         long ticks = 0;
622                         int parsedDigits = 0;
623
624                         bool error = false;
625
626                         int i = start;
627                         while (i < s.Length) {
628                                 if (s [i] == 'T') {
629                                         isTime = true;
630                                         parseStep = 4;
631                                         i++;
632                                         start = i;
633                                         continue;
634                                 }
635                                 for (; i < s.Length; i++)
636                                         if (s [i] < '0' || '9' < s [i])
637                                                 break;
638                                 if (parseStep == 7)
639                                         parsedDigits = i - start;
640                                 int value = int.Parse (s.Substring (start, i - start), CultureInfo.InvariantCulture);
641                                 if (parseStep == 7) {
642                                         // adjust to 7 digits so that it makes sense as millisecond digits
643                                         for (; parsedDigits > 7; parsedDigits--)
644                                                 value /= 10;
645                                         for (; parsedDigits < 7; parsedDigits++)
646                                                 value *= 10;
647                                 }
648                                 switch (s [i]) {
649                                 case 'Y':
650                                         days += value * 365;
651                                         if (parseStep > 0)
652                                                 error = true;
653                                         else
654                                                 parseStep = 1;
655                                         break;
656                                 case 'M':
657                                         if (parseStep < 2) {
658                                                 days += 365 * (value / 12) + 30 * (value % 12);
659                                                 parseStep = 2;
660                                         } else if (isTime && parseStep < 6) {
661                                                 minutes = value;
662                                                 parseStep = 6;
663                                         }
664                                         else
665                                                 error = true;
666                                         break;
667                                 case 'D':
668                                         days += value;
669                                         if (parseStep > 2)
670                                                 error = true;
671                                         else
672                                                 parseStep = 3;
673                                         break;
674                                 case 'H':
675                                         hours = value;
676                                         if (!isTime || parseStep > 4)
677                                                 error = true;
678                                         else
679                                                 parseStep = 5;
680                                         break;
681                                 case 'S':
682                                         if (parseStep == 7)
683                                                 ticks = value;
684                                         else
685                                                 seconds = value;
686                                         if (!isTime || parseStep > 7)
687                                                 error = true;
688                                         else
689                                                 parseStep = 8;
690                                         break;
691                                 case '.':
692                                         if (parseStep > 7)
693                                                 error = true;
694                                         seconds = value;
695                                         parseStep = 7;
696                                         break;
697                                 default:
698                                         error = true;
699                                         break;
700                                 }
701                                 if (error)
702                                         break;
703                                 ++i;
704                                 start = i;
705                         }
706                         if (error)
707                                 throw new ArgumentException ("Invalid format string for duration schema datatype.");
708                         TimeSpan ts = new TimeSpan (days, hours, minutes, seconds);
709                         if (minusValue)
710                                 return TimeSpan.FromTicks (- (ts.Ticks + ticks));
711                         else
712                                 return TimeSpan.FromTicks (ts.Ticks + ticks);
713                 }
714
715                 [CLSCompliant (false)]
716                 public static UInt16 ToUInt16(string s)
717                 {
718                         return UInt16.Parse(s, CultureInfo.InvariantCulture);
719                 }
720
721                 [CLSCompliant (false)]
722                 public static UInt32 ToUInt32(string s)
723                 {
724                         return UInt32.Parse(s, CultureInfo.InvariantCulture);
725                 }
726
727                 [CLSCompliant (false)]
728                 public static UInt64 ToUInt64(string s)
729                 {
730                         return UInt64.Parse(s, CultureInfo.InvariantCulture);
731                 }
732
733                 public static string VerifyName (string name)
734                 {
735                         if (name == null || name.Length == 0)
736                                 throw new ArgumentNullException("name");
737
738                         if (!XmlChar.IsName (name))
739                                 throw new XmlException("'" + name + "' is not a valid XML Name");
740                         return name;
741                         
742                 }
743
744                 public static string VerifyNCName (string ncname)
745                 {
746                         if (ncname == null || ncname.Length == 0)
747                                 throw new ArgumentNullException("ncname");
748
749                         if (!XmlChar.IsNCName (ncname))
750                                 throw new XmlException ("'" + ncname + "' is not a valid XML NCName");
751                         return ncname;
752                 }
753
754 #if NET_2_0
755                 public static string VerifyTOKEN (string name)
756 #else
757                 internal static string VerifyTOKEN (string name)
758 #endif
759                 {
760                         if (name == null)
761                                 throw new ArgumentNullException("name");
762
763                         if (name.Length == 0)
764                                 return name;
765
766                         if (XmlChar.IsWhitespace (name [0]) ||
767                                 XmlChar.IsWhitespace (name [name.Length - 1]))
768                                 throw new XmlException ("Whitespace characters (#xA, #xD, #x9, #x20) are not allowed as leading or trailing whitespaces of xs:token.");
769
770                         for (int i = 0; i < name.Length; i++)
771                                 if (XmlChar.IsWhitespace (name [i]) && name [i] != ' ')
772                                 throw new XmlException ("Either #xA, #xD or #x9 are not allowed inside xs:token.");
773
774                         return name;
775                 }
776
777 #if NET_2_0
778                 public static string VerifyNMTOKEN (string name)
779 #else
780                 internal static string VerifyNMTOKEN (string name)
781 #endif
782                 {
783                         if (name == null)
784                                 throw new ArgumentNullException("name");
785
786                         if (!XmlChar.IsNmToken (name))
787                                 throw new XmlException("'" + name + "' is not a valid XML NMTOKEN");
788                         return name;
789                         
790                 }
791
792                 // It is documented as public method, but in fact it is not.
793                 internal static byte [] FromBinHexString (string s)
794                 {
795                         char [] chars = s.ToCharArray ();
796                         byte [] bytes = new byte [chars.Length / 2 + chars.Length % 2];
797                         FromBinHexString (chars, 0, chars.Length, bytes);
798                         return bytes;
799                 }
800
801                 internal static int FromBinHexString (char [] chars, int offset, int charLength, byte [] buffer)
802                 {
803                         int bufIndex = offset;
804                         for (int i = 0; i < charLength - 1; i += 2) {
805                                 buffer [bufIndex] = (chars [i] > '9' ?
806                                                 (byte) (chars [i] - 'A' + 10) :
807                                                 (byte) (chars [i] - '0'));
808                                 buffer [bufIndex] <<= 4;
809                                 buffer [bufIndex] += chars [i + 1] > '9' ?
810                                                 (byte) (chars [i + 1] - 'A' + 10) : 
811                                                 (byte) (chars [i + 1] - '0');
812                                 bufIndex++;
813                         }
814                         if (charLength %2 != 0)
815                                 buffer [bufIndex++] = (byte)
816                                         ((chars [charLength - 1] > '9' ?
817                                                 (byte) (chars [charLength - 1] - 'A' + 10) :
818                                                 (byte) (chars [charLength - 1] - '0'))
819                                         << 4);
820
821                         return bufIndex - offset;
822                 }
823
824 #if NET_2_0 // actually NET_3_5
825 #if !TARGET_JVM
826
827                 public static DateTimeOffset ToDateTimeOffset (string s)
828                 {
829                         return ToDateTimeOffset (s, datetimeFormats);
830                 }
831
832                 public static DateTimeOffset ToDateTimeOffset (string s, string format)
833                 {
834                         return ToDateTimeOffset (s, format);
835                 }
836
837                 public static DateTimeOffset ToDateTimeOffset (string s, string [] formats)
838                 {
839                         return ToDateTimeOffset (s, formats);
840                 }
841
842                 public static string ToString (DateTimeOffset value)
843                 {
844                         return ToString (value, "yyyy-MM-ddTHH:mm:ss.fffffffzzz");
845                 }
846
847                 public static string ToString (DateTimeOffset value, string format)
848                 {
849                         return value.ToString (format, CultureInfo.InvariantCulture);
850                 }
851 #endif
852 #endif
853         }
854 }