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