Merge pull request #820 from brendanzagaeski/master
[mono.git] / mcs / class / System.XML / System.Xml / XmlConvert.cs
index 610334354030715b995245f1dd4bdfad15950d6d..bac049be278329041cd437465060087ed38a8e55 100644 (file)
@@ -54,6 +54,20 @@ namespace System.Xml {
 
                static readonly string [] datetimeFormats = {
                  // dateTime
+#if NET_2_0
+                 "yyyy-MM-ddTHH:mm:sszzz",
+                 "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
+                 "yyyy-MM-ddTHH:mm:ssZ",
+                 "yyyy-MM-ddTHH:mm:ss.FFFFFFFZ",
+                 "yyyy-MM-ddTHH:mm:ss",
+                 "yyyy-MM-ddTHH:mm:ss.FFFFFFF",
+                 "HH:mm:ss",
+                 "HH:mm:ss.FFFFFFF",
+                 "HH:mm:sszzz",
+                 "HH:mm:ss.FFFFFFFzzz",
+                 "HH:mm:ssZ",
+                 "HH:mm:ss.FFFFFFFZ",
+#else // it is not required in trunk but should make it easy to backport...
                  "yyyy-MM-ddTHH:mm:sszzz",
                  "yyyy-MM-ddTHH:mm:ss.fzzz",
                  "yyyy-MM-ddTHH:mm:ss.ffzzz",
@@ -103,6 +117,7 @@ namespace System.Xml {
                  "HH:mm:ss.fffffZ",
                  "HH:mm:ss.ffffffZ",
                  "HH:mm:ss.fffffffZ",
+#endif
                  // date
                  "yyyy-MM-dd",
                  "yyyy-MM-ddzzz",
@@ -125,44 +140,6 @@ namespace System.Xml {
                  "---ddZ",
                };
 
-#if NET_2_0
-               static readonly string [] defaultDateTimeFormats = new string [] {
-                       "yyyy-MM-ddTHH:mm:ss", // dateTime(1)
-                       "yyyy-MM-ddTHH:mm:ss.FFFFFFF", // dateTime(2)
-                       "yyyy-MM-dd", // date
-                       "HH:mm:ss", // time
-                       "yyyy-MM", // gYearMonth
-                       "yyyy", // gYear
-                       "--MM-dd", // gMonthDay
-                       "---dd", // gDay
-                       };
-
-               static readonly string [] roundtripDateTimeFormats;
-               static readonly string [] localDateTimeFormats;
-               static readonly string [] utcDateTimeFormats;
-               static readonly string [] unspecifiedDateTimeFormats;
-
-               static XmlConvert ()
-               {
-                       int l = defaultDateTimeFormats.Length;
-                       roundtripDateTimeFormats = new string [l];
-                       localDateTimeFormats = new string [l];
-                       utcDateTimeFormats = new string [l * 3];
-                       unspecifiedDateTimeFormats = new string [l * 4];
-                       for (int i = 0; i < l; i++) {
-                               string s = defaultDateTimeFormats [i];
-                               localDateTimeFormats [i] = s + "zzz";
-                               roundtripDateTimeFormats [i] = s + 'K';
-                               utcDateTimeFormats [i * 3] = s;
-                               utcDateTimeFormats [i * 3 + 1] = s + 'Z';
-                               utcDateTimeFormats [i * 3 + 2] = s + "zzz";
-                               unspecifiedDateTimeFormats [i * 4] = s;
-                               unspecifiedDateTimeFormats [i * 4 + 1] = localDateTimeFormats [i];
-                               unspecifiedDateTimeFormats [i * 4 + 2] = roundtripDateTimeFormats [i];
-                               unspecifiedDateTimeFormats [i * 4 + 3] = utcDateTimeFormats [i];
-                       }
-               }
-#endif
                static DateTimeStyles _defaultStyle = DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite;
                
                public XmlConvert()
@@ -348,22 +325,17 @@ namespace System.Xml {
                }
                
 #if NET_2_0
-               public static DateTime ToDateTime (string value, XmlDateTimeSerializationMode mode)
+               public static DateTime ToDateTime (string s, XmlDateTimeSerializationMode dateTimeOption)
                {
-                       DateTime dt;
-                       switch (mode) {
+                       switch (dateTimeOption) {
                        case XmlDateTimeSerializationMode.Local:
-                               dt = ToDateTime (value, localDateTimeFormats);
-                               return dt == DateTime.MinValue || dt == DateTime.MaxValue ? dt : dt.ToLocalTime ();
+                return ToDateTime(s, datetimeFormats, _defaultStyle | DateTimeStyles.AssumeLocal).ToLocalTime();
                        case XmlDateTimeSerializationMode.RoundtripKind:
-                               return ToDateTime (value, roundtripDateTimeFormats, _defaultStyle | DateTimeStyles.RoundtripKind);
+                return ToDateTime(s, datetimeFormats, _defaultStyle | DateTimeStyles.RoundtripKind);
                        case XmlDateTimeSerializationMode.Utc:
-                               dt = ToDateTime (value, utcDateTimeFormats);
-                               return dt == DateTime.MinValue || dt == DateTime.MaxValue ? dt : dt.ToUniversalTime ();
-                       case XmlDateTimeSerializationMode.Unspecified:
-                               return ToDateTime (value, unspecifiedDateTimeFormats);
+                return ToDateTime(s, datetimeFormats, _defaultStyle | DateTimeStyles.AssumeUniversal).ToUniversalTime();
                        default:
-                               return ToDateTime (value, defaultDateTimeFormats);
+                               return new DateTime (ToDateTime(s, datetimeFormats, _defaultStyle | DateTimeStyles.RoundtripKind).Ticks, DateTimeKind.Unspecified);
                        }
                }
 #endif
@@ -384,7 +356,11 @@ namespace System.Xml {
 
                private static DateTime ToDateTime (string s, string [] formats, DateTimeStyles style) 
                {
-                       return DateTime.ParseExact (s, formats, DateTimeFormatInfo.InvariantInfo, style);
+                       try {
+                               return DateTime.ParseExact (s, formats, DateTimeFormatInfo.InvariantInfo, style);
+                       } catch (ArgumentOutOfRangeException) {
+                               return DateTime.MinValue;
+                       }
                }
                
                public static Decimal ToDecimal(string s)
@@ -415,12 +391,17 @@ namespace System.Xml {
                        while (Char.IsWhiteSpace (s [sEndPos]))
                                sEndPos--;
 
+                       if (TryParseStringConstant ("NaN", s, sidx, sEndPos))
+                               return Single.NaN;
                        if (TryParseStringConstant ("INF", s, sidx, sEndPos))
                                return Single.PositiveInfinity;
                        if (TryParseStringConstant ("-INF", s, sidx, sEndPos))
                                return Single.NegativeInfinity;
-                       if (TryParseStringConstant ("NaN", s, sidx, sEndPos))
-                               return Single.NaN;
+                       // Handle these here because Single.Parse("Infinity") is invalid while XmlConvert.ToSingle("Infinity") is valid.
+                       if (TryParseStringConstant ("Infinity", s, sidx, sEndPos))
+                               return Single.PositiveInfinity;
+                       if (TryParseStringConstant ("-Infinity", s, sidx, sEndPos))
+                               return Single.NegativeInfinity;
                        return 0;
                }
 
@@ -531,25 +512,33 @@ namespace System.Xml {
 
                        StringBuilder builder = new StringBuilder ();
                        if (value.Ticks < 0) {
+                               if (value == TimeSpan.MinValue)
+                                       return "-P10675199DT2H48M5.4775808S";  // There's one fewer tick on the positive side, so we cannot Negate this value; just hard-code it
                                builder.Append ('-');
                                value = value.Negate ();
                        }
                        builder.Append ('P');
                        if (value.Days > 0)
                                builder.Append (value.Days).Append ('D');
-                       if (value.Days > 0 || value.Hours > 0 || value.Minutes > 0 || value.Seconds > 0 || value.Milliseconds > 0) {
+                       long ticks = value.Ticks % TimeSpan.TicksPerMillisecond;
+                       if (value.Hours > 0 || value.Minutes > 0 || value.Seconds > 0 || value.Milliseconds > 0 || ticks > 0) {
                                builder.Append('T');
                                if (value.Hours > 0)
                                        builder.Append (value.Hours).Append ('H');
                                if (value.Minutes > 0) 
                                        builder.Append (value.Minutes).Append ('M');
-                               if (value.Seconds > 0 || value.Milliseconds > 0) {
+                               if (value.Seconds > 0 || value.Milliseconds > 0 || ticks > 0) {
                                        builder.Append (value.Seconds);
-                                       long ticks = value.Ticks % TimeSpan.TicksPerMillisecond;
+                                       bool trimZero = true;
                                        if (ticks > 0)
                                                builder.Append ('.').AppendFormat ("{0:0000000}", value.Ticks % TimeSpan.TicksPerSecond);
                                        else if (value.Milliseconds > 0)
                                                builder.Append ('.').AppendFormat ("{0:000}", value.Milliseconds);
+                                       else
+                                               trimZero = false;
+                                       if (trimZero)
+                                               while (builder [builder.Length - 1] == '0')
+                                                       builder.Remove (builder.Length - 1, 1);
 
                                        builder.Append ('S');
                                }
@@ -594,11 +583,11 @@ namespace System.Xml {
                }
 
 #if NET_2_0
-               public static string ToString (DateTime value, XmlDateTimeSerializationMode mode)
+               public static string ToString (DateTime value, XmlDateTimeSerializationMode dateTimeOption)
                {
                        // Unlike usual DateTime formatting, it preserves
                        // MaxValue/MinValue as is.
-                       switch (mode) {
+                       switch (dateTimeOption) {
                        case XmlDateTimeSerializationMode.Local:
                                return (value == DateTime.MinValue ? DateTime.MinValue : value == DateTime.MaxValue ? value : value.ToLocalTime ()).ToString (
                                        "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
@@ -630,6 +619,7 @@ namespace System.Xml {
 
                public static TimeSpan ToTimeSpan(string s)
                {
+                       s = s.Trim (XmlChar.WhitespaceChars);
                        if (s.Length == 0)
                                throw new FormatException ("Invalid format string for duration schema datatype.");
 
@@ -771,37 +761,33 @@ namespace System.Xml {
                        
                }
 
-               public static string VerifyNCName (string ncname)
+               public static string VerifyNCName (string name)
                {
-                       if (ncname == null || ncname.Length == 0)
-                               throw new ArgumentNullException("ncname");
+                       if (name == null || name.Length == 0)
+                               throw new ArgumentNullException("name");
 
-                       if (!XmlChar.IsNCName (ncname))
-                               throw new XmlException ("'" + ncname + "' is not a valid XML NCName");
-                       return ncname;
+                       if (!XmlChar.IsNCName (name))
+                               throw new XmlException ("'" + name + "' is not a valid XML NCName");
+                       return name;
                }
 
-#if NET_2_0
-               public static string VerifyTOKEN (string name)
-#else
-               internal static string VerifyTOKEN (string name)
-#endif
+               public static string VerifyTOKEN (string token)
                {
-                       if (name == null)
-                               throw new ArgumentNullException("name");
+                       if (token == null)
+                               throw new ArgumentNullException("token");
 
-                       if (name.Length == 0)
-                               return name;
+                       if (token.Length == 0)
+                               return token;
 
-                       if (XmlChar.IsWhitespace (name [0]) ||
-                               XmlChar.IsWhitespace (name [name.Length - 1]))
+                       if (XmlChar.IsWhitespace (token [0]) ||
+                               XmlChar.IsWhitespace (token [token.Length - 1]))
                                throw new XmlException ("Whitespace characters (#xA, #xD, #x9, #x20) are not allowed as leading or trailing whitespaces of xs:token.");
 
-                       for (int i = 0; i < name.Length; i++)
-                               if (XmlChar.IsWhitespace (name [i]) && name [i] != ' ')
-                               throw new XmlException ("Either #xA, #xD or #x9 are not allowed inside xs:token.");
+                       for (int i = 0; i < token.Length; i++)
+                               if (XmlChar.IsWhitespace (token [i]) && token [i] != ' ')
+                                       throw new XmlException ("Either #xA, #xD or #x9 are not allowed inside xs:token.");
 
-                       return name;
+                       return token;
                }
 
 #if NET_2_0
@@ -866,7 +852,10 @@ namespace System.Xml {
 
                public static DateTimeOffset ToDateTimeOffset (string s, string [] formats)
                {
-                       return DateTimeOffset.ParseExact (s, formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
+                       DateTimeStyles style = DateTimeStyles.AllowLeadingWhite |
+                                              DateTimeStyles.AllowTrailingWhite |
+                                              DateTimeStyles.AssumeUniversal;
+                       return DateTimeOffset.ParseExact (s, formats, CultureInfo.InvariantCulture, style);
                }
 
                public static string ToString (DateTimeOffset value)
@@ -887,6 +876,66 @@ namespace System.Xml {
                        return new Uri (s, UriKind.RelativeOrAbsolute);
                }
 
+#endif
+
+#if NET_4_0
+               public static bool IsNCNameChar (char ch)
+               {
+                       return XmlChar.IsNCNameChar (ch);
+               }
+
+               public static bool IsPublicIdChar (char ch)
+               {
+                       return XmlChar.IsPubidChar (ch);
+               }
+
+               public static bool IsStartNCNameChar (char ch)
+               {
+                       return XmlChar.IsFirstNameChar (ch);
+               }
+
+               public static bool IsWhitespaceChar (char ch)
+               {
+                       return XmlChar.IsWhitespace (ch);
+               }
+
+               public static bool IsXmlChar (char ch)
+               {
+                       return XmlChar.IsValid (ch);
+               }
+
+               public static bool IsXmlSurrogatePair (char lowChar, char highChar)
+               {
+                       return 0xD800 <= lowChar && lowChar <= 0xDBFF && 0xDC00 <= highChar && highChar <= 0xDFFF;
+               }
+               
+               public static string VerifyPublicId (string publicId)
+               {
+                       if (publicId == null)
+                               throw new ArgumentNullException ("publicId");
+                       if (XmlChar.IsPubid (publicId))
+                               return publicId;
+                       throw new XmlException (string.Format ("'{0}' is not a valid PUBLIC ID", publicId));
+               }
+
+               public static string VerifyWhitespace (string content)
+               {
+                       if (content == null)
+                               throw new ArgumentNullException ("content");
+                       if (XmlChar.IsWhitespace (content))
+                               return content;
+                       throw new XmlException (string.Format ("'{0}' is not whitespace", content));
+               }
+
+               public static string VerifyXmlChars (string content)
+               {
+                       if (content == null)
+                               throw new ArgumentNullException ("content");
+                       var idx = XmlChar.IndexOfInvalid (content, true);
+                       if (idx < 0)
+                               return content;
+                       throw new XmlException (string.Format ("Invalid XML character was found in the content, at index {0}.", idx));
+               }
 #endif
        }
 }