2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[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("long", XmlSchema.Namespace);
317                         elementType = typeof(long);
318                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
319                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
320
321                         element = new Element("int", XmlSchema.Namespace);
322                         elementType = typeof(int);
323                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
324                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
325
326                         element = new Element("float", XmlSchema.Namespace);
327                         elementType = typeof(float);
328                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
329                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
330
331                         element = new Element("decimal", XmlSchema.Namespace);
332                         elementType = typeof(decimal);
333                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
334                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
335
336                         element = new Element("short", XmlSchema.Namespace);
337                         elementType = typeof(short);
338                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
339                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
340
341                         element = new Element("anyType", XmlSchema.Namespace);
342                         elementType = typeof(object);
343                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
344                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
345
346                         element = new Element("dateTime", XmlSchema.Namespace);
347                         elementType = typeof(DateTime);
348                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
349                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
350
351                         element = new Element("duration", XmlSchema.Namespace);
352                         elementType = typeof(TimeSpan);
353                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
354                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
355
356                         element = new Element("Fault", SoapEnvelopeNamespace);
357                         elementType = typeof(System.Runtime.Serialization.Formatters.SoapFault);
358                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
359                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
360
361                         element = new Element("base64", SoapEncodingNamespace);
362                         elementType = typeof(byte[]);
363                         xmlNodeToTypeTable.Add(element, elementType.AssemblyQualifiedName);
364                         typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
365                 }
366                 
367                 public static string GetXsdValue (object value)
368                 {
369                         if (value is DateTime) {
370                                 return SoapDateTime.ToString ((DateTime)value);
371                         }
372                         else if (value is decimal) {
373                                 return ((decimal) value).ToString (CultureInfo.InvariantCulture);
374                         }
375                         else if (value is double) {
376                                 return ((double) value).ToString (CultureInfo.InvariantCulture);
377                         }
378                         else if (value is float) {
379                                 return ((float) value).ToString (CultureInfo.InvariantCulture);
380                         }
381                         else if (value is TimeSpan) {
382                                 return SoapDuration.ToString ((TimeSpan)value);
383                         }
384                         else {
385                                 return value.ToString ();
386                         }
387                 }
388                 
389                 public static object ParseXsdValue (string value, Type type)
390                 {
391                         if (type == typeof(DateTime)) {
392                                 return SoapDateTime.Parse (value);
393                         }
394                         else if (type == typeof(decimal)) {
395                                 return decimal.Parse (value, CultureInfo.InvariantCulture);
396                         }
397                         else if (type == typeof(double)) {
398                                 return double.Parse (value, CultureInfo.InvariantCulture);
399                         }
400                         else if (type == typeof(float)) {
401                                 return float.Parse (value, CultureInfo.InvariantCulture);
402                         }
403                         else if (type == typeof (TimeSpan)) {
404                                 return SoapDuration.Parse (value);
405                         }
406                         else if(type.IsEnum) {
407                                 return Enum.Parse(type, value);
408                         }
409                         else {
410                                 return Convert.ChangeType (value, type, CultureInfo.InvariantCulture);
411                         }
412                 }
413
414         }
415 }