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 
FromByteArrayBase64(byte[] value)43 		internal static string FromByteArrayBase64 (byte[] value)
44 		{
45 			return value == null ? String.Empty : Convert.ToBase64String(value);
46 		}
47 
FromByteArrayHex(byte[] value)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 
FromChar(char value)57 		internal static string FromChar (char value)
58 		{
59 			return ((int) value).ToString (CultureInfo.InvariantCulture);
60 		}
61 
FromDate(DateTime value)62 		internal static string FromDate (DateTime value)
63 		{
64 			return XmlConvert.ToString (value, "yyyy-MM-dd");
65 		}
66 
FromDateTime(DateTime value)67 		internal static string FromDateTime (DateTime value)
68 		{
69 			return XmlConvert.ToString (value, XmlDateTimeSerializationMode.RoundtripKind);
70 		}
71 
FromTime(DateTime value)72 		internal static string FromTime (DateTime value)
73 		{
74 			return XmlConvert.ToString (value, "HH:mm:ss.fffffffzzz");
75 		}
76 
FromEnum(long value, string[] values, long[] ids)77 		internal static string FromEnum (long value, string[] values, long[] ids)
78 		{
79 			return FromEnum (value, values, ids, (string) null);
80 		}
81 
FromEnum(long value, string[] values, long[] ids, string typeName)82 		internal static string FromEnum (long value, string[] values, long[] ids, string typeName)
83 		{
84 			StringBuilder sb = new StringBuilder();
85 			int length = ids.Length;
86 			long valueToProcess = value;
87 			int zeroValue = -1;
88 
89 			for (int i = 0; i < length; i ++) {
90 				if (ids[i] == 0) {
91 					zeroValue = i;
92 				} else {
93 					if (valueToProcess == 0) {
94 						break;
95 					}
96 
97 					if ((ids[i] & value) == ids[i]) {
98 						if (sb.Length != 0)
99 							sb.Append (' ');
100 						sb.Append (values[i]);
101 						valueToProcess &= ~ids[i];
102 					}
103 				}
104 			}
105 
106 			if (valueToProcess != 0) {
107 				if (typeName != null)
108 					throw new InvalidOperationException (string.Format (CultureInfo.CurrentCulture,
109 						"'{0}' is not a valid value for {1}.", value, typeName));
110 				else
111 					throw new InvalidOperationException (string.Format (CultureInfo.CurrentCulture,
112 						"'{0}' is not a valid value.", value));
113 			}
114 			if (sb.Length == 0 && zeroValue != -1) {
115 				sb.Append (values[zeroValue]);
116 			}
117 			return sb.ToString ();
118 		}
119 
FromXmlName(string name)120 		internal static string FromXmlName (string name)
121 		{
122 			return XmlConvert.EncodeName (name);
123 		}
124 
FromXmlNCName(string ncName)125 		internal static string FromXmlNCName (string ncName)
126 		{
127 			return XmlConvert.EncodeLocalName (ncName);
128 		}
129 
FromXmlNmToken(string nmToken)130 		internal static string FromXmlNmToken (string nmToken)
131 		{
132 			return XmlConvert.EncodeNmToken (nmToken);
133 		}
134 
FromXmlNmTokens(string nmTokens)135 		internal static string FromXmlNmTokens (string nmTokens)
136 		{
137 			string [] tokens = nmTokens.Split (' ');
138 			for (int i=0; i<tokens.Length; i++)
139 				tokens [i] = FromXmlNmToken (tokens [i]);
140 			return String.Join (" ", tokens);
141 		}
142 
ToByteArrayBase64(string value)143 		internal static byte[] ToByteArrayBase64 (string value)
144 		{
145 			return Convert.FromBase64String(value);
146 		}
147 
ToChar(string value)148 		internal static char ToChar (string value)
149 		{
150 			return (char) XmlConvert.ToUInt16 (value);
151 		}
152 
ToDate(string value)153 		internal static DateTime ToDate (string value)
154 		{
155 			return ToDateTime (value);
156 		}
157 
ToDateTime(string value)158 		internal static DateTime ToDateTime (string value)
159 		{
160 			return XmlConvert.ToDateTime (value, XmlDateTimeSerializationMode.RoundtripKind);
161 		}
162 
ToTime(string value)163 		internal static DateTime ToTime (string value)
164 		{
165 			return ToDateTime (value);
166 		}
167 
ToEnum(string value, Hashtable values, string typeName, bool validate)168 		internal static long ToEnum (string value, Hashtable values, string typeName, bool validate)
169 		{
170 			// Assuming that h contains map from value to Enumerated Name
171 /*
172 			You can try :
173 				return ToEnum ("One Two", h, "SomeType");
174 			where:
175 				(1) no keys and values for h.
176 				(2) string keys and long values.
177 
178 			according to MSDN docs (for .NET 2.0) the hashtable "consists of the
179 			identifiers as keys and the constants as integral numbers"
180 */
181 			long enumValue = 0;
182 			string[] names = value.Split (' ');
183 
184 			foreach (string name in names) {
185 				object nameValue = values[name];
186 				if (nameValue != null) {
187 					enumValue |= (long) nameValue;
188 				} else if (validate && name.Length != 0) {
189 					throw new InvalidOperationException (String.Format ("'{0}' is not a valid member of type {1}.", name, typeName));
190 				}
191 			}
192 
193 			return enumValue;
194 		}
195 
ToXmlName(string value)196 		internal static string ToXmlName (string value)
197 		{
198 			return XmlConvert.DecodeName (value);
199 		}
200 
ToXmlNCName(string value)201 		internal static string ToXmlNCName (string value)
202 		{
203 			return ToXmlName (value);
204 		}
205 
ToXmlNmToken(string value)206 		internal static string ToXmlNmToken (string value)
207 		{
208 			return ToXmlName (value);
209 		}
210 
ToXmlNmTokens(string value)211 		internal static string ToXmlNmTokens (string value)
212 		{
213 			return ToXmlName (value);
214 		}
215 
ToXmlString(TypeData type, object value)216 		internal static string ToXmlString (TypeData type, object value)
217 		{
218 			if (value == null) return null;
219 			switch (type.XmlType)
220 			{
221 				case "boolean": return XmlConvert.ToString ((bool)value);
222 				case "unsignedByte": return XmlConvert.ToString ((byte)value);
223 				case "char": return XmlConvert.ToString ((int)(char)value);
224 				case "dateTime": return XmlConvert.ToString ((DateTime)value, XmlDateTimeSerializationMode.RoundtripKind);
225 				case "date": return ((DateTime)value).ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
226 				case "time": return ((DateTime)value).ToString("HH:mm:ss.FFFFFFF", CultureInfo.InvariantCulture);
227 				case "decimal": return XmlConvert.ToString ((decimal)value);
228 				case "double": return XmlConvert.ToString ((double)value);
229 				case "short": return XmlConvert.ToString ((Int16)value);
230 				case "int": return XmlConvert.ToString ((Int32)value);
231 				case "long": return XmlConvert.ToString ((Int64)value);
232 				case "byte": return XmlConvert.ToString ((sbyte)value);
233 				case "float": return XmlConvert.ToString ((Single)value);
234 				case "unsignedShort": return XmlConvert.ToString ((UInt16)value);
235 				case "unsignedInt": return XmlConvert.ToString ((UInt32)value);
236 				case "unsignedLong": return XmlConvert.ToString ((UInt64)value);
237 				case "guid": return XmlConvert.ToString ((Guid)value);
238 				case "base64":
239 				case "base64Binary": return value == null ? String.Empty : Convert.ToBase64String ((byte[])value);
240 				case "hexBinary": return value == null ? String.Empty : XmlConvert.ToBinHexString ((byte[]) value);
241 				case "duration": return (string) value;
242 			default: return value is IFormattable ? ((IFormattable) value).ToString (null, CultureInfo.InvariantCulture) : value.ToString ();
243 			}
244 		}
245 
246 		static string[] allTimeFormats = new string[] {
247 			"HH:mm:ss.fffffffzzzzzz",
248 			"HH:mm:ss",
249 			"HH:mm:ss.f",
250 			"HH:mm:ss.ff",
251 			"HH:mm:ss.fff",
252 			"HH:mm:ss.ffff",
253 			"HH:mm:ss.fffff",
254 			"HH:mm:ss.ffffff",
255 			"HH:mm:ss.fffffff",
256 			"HH:mm:ssZ",
257 			"HH:mm:ss.fZ",
258 			"HH:mm:ss.ffZ",
259 			"HH:mm:ss.fffZ",
260 			"HH:mm:ss.ffffZ",
261 			"HH:mm:ss.fffffZ",
262 			"HH:mm:ss.ffffffZ",
263 			"HH:mm:ss.fffffffZ",
264 			"HH:mm:sszzzzzz",
265 			"HH:mm:ss.fzzzzzz",
266 			"HH:mm:ss.ffzzzzzz",
267 			"HH:mm:ss.fffzzzzzz",
268 			"HH:mm:ss.ffffzzzzzz",
269 			"HH:mm:ss.fffffzzzzzz",
270 			"HH:mm:ss.ffffffzzzzzz",
271 		};
272 
FromXmlString(TypeData type, string value)273 		internal static object FromXmlString (TypeData type, string value)
274 		{
275 			if (value == null) return null;
276 
277 			switch (type.XmlType)
278 			{
279 				case "boolean": return XmlConvert.ToBoolean (value);
280 				case "unsignedByte": return XmlConvert.ToByte (value);
281 				case "char": return (char)XmlConvert.ToInt32 (value);
282 				case "dateTime": return XmlConvert.ToDateTime (value, XmlDateTimeSerializationMode.RoundtripKind);
283 				case "date": return XmlConvert.ToDateTime (value).Date;
284 				case "time": return DateTime.ParseExact(value, allTimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite | DateTimeStyles.NoCurrentDateDefault | DateTimeStyles.RoundtripKind);
285 				case "decimal": return XmlConvert.ToDecimal (value);
286 				case "double": return XmlConvert.ToDouble (value);
287 				case "short": return XmlConvert.ToInt16 (value);
288 				case "int": return XmlConvert.ToInt32 (value);
289 				case "long": return XmlConvert.ToInt64 (value);
290 				case "byte": return XmlConvert.ToSByte (value);
291 				case "float": return XmlConvert.ToSingle (value);
292 				case "unsignedShort": return XmlConvert.ToUInt16 (value);
293 				case "unsignedInt": return XmlConvert.ToUInt32 (value);
294 				case "unsignedLong": return XmlConvert.ToUInt64 (value);
295 				case "guid": return XmlConvert.ToGuid (value);
296 				case "base64":
297 				case "base64Binary": return Convert.FromBase64String (value);
298 				case "hexBinary": return XmlConvert.FromBinHexString (value);
299 				case "duration": return value;
300 				default:
301 					if (type.Type != null)
302 						return Convert.ChangeType (value, type.Type, null);
303 					else
304 						return value;
305 			}
306 		}
307 
GenerateToXmlString(TypeData type, string value)308 		internal static string GenerateToXmlString (TypeData type, string value)
309 		{
310 			if (type.NullableOverride)
311 				return "(" + value + " != null ? " + GenerateToXmlStringCore (type, value) + " : null)";
312 			else
313 				return GenerateToXmlStringCore (type, value);
314 		}
315 
GenerateToXmlStringCore(TypeData type, string value)316 		static string GenerateToXmlStringCore (TypeData type, string value)
317 		{
318 			if (type.NullableOverride)
319 				value = value + ".Value";
320 			switch (type.XmlType)
321 			{
322 				case "boolean": return "(" + value + "?\"true\":\"false\")";
323 				case "unsignedByte": return value + ".ToString(CultureInfo.InvariantCulture)";
324 				case "char": return "((int)(" + value + ")).ToString(CultureInfo.InvariantCulture)";
325 				case "dateTime": return "XmlConvert.ToString (" + value + ", XmlDateTimeSerializationMode.RoundtripKind)";
326 				case "date": return value + ".ToString(\"yyyy-MM-dd\", CultureInfo.InvariantCulture)";
327 				case "time": return value + ".ToString(\"HH:mm:ss.FFFFFFF\", CultureInfo.InvariantCulture)";
328 				case "decimal": return "XmlConvert.ToString (" + value + ")";
329 				case "double": return "XmlConvert.ToString (" + value + ")";
330 				case "short": return value + ".ToString(CultureInfo.InvariantCulture)";
331 				case "int": return value + ".ToString(CultureInfo.InvariantCulture)";
332 				case "long": return value + ".ToString(CultureInfo.InvariantCulture)";
333 				case "byte": return value + ".ToString(CultureInfo.InvariantCulture)";
334 				case "float": return "XmlConvert.ToString (" + value + ")";
335 				case "unsignedShort": return value + ".ToString(CultureInfo.InvariantCulture)";
336 				case "unsignedInt": return value + ".ToString(CultureInfo.InvariantCulture)";
337 				case "unsignedLong": return value + ".ToString(CultureInfo.InvariantCulture)";
338 				case "guid": return "XmlConvert.ToString (" + value + ")";
339 				case "base64":
340 				case "base64Binary": return value + " == null ? String.Empty : Convert.ToBase64String (" + value + ")";
341 				case "hexBinary": return value + " == null ? String.Empty : ToBinHexString (" + value + ")";
342 				case "duration": return value;
343 				case "NMTOKEN":
344 				case "Name":
345 				case "NCName":
346 				case "language":
347 				case "ENTITY":
348 				case "ID":
349 				case "IDREF":
350 				case "NOTATION":
351 				case "token":
352 				case "normalizedString":
353 				case "string": return value;
354 				default: return "((" + value + " != null) ? (" + value + ").ToString() : null)";
355 			}
356 		}
357 
GenerateFromXmlString(TypeData type, string value)358 		internal static string GenerateFromXmlString (TypeData type, string value)
359 		{
360 			if (type.NullableOverride)
361 				return String.Concat ("(", value, " != null ? (", type.CSharpName, "?)", GenerateFromXmlStringCore (type, value), " : null)");
362 			else
363 				return GenerateFromXmlStringCore (type, value);
364 		}
365 
GenerateFromXmlStringCore(TypeData type, string value)366 		static string GenerateFromXmlStringCore (TypeData type, string value)
367 		{
368 			switch (type.XmlType)
369 			{
370 				case "boolean": return "XmlConvert.ToBoolean (" + value + ")";
371 				case "unsignedByte": return "byte.Parse (" + value + ", CultureInfo.InvariantCulture)";
372 				case "char": return "(char)Int32.Parse (" + value + ", CultureInfo.InvariantCulture)";
373 				case "dateTime": return "XmlConvert.ToDateTime (" + value + ", XmlDateTimeSerializationMode.RoundtripKind)";
374 				case "date": return "XmlConvert.ToDateTime (" + value + ").Date";
375 				case "time": return "DateTime.ParseExact (" + value + ", \"HH:mm:ss.FFFFFFF\", CultureInfo.InvariantCulture)";
376 				case "decimal": return "Decimal.Parse (" + value + ", CultureInfo.InvariantCulture)";
377 				case "double": return "XmlConvert.ToDouble (" + value + ")";
378 				case "short": return "Int16.Parse (" + value + ", CultureInfo.InvariantCulture)";
379 				case "int": return "Int32.Parse (" + value + ", CultureInfo.InvariantCulture)";
380 				case "long": return "Int64.Parse (" + value + ", CultureInfo.InvariantCulture)";
381 				case "byte": return "SByte.Parse (" + value + ", CultureInfo.InvariantCulture)";
382 				case "float": return "XmlConvert.ToSingle (" + value + ")";
383 				case "unsignedShort": return "UInt16.Parse (" + value + ", CultureInfo.InvariantCulture)";
384 				case "unsignedInt": return "UInt32.Parse (" + value + ", CultureInfo.InvariantCulture)";
385 				case "unsignedLong": return "UInt64.Parse (" + value + ", CultureInfo.InvariantCulture)";
386 				case "guid": return "XmlConvert.ToGuid (" + value + ")";
387 				case "base64:":
388 				case "base64Binary": return "Convert.FromBase64String (" + value + ")";
389 				case "hexBinary": return "FromBinHexString (" + value + ")";
390 				case "duration": return value;
391 				default: return value;
392 			}
393 		}
394 
395 		#endregion // Methods
396 	}
397 }
398