Merge pull request #819 from brendanzagaeski/patch-1
[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                 static DateTimeStyles _defaultStyle = DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite;
144                 
145                 public XmlConvert()
146                 {}
147
148                 private static string TryDecoding (string s)
149                 {
150                         if (s == null || s.Length < 6)
151                                 return s;
152
153                         char c = '\uFFFF';
154                         try {
155                                 c = (char) Int32.Parse (s.Substring (1, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
156                         } catch {
157                                 return s [0] + DecodeName (s.Substring (1));
158                         }
159                         
160                         if (s.Length == 6)
161                                 return c.ToString ();
162                         return c + DecodeName (s.Substring (6));
163                 }
164                 
165                 public static string DecodeName (string name)
166                 {
167                         if (name == null || name.Length == 0)
168                                 return name;
169
170                         int pos = name.IndexOf ('_');
171                         if (pos == -1 || pos + 6 >= name.Length)
172                                 return name;
173
174                         if ((name [pos + 1] != 'X' && name [pos + 1] != 'x') || name [pos + 6] != '_')
175                                 return name [0] + DecodeName (name.Substring (1));
176
177                         return name.Substring (0, pos) + TryDecoding (name.Substring (pos + 1));
178                 }
179
180                 public static string EncodeLocalName (string name)
181                 {
182                         if (name == null)
183                                 return name;
184
185                         string encoded = EncodeName (name);
186                         int pos = encoded.IndexOf (':');
187                         if (pos == -1)
188                                 return encoded;
189                         return encoded.Replace (":", encodedColon);
190                 }
191
192                 internal static bool IsInvalid (char c, bool firstOnlyLetter)
193                 {
194                         if (c == ':') // Special case. allowed in EncodeName, but encoded in EncodeLocalName
195                                 return false;
196                         
197                         if (firstOnlyLetter)
198                                 return !XmlChar.IsFirstNameChar (c);
199                         else
200                                 return !XmlChar.IsNameChar (c);
201                 }
202
203                 private static string EncodeName (string name, bool nmtoken)
204                 {
205                         if (name == null || name.Length == 0)
206                                 return name;
207
208                         StringBuilder sb = new StringBuilder ();
209                         int length = name.Length;
210                         for (int i = 0; i < length; i++) {
211                                 char c = name [i];
212                                 if (IsInvalid (c, i == 0 && !nmtoken))
213                                         sb.AppendFormat ("_x{0:X4}_", (int) c);
214                                 else if (c == '_' && i + 6 < length && name [i+1] == 'x' && name [i + 6] == '_')
215                                         sb.Append ("_x005F_");
216                                 else
217                                         sb.Append (c);
218                         }
219                         return sb.ToString ();
220                 }
221
222                 public static string EncodeName (string name)
223                 {
224                         return EncodeName (name, false);
225                 }
226                 
227                 public static string EncodeNmToken (string name)
228                 {
229                         if (name == String.Empty)
230                                 throw new XmlException ("Invalid NmToken: ''");
231                         return EncodeName (name, true);
232                 }
233
234                 // {true, false, 1, 0}
235                 public static bool ToBoolean(string s)
236                 {
237                         s = s.Trim (XmlChar.WhitespaceChars);
238                         switch(s)
239                         {
240                                 case "1":
241                                         return true;
242                                 case "true":
243                                         return true;
244                                 case "0":
245                                         return false;
246                                 case "false":
247                                         return false;
248                                 default:
249                                         throw new FormatException(s + " is not a valid boolean value");
250                         }
251                 }
252
253                 // LAMESPEC: It has been documented as public, but is marked as internal.
254                 internal static string ToBinHexString (byte [] buffer)
255                 {
256                         StringWriter w = new StringWriter ();
257                         WriteBinHex (buffer, 0, buffer.Length, w);
258                         return w.ToString ();
259                 }
260
261                 internal static void WriteBinHex (byte [] buffer, int index, int count, TextWriter w)
262                 {
263                         if (buffer == null)
264                                 throw new ArgumentNullException ("buffer");
265                         if (index < 0) {
266                                 throw new ArgumentOutOfRangeException (
267 #if !NET_2_1
268                                         "index", index,
269 #endif
270                                         "index must be non negative integer.");
271                         }
272                         if (count < 0) {
273                                 throw new ArgumentOutOfRangeException (
274 #if !NET_2_1
275                                         "count", count,
276 #endif
277                                         "count must be non negative integer.");
278                         }
279                         if (buffer.Length < index + count)
280                                 throw new ArgumentOutOfRangeException ("index and count must be smaller than the length of the buffer.");
281
282                         // Copied from XmlTextWriter.WriteBinHex ()
283                         int end = index + count;
284                         for (int i = index; i < end; i++) {
285                                 int val = buffer [i];
286                                 int high = val >> 4;
287                                 int low = val & 15;
288                                 if (high > 9)
289                                         w.Write ((char) (high + 55));
290                                 else
291                                         w.Write ((char) (high + 0x30));
292                                 if (low > 9)
293                                         w.Write ((char) (low + 55));
294                                 else
295                                         w.Write ((char) (low + 0x30));
296                         }
297                 }
298
299                 public static byte ToByte(string s)
300                 {
301                         return Byte.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
302                 }
303
304                 public static char ToChar(string s)
305                 {
306 #if !NET_2_1
307                         return Char.Parse(s);
308 #else
309                         if (s == null)
310                                 throw new ArgumentNullException ("s");
311
312                         if (s.Length != 1)
313                                 throw new FormatException ("String contain more than one char");
314
315                         return s [0];
316 #endif
317                 }
318
319 #if NET_2_0
320                 [Obsolete]
321 #endif
322                 public static DateTime ToDateTime (string s)
323                 {
324                         return ToDateTime (s, datetimeFormats);
325                 }
326                 
327 #if NET_2_0
328                 public static DateTime ToDateTime (string s, XmlDateTimeSerializationMode dateTimeOption)
329                 {
330                         switch (dateTimeOption) {
331                         case XmlDateTimeSerializationMode.Local:
332                 return ToDateTime(s, datetimeFormats, _defaultStyle | DateTimeStyles.AssumeLocal).ToLocalTime();
333                         case XmlDateTimeSerializationMode.RoundtripKind:
334                 return ToDateTime(s, datetimeFormats, _defaultStyle | DateTimeStyles.RoundtripKind);
335                         case XmlDateTimeSerializationMode.Utc:
336                 return ToDateTime(s, datetimeFormats, _defaultStyle | DateTimeStyles.AssumeUniversal).ToUniversalTime();
337                         default:
338                                 return new DateTime (ToDateTime(s, datetimeFormats, _defaultStyle | DateTimeStyles.RoundtripKind).Ticks, DateTimeKind.Unspecified);
339                         }
340                 }
341 #endif
342                 public static DateTime ToDateTime(string s, string format)
343                 {
344                         //DateTimeFormatInfo d = new DateTimeFormatInfo();
345                         //d.FullDateTimePattern = format;
346                         //return DateTime.Parse(s, d);
347                         DateTimeStyles style = DateTimeStyles.AllowLeadingWhite |
348                                                DateTimeStyles.AllowTrailingWhite;                       
349                         return DateTime.ParseExact (s, format, DateTimeFormatInfo.InvariantInfo, style);
350                 }
351
352                 public static DateTime ToDateTime(string s, string[] formats)
353                 {
354                         return ToDateTime (s, formats, _defaultStyle);                  
355                 }
356
357                 private static DateTime ToDateTime (string s, string [] formats, DateTimeStyles style) 
358                 {
359                         try {
360                                 return DateTime.ParseExact (s, formats, DateTimeFormatInfo.InvariantInfo, style);
361                         } catch (ArgumentOutOfRangeException) {
362                                 return DateTime.MinValue;
363                         }
364                 }
365                 
366                 public static Decimal ToDecimal(string s)
367                 {
368                         return Decimal.Parse(s, CultureInfo.InvariantCulture);
369                 }
370
371                 public static double ToDouble(string s)
372                 {
373                         if (s == null)
374                                 throw new ArgumentNullException();
375
376                         float f = TryParseStringFloatConstants (s);
377                         if (f != 0)
378                                 return f;
379
380                         return Double.Parse (s, floatStyle, CultureInfo.InvariantCulture);
381                 }
382
383                 static float TryParseStringFloatConstants (string s)
384                 {
385                         int sidx = 0;
386                         while (sidx < s.Length && Char.IsWhiteSpace (s [sidx]))
387                                 sidx++;
388                         if (sidx == s.Length)
389                                 throw new FormatException ();
390                         int sEndPos = s.Length - 1;
391                         while (Char.IsWhiteSpace (s [sEndPos]))
392                                 sEndPos--;
393
394                         if (TryParseStringConstant ("NaN", s, sidx, sEndPos))
395                                 return Single.NaN;
396                         if (TryParseStringConstant ("INF", s, sidx, sEndPos))
397                                 return Single.PositiveInfinity;
398                         if (TryParseStringConstant ("-INF", s, sidx, sEndPos))
399                                 return Single.NegativeInfinity;
400                         // Handle these here because Single.Parse("Infinity") is invalid while XmlConvert.ToSingle("Infinity") is valid.
401                         if (TryParseStringConstant ("Infinity", s, sidx, sEndPos))
402                                 return Single.PositiveInfinity;
403                         if (TryParseStringConstant ("-Infinity", s, sidx, sEndPos))
404                                 return Single.NegativeInfinity;
405                         return 0;
406                 }
407
408                 static bool TryParseStringConstant (string format, string s, int start, int end)
409                 {
410                         return end - start + 1 == format.Length && String.CompareOrdinal (format, 0, s, start, format.Length) == 0;
411                 }
412
413                 public static Guid ToGuid (string s)
414                 {
415                         try {
416                                 return new Guid(s);
417                         } catch (FormatException ex) {
418                                 throw new FormatException (String.Format ("Invalid Guid input '{0}'", ex.InnerException));
419                         }
420                 }
421
422                 public static short ToInt16(string s)
423                 {
424                         return Int16.Parse (s, integerStyle, CultureInfo.InvariantCulture);
425                 }
426
427                 public static int ToInt32(string s)
428                 {
429                         return Int32.Parse (s, integerStyle, CultureInfo.InvariantCulture);
430                 }
431
432                 public static long ToInt64(string s)
433                 {
434                         return Int64.Parse (s, integerStyle, CultureInfo.InvariantCulture);
435                 }
436
437                 [CLSCompliant (false)]
438                 public static SByte ToSByte(string s)
439                 {
440                         return SByte.Parse(s, integerStyle, CultureInfo.InvariantCulture);
441                 }
442
443                 public static float ToSingle(string s)
444                 {
445                         if (s == null)
446                                 throw new ArgumentNullException();
447
448                         float f = TryParseStringFloatConstants (s);
449                         if (f != 0)
450                                 return f;
451
452                         return Single.Parse(s, floatStyle, CultureInfo.InvariantCulture);
453                 }
454
455                 public static string ToString(Guid value)
456                 {
457                         return value.ToString("D", CultureInfo.InvariantCulture);
458                 }
459
460                 public static string ToString(int value)
461                 {
462                         return value.ToString(CultureInfo.InvariantCulture);
463                 }
464
465                 public static string ToString(short value)
466                 {
467                         return value.ToString(CultureInfo.InvariantCulture);
468                 }
469
470                 public static string ToString(byte value)
471                 {
472                         return value.ToString(CultureInfo.InvariantCulture);
473                 }
474
475                 public static string ToString(long value)
476                 {
477                         return value.ToString(CultureInfo.InvariantCulture);
478                 }
479
480                 public static string ToString(char value)
481                 {
482                         return value.ToString(CultureInfo.InvariantCulture);
483                 }
484
485                 public static string ToString(bool value)
486                 {
487                         if (value) return "true";
488                         return "false";
489                 }
490
491                 [CLSCompliant (false)]
492                 public static string ToString(SByte value)
493                 {
494                         return value.ToString(CultureInfo.InvariantCulture);
495                 }
496
497                 public static string ToString(Decimal value)
498                 {
499                         return value.ToString (CultureInfo.InvariantCulture);
500                 }
501
502                 [CLSCompliant (false)]
503                 public static string ToString(UInt64 value)
504                 {
505                         return value.ToString(CultureInfo.InvariantCulture);
506                 }
507
508                 public static string ToString (TimeSpan value)
509                 {
510                         if (value == TimeSpan.Zero)
511                                 return "PT0S";
512
513                         StringBuilder builder = new StringBuilder ();
514                         if (value.Ticks < 0) {
515                                 if (value == TimeSpan.MinValue)
516                                         return "-P10675199DT2H48M5.4775808S";  // There's one fewer tick on the positive side, so we cannot Negate this value; just hard-code it
517                                 builder.Append ('-');
518                                 value = value.Negate ();
519                         }
520                         builder.Append ('P');
521                         if (value.Days > 0)
522                                 builder.Append (value.Days).Append ('D');
523                         long ticks = value.Ticks % TimeSpan.TicksPerMillisecond;
524                         if (value.Hours > 0 || value.Minutes > 0 || value.Seconds > 0 || value.Milliseconds > 0 || ticks > 0) {
525                                 builder.Append('T');
526                                 if (value.Hours > 0)
527                                         builder.Append (value.Hours).Append ('H');
528                                 if (value.Minutes > 0) 
529                                         builder.Append (value.Minutes).Append ('M');
530                                 if (value.Seconds > 0 || value.Milliseconds > 0 || ticks > 0) {
531                                         builder.Append (value.Seconds);
532                                         bool trimZero = true;
533                                         if (ticks > 0)
534                                                 builder.Append ('.').AppendFormat ("{0:0000000}", value.Ticks % TimeSpan.TicksPerSecond);
535                                         else if (value.Milliseconds > 0)
536                                                 builder.Append ('.').AppendFormat ("{0:000}", value.Milliseconds);
537                                         else
538                                                 trimZero = false;
539                                         if (trimZero)
540                                                 while (builder [builder.Length - 1] == '0')
541                                                         builder.Remove (builder.Length - 1, 1);
542
543                                         builder.Append ('S');
544                                 }
545                         }
546                         return builder.ToString ();
547                 }
548
549                 public static string ToString(double value)
550                 {
551                         if (Double.IsNegativeInfinity(value)) return "-INF";
552                         if (Double.IsPositiveInfinity(value)) return "INF";
553                         if (Double.IsNaN(value)) return "NaN";
554                         return value.ToString("R", CultureInfo.InvariantCulture);
555                 }
556
557                 public static string ToString(float value)
558                 {
559                         if (Single.IsNegativeInfinity(value)) return "-INF";
560                         if (Single.IsPositiveInfinity(value)) return "INF";
561                         if (Single.IsNaN(value)) return "NaN";
562                         return value.ToString("R", CultureInfo.InvariantCulture);
563                 }
564
565                 [CLSCompliant (false)]
566                 public static string ToString(UInt32 value)
567                 {
568                         return value.ToString(CultureInfo.InvariantCulture);
569                 }
570
571                 [CLSCompliant (false)]
572                 public static string ToString(UInt16 value)
573                 {
574                         return value.ToString(CultureInfo.InvariantCulture);
575                 }
576
577 #if NET_2_0
578                 [Obsolete]
579 #endif
580                 public static string ToString (DateTime value)
581                 {
582                         return value.ToString ("yyyy-MM-ddTHH:mm:ss.fffffffzzz", CultureInfo.InvariantCulture);
583                 }
584
585 #if NET_2_0
586                 public static string ToString (DateTime value, XmlDateTimeSerializationMode dateTimeOption)
587                 {
588                         // Unlike usual DateTime formatting, it preserves
589                         // MaxValue/MinValue as is.
590                         switch (dateTimeOption) {
591                         case XmlDateTimeSerializationMode.Local:
592                                 return (value == DateTime.MinValue ? DateTime.MinValue : value == DateTime.MaxValue ? value : value.ToLocalTime ()).ToString (
593                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
594                                         CultureInfo.InvariantCulture);
595                         case XmlDateTimeSerializationMode.RoundtripKind:
596                                 return value.ToString (
597                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
598                                         CultureInfo.InvariantCulture);
599                         default:
600                                 return value.ToString (
601                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
602                                         CultureInfo.InvariantCulture);
603                         case XmlDateTimeSerializationMode.Utc:
604                                 return (value == DateTime.MinValue ? DateTime.MinValue : value == DateTime.MaxValue ? value : value.ToUniversalTime ()).ToString (
605                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFFZ",
606                                         CultureInfo.InvariantCulture);
607                         case XmlDateTimeSerializationMode.Unspecified:
608                                 return value.ToString (
609                                         "yyyy-MM-ddTHH:mm:ss.FFFFFFF",
610                                         CultureInfo.InvariantCulture);
611                         }
612                 }
613 #endif
614
615                 public static string ToString(DateTime value, string format)
616                 {
617                         return value.ToString(format, CultureInfo.InvariantCulture);
618                 }
619
620                 public static TimeSpan ToTimeSpan(string s)
621                 {
622                         s = s.Trim (XmlChar.WhitespaceChars);
623                         if (s.Length == 0)
624                                 throw new FormatException ("Invalid format string for duration schema datatype.");
625
626                         int start = 0;
627                         if (s [0] == '-')
628                                 start = 1;
629                         bool minusValue = (start == 1);
630
631                         if (s [start] != 'P')
632                                 throw new FormatException ("Invalid format string for duration schema datatype.");
633                         start++;
634
635                         int parseStep = 0;
636                         int days = 0;
637                         bool isTime = false;
638                         int hours = 0;
639                         int minutes = 0;
640                         int seconds = 0;
641                         long ticks = 0;
642                         int parsedDigits = 0;
643
644                         bool error = false;
645
646                         int i = start;
647                         while (i < s.Length) {
648                                 if (s [i] == 'T') {
649                                         isTime = true;
650                                         parseStep = 4;
651                                         i++;
652                                         start = i;
653                                         continue;
654                                 }
655                                 for (; i < s.Length; i++)
656                                         if (s [i] < '0' || '9' < s [i])
657                                                 break;
658                                 if (parseStep == 7)
659                                         parsedDigits = i - start;
660                                 int value = int.Parse (s.Substring (start, i - start), CultureInfo.InvariantCulture);
661                                 if (parseStep == 7) {
662                                         // adjust to 7 digits so that it makes sense as millisecond digits
663                                         for (; parsedDigits > 7; parsedDigits--)
664                                                 value /= 10;
665                                         for (; parsedDigits < 7; parsedDigits++)
666                                                 value *= 10;
667                                 }
668                                 switch (s [i]) {
669                                 case 'Y':
670                                         days += value * 365;
671                                         if (parseStep > 0)
672                                                 error = true;
673                                         else
674                                                 parseStep = 1;
675                                         break;
676                                 case 'M':
677                                         if (parseStep < 2) {
678                                                 days += 365 * (value / 12) + 30 * (value % 12);
679                                                 parseStep = 2;
680                                         } else if (isTime && parseStep < 6) {
681                                                 minutes = value;
682                                                 parseStep = 6;
683                                         }
684                                         else
685                                                 error = true;
686                                         break;
687                                 case 'D':
688                                         days += value;
689                                         if (parseStep > 2)
690                                                 error = true;
691                                         else
692                                                 parseStep = 3;
693                                         break;
694                                 case 'H':
695                                         hours = value;
696                                         if (!isTime || parseStep > 4)
697                                                 error = true;
698                                         else
699                                                 parseStep = 5;
700                                         break;
701                                 case 'S':
702                                         if (parseStep == 7)
703                                                 ticks = value;
704                                         else
705                                                 seconds = value;
706                                         if (!isTime || parseStep > 7)
707                                                 error = true;
708                                         else
709                                                 parseStep = 8;
710                                         break;
711                                 case '.':
712                                         if (parseStep > 7)
713                                                 error = true;
714                                         seconds = value;
715                                         parseStep = 7;
716                                         break;
717                                 default:
718                                         error = true;
719                                         break;
720                                 }
721                                 if (error)
722                                         break;
723                                 ++i;
724                                 start = i;
725                         }
726                         if (error)
727                                 throw new FormatException ("Invalid format string for duration schema datatype.");
728                         TimeSpan ts = new TimeSpan (days, hours, minutes, seconds);
729                         if (minusValue)
730                                 return TimeSpan.FromTicks (- (ts.Ticks + ticks));
731                         else
732                                 return TimeSpan.FromTicks (ts.Ticks + ticks);
733                 }
734
735                 [CLSCompliant (false)]
736                 public static UInt16 ToUInt16(string s)
737                 {
738                         return UInt16.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
739                 }
740
741                 [CLSCompliant (false)]
742                 public static UInt32 ToUInt32(string s)
743                 {
744                         return UInt32.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
745                 }
746
747                 [CLSCompliant (false)]
748                 public static UInt64 ToUInt64(string s)
749                 {
750                         return UInt64.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
751                 }
752
753                 public static string VerifyName (string name)
754                 {
755                         if (name == null || name.Length == 0)
756                                 throw new ArgumentNullException("name");
757
758                         if (!XmlChar.IsName (name))
759                                 throw new XmlException("'" + name + "' is not a valid XML Name");
760                         return name;
761                         
762                 }
763
764                 public static string VerifyNCName (string name)
765                 {
766                         if (name == null || name.Length == 0)
767                                 throw new ArgumentNullException("name");
768
769                         if (!XmlChar.IsNCName (name))
770                                 throw new XmlException ("'" + name + "' is not a valid XML NCName");
771                         return name;
772                 }
773
774                 public static string VerifyTOKEN (string token)
775                 {
776                         if (token == null)
777                                 throw new ArgumentNullException("token");
778
779                         if (token.Length == 0)
780                                 return token;
781
782                         if (XmlChar.IsWhitespace (token [0]) ||
783                                 XmlChar.IsWhitespace (token [token.Length - 1]))
784                                 throw new XmlException ("Whitespace characters (#xA, #xD, #x9, #x20) are not allowed as leading or trailing whitespaces of xs:token.");
785
786                         for (int i = 0; i < token.Length; i++)
787                                 if (XmlChar.IsWhitespace (token [i]) && token [i] != ' ')
788                                         throw new XmlException ("Either #xA, #xD or #x9 are not allowed inside xs:token.");
789
790                         return token;
791                 }
792
793 #if NET_2_0
794                 public static string VerifyNMTOKEN (string name)
795 #else
796                 internal static string VerifyNMTOKEN (string name)
797 #endif
798                 {
799                         if (name == null)
800                                 throw new ArgumentNullException("name");
801
802                         if (!XmlChar.IsNmToken (name))
803                                 throw new XmlException("'" + name + "' is not a valid XML NMTOKEN");
804                         return name;
805                         
806                 }
807
808                 // It is documented as public method, but in fact it is not.
809                 internal static byte [] FromBinHexString (string s)
810                 {
811                         char [] chars = s.ToCharArray ();
812                         byte [] bytes = new byte [chars.Length / 2 + chars.Length % 2];
813                         FromBinHexString (chars, 0, chars.Length, bytes);
814                         return bytes;
815                 }
816
817                 internal static int FromBinHexString (char [] chars, int offset, int charLength, byte [] buffer)
818                 {
819                         int bufIndex = offset;
820                         for (int i = 0; i < charLength - 1; i += 2) {
821                                 buffer [bufIndex] = (chars [i] > '9' ?
822                                                 (byte) (chars [i] - 'A' + 10) :
823                                                 (byte) (chars [i] - '0'));
824                                 buffer [bufIndex] <<= 4;
825                                 buffer [bufIndex] += chars [i + 1] > '9' ?
826                                                 (byte) (chars [i + 1] - 'A' + 10) : 
827                                                 (byte) (chars [i + 1] - '0');
828                                 bufIndex++;
829                         }
830                         if (charLength %2 != 0)
831                                 buffer [bufIndex++] = (byte)
832                                         ((chars [charLength - 1] > '9' ?
833                                                 (byte) (chars [charLength - 1] - 'A' + 10) :
834                                                 (byte) (chars [charLength - 1] - '0'))
835                                         << 4);
836
837                         return bufIndex - offset;
838                 }
839
840 #if NET_2_0 // actually NET_3_5
841 #if !TARGET_JVM
842
843                 public static DateTimeOffset ToDateTimeOffset (string s)
844                 {
845                         return ToDateTimeOffset (s, datetimeFormats);
846                 }
847
848                 public static DateTimeOffset ToDateTimeOffset (string s, string format)
849                 {
850                         return DateTimeOffset.ParseExact (s, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
851                 }
852
853                 public static DateTimeOffset ToDateTimeOffset (string s, string [] formats)
854                 {
855                         DateTimeStyles style = DateTimeStyles.AllowLeadingWhite |
856                                                DateTimeStyles.AllowTrailingWhite |
857                                                DateTimeStyles.AssumeUniversal;
858                         return DateTimeOffset.ParseExact (s, formats, CultureInfo.InvariantCulture, style);
859                 }
860
861                 public static string ToString (DateTimeOffset value)
862                 {
863                         return ToString (value, "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz");
864                 }
865
866                 public static string ToString (DateTimeOffset value, string format)
867                 {
868                         return value.ToString (format, CultureInfo.InvariantCulture);
869                 }
870 #endif
871
872                 // it is used only from 2.1 System.Xml.Serialization.dll from
873                 // MS Silverlight SDK. We don't use it so far.
874                 internal static Uri ToUri (string s)
875                 {
876                         return new Uri (s, UriKind.RelativeOrAbsolute);
877                 }
878
879 #endif
880
881 #if NET_4_0
882                 public static bool IsNCNameChar (char ch)
883                 {
884                         return XmlChar.IsNCNameChar (ch);
885                 }
886
887                 public static bool IsPublicIdChar (char ch)
888                 {
889                         return XmlChar.IsPubidChar (ch);
890                 }
891
892                 public static bool IsStartNCNameChar (char ch)
893                 {
894                         return XmlChar.IsFirstNameChar (ch);
895                 }
896
897                 public static bool IsWhitespaceChar (char ch)
898                 {
899                         return XmlChar.IsWhitespace (ch);
900                 }
901
902                 public static bool IsXmlChar (char ch)
903                 {
904                         return XmlChar.IsValid (ch);
905                 }
906
907                 public static bool IsXmlSurrogatePair (char lowChar, char highChar)
908                 {
909                         return 0xD800 <= lowChar && lowChar <= 0xDBFF && 0xDC00 <= highChar && highChar <= 0xDFFF;
910                 }
911                 
912                 public static string VerifyPublicId (string publicId)
913                 {
914                         if (publicId == null)
915                                 throw new ArgumentNullException ("publicId");
916                         if (XmlChar.IsPubid (publicId))
917                                 return publicId;
918                         throw new XmlException (string.Format ("'{0}' is not a valid PUBLIC ID", publicId));
919                 }
920
921                 public static string VerifyWhitespace (string content)
922                 {
923                         if (content == null)
924                                 throw new ArgumentNullException ("content");
925                         if (XmlChar.IsWhitespace (content))
926                                 return content;
927                         throw new XmlException (string.Format ("'{0}' is not whitespace", content));
928                 }
929
930                 public static string VerifyXmlChars (string content)
931                 {
932                         if (content == null)
933                                 throw new ArgumentNullException ("content");
934                         var idx = XmlChar.IndexOfInvalid (content, true);
935                         if (idx < 0)
936                                 return content;
937                         throw new XmlException (string.Format ("Invalid XML character was found in the content, at index {0}.", idx));
938                 }
939 #endif
940         }
941 }