2005-06-05 Peter Bartok <pbartok@novell.com>
[mono.git] / mcs / class / System.Runtime.Serialization.Formatters.Soap / System.Runtime.Serialization.Formatters.Soap / SoapTypeMapper.cs
1 // created on 09/04/2003 at 18:58
2 //
3 //      System.Runtime.Serialization.Formatters.Soap.SoapTypeMapper
4 //
5 //      Authors:
6 //              Jean-Marc Andre (jean-marc.andre@polymtl.ca)
7 //
8
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System;
31 using System.Reflection;
32 using System.Collections;
33 using System.Runtime.Remoting;
34 using System.Xml;
35 using System.Xml.Serialization;
36 using System.Runtime.Serialization.Formatters;
37 using System.Xml.Schema;
38 using System.Runtime.Remoting.Metadata.W3cXsd2001;
39 using System.Globalization;
40
41 namespace System.Runtime.Serialization.Formatters.Soap {
42
43         internal class Element
44         {
45                 private string _prefix;
46                 private string _localName;
47                 private string _namespaceURI;
48
49                 public Element(string prefix, string localName, string namespaceURI) 
50                 {
51                         _prefix = prefix;
52                         _localName = localName;
53                         _namespaceURI = namespaceURI;
54                 }
55
56                 public Element(string localName, string namespaceURI): this(null, localName, namespaceURI)
57                 {
58                 }
59
60                 public string Prefix
61                 {
62                         get
63                         {
64                                 return _prefix;
65                         }
66                 }
67
68                 public string LocalName
69                 {
70                         get
71                         {
72                                 return _localName;
73                         }
74                 }
75
76                 public string NamespaceURI 
77                 {
78                         get 
79                         {
80                                 return _namespaceURI;
81                         }
82                 }
83
84                 public override bool Equals(object obj) 
85                 {
86                         Element element = obj as Element;
87                         return (_localName == XmlConvert.DecodeName(element._localName) &&
88                                 _namespaceURI == XmlConvert.DecodeName(element._namespaceURI))?true:false;
89                 }
90
91                 public override int GetHashCode()
92                 {
93                         return (String.Format("{0} {1}", 
94                                 XmlConvert.DecodeName(_localName),
95                                 XmlConvert.DecodeName(_namespaceURI))).GetHashCode();
96                 }
97
98                 public override string ToString() 
99                 {
100                         return string.Format("Element.Prefix = {0}, Element.LocalName = {1}, Element.NamespaceURI = {2}", this.Prefix, this.LocalName, this.NamespaceURI);
101                 }
102         }
103
104         internal class SoapTypeMapper {
105                 private static Hashtable xmlNodeToTypeTable = new Hashtable();
106                 private static Hashtable typeToXmlNodeTable = new Hashtable();
107                 public static readonly string SoapEncodingNamespace = "http://schemas.xmlsoap.org/soap/encoding/";
108                 public static readonly string SoapEncodingPrefix = "SOAP-ENC";
109                 public static readonly string SoapEnvelopeNamespace = "http://schemas.xmlsoap.org/soap/envelope/";
110                 public static readonly string SoapEnvelopePrefix = "SOAP-ENV";
111                 //internal static readonly string SoapEnvelope;
112                 private XmlTextWriter _xmlWriter;
113                 private long _prefixNumber;
114                 private Hashtable namespaceToPrefixTable = new Hashtable();
115                 private SerializationBinder _binder;
116                 private static ArrayList _canBeValueTypeList;
117                 private FormatterAssemblyStyle _assemblyFormat = FormatterAssemblyStyle.Full;
118                 private Element elementString;
119
120
121                 // Constructor used by SoapReader
122                 public SoapTypeMapper(SerializationBinder binder) 
123                 {
124                         _binder = binder;
125                 }
126
127                 // Constructor used by SoapWriter
128                 public SoapTypeMapper(
129                         XmlTextWriter xmlWriter, 
130                         FormatterAssemblyStyle assemblyFormat,
131                         FormatterTypeStyle typeFormat) 
132                 {
133                         _xmlWriter = xmlWriter;
134                         _assemblyFormat = assemblyFormat;
135                         _prefixNumber = 1;
136                         //Type elementType = typeof(string);
137                         if(typeFormat == FormatterTypeStyle.XsdString)\r
138                         {
139                                 elementString = new Element("xsd", "string", XmlSchema.Namespace);
140                         }
141                         else
142                         {
143                                 elementString = new Element(SoapEncodingPrefix, "string", SoapEncodingNamespace);
144                         }
145 //                      typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
146                 }
147                 
148                 static SoapTypeMapper() {
149 //                      SoapEnvelope = String.Format(
150 //                              "<{0}:Envelope xmlns:{0}='{1}' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='{2}' xmlns:{3}='{4}' xmlns:clr='{5}' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>",
151 //                              SoapEnvelopePrefix,
152 //                              SoapEnvelopeNamespace,
153 //                              XmlSchema.Namespace,
154 //                              SoapEncodingPrefix,
155 //                              SoapEncodingNamespace,
156 //                              SoapServices.XmlNsForClrType);
157                         _canBeValueTypeList = new ArrayList();
158                         _canBeValueTypeList.Add(typeof(DateTime).ToString());
159                         _canBeValueTypeList.Add(typeof(TimeSpan).ToString());
160                         _canBeValueTypeList.Add(typeof(string).ToString());
161                         _canBeValueTypeList.Add(typeof(decimal).ToString());
162                         _canBeValueTypeList.Sort();
163                         InitMappingTables();
164                         
165                 }
166
167                 public Type this[Element element]
168                 {
169                         get 
170                         {
171                                 Type type = null;
172
173                                 string localName = XmlConvert.DecodeName(element.LocalName);
174                                 string namespaceURI = XmlConvert.DecodeName(element.NamespaceURI);
175                                 string typeNamespace, assemblyName;
176                                 SoapServices.DecodeXmlNamespaceForClrTypeNamespace(
177                                         element.NamespaceURI, 
178                                         out typeNamespace, 
179                                         out assemblyName);
180                                 string typeName = typeNamespace + Type.Delimiter + localName;
181
182                                 if(assemblyName != null && assemblyName != string.Empty && _binder != null) 
183                                 {
184                                         type = _binder.BindToType(assemblyName, typeName);
185                                 }
186                                 if(type == null) 
187                                 {
188                                         string assemblyQualifiedName = (string)xmlNodeToTypeTable[element];
189                                         if(assemblyQualifiedName != null)
190                                                 type = Type.GetType(assemblyQualifiedName);
191                                         else
192                                         { 
193
194                                                 type = Type.GetType(element.LocalName);
195                                                 if(type == null) 
196                                                 { 
197
198                                                         type = Type.GetType(typeName);
199                                                         if(type == null) 
200                                                         {
201
202                                                                 if(assemblyName == null || assemblyName == String.Empty)
203                                                                         throw new SerializationException(
204                                                                                 String.Format("Parse Error, no assembly associated with XML key {0} {1}", 
205                                                                                 localName, 
206                                                                                 namespaceURI));
207                                                                 type = FormatterServices.GetTypeFromAssembly(
208                                                                         Assembly.Load(assemblyName), 
209                                                                         typeName);
210                                                         }
211                                                 }
212                                         }
213                                         if(type == null)
214                                                 throw new SerializationException();
215                                 }
216                                 return type;
217                         }
218                 }
219
220
221                 public Element this[string typeFullName, string assemblyName]
222                 {
223                         get 
224                         {
225                                 Element element;
226                                 string typeNamespace = string.Empty;
227                                 string typeName = typeFullName;
228                                 if(_assemblyFormat == FormatterAssemblyStyle.Simple)\r
229                                 {
230                                         string[] items = assemblyName.Split(',');
231                                         assemblyName = items[0];
232                                 }
233                                 string assemblyQualifiedName = typeFullName + ", " + assemblyName;
234                                 element = (Element) typeToXmlNodeTable[assemblyQualifiedName];
235                                 if(element == null)
236                                 {
237                                         int typeNameIndex = typeFullName.LastIndexOf('.');
238                                         if(typeNameIndex != -1) 
239                                         {
240                                                 typeNamespace = typeFullName.Substring(0, typeNameIndex);
241                                                 typeName = typeFullName.Substring(typeNamespace.Length + 1);
242                                         }
243                                         string namespaceURI = 
244                                                 SoapServices.CodeXmlNamespaceForClrTypeNamespace(
245                                                 typeNamespace, 
246                                                 (!assemblyName.StartsWith("mscorlib"))?assemblyName:String.Empty);
247                                         string prefix = (string) namespaceToPrefixTable[namespaceURI];
248                                         if(prefix == null || prefix == string.Empty)
249                                         {
250                                                 prefix = "a" + (_prefixNumber++).ToString();
251                                                 namespaceToPrefixTable[namespaceURI] = prefix;
252
253                                         }
254                                         element = new Element(
255                                                 prefix, 
256                                                 XmlConvert.EncodeName(typeName), 
257                                                 namespaceURI);
258                                 }
259                                 return element;
260                         }
261                 }
262
263                 public Element this[Type type]
264                 {
265                         get 
266                         {
267                                 if(type == typeof(string)) return elementString;
268                                 Element element = (Element) typeToXmlNodeTable[type.AssemblyQualifiedName];
269                                 if(element == null)
270                                 {
271                                         element = this[type.FullName, type.Assembly.FullName];
272 //                                      if(_assemblyFormat == FormatterAssemblyStyle.Full)
273 //                                              element = this[type.FullName, type.Assembly.FullName];
274 //                                      else
275 //                                              element = this[type.FullName, type.Assembly.GetName().Name];
276
277                                 }
278                                 else
279                                 {
280                                         element = new Element((element.Prefix == null)?_xmlWriter.LookupPrefix(element.NamespaceURI):element.Prefix, element.LocalName, element.NamespaceURI);
281                                 }
282                                 if(element == null)
283                                         throw new SerializationException("Oooops");
284                                 return element;
285                         }
286                 }
287
288                 public static bool CanBeValue(Type type)
289                 {
290                         if(type.IsPrimitive) return true;
291                         if(type.IsEnum) return true;
292                         if(_canBeValueTypeList.BinarySearch(type.ToString()) >= 0) 
293                         {
294                                 return true;
295                         }
296                         return false;
297                 }
298
299                 private static void InitMappingTables() 
300                 {
301                         Element element;
302                         Type elementType;
303                         element = new Element("Array", SoapEncodingNamespace);
304                         elementType = typeof(System.Array);
305                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
306                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
307
308                         element = new Element("string", XmlSchema.Namespace);
309                         elementType = typeof(string);
310                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
311 //                      typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
312
313                         element = new Element("string", SoapEncodingNamespace);
314                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
315
316                         element = new Element("boolean", XmlSchema.Namespace);
317                         elementType = typeof(bool);
318                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
319                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
320
321                         element = new Element("byte", XmlSchema.Namespace);
322                         elementType = typeof(sbyte);
323                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
324                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
325
326                         element = new Element("unsignedByte", XmlSchema.Namespace);
327                         elementType = typeof(byte);
328                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
329                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
330
331                         element = new Element("long", XmlSchema.Namespace);
332                         elementType = typeof(long);
333                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
334                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
335
336                         element = new Element("unsignedLong", XmlSchema.Namespace);
337                         elementType = typeof(ulong);
338                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
339                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
340
341                         element = new Element("int", XmlSchema.Namespace);
342                         elementType = typeof(int);
343                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
344                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
345
346                         element = new Element("unsignedInt", XmlSchema.Namespace);
347                         elementType = typeof(uint);
348                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
349                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
350
351                         element = new Element("float", XmlSchema.Namespace);
352                         elementType = typeof(float);
353                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
354                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
355
356                         element = new Element("double", XmlSchema.Namespace);
357                         elementType = typeof(double);
358                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
359                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
360
361                         element = new Element("decimal", XmlSchema.Namespace);
362                         elementType = typeof(decimal);
363                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
364                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
365
366                         element = new Element("short", XmlSchema.Namespace);
367                         elementType = typeof(short);
368                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
369                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
370
371                         element = new Element("unsignedShort", XmlSchema.Namespace);
372                         elementType = typeof(ushort);
373                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
374                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
375
376                         element = new Element("anyType", XmlSchema.Namespace);
377                         elementType = typeof(object);
378                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
379                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
380
381                         element = new Element("dateTime", XmlSchema.Namespace);
382                         elementType = typeof(DateTime);
383                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
384                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
385
386                         element = new Element("duration", XmlSchema.Namespace);
387                         elementType = typeof(TimeSpan);
388                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
389                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
390
391                         element = new Element("Fault", SoapEnvelopeNamespace);
392                         elementType = typeof(System.Runtime.Serialization.Formatters.SoapFault);
393                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
394                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
395
396                         element = new Element("base64", SoapEncodingNamespace);
397                         elementType = typeof(byte[]);
398                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
399                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
400                 }
401                 
402                 public static string GetXsdValue (object value)
403                 {
404                         if (value is DateTime) {
405                                 return SoapDateTime.ToString ((DateTime)value);
406                         }
407                         else if (value is decimal) {
408                                 return ((decimal) value).ToString (CultureInfo.InvariantCulture);
409                         }
410                         else if (value is double) {
411                                 return ((double) value).ToString (CultureInfo.InvariantCulture);
412                         }
413                         else if (value is float) {
414                                 return ((float) value).ToString (CultureInfo.InvariantCulture);
415                         }
416                         else if (value is TimeSpan) {
417                                 return SoapDuration.ToString ((TimeSpan)value);
418                         }
419                         else if (value is bool) {
420                                 return ((bool) value) ? "true" : "false";
421                         }
422                         else {
423                                 return value.ToString ();
424                         }
425                 }
426                 
427                 public static object ParseXsdValue (string value, Type type)
428                 {
429                         if (type == typeof(DateTime)) {
430                                 return SoapDateTime.Parse (value);
431                         }
432                         else if (type == typeof(decimal)) {
433                                 return decimal.Parse (value, CultureInfo.InvariantCulture);
434                         }
435                         else if (type == typeof(double)) {
436                                 return double.Parse (value, CultureInfo.InvariantCulture);
437                         }
438                         else if (type == typeof(float)) {
439                                 return float.Parse (value, CultureInfo.InvariantCulture);
440                         }
441                         else if (type == typeof (TimeSpan)) {
442                                 return SoapDuration.Parse (value);
443                         }
444                         else if(type.IsEnum) {
445                                 return Enum.Parse(type, value);
446                         }
447                         else {
448                                 return Convert.ChangeType (value, type, CultureInfo.InvariantCulture);
449                         }
450                 }
451
452         }
453 }