Add missing files to get the XML tests running
[mono.git] / mcs / class / System.XML / System.Xml.Serialization / SoapReflectionImporter.cs
1 // 
2 // System.Xml.Serialization.SoapReflectionImporter 
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //
7 // Copyright (C) Tim Coleman, 2002
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System.Collections;
32 using System.Globalization;
33 using System.Reflection;
34 using System.Xml;
35 using System.Xml.Schema;
36
37 namespace System.Xml.Serialization {
38         public class SoapReflectionImporter {
39
40                 SoapAttributeOverrides attributeOverrides;
41                 string initialDefaultNamespace;
42                 ArrayList includedTypes;
43                 ArrayList relatedMaps = new ArrayList ();
44                 ReflectionHelper helper = new ReflectionHelper();
45
46                 #region Constructors
47
48                 public SoapReflectionImporter (): this (null, null)
49                 { 
50                 }
51
52                 public SoapReflectionImporter (SoapAttributeOverrides attributeOverrides): this (attributeOverrides, null)
53                 { 
54                 }
55
56                 public SoapReflectionImporter (string defaultNamespace): this (null, defaultNamespace)
57                 {
58                 }
59
60                 public SoapReflectionImporter (SoapAttributeOverrides attributeOverrides, string defaultNamespace)
61                 { 
62                         if (defaultNamespace == null) initialDefaultNamespace = String.Empty;
63                         else initialDefaultNamespace = defaultNamespace;
64
65                         if (attributeOverrides == null) this.attributeOverrides = new SoapAttributeOverrides();
66                         else this.attributeOverrides = attributeOverrides;
67                 }
68
69                 #endregion // Constructors
70
71                 #region Methods
72
73                 public XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members)
74                 {
75                         return ImportMembersMapping (elementName, ns, members, true, true, false);
76                 }
77
78                 public XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors)
79                 { 
80                         return ImportMembersMapping (elementName, ns, members, hasWrapperElement, writeAccessors, false);
81                 }
82
83                 public XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors, bool validate)
84                 {
85                         elementName = XmlConvert.EncodeLocalName (elementName);
86                         XmlMemberMapping[] mapping = new XmlMemberMapping[members.Length];
87                         for (int n=0; n<members.Length; n++)
88                         {
89                                 XmlTypeMapMember mapMem = CreateMapMember (members[n], ns);
90                                 mapping[n] = new XmlMemberMapping (XmlConvert.EncodeLocalName (members[n].MemberName), ns, mapMem, true);
91                         }
92                         XmlMembersMapping mps = new XmlMembersMapping (elementName, ns, hasWrapperElement, writeAccessors, mapping);
93                         mps.RelatedMaps = relatedMaps;
94                         mps.Format = SerializationFormat.Encoded;
95                         Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
96                         mps.Source = new MembersSerializationSource (elementName, hasWrapperElement, members, writeAccessors, false, null, extraTypes);
97                         return mps;
98                 }
99
100                 public XmlTypeMapping ImportTypeMapping (Type type)
101                 { 
102                         return ImportTypeMapping (type, null);
103                 }
104
105                 public XmlTypeMapping ImportTypeMapping (Type type, string defaultNamespace)
106                 {
107                         if (type == null)
108                                 throw new ArgumentNullException ("type");
109
110                         if (type == typeof (void))
111                                 throw new InvalidOperationException ("Type " + type.Name + " may not be serialized.");
112
113                         return ImportTypeMapping (TypeTranslator.GetTypeData (type),
114                                 defaultNamespace);
115                 }
116
117                 internal XmlTypeMapping ImportTypeMapping (TypeData typeData, string defaultNamespace)
118                 {
119                         if (typeData == null)
120                                 throw new ArgumentNullException ("typeData");
121
122                         if (typeData.Type == null)
123                                 throw new ArgumentException ("Specified TypeData instance does not have Type set.");
124
125                         string oldNs = initialDefaultNamespace;
126                         if (defaultNamespace == null) defaultNamespace = initialDefaultNamespace;
127                         if (defaultNamespace == null) defaultNamespace = string.Empty;
128                         initialDefaultNamespace = defaultNamespace; 
129
130                         XmlTypeMapping map;
131                         switch (typeData.SchemaType) {
132                                 case SchemaTypes.Class: map = ImportClassMapping (typeData, defaultNamespace); break;
133                                 case SchemaTypes.Array: map = ImportListMapping (typeData, defaultNamespace); break;
134                                 case SchemaTypes.XmlNode: throw CreateTypeException (typeData.Type);
135                                 case SchemaTypes.Primitive: map = ImportPrimitiveMapping (typeData, defaultNamespace); break;
136                                 case SchemaTypes.Enum: map = ImportEnumMapping (typeData, defaultNamespace); break;
137                                 case SchemaTypes.XmlSerializable:
138                                 default: throw new NotSupportedException ("Type " + typeData.Type.FullName + " not supported for XML serialization");
139                         }
140                         map.RelatedMaps = relatedMaps;
141                         map.Format = SerializationFormat.Encoded;
142                         Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
143                         map.Source = new SoapTypeSerializationSource (typeData.Type, attributeOverrides, defaultNamespace, extraTypes);
144                         
145                         initialDefaultNamespace = oldNs;
146                         return map;
147                 }
148
149                 XmlTypeMapping CreateTypeMapping (TypeData typeData, string defaultXmlType, string defaultNamespace)
150                 {
151                         string membersNamespace = defaultNamespace;
152                         bool includeInSchema = true;
153
154                         SoapAttributes atts = null;
155                         if (defaultXmlType == null) defaultXmlType = typeData.XmlType;
156
157                         if (!typeData.IsListType)
158                         {
159                                 if (attributeOverrides != null) 
160                                         atts = attributeOverrides[typeData.Type];
161
162                                 if (atts != null && typeData.SchemaType == SchemaTypes.Primitive)
163                                         throw new InvalidOperationException ("SoapType attribute may not be specified for the type " + typeData.FullTypeName);
164                         }
165
166                         if (atts == null) 
167                                 atts = new SoapAttributes (typeData.Type);
168
169                         if (atts.SoapType != null)
170                         {
171                                 if (atts.SoapType.Namespace != null && atts.SoapType.Namespace != string.Empty)
172                                         membersNamespace = atts.SoapType.Namespace;
173
174                                 if (atts.SoapType.TypeName != null && atts.SoapType.TypeName != string.Empty)
175                                         defaultXmlType = XmlConvert.EncodeLocalName (atts.SoapType.TypeName);
176
177                                 includeInSchema = atts.SoapType.IncludeInSchema;
178                         }
179
180                         if (membersNamespace == null) membersNamespace = "";
181                         XmlTypeMapping map = new XmlTypeMapping (defaultXmlType, membersNamespace, typeData, defaultXmlType, membersNamespace);
182                         map.IncludeInSchema = includeInSchema;
183                         relatedMaps.Add (map);
184
185                         return map;
186                 }
187
188                 XmlTypeMapping ImportClassMapping (Type type, string defaultNamespace)
189                 {
190                         TypeData typeData = TypeTranslator.GetTypeData (type);
191                         return ImportClassMapping (typeData, defaultNamespace);
192                 }
193
194                 XmlTypeMapping ImportClassMapping (TypeData typeData, string defaultNamespace)
195                 {
196                         Type type = typeData.Type;
197
198                         if (type.IsValueType) throw CreateStructException (type);
199
200                         if (type == typeof (object)) defaultNamespace = XmlSchema.Namespace;
201
202                         ReflectionHelper.CheckSerializableType (type, false);
203                         XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, defaultNamespace));
204                         if (map != null) return map;
205
206                         map = CreateTypeMapping (typeData, null, defaultNamespace);
207                         helper.RegisterClrType (map, type, map.Namespace);
208                         map.MultiReferenceType = true;
209
210                         ClassMap classMap = new ClassMap ();
211                         map.ObjectMap = classMap;
212
213                         // Import members
214
215                         try
216                         {
217                                 ICollection members = GetReflectionMembers (type);
218                                 foreach (XmlReflectionMember rmember in members)
219                                 {
220                                         if (rmember.SoapAttributes.SoapIgnore) continue;
221                                         classMap.AddMember (CreateMapMember (rmember, map.Namespace));
222                                 }
223                         }
224                         catch (Exception ex) 
225                         {
226                                 throw helper.CreateError (map, ex.Message);
227                         }
228
229                         // Import included classes
230
231                         SoapIncludeAttribute[] includes = (SoapIncludeAttribute[])type.GetCustomAttributes (typeof (SoapIncludeAttribute), false);
232                         for (int n=0; n<includes.Length; n++)
233                         {
234                                 Type includedType = includes[n].Type;
235                                 ImportTypeMapping (includedType);
236                         }
237
238                         if (type == typeof (object) && includedTypes != null)
239                         {
240                                 foreach (Type intype in includedTypes)
241                                         map.DerivedTypes.Add (ImportTypeMapping (intype));
242                         }
243
244                         // Register inheritance relations
245
246                         if (type.BaseType != null)
247                         {
248                                 XmlTypeMapping bmap = ImportClassMapping (type.BaseType, defaultNamespace);
249                                 
250                                 if (type.BaseType != typeof (object))
251                                         map.BaseMap = bmap;
252                                         
253                                 // At this point, derived classes of this map must be already registered
254                                 
255                                 RegisterDerivedMap (bmap, map);
256                         }
257                         
258                         return map;
259                 }
260                 
261                 void RegisterDerivedMap (XmlTypeMapping map, XmlTypeMapping derivedMap)
262                 {
263                         map.DerivedTypes.Add (derivedMap);
264                         map.DerivedTypes.AddRange (derivedMap.DerivedTypes);
265                         
266                         if (map.BaseMap != null)
267                                 RegisterDerivedMap (map.BaseMap, derivedMap);
268                         else {
269                                 XmlTypeMapping obmap = ImportTypeMapping (typeof(object));
270                                 if (obmap != map)
271                                         obmap.DerivedTypes.Add (derivedMap);
272                         }
273                 }
274
275                 string GetTypeNamespace (TypeData typeData, string defaultNamespace)
276                 {
277                         string membersNamespace = defaultNamespace;
278
279                         SoapAttributes atts = null;
280
281                         if (!typeData.IsListType)
282                         {
283                                 if (attributeOverrides != null)
284                                         atts = attributeOverrides[typeData.Type];
285                         }
286
287                         if (atts == null)
288                                 atts = new SoapAttributes (typeData.Type);
289
290                         if (atts.SoapType != null)
291                         {
292                                 if (atts.SoapType.Namespace != null && atts.SoapType.Namespace != string.Empty)
293                                         membersNamespace = atts.SoapType.Namespace;
294                         }
295
296                         if (membersNamespace == null) return "";
297                         else return membersNamespace;
298                 }
299                 
300                 XmlTypeMapping ImportListMapping (TypeData typeData, string defaultNamespace)
301                 {
302                         Type type = typeData.Type;
303
304                         XmlTypeMapping map = helper.GetRegisteredClrType (type, XmlSerializer.EncodingNamespace);
305                         if (map != null) return map;
306
307                         ListMap obmap = new ListMap ();
308                         TypeData itemTypeData = typeData.ListItemTypeData;
309
310                         map = CreateTypeMapping (typeData, "Array", XmlSerializer.EncodingNamespace);
311                         helper.RegisterClrType (map, type, XmlSerializer.EncodingNamespace);
312                         map.MultiReferenceType = true;
313                         map.ObjectMap = obmap;
314
315                         XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, itemTypeData);
316                         
317                         if (elem.TypeData.IsComplexType) {
318                                 elem.MappedType = ImportTypeMapping (typeData.ListItemType, defaultNamespace);
319                                 elem.TypeData = elem.MappedType.TypeData;
320                         }
321                                 
322                         elem.ElementName = "Item";
323                         elem.Namespace = string.Empty;
324                         elem.IsNullable = true; // By default, items are nullable
325
326                         XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
327                         list.Add (elem);
328
329                         obmap.ItemInfo = list;
330                         XmlTypeMapping objMap = ImportTypeMapping (typeof(object), defaultNamespace);
331                         objMap.DerivedTypes.Add (map);
332
333                         // Register any of the including types as a derived class of object
334                         SoapIncludeAttribute[] includes = (SoapIncludeAttribute[])type.GetCustomAttributes (typeof (SoapIncludeAttribute), false);
335                         for (int i = 0; i < includes.Length; i++)
336                         {
337                                 Type includedType = includes[i].Type;
338                                 objMap.DerivedTypes.Add(ImportTypeMapping (includedType, defaultNamespace));
339                         }
340                         
341                         return map;
342                 }
343                 
344                 XmlTypeMapping ImportPrimitiveMapping (TypeData typeData, string defaultNamespace)
345                 {
346                         Type type = typeData.Type;
347                         XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, defaultNamespace));
348                         if (map != null) return map;
349                         map = CreateTypeMapping (typeData, null, defaultNamespace);
350                         helper.RegisterClrType (map, type, map.Namespace);
351                         return map;
352                 }
353
354                 XmlTypeMapping ImportEnumMapping (TypeData typeData, string defaultNamespace)
355                 {
356                         Type type = typeData.Type;
357                         XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, defaultNamespace));
358                         if (map != null) return map;
359                         
360                         ReflectionHelper.CheckSerializableType (type, false);
361                                 
362                         map = CreateTypeMapping (typeData, null, defaultNamespace);
363                         helper.RegisterClrType (map, type, map.Namespace);
364
365                         map.MultiReferenceType = true;
366                         
367                         string [] names = Enum.GetNames (type);
368                         EnumMap.EnumMapMember[] members = new EnumMap.EnumMapMember[names.Length];
369                         for (int n=0; n<names.Length; n++)
370                         {
371                                 FieldInfo field = type.GetField (names[n]);
372                                 string xmlName = names[n];
373                                 object[] atts = field.GetCustomAttributes (typeof(SoapEnumAttribute), false);
374                                 if (atts.Length > 0) xmlName = ((SoapEnumAttribute)atts[0]).Name;
375                                 long value = ((IConvertible) field.GetValue (null)).ToInt64 (CultureInfo.InvariantCulture);
376                                 members[n] = new EnumMap.EnumMapMember (XmlConvert.EncodeLocalName (xmlName), names[n], value);
377                         }
378
379                         bool isFlags = type.IsDefined (typeof (FlagsAttribute), false);
380                         map.ObjectMap = new EnumMap (members, isFlags);
381                         ImportTypeMapping (typeof(object), defaultNamespace).DerivedTypes.Add (map);
382                         return map;
383                 }
384
385                 ICollection GetReflectionMembers (Type type)
386                 {
387                         ArrayList members = new ArrayList();
388                         PropertyInfo[] properties = type.GetProperties (BindingFlags.Instance | BindingFlags.Public);
389                         foreach (PropertyInfo prop in properties)
390                         {
391                                 if (!prop.CanRead) continue;
392                                 if (!prop.CanWrite && (TypeTranslator.GetTypeData (prop.PropertyType).SchemaType != SchemaTypes.Array || prop.PropertyType.IsArray))
393                                         continue;
394                                         
395                                 SoapAttributes atts = attributeOverrides[type, prop.Name];
396                                 if (atts == null) atts = new SoapAttributes (prop);
397                                 if (atts.SoapIgnore) continue;
398                                 XmlReflectionMember member = new XmlReflectionMember(prop.Name, prop.PropertyType, atts);
399                                 members.Add (member);
400                         }
401
402                         FieldInfo[] fields = type.GetFields (BindingFlags.Instance | BindingFlags.Public);
403                         foreach (FieldInfo field in fields)
404                         {
405                                 SoapAttributes atts = attributeOverrides[type, field.Name];
406                                 if (atts == null) atts = new SoapAttributes (field);
407                                 if (atts.SoapIgnore) continue;
408                                 XmlReflectionMember member = new XmlReflectionMember(field.Name, field.FieldType, atts);
409                                 members.Add (member);
410                         }
411                         return members;
412                 }
413                 
414                 private XmlTypeMapMember CreateMapMember (XmlReflectionMember rmember, string defaultNamespace)
415                 {
416                         XmlTypeMapMember mapMember;
417                         SoapAttributes atts = rmember.SoapAttributes;
418                         TypeData typeData = TypeTranslator.GetTypeData (rmember.MemberType);
419
420                         if (atts.SoapAttribute != null)
421                         {
422                                 // An attribute
423
424                                 if (atts.SoapElement != null)
425                                         throw new Exception ("SoapAttributeAttribute and SoapElementAttribute cannot be applied to the same member");
426
427                                 XmlTypeMapMemberAttribute mapAttribute = new XmlTypeMapMemberAttribute ();
428                                 if (atts.SoapAttribute.AttributeName.Length == 0) 
429                                         mapAttribute.AttributeName = XmlConvert.EncodeLocalName (rmember.MemberName);
430                                 else 
431                                         mapAttribute.AttributeName = XmlConvert.EncodeLocalName (atts.SoapAttribute.AttributeName);
432
433                                 mapAttribute.Namespace = (atts.SoapAttribute.Namespace != null) ? atts.SoapAttribute.Namespace : "";
434                                 if (typeData.IsComplexType)
435                                         mapAttribute.MappedType = ImportTypeMapping (typeData.Type, defaultNamespace);
436
437                                 typeData = TypeTranslator.GetTypeData (rmember.MemberType, atts.SoapAttribute.DataType);
438                                 mapMember = mapAttribute;
439                         }
440                         else
441                         {
442                                 if (typeData.SchemaType == SchemaTypes.Array) mapMember = new XmlTypeMapMemberList ();
443                                 else mapMember = new XmlTypeMapMemberElement ();
444
445                                 if (atts.SoapElement != null && atts.SoapElement.DataType.Length != 0)
446                                         typeData = TypeTranslator.GetTypeData (rmember.MemberType, atts.SoapElement.DataType);
447
448                                 // Creates an ElementInfo that identifies the element
449                                 XmlTypeMapElementInfoList infoList = new XmlTypeMapElementInfoList();
450                                 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (mapMember, typeData);
451
452                                 elem.ElementName = XmlConvert.EncodeLocalName ((atts.SoapElement != null && atts.SoapElement.ElementName.Length != 0) ? atts.SoapElement.ElementName : rmember.MemberName);
453                                 elem.Namespace = string.Empty;
454                                 elem.IsNullable = (atts.SoapElement != null) ? atts.SoapElement.IsNullable : false;
455                                 if (typeData.IsComplexType)
456                                         elem.MappedType = ImportTypeMapping (typeData.Type, defaultNamespace);
457                                 
458                                 infoList.Add (elem);
459                                 ((XmlTypeMapMemberElement)mapMember).ElementInfo = infoList;
460                         }
461
462                         mapMember.TypeData = typeData;
463                         mapMember.Name = rmember.MemberName;
464                         mapMember.IsReturnValue = rmember.IsReturnValue;
465                         return mapMember;
466                 }
467                 
468                 public void IncludeType (Type type)
469                 {
470                         if (type == null)
471                                 throw new ArgumentNullException ("type");
472
473                         if (includedTypes == null) includedTypes = new ArrayList ();
474                         if (!includedTypes.Contains (type))
475                                 includedTypes.Add (type);
476                 }
477
478                 public void IncludeTypes (ICustomAttributeProvider provider)
479                 { 
480                         object[] ats = provider.GetCustomAttributes (typeof(SoapIncludeAttribute), true);
481                         foreach (SoapIncludeAttribute at in ats)
482                                 IncludeType (at.Type);
483                 }
484
485                 Exception CreateTypeException (Type type)
486                 {
487                         return new NotSupportedException ("The type " + type.FullName + " may not be serialized with SOAP-encoded messages. Set the Use for your message to Literal");
488                 }
489
490                 Exception CreateStructException (Type type)
491                 {
492                         return new NotSupportedException ("Cannot serialize " + type.FullName + ". Nested structs are not supported with encoded SOAP");
493                 }
494
495
496                 
497
498                 #endregion // Methods
499         }
500 }