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