b28d42eb6a66a0b9edb29c130f34704cc813a02d
[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.h-ho.ne.jp)
9 //
10 // (C) 2002 Ximian, Inc (http://www.ximian.com)
11 //
12 using System;
13 using System.Text;
14 using System.Globalization;
15 using System.Xml.Schema;
16
17 namespace System.Xml {
18
19         public class XmlConvert {
20
21                 static string encodedColon;
22                 static string [] datetimeFormats;
23
24                 static XmlConvert ()
25                 {
26                         encodedColon = "_x003A_";
27                         datetimeFormats = new string[] {
28                           // dateTime
29                           "yyyy-MM-ddTHH:mm:ss",
30                           "yyyy-MM-ddTHH:mm:ss.f",
31                           "yyyy-MM-ddTHH:mm:ss.ff",
32                           "yyyy-MM-ddTHH:mm:ss.fff",
33                           "yyyy-MM-ddTHH:mm:ss.ffff",
34                           "yyyy-MM-ddTHH:mm:ss.fffff",
35                           "yyyy-MM-ddTHH:mm:ss.ffffff",
36                           "yyyy-MM-ddTHH:mm:ss.fffffff",
37                           "yyyy-MM-ddTHH:mm:sszzz",
38                           "yyyy-MM-ddTHH:mm:ss.fzzz",
39                           "yyyy-MM-ddTHH:mm:ss.ffzzz",
40                           "yyyy-MM-ddTHH:mm:ss.fffzzz",
41                           "yyyy-MM-ddTHH:mm:ss.ffffzzz",
42                           "yyyy-MM-ddTHH:mm:ss.fffffzzz",
43                           "yyyy-MM-ddTHH:mm:ss.ffffffzzz",
44                           "yyyy-MM-ddTHH:mm:ss.fffffffzzz",
45                           "yyyy-MM-ddTHH:mm:ssZ",
46                           "yyyy-MM-ddTHH:mm:ss.fZ",
47                           "yyyy-MM-ddTHH:mm:ss.ffZ",
48                           "yyyy-MM-ddTHH:mm:ss.fffZ",
49                           "yyyy-MM-ddTHH:mm:ss.ffffZ",
50                           "yyyy-MM-ddTHH:mm:ss.fffffZ",
51                           "yyyy-MM-ddTHH:mm:ss.ffffffZ",
52                           "yyyy-MM-ddTHH:mm:ss.fffffffZ",
53                           // time
54                           "HH:mm:ss",
55                           "HH:mm:ss.f",
56                           "HH:mm:ss.ff",
57                           "HH:mm:ss.fff",
58                           "HH:mm:ss.ffff",
59                           "HH:mm:ss.fffff",
60                           "HH:mm:ss.ffffff",
61                           "HH:mm:ss.fffffff",
62                           "HH:mm:sszzz",
63                           "HH:mm:ss.fzzz",
64                           "HH:mm:ss.ffzzz",
65                           "HH:mm:ss.fffzzz",
66                           "HH:mm:ss.ffffzzz",
67                           "HH:mm:ss.fffffzzz",
68                           "HH:mm:ss.ffffffzzz",
69                           "HH:mm:ss.fffffffzzz",
70                           "HH:mm:ssZ",
71                           "HH:mm:ss.fZ",
72                           "HH:mm:ss.ffZ",
73                           "HH:mm:ss.fffZ",
74                           "HH:mm:ss.ffffZ",
75                           "HH:mm:ss.fffffZ",
76                           "HH:mm:ss.ffffffZ",
77                           "HH:mm:ss.fffffffZ",
78                           // date
79                           "yyyy-MM-dd",
80                           "yyyy-MM-ddzzz",
81                           "yyyy-MM-ddZ",
82                           // gYearMonth
83                           "yyyy-MM",
84                           "yyyy-MMzzz",
85                           "yyyy-MMZ",
86                           // gYear
87                           "yyyy",
88                           "yyyyzzz",
89                           "yyyyZ",
90                           // gMonthDay
91                           "--MM-dd",
92                           "--MM-ddzzz",
93                           "--MM-ddZ",
94                           // gDay
95                           "---dd",
96                           "---ddzzz",
97                           "---ddZ",
98                         };
99                 }
100
101                 public XmlConvert()
102                 {}
103
104                 private static string TryDecoding (string s)
105                 {
106                         if (s == null || s.Length < 6)
107                                 return s;
108
109                         char c = '\uFFFF';
110                         try {
111                                 c = (char) Int32.Parse (s.Substring (1, 4), NumberStyles.HexNumber);
112                         } catch {
113                                 return s [0] + DecodeName (s.Substring (1));
114                         }
115                         
116                         if (s.Length == 6)
117                                 return c.ToString ();
118                         return c + DecodeName (s.Substring (6));
119                 }
120                 
121                 public static string DecodeName (string name)
122                 {
123                         if (name == null || name.Length == 0)
124                                 return name;
125
126                         int pos = name.IndexOf ('_');
127                         if (pos == -1 || pos + 6 >= name.Length)
128                                 return name;
129
130                         if (Char.ToUpper (name [pos + 1]) != 'X' || name [pos + 6] != '_')
131                                 return name [0] + DecodeName (name.Substring (1));
132
133                         return name.Substring (0, pos) + TryDecoding (name.Substring (pos + 1));
134                 }
135
136                 public static string EncodeLocalName (string name)
137                 {
138                         string encoded = EncodeName (name);
139                         int pos = encoded.IndexOf (':');
140                         if (pos == -1)
141                                 return encoded;
142                         return encoded.Replace (":", encodedColon);
143                 }
144
145                 internal static bool IsInvalid (char c, bool firstOnlyLetter)
146                 {
147                         if (c == ':') // Special case. allowed in EncodeName, but encoded in EncodeLocalName
148                                 return false;
149                         
150                         if (firstOnlyLetter)
151                                 return !XmlChar.IsFirstNameChar (c);
152                         else
153                                 return !XmlChar.IsNameChar (c);
154                 }
155
156                 private static string EncodeName (string name, bool nmtoken)
157                 {
158                         StringBuilder sb = new StringBuilder ();
159                         int length = name.Length;
160                         for (int i = 0; i < length; i++) {
161                                 char c = name [i];
162                                 if (IsInvalid (c, i == 0 && !nmtoken))
163                                         sb.AppendFormat ("_x{0:X4}_", (int) c);
164                                 else if (c == '_' && i + 6 < length && name [i+1] == 'x' && name [i + 6] == '_')
165                                         sb.Append ("_x005F_");
166                                 else
167                                         sb.Append (c);
168                         }
169                         return sb.ToString ();
170                 }
171
172                 public static string EncodeName (string name)
173                 {
174                         return EncodeName (name, false);
175                 }
176                 
177                 public static string EncodeNmToken(string name)
178                 {
179                         return EncodeName (name, true);
180                 }
181
182                 // {true, false, 1, 0}
183                 public static bool ToBoolean(string s)
184                 {
185                         s = s.Trim (XmlChar.WhitespaceChars);
186                         switch(s)
187                         {
188                                 case "1":
189                                         return true;
190                                 case "true":
191                                         return true;
192                                 case "0":
193                                         return false;
194                                 case "false":
195                                         return false;
196                                 default:
197                                         throw new FormatException(s + " is not a valid boolean value");
198                         }
199                 }
200
201                 public static byte ToByte(string s)
202                 {
203                         return Byte.Parse(s, CultureInfo.InvariantCulture);
204                 }
205
206                 public static char ToChar(string s)
207                 {
208                         return Char.Parse(s);
209                 }
210
211                 public static DateTime ToDateTime(string s)
212                 {
213                         return ToDateTime(s, datetimeFormats);
214                 }
215                 
216                 public static DateTime ToDateTime(string s, string format)
217                 {
218                         DateTimeFormatInfo d = new DateTimeFormatInfo();
219                         d.FullDateTimePattern = format;
220                         return DateTime.Parse(s, d);
221                 }
222
223                 public static DateTime ToDateTime(string s, string[] formats)
224                 {
225                         DateTimeStyles style = DateTimeStyles.AllowLeadingWhite |
226                                                DateTimeStyles.AllowTrailingWhite;
227                         return DateTime.ParseExact (s, formats, DateTimeFormatInfo.InvariantInfo, style);
228                 }
229                 
230                 public static Decimal ToDecimal(string s)
231                 {
232                         return Decimal.Parse(s, NumberFormatInfo.InvariantInfo);
233                 }
234                 
235                 public static double ToDouble(string s)
236                 {
237                         if (s == "INF") return System.Double.PositiveInfinity;
238                         if (s == "-INF") return System.Double.NegativeInfinity;
239                         if (s == "NaN") return System.Double.NaN;
240                         return Double.Parse(s, CultureInfo.InvariantCulture);
241                 }
242
243                 public static Guid ToGuid(string s)
244                 {
245                         return new Guid(s);
246                 }
247
248                 public static short ToInt16(string s)
249                 {
250                         return Int16.Parse(s, CultureInfo.InvariantCulture);
251                 }
252
253                 public static int ToInt32(string s)
254                 {
255                         return Int32.Parse(s, CultureInfo.InvariantCulture);
256                 }
257
258                 public static long ToInt64(string s)
259                 {
260                         return Int64.Parse(s, CultureInfo.InvariantCulture);
261                 }
262
263                 [CLSCompliant (false)]
264                 public static SByte ToSByte(string s)
265                 {
266                         return SByte.Parse(s, CultureInfo.InvariantCulture);
267                 }
268
269                 public static float ToSingle(string s)
270                 {
271                         if (s == "INF") return System.Single.PositiveInfinity;
272                         if (s == "-INF") return System.Single.NegativeInfinity;
273                         if (s == "NaN") return System.Single.NaN;
274                         return Single.Parse(s, CultureInfo.InvariantCulture);
275                 }
276
277                 public static string ToString(Guid value)
278                 {
279                         return value.ToString("D",CultureInfo.InvariantCulture);
280                 }
281
282                 public static string ToString(int value)
283                 {
284                         return value.ToString(CultureInfo.InvariantCulture);
285                 }
286
287                 public static string ToString(short value)
288                 {
289                         return value.ToString(CultureInfo.InvariantCulture);
290                 }
291
292                 public static string ToString(byte value)
293                 {
294                         return value.ToString(CultureInfo.InvariantCulture);
295                 }
296
297                 public static string ToString(long value)
298                 {
299                         return value.ToString(CultureInfo.InvariantCulture);
300                 }
301
302                 public static string ToString(char value)
303                 {
304                         return value.ToString(CultureInfo.InvariantCulture);
305                 }
306
307                 public static string ToString(bool value)
308                 {
309                         if (value) return "true";
310                         return "false";
311                 }
312
313                 [CLSCompliant (false)]
314                 public static string ToString(SByte value)
315                 {
316                         return value.ToString(CultureInfo.InvariantCulture);
317                 }
318
319                 public static string ToString(Decimal value)
320                 {
321                         return value.ToString(CultureInfo.InvariantCulture);
322                 }
323
324                 [CLSCompliant (false)]
325                 public static string ToString(UInt64 value)
326                 {
327                         return value.ToString(CultureInfo.InvariantCulture);
328                 }
329
330                 public static string ToString(TimeSpan value)
331                 {
332                         StringBuilder builder = new StringBuilder();
333                         if (value.Ticks < 0) {
334                                 builder.Append('-');
335                                 value = value.Negate();
336                         }
337                         builder.Append('P');
338                         if (value.Days > 0) builder.Append(value.Days).Append('D');
339                         if (value.Days > 0 || value.Minutes > 0 || value.Seconds > 0 || value.Milliseconds > 0) {
340                                 builder.Append('T');
341                                 if (value.Hours > 0) builder.Append(value.Hours).Append('D');
342                                 if (value.Minutes > 0) builder.Append(value.Minutes).Append('M');
343                                 if (value.Seconds > 0 || value.Milliseconds > 0) {
344                                         builder.Append(value.Seconds);
345                                         if (value.Milliseconds > 0) builder.Append('.').Append(String.Format("{0:000}", value.Milliseconds));
346                                         builder.Append('S');
347                                 }
348                         }
349                         return builder.ToString();
350                 }
351
352                 public static string ToString(double value)
353                 {
354                         if (Double.IsNegativeInfinity(value)) return "-INF";
355                         if (Double.IsPositiveInfinity(value)) return "INF";
356                         if (Double.IsNaN(value)) return "NaN";
357                         return value.ToString(CultureInfo.InvariantCulture);
358                 }
359
360                 public static string ToString(float value)
361                 {
362                         if (Single.IsNegativeInfinity(value)) return "-INF";
363                         if (Single.IsPositiveInfinity(value)) return "INF";
364                         if (Single.IsNaN(value)) return "NaN";
365                         return value.ToString(CultureInfo.InvariantCulture);
366                 }
367
368                 [CLSCompliant (false)]
369                 public static string ToString(UInt32 value)
370                 {
371                         return value.ToString(CultureInfo.InvariantCulture);
372                 }
373
374                 [CLSCompliant (false)]
375                 public static string ToString(UInt16 value)
376                 {
377                         return value.ToString(CultureInfo.InvariantCulture);
378                 }
379
380                 public static string ToString(DateTime value)
381                 {
382                         return value.ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz", CultureInfo.InvariantCulture);
383                 }
384
385                 public static string ToString(DateTime value, string format)
386                 {
387                         return value.ToString(format, CultureInfo.InvariantCulture);
388                 }
389
390                 public static TimeSpan ToTimeSpan(string s)
391                 {
392                         if (s.Length == 0)
393                                 throw new ArgumentException ("Invalid format string for duration schema datatype.");
394
395                         int start = 0;
396                         if (s [0] == '-')
397                                 start = 1;
398                         bool minusValue = (start == 1);
399
400                         if (s [start] != 'P')
401                                 throw new ArgumentException ("Invalid format string for duration schema datatype.");
402                         start++;
403
404                         int parseStep = 0;
405                         int days = 0;
406                         bool isTime = false;
407                         int hours = 0;
408                         int minutes = 0;
409                         int seconds = 0;
410
411                         bool error = false;
412
413                         int i = start;
414                         while (i < s.Length) {
415                                 if (s [i] == 'T') {
416                                         isTime = true;
417                                         parseStep = 4;
418                                         i++;
419                                         start = i;
420                                         continue;
421                                 }
422                                 for (; i < s.Length; i++) {
423                                         if (!Char.IsDigit (s [i]))
424                                                 break;
425                                 }
426                                 int value = int.Parse (s.Substring (start, i - start));
427                                 switch (s [i]) {
428                                 case 'Y':
429                                         days += value * 365;
430                                         if (parseStep > 0)
431                                                 error = true;
432                                         else
433                                                 parseStep = 1;
434                                         break;
435                                 case 'M':
436                                         if (parseStep < 2) {
437                                                 days += 365 * (value / 12) + 30 * (value % 12);
438                                                 parseStep = 2;
439                                         } else if (isTime && parseStep < 6) {
440                                                 minutes = value;
441                                                 parseStep = 6;
442                                         }
443                                         else
444                                                 error = true;
445                                         break;
446                                 case 'D':
447                                         days += value;
448                                         if (parseStep > 2)
449                                                 error = true;
450                                         else
451                                                 parseStep = 3;
452                                         break;
453                                 case 'H':
454                                         hours = value;
455                                         if (!isTime || parseStep > 4)
456                                                 error = true;
457                                         else
458                                                 parseStep = 5;
459                                         break;
460                                 case 'S':
461                                         seconds = value;
462                                         if (!isTime || parseStep > 6)
463                                                 error = true;
464                                         else
465                                                 parseStep = 7;
466                                         break;
467                                 default:
468                                         error = true;
469                                         break;
470                                 }
471                                 if (error)
472                                         break;
473                                 ++i;
474                                 start = i;
475                         }
476                         if (error)
477                                 throw new ArgumentException ("Invalid format string for duration schema datatype.");
478                         TimeSpan ts = new TimeSpan (days, hours, minutes, seconds);
479                         return minusValue ? -ts : ts;
480                 }
481
482                 [CLSCompliant (false)]
483                 public static UInt16 ToUInt16(string s)
484                 {
485                         return UInt16.Parse(s, CultureInfo.InvariantCulture);
486                 }
487
488                 [CLSCompliant (false)]
489                 public static UInt32 ToUInt32(string s)
490                 {
491                         return UInt32.Parse(s, CultureInfo.InvariantCulture);
492                 }
493
494                 [CLSCompliant (false)]
495                 public static UInt64 ToUInt64(string s)
496                 {
497                         return UInt64.Parse(s, CultureInfo.InvariantCulture);
498                 }
499
500                 public static string VerifyName (string name)
501                 {
502                         if(name == null)
503                                 throw new ArgumentNullException("name");
504
505                         if(!XmlChar.IsName (name))
506                                 throw new XmlException("'" + name + "' is not a valid XML Name");
507                         return name;
508                         
509                 }
510
511                 public static string VerifyNCName(string ncname)
512                 {
513                         if(ncname == null)
514                                 throw new ArgumentNullException("ncname");
515
516                         if(!XmlChar.IsNCName (ncname))
517                                 throw new XmlException ("'" + ncname + "' is not a valid XML NCName");
518                         return ncname;
519                 }
520
521                 // It is documented as public method, but in fact it is not.
522                 internal static byte [] FromBinHexString (string s)
523                 {
524                         char [] chars = s.ToCharArray ();
525                         byte [] bytes = new byte [chars.Length / 2 + chars.Length % 2];
526                         FromBinHexString (chars, 0, chars.Length, bytes);
527                         return bytes;
528                 }
529
530                 internal static int FromBinHexString (char [] chars, int offset, int charLength, byte [] buffer)
531                 {
532                         int bufIndex = offset;
533                         for (int i = 0; i < charLength - 1; i += 2) {
534                                 buffer [bufIndex] = (chars [i] > '9' ?
535                                                 (byte) (chars [i] - 'A' + 10) :
536                                                 (byte) (chars [i] - '0'));
537                                 buffer [bufIndex] <<= 4;
538                                 buffer [bufIndex] += chars [i + 1] > '9' ?
539                                                 (byte) (chars [i + 1] - 'A' + 10) : 
540                                                 (byte) (chars [i + 1] - '0');
541                                 bufIndex++;
542                         }
543                         if (charLength %2 != 0)
544                                 buffer [bufIndex++] = (byte)
545                                         ((chars [charLength - 1] > '9' ?
546                                                 (byte) (chars [charLength - 1] - 'A' + 10) :
547                                                 (byte) (chars [charLength - 1] - '0'))
548                                         << 4);
549
550                         return bufIndex - offset;
551                 }
552         }
553 }