2007-04-25 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml.Serialization / XmlCustomFormatter.cs
1 //
2 // System.Xml.Serialization.XmlCustomFormatter.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //   Lluis Sanchez Gual (lluis@ximian.com)
7 //
8 // Copyright (C) Tim Coleman, 2002
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Collections;
34 using System.Text;
35 using System.Xml;
36 using System.Globalization;
37
38 namespace System.Xml.Serialization {
39         internal class XmlCustomFormatter {
40
41                 #region Methods
42
43                 internal static string FromByteArrayBase64 (byte[] value)
44                 {
45                         return value == null ? String.Empty : Convert.ToBase64String(value);
46                 }
47
48                 internal static string FromByteArrayHex (byte[] value)
49                 {
50                         if (value == null) return null;
51                         StringBuilder output = new StringBuilder ();
52                         foreach (byte val in value)
53                                 output.Append (val.ToString ("X2", CultureInfo.InvariantCulture));
54                         return output.ToString ();
55                 }
56
57                 internal static string FromChar (char value)
58                 {
59                         return ((int) value).ToString (CultureInfo.InvariantCulture);
60                 }
61
62                 internal static string FromDate (DateTime value)
63                 {
64                         return XmlConvert.ToString (value, "yyyy-MM-dd");
65                 }
66
67                 internal static string FromDateTime (DateTime value)
68                 {
69 #if NET_2_0
70                         return XmlConvert.ToString (value, "yyyy-MM-ddTHH:mm:ss.FFFFFFF");
71 #else
72                         return XmlConvert.ToString (value, "yyyy-MM-ddTHH:mm:ss.fffffffzzz");
73 #endif
74                 }
75
76                 internal static string FromTime (DateTime value)
77                 {
78                         return XmlConvert.ToString (value, "HH:mm:ss.fffffffzzz");
79                 }
80
81                 internal static string FromEnum (long value, string[] values, long[] ids)
82                 {
83                         return FromEnum (value, values, ids, (string) null);
84                 }
85
86                 internal static string FromEnum (long value, string[] values, long[] ids, string typeName)
87                 {
88                         StringBuilder sb = new StringBuilder();
89                         int length = ids.Length;
90                         long valueToProcess = value;
91                         int zeroValue = -1;
92
93                         for (int i = 0; i < length; i ++) {
94                                 if (ids[i] == 0) {
95                                         zeroValue = i;
96                                 } else {
97                                         if (valueToProcess == 0) {
98                                                 break;
99                                         }
100
101                                         if ((ids[i] & value) == ids[i]) {
102                                                 if (sb.Length != 0)
103                                                         sb.Append (' ');
104                                                 sb.Append (values[i]);
105                                                 valueToProcess &= ~ids[i];
106                                         }
107                                 }
108                         }
109
110                         if (valueToProcess != 0) {
111 #if NET_2_0
112                                 if (typeName != null)
113                                         throw new InvalidOperationException (string.Format (CultureInfo.CurrentCulture,
114                                                 "'{0}' is not a valid value for {1}.", value, typeName));
115                                 else
116                                         throw new InvalidOperationException (string.Format (CultureInfo.CurrentCulture,
117                                                 "'{0}' is not a valid value.", value));
118 #else
119                                 return value.ToString ();
120 #endif
121                         }
122                         if (sb.Length == 0 && zeroValue != -1) {
123                                 sb.Append (values[zeroValue]);
124                         }
125                         return sb.ToString ();
126                 }
127
128                 internal static string FromXmlName (string name)
129                 {
130                         return XmlConvert.EncodeName (name);
131                 }
132
133                 internal static string FromXmlNCName (string ncName)
134                 {
135                         return XmlConvert.EncodeLocalName (ncName);
136                 }
137
138                 internal static string FromXmlNmToken (string nmToken)
139                 {
140                         return XmlConvert.EncodeNmToken (nmToken);
141                 }
142
143                 internal static string FromXmlNmTokens (string nmTokens)
144                 {
145                         string [] tokens = nmTokens.Split (' ');
146                         for (int i=0; i<tokens.Length; i++)
147                                 tokens [i] = FromXmlNmToken (tokens [i]);
148                         return String.Join (" ", tokens);
149                 }
150
151                 internal static byte[] ToByteArrayBase64 (string value)
152                 {
153                         return Convert.FromBase64String(value);
154                 }
155
156                 internal static char ToChar (string value)
157                 {
158                         return (char) XmlConvert.ToUInt16 (value);
159                 }
160
161                 internal static DateTime ToDate (string value)
162                 {
163                         return ToDateTime (value);
164                 }
165
166                 internal static DateTime ToDateTime (string value)
167                 {
168                         return XmlConvert.ToDateTime (value);
169                 }
170
171                 internal static DateTime ToTime (string value)
172                 {
173                         return ToDateTime (value);
174                 }
175
176                 internal static long ToEnum (string value, Hashtable values, string typeName, bool validate)
177                 {
178                         // Assuming that h contains map from value to Enumerated Name
179 /*
180                         You can try : 
181                                 return ToEnum ("One Two", h, "SomeType");
182                         where:
183                                 (1) no keys and values for h.
184                                 (2) string keys and long values.
185                         
186                         according to MSDN docs (for .NET 2.0) the hashtable "consists of the
187                         identifiers as keys and the constants as integral numbers"
188 */
189                         long enumValue = 0;
190                         string[] names = value.Split (' ');
191
192                         foreach (string name in names) {
193                                 object nameValue = values[name];
194                                 if (nameValue != null) {
195                                         enumValue |= (long) nameValue;
196                                 } else if (validate && name.Length != 0) {
197                                         throw new InvalidOperationException (String.Format ("'{0}' is not a valid member of type {1}.", name, typeName));
198                                 }
199                         }
200
201                         return enumValue;
202                 }
203
204                 internal static string ToXmlName (string value)
205                 {
206                         return XmlConvert.DecodeName (value);
207                 }
208
209                 internal static string ToXmlNCName (string value)
210                 {
211                         return ToXmlName (value);
212                 }
213
214                 internal static string ToXmlNmToken (string value)
215                 {
216                         return ToXmlName (value);
217                 }
218
219                 internal static string ToXmlNmTokens (string value)
220                 {
221                         return ToXmlName (value);
222                 }
223
224                 internal static string ToXmlString (TypeData type, object value)
225                 {
226                         if (value == null) return null;
227                         switch (type.XmlType)
228                         {
229                                 case "boolean": return XmlConvert.ToString ((bool)value);
230                                 case "unsignedByte": return XmlConvert.ToString ((byte)value);
231                                 case "char": return XmlConvert.ToString ((int)(char)value);
232 #if NET_2_0
233                                 case "dateTime": return XmlConvert.ToString ((DateTime)value, XmlDateTimeSerializationMode.Unspecified);
234                                 case "date": return ((DateTime)value).ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
235                                 case "time": return ((DateTime)value).ToString("HH:mm:ss.FFFFFFF", CultureInfo.InvariantCulture);
236 #else
237                                 case "dateTime": return XmlConvert.ToString ((DateTime)value);
238                                 case "date": return ((DateTime)value).ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
239                                 case "time": return ((DateTime)value).ToString("HH:mm:ss.fffffffzzz", CultureInfo.InvariantCulture);
240 #endif
241                                 case "decimal": return XmlConvert.ToString ((decimal)value);
242                                 case "double": return XmlConvert.ToString ((double)value);
243                                 case "short": return XmlConvert.ToString ((Int16)value);
244                                 case "int": return XmlConvert.ToString ((Int32)value);
245                                 case "long": return XmlConvert.ToString ((Int64)value);
246                                 case "byte": return XmlConvert.ToString ((sbyte)value);
247                                 case "float": return XmlConvert.ToString ((Single)value);
248                                 case "unsignedShort": return XmlConvert.ToString ((UInt16)value);
249                                 case "unsignedInt": return XmlConvert.ToString ((UInt32)value);
250                                 case "unsignedLong": return XmlConvert.ToString ((UInt64)value);
251                                 case "guid": return XmlConvert.ToString ((Guid)value);
252                                 case "base64":
253                                 case "base64Binary": return value == null ? String.Empty : Convert.ToBase64String ((byte[])value);
254                                 case "hexBinary": return value == null ? String.Empty : XmlConvert.ToBinHexString ((byte[]) value);
255                                 case "duration": return XmlConvert.ToString ((TimeSpan) value);
256                         default: return value is IFormattable ? ((IFormattable) value).ToString (null, CultureInfo.InvariantCulture) : value.ToString ();
257                         }
258                 }
259
260                 internal static object FromXmlString (TypeData type, string value)
261                 {
262                         if (value == null) return null;
263
264                         switch (type.XmlType)
265                         {
266                                 case "boolean": return XmlConvert.ToBoolean (value);
267                                 case "unsignedByte": return XmlConvert.ToByte (value);
268                                 case "char": return (char)XmlConvert.ToInt32 (value);
269                                 case "dateTime": return XmlConvert.ToDateTime (value);
270                                 case "date": return DateTime.ParseExact (value, "yyyy-MM-dd", null);
271 #if NET_2_0
272                                 case "time": return DateTime.ParseExact (value, "HH:mm:ss.FFFFFFF", null);
273 #else
274                                 case "time": return DateTime.ParseExact (value, "HH:mm:ss.fffffffzzz", null);
275 #endif
276                                 case "decimal": return XmlConvert.ToDecimal (value);
277                                 case "double": return XmlConvert.ToDouble (value);
278                                 case "short": return XmlConvert.ToInt16 (value);
279                                 case "int": return XmlConvert.ToInt32 (value);
280                                 case "long": return XmlConvert.ToInt64 (value);
281                                 case "byte": return XmlConvert.ToSByte (value);
282                                 case "float": return XmlConvert.ToSingle (value);
283                                 case "unsignedShort": return XmlConvert.ToUInt16 (value);
284                                 case "unsignedInt": return XmlConvert.ToUInt32 (value);
285                                 case "unsignedLong": return XmlConvert.ToUInt64 (value);
286                                 case "guid": return XmlConvert.ToGuid (value);
287                                 case "base64":
288                                 case "base64Binary": return Convert.FromBase64String (value);
289                                 case "hexBinary": return XmlConvert.FromBinHexString (value);
290                                 case "duration": return XmlConvert.ToTimeSpan (value);
291                                 default: 
292                                         if (type.Type != null)
293                                                 return Convert.ChangeType (value, type.Type);
294                                         else
295                                                 return value;
296                         }
297                 }
298
299                 internal static string GenerateToXmlString (TypeData type, string value)
300                 {
301                         if (type.NullableOverride)
302                                 return "(" + value + " != null ? " + GenerateToXmlStringCore (type, value) + " : null)";
303                         else
304                                 return GenerateToXmlStringCore (type, value);
305                 }
306
307                 static string GenerateToXmlStringCore (TypeData type, string value)
308                 {
309                         if (type.NullableOverride)
310                                 value = value + ".Value";
311                         switch (type.XmlType)
312                         {
313                                 case "boolean": return "(" + value + "?\"true\":\"false\")";
314                                 case "unsignedByte": return value + ".ToString(CultureInfo.InvariantCulture)";
315                                 case "char": return "((int)(" + value + ")).ToString(CultureInfo.InvariantCulture)";
316                                 case "dateTime": return value + ".ToString(\"yyyy-MM-ddTHH:mm:ss.fffffffzzz\", CultureInfo.InvariantCulture)";
317                                 case "date": return value + ".ToString(\"yyyy-MM-dd\", CultureInfo.InvariantCulture)";
318                                 case "time": return value + ".ToString(\"HH:mm:ss.fffffffzzz\", CultureInfo.InvariantCulture)";
319                                 case "decimal": return "XmlConvert.ToString (" + value + ")";
320                                 case "double": return "XmlConvert.ToString (" + value + ")";
321                                 case "short": return value + ".ToString(CultureInfo.InvariantCulture)";
322                                 case "int": return value + ".ToString(CultureInfo.InvariantCulture)";
323                                 case "long": return value + ".ToString(CultureInfo.InvariantCulture)";
324                                 case "byte": return value + ".ToString(CultureInfo.InvariantCulture)";
325                                 case "float": return "XmlConvert.ToString (" + value + ")";
326                                 case "unsignedShort": return value + ".ToString(CultureInfo.InvariantCulture)";
327                                 case "unsignedInt": return value + ".ToString(CultureInfo.InvariantCulture)";
328                                 case "unsignedLong": return value + ".ToString(CultureInfo.InvariantCulture)";
329                                 case "guid": return "XmlConvert.ToString (" + value + ")";
330                                 case "base64":
331                                 case "base64Binary": return value + " == null ? String.Empty : Convert.ToBase64String (" + value + ")";
332                                 case "hexBinary": return value + " == null ? String.Empty : ToBinHexString (" + value + ")";
333                                 case "duration": return "XmlConvert.ToString (" + value + ")";
334                                 case "NMTOKEN":
335                                 case "Name":
336                                 case "NCName":
337                                 case "language":
338                                 case "ENTITY":
339                                 case "ID":
340                                 case "IDREF":
341                                 case "NOTATION":
342                                 case "token":
343                                 case "normalizedString":
344                                 case "string": return value;
345                                 default: return "((" + value + " != null) ? (" + value + ").ToString() : null)";
346                         }
347                 }
348
349                 internal static string GenerateFromXmlString (TypeData type, string value)
350                 {
351                         if (type.NullableOverride)
352                                 return String.Concat ("(", value, " != null ? (", type.CSharpName, "?)", GenerateFromXmlStringCore (type, value), " : null)");
353                         else
354                                 return GenerateFromXmlStringCore (type, value);
355                 }
356
357                 static string GenerateFromXmlStringCore (TypeData type, string value)
358                 {
359                         switch (type.XmlType)
360                         {
361                                 case "boolean": return "XmlConvert.ToBoolean (" + value + ")";
362                                 case "unsignedByte": return "byte.Parse (" + value + ", CultureInfo.InvariantCulture)";
363                                 case "char": return "(char)Int32.Parse (" + value + ", CultureInfo.InvariantCulture)";
364 #if NET_2_0
365                                 case "dateTime": return "XmlConvert.ToDateTime (" + value + ", XmlDateTimeSerializationMode.Unspecified)";
366                                 case "date": return "DateTime.ParseExact (" + value + ", \"yyyy-MM-dd\", CultureInfo.InvariantCulture)";
367                                 case "time": return "DateTime.ParseExact (" + value + ", \"HH:mm:ss.FFFFFFF\", CultureInfo.InvariantCulture)";
368 #else
369                                 case "dateTime": return "XmlConvert.ToDateTime (" + value + ")";
370                                 case "date": return "DateTime.ParseExact (" + value + ", \"yyyy-MM-dd\", CultureInfo.InvariantCulture)";
371                                 case "time": return "DateTime.ParseExact (" + value + ", \"HH:mm:ss.fffffffzzz\", CultureInfo.InvariantCulture)";
372 #endif
373                                 case "decimal": return "Decimal.Parse (" + value + ", CultureInfo.InvariantCulture)";
374                                 case "double": return "XmlConvert.ToDouble (" + value + ")";
375                                 case "short": return "Int16.Parse (" + value + ", CultureInfo.InvariantCulture)";
376                                 case "int": return "Int32.Parse (" + value + ", CultureInfo.InvariantCulture)";
377                                 case "long": return "Int64.Parse (" + value + ", CultureInfo.InvariantCulture)";
378                                 case "byte": return "SByte.Parse (" + value + ", CultureInfo.InvariantCulture)";
379                                 case "float": return "XmlConvert.ToSingle (" + value + ")";
380                                 case "unsignedShort": return "UInt16.Parse (" + value + ", CultureInfo.InvariantCulture)";
381                                 case "unsignedInt": return "UInt32.Parse (" + value + ", CultureInfo.InvariantCulture)";
382                                 case "unsignedLong": return "UInt64.Parse (" + value + ", CultureInfo.InvariantCulture)";
383                                 case "guid": return "XmlConvert.ToGuid (" + value + ")";
384                                 case "base64:":
385                                 case "base64Binary": return "Convert.FromBase64String (" + value + ")";
386                                 case "hexBinary": return "FromBinHexString (" + value + ")";
387                                 case "duration": return "XmlConvert.ToTimeSpan (" + value + ")";
388                                 default: return value;
389                         }
390                 }
391
392                 #endregion // Methods
393         }
394 }