* XmlReflectionImporter.cs: Throw exception if a value type member has the
[mono.git] / mcs / class / System.XML / System.Xml.Serialization / XmlReflectionImporter.cs
1 // 
2 // System.Xml.Serialization.XmlReflectionImporter 
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //   Erik LeBel (eriklebel@yahoo.ca)
7 //   Lluis Sanchez Gual (lluis@ximian.com)
8 //
9 // Copyright (C) Tim Coleman, 2002
10 // (C) 2003 Erik LeBel
11 //
12
13 using System.Reflection;
14 using System.Collections;
15
16 namespace System.Xml.Serialization {
17         public class XmlReflectionImporter {
18
19                 string initialDefaultNamespace;
20                 XmlAttributeOverrides attributeOverrides;
21                 ArrayList includedTypes;
22                 ReflectionHelper helper = new ReflectionHelper();
23                 int arrayChoiceCount = 1;
24                 ArrayList relatedMaps = new ArrayList ();
25                 bool allowPrivateTypes = false;
26
27                 static readonly string errSimple = "Cannot serialize object of type '{0}'. Base " +
28                         "type '{1}' has simpleContent and can be only extended by adding XmlAttribute " +
29                         "elements. Please consider changing XmlTextMember of the base class to string array";
30
31                 #region Constructors
32
33                 public XmlReflectionImporter ()
34                         : this (null, null)
35                 {
36                 }
37
38                 public XmlReflectionImporter (string defaultNamespace)
39                         : this (null, defaultNamespace)
40                 {
41                 }
42
43                 public XmlReflectionImporter (XmlAttributeOverrides attributeOverrides)
44                         : this (attributeOverrides, null)
45                 {
46                 }
47
48                 public XmlReflectionImporter (XmlAttributeOverrides attributeOverrides, string defaultNamespace)
49                 {
50                         if (defaultNamespace == null)
51                                 this.initialDefaultNamespace = String.Empty;
52                         else
53                                 this.initialDefaultNamespace = defaultNamespace;
54
55                         if (attributeOverrides == null)
56                                 this.attributeOverrides = new XmlAttributeOverrides();
57                         else
58                                 this.attributeOverrides = attributeOverrides;
59                 }
60
61                 void Reset ()
62                 {
63                         helper = new ReflectionHelper();
64                         arrayChoiceCount = 1;
65                 }
66                 
67                 internal bool AllowPrivateTypes
68                 {
69                         get { return allowPrivateTypes; }
70                         set { allowPrivateTypes = value; }
71                 }
72
73                 #endregion // Constructors
74
75                 #region Methods
76
77                 public XmlMembersMapping ImportMembersMapping (string elementName,
78                         string ns,
79                         XmlReflectionMember [] members,
80                         bool hasWrapperElement)
81                 {
82                         Reset ();
83                         XmlMemberMapping[] mapping = new XmlMemberMapping[members.Length];
84                         for (int n=0; n<members.Length; n++)
85                         {
86                                 XmlTypeMapMember mapMem = CreateMapMember (members[n], ns);
87                                 mapping[n] = new XmlMemberMapping (members[n].MemberName, ns, mapMem, false);
88                         }
89                         XmlMembersMapping mps = new XmlMembersMapping (elementName, ns, hasWrapperElement, false, mapping);
90                         mps.RelatedMaps = relatedMaps;
91                         mps.Format = SerializationFormat.Literal;
92                         return mps;
93                 }
94
95                 public XmlTypeMapping ImportTypeMapping (Type type)
96                 {
97                         return ImportTypeMapping (type, null, null);
98                 }
99
100                 public XmlTypeMapping ImportTypeMapping (Type type, string defaultNamespace)
101                 {
102                         return ImportTypeMapping (type, null, defaultNamespace);
103                 }
104
105                 public XmlTypeMapping ImportTypeMapping (Type type, XmlRootAttribute group)
106                 {
107                         return ImportTypeMapping (type, group, null);
108                 }
109
110                 public XmlTypeMapping ImportTypeMapping (Type type, XmlRootAttribute root, string defaultNamespace)
111                 {
112                         if (type == null)
113                                 throw new ArgumentNullException ("type");
114
115                         if (type == typeof (void))
116                                 throw new InvalidOperationException ("Type " + type.Name + " may not be serialized.");
117
118                         if (defaultNamespace == null) defaultNamespace = initialDefaultNamespace;
119                         if (defaultNamespace == null) defaultNamespace = string.Empty;
120
121                         XmlTypeMapping map;
122
123                         switch (TypeTranslator.GetTypeData(type).SchemaType)
124                         {
125                                 case SchemaTypes.Class: map = ImportClassMapping (type, root, defaultNamespace); break;
126                                 case SchemaTypes.Array: map = ImportListMapping (type, root, defaultNamespace, null, 0); break;
127                                 case SchemaTypes.XmlNode: map = ImportXmlNodeMapping (type, root, defaultNamespace); break;
128                                 case SchemaTypes.Primitive: map = ImportPrimitiveMapping (type, root, defaultNamespace); break;
129                                 case SchemaTypes.Enum: map = ImportEnumMapping (type, root, defaultNamespace); break;
130                                 case SchemaTypes.XmlSerializable: map = ImportXmlSerializableMapping (type, root, defaultNamespace); break;
131                                 default: throw new NotSupportedException ("Type " + type.FullName + " not supported for XML stialization");
132                         }
133
134                         map.RelatedMaps = relatedMaps;
135                         map.Format = SerializationFormat.Literal;
136                         return map;
137                 }
138
139                 XmlTypeMapping CreateTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultXmlType, string defaultNamespace)
140                 {
141                         string rootNamespace = defaultNamespace;
142                         string typeNamespace = null;
143                         
144                         string elementName;
145                         bool includeInSchema = true;
146                         XmlAttributes atts = null;
147                         if (defaultXmlType == null) defaultXmlType = typeData.XmlType;
148
149                         if (!typeData.IsListType)
150                         {
151                                 if (attributeOverrides != null) 
152                                         atts = attributeOverrides[typeData.Type];
153
154                                 if (atts != null && typeData.SchemaType == SchemaTypes.Primitive)
155                                         throw new InvalidOperationException ("XmlRoot and XmlType attributes may not be specified for the type " + typeData.FullTypeName);
156                         }
157
158                         if (atts == null) 
159                                 atts = new XmlAttributes (typeData.Type);
160
161                         if (atts.XmlRoot != null && root == null)
162                                 root = atts.XmlRoot;
163
164                         if (atts.XmlType != null)
165                         {
166                                 if (atts.XmlType.Namespace != null && atts.XmlType.Namespace != string.Empty && typeData.SchemaType != SchemaTypes.Enum)
167                                         typeNamespace = atts.XmlType.Namespace;
168
169                                 if (atts.XmlType.TypeName != null && atts.XmlType.TypeName != string.Empty)
170                                         defaultXmlType = atts.XmlType.TypeName;
171                                         
172                                 includeInSchema = atts.XmlType.IncludeInSchema;
173                         }
174
175                         elementName = defaultXmlType;
176
177                         if (root != null)
178                         {
179                                 if (root.ElementName != null && root.ElementName != String.Empty)
180                                         elementName = root.ElementName;
181                                 if (root.Namespace != null && root.Namespace != String.Empty)
182                                         rootNamespace = root.Namespace;
183                         }
184
185                         if (rootNamespace == null) rootNamespace = "";
186                         if (typeNamespace == null || typeNamespace.Length == 0) typeNamespace = rootNamespace;
187                         
188                         XmlTypeMapping map = new XmlTypeMapping (elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
189                         map.IncludeInSchema = includeInSchema;
190                         relatedMaps.Add (map);
191                         
192                         return map;
193                 }
194
195                 XmlTypeMapping ImportClassMapping (Type type, XmlRootAttribute root, string defaultNamespace)
196                 {
197                         TypeData typeData = TypeTranslator.GetTypeData (type);
198                         XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
199                         if (map != null) return map;
200
201                         if (!allowPrivateTypes)
202                                 ReflectionHelper.CheckSerializableType (type);
203                         
204                         map = CreateTypeMapping (typeData, root, null, defaultNamespace);
205                         helper.RegisterClrType (map, type, map.XmlTypeNamespace);
206                         helper.RegisterSchemaType (map, map.XmlType, map.XmlTypeNamespace);
207
208                         // Import members
209
210                         ClassMap classMap = new ClassMap ();
211                         map.ObjectMap = classMap;
212
213 //                      try
214 //                      {
215                                 ICollection members = GetReflectionMembers (type);
216                                 foreach (XmlReflectionMember rmember in members)
217                                 {
218                                         if (rmember.XmlAttributes.XmlIgnore) continue;
219                                         XmlTypeMapMember mem = CreateMapMember (rmember, map.XmlTypeNamespace);
220                                         mem.CheckOptionalValueType (type);
221                                         classMap.AddMember (mem);
222                                 }
223 //                      }
224 //                      catch (Exception ex) {
225 //                              throw helper.CreateError (map, ex.Message);
226 //                      }
227
228                         ImportIncludedTypes (type, defaultNamespace);
229                         
230                         // Import extra classes
231
232                         if (type == typeof (object) && includedTypes != null)
233                         {
234                                 foreach (Type intype in includedTypes)
235                                         map.DerivedTypes.Add (ImportTypeMapping (intype, defaultNamespace));
236                         }
237
238                         // Register inheritance relations
239
240                         if (type.BaseType != null)
241                         {
242                                 XmlTypeMapping bmap = ImportClassMapping (type.BaseType, root, defaultNamespace);
243                                 
244                                 if (type.BaseType != typeof (object))
245                                         map.BaseMap = bmap;
246                                 
247                                 // At this point, derived classes of this map must be already registered
248                                 
249                                 bmap.DerivedTypes.Add (map);
250                                 bmap.DerivedTypes.AddRange (map.DerivedTypes);
251                                 
252                                 if (((ClassMap)bmap.ObjectMap).HasSimpleContent && classMap.ElementMembers != null && classMap.ElementMembers.Count != 1)
253                                         throw new InvalidOperationException (String.Format (errSimple, map.TypeData.TypeName, map.BaseMap.TypeData.TypeName));
254                         }
255                         
256                         return map;
257                 }
258
259                 string GetTypeNamespace (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
260                 {
261                         string typeNamespace = null;
262                         
263                         XmlAttributes atts = null;
264                         if (!typeData.IsListType)
265                         {
266                                 if (attributeOverrides != null)
267                                         atts = attributeOverrides[typeData.Type];
268                         }
269
270                         if (atts == null)
271                                 atts = new XmlAttributes (typeData.Type);
272
273                         if (atts.XmlType != null)
274                         {
275                                 if (atts.XmlType.Namespace != null && atts.XmlType.Namespace.Length != 0 && typeData.SchemaType != SchemaTypes.Enum)
276                                         typeNamespace = atts.XmlType.Namespace;
277                         }
278
279                         if (typeNamespace != null && typeNamespace.Length != 0) return typeNamespace;
280                         
281                         if (atts.XmlRoot != null && root == null)
282                                 root = atts.XmlRoot;
283
284                         if (root != null)
285                         {
286                                 if (root.Namespace != null && root.Namespace.Length != 0)
287                                         return root.Namespace;
288                         }
289
290                         if (defaultNamespace == null) return "";
291                         else return defaultNamespace;
292                 }
293
294                 XmlTypeMapping ImportListMapping (Type type, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
295                 {
296                         TypeData typeData = TypeTranslator.GetTypeData (type);
297                         ListMap obmap = new ListMap ();
298
299                         if (!allowPrivateTypes)
300                                 ReflectionHelper.CheckSerializableType (type);
301                         
302                         if (atts == null) atts = new XmlAttributes();
303                         Type itemType = typeData.ListItemType;
304
305                         // warning: byte[][] should not be considered multiarray
306                         bool isMultiArray = (type.IsArray && (TypeTranslator.GetTypeData(itemType).SchemaType == SchemaTypes.Array) && itemType.IsArray);
307
308                         XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
309
310                         foreach (XmlArrayItemAttribute att in atts.XmlArrayItems)
311                         {
312                                 if (att.NestingLevel != nestingLevel) continue;
313                                 Type elemType = (att.Type != null) ? att.Type : itemType;
314                                 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData(elemType, att.DataType));
315                                 elem.Namespace = att.Namespace != null ? att.Namespace : defaultNamespace;
316                                 if (elem.Namespace == null) elem.Namespace = "";
317                                 elem.Form = att.Form;
318                                 elem.IsNullable = att.IsNullable && CanBeNull (elem.TypeData);
319                                 elem.NestingLevel = att.NestingLevel;
320
321                                 if (isMultiArray)
322                                         elem.MappedType = ImportListMapping (elemType, null, elem.Namespace, atts, nestingLevel + 1);
323                                 else if (elem.TypeData.IsComplexType)
324                                         elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
325
326                                 if (att.ElementName != null) elem.ElementName = att.ElementName;
327                                 else if (elem.MappedType != null) elem.ElementName = elem.MappedType.ElementName;
328                                 else elem.ElementName = TypeTranslator.GetTypeData(elemType).XmlType;
329
330                                 list.Add (elem);
331                         }
332
333                         if (list.Count == 0)
334                         {
335                                 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData (itemType));
336                                 if (isMultiArray)
337                                         elem.MappedType = ImportListMapping (itemType, null, defaultNamespace, atts, nestingLevel + 1);
338                                 else if (elem.TypeData.IsComplexType)
339                                         elem.MappedType = ImportTypeMapping (itemType, null, defaultNamespace);
340
341                                 if (elem.MappedType != null) elem.ElementName = elem.MappedType.XmlType;
342                                 else elem.ElementName = TypeTranslator.GetTypeData(itemType).XmlType ;
343
344                                 elem.Namespace = (defaultNamespace != null) ? defaultNamespace : "";
345                                 elem.IsNullable = CanBeNull (elem.TypeData);
346                                 list.Add (elem);
347                         }
348
349                         obmap.ItemInfo = list;
350
351                         // If there can be different element names (types) in the array, then its name cannot
352                         // be "ArrayOfXXX" it must be something like ArrayOfChoiceNNN
353
354                         string baseName;
355                         if (list.Count > 1)
356                                 baseName = "ArrayOfChoice" + (arrayChoiceCount++);
357                         else
358                         {
359                                 XmlTypeMapElementInfo elem = ((XmlTypeMapElementInfo)list[0]);
360                                 if (elem.MappedType != null) baseName = TypeTranslator.GetArrayName (elem.MappedType.XmlType);
361                                 else baseName = TypeTranslator.GetArrayName (elem.ElementName);
362                         }
363
364                         // Avoid name colisions
365
366                         int nameCount = 1;
367                         string name = baseName;
368
369                         do {
370                                 XmlTypeMapping foundMap = helper.GetRegisteredSchemaType (name, defaultNamespace);
371                                 if (foundMap == null) nameCount = -1;
372                                 else if (obmap.Equals (foundMap.ObjectMap) && typeData.Type == foundMap.TypeData.Type) return foundMap;
373                                 else name = baseName + (nameCount++);
374                         }
375                         while (nameCount != -1);
376
377                         XmlTypeMapping map = CreateTypeMapping (typeData, root, name, defaultNamespace);
378                         map.ObjectMap = obmap;
379                         
380                         // Register any of the including types as a derived class of object
381                         XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
382                         
383                         XmlTypeMapping objectMapping = ImportTypeMapping (typeof(object));
384                         for (int i = 0; i < includes.Length; i++)
385                         {
386                                 Type includedType = includes[i].Type;
387                                 objectMapping.DerivedTypes.Add(ImportTypeMapping (includedType, null, defaultNamespace));
388                         }
389                         
390                         // Register this map as a derived class of object
391
392                         helper.RegisterSchemaType (map, name, defaultNamespace);
393                         ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
394
395                         return map;
396                 }
397
398                 XmlTypeMapping ImportXmlNodeMapping (Type type, XmlRootAttribute root, string defaultNamespace)
399                 {
400                         XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (TypeTranslator.GetTypeData (type), root, defaultNamespace));
401                         if (map != null) return map;
402
403                         // Registers the maps for XmlNode and XmlElement
404
405                         XmlTypeMapping nodeMap = CreateTypeMapping (TypeTranslator.GetTypeData (typeof(XmlNode)), root, null, defaultNamespace);
406                         helper.RegisterClrType (nodeMap, typeof(XmlNode), nodeMap.XmlTypeNamespace);
407
408                         XmlTypeMapping elemMap = CreateTypeMapping (TypeTranslator.GetTypeData (typeof(XmlElement)), root, null, defaultNamespace);
409                         helper.RegisterClrType (elemMap, typeof(XmlElement), elemMap.XmlTypeNamespace);
410
411                         XmlTypeMapping textMap = CreateTypeMapping (TypeTranslator.GetTypeData (typeof(XmlText)), root, null, defaultNamespace);
412                         helper.RegisterClrType (elemMap, typeof(XmlText), textMap.XmlTypeNamespace);
413
414                         XmlTypeMapping obmap = ImportTypeMapping (typeof(object));
415                         obmap.DerivedTypes.Add (nodeMap);
416                         obmap.DerivedTypes.Add (elemMap);
417                         obmap.DerivedTypes.Add (textMap);
418                         nodeMap.DerivedTypes.Add (elemMap);
419                         nodeMap.DerivedTypes.Add (textMap);
420
421                         return helper.GetRegisteredClrType (type, GetTypeNamespace (TypeTranslator.GetTypeData (type), root, defaultNamespace));
422                 }
423
424                 XmlTypeMapping ImportPrimitiveMapping (Type type, XmlRootAttribute root, string defaultNamespace)
425                 {
426                         TypeData typeData = TypeTranslator.GetTypeData (type);
427                         XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
428                         if (map != null) return map;
429                         map = CreateTypeMapping (typeData, root, null, defaultNamespace);
430                         helper.RegisterClrType (map, type, map.XmlTypeNamespace);
431                         return map;
432                 }
433
434                 XmlTypeMapping ImportEnumMapping (Type type, XmlRootAttribute root, string defaultNamespace)
435                 {
436                         TypeData typeData = TypeTranslator.GetTypeData (type);
437                         XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
438                         if (map != null) return map;
439                         map = CreateTypeMapping (typeData, root, null, defaultNamespace);
440                         helper.RegisterClrType (map, type, map.XmlTypeNamespace);
441
442                         string [] names = Enum.GetNames (type);
443                         ArrayList members = new ArrayList();
444                         foreach (string name in names)
445                         {
446                                 MemberInfo[] mem = type.GetMember (name);
447                                 string xmlName = null;
448                                 object[] atts = mem[0].GetCustomAttributes (typeof(XmlIgnoreAttribute), false);
449                                 if (atts.Length > 0) continue;
450                                 atts = mem[0].GetCustomAttributes (typeof(XmlEnumAttribute), false);
451                                 if (atts.Length > 0) xmlName = ((XmlEnumAttribute)atts[0]).Name;
452                                 if (xmlName == null) xmlName = name;
453                                 members.Add (new EnumMap.EnumMapMember (xmlName, name));
454                         }
455
456                         bool isFlags = type.GetCustomAttributes (typeof(FlagsAttribute),false).Length > 0;
457                         map.ObjectMap = new EnumMap ((EnumMap.EnumMapMember[])members.ToArray (typeof(EnumMap.EnumMapMember)), isFlags);
458                         ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
459                         return map;
460                 }
461
462                 XmlTypeMapping ImportXmlSerializableMapping (Type type, XmlRootAttribute root, string defaultNamespace)
463                 {
464                         TypeData typeData = TypeTranslator.GetTypeData (type);
465                         XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
466                         if (map != null) return map;
467                         map = CreateTypeMapping (typeData, root, null, defaultNamespace);
468                         helper.RegisterClrType (map, type, map.XmlTypeNamespace);
469                         return map;
470                 }
471
472                 void ImportIncludedTypes (Type type, string defaultNamespace)
473                 {
474                         XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
475                         for (int n=0; n<includes.Length; n++)
476                         {
477                                 Type includedType = includes[n].Type;
478                                 ImportTypeMapping (includedType, null, defaultNamespace);
479                         }
480                 }
481
482                 public ICollection GetReflectionMembers (Type type)
483                 {
484                         ArrayList members = new ArrayList();
485                         PropertyInfo[] properties = type.GetProperties (BindingFlags.Instance | BindingFlags.Public);
486                         foreach (PropertyInfo prop in properties)
487                         {
488                                 if (!prop.CanRead) continue;
489                                 if (!prop.CanWrite && TypeTranslator.GetTypeData (prop.PropertyType).SchemaType != SchemaTypes.Array)
490                                         continue;
491                                 if (prop.GetIndexParameters().Length > 0) continue;
492                                         
493                                 XmlAttributes atts = attributeOverrides[type, prop.Name];
494                                 if (atts == null) atts = new XmlAttributes (prop);
495                                 if (atts.XmlIgnore) continue;
496                                 XmlReflectionMember member = new XmlReflectionMember(prop.Name, prop.PropertyType, atts);
497                                 members.Add (member);
498                         }
499
500                         FieldInfo[] fields = type.GetFields (BindingFlags.Instance | BindingFlags.Public);
501                         foreach (FieldInfo field in fields)
502                         {
503                                 XmlAttributes atts = attributeOverrides[type, field.Name];
504                                 if (atts == null) atts = new XmlAttributes (field);
505                                 if (atts.XmlIgnore) continue;
506                                 XmlReflectionMember member = new XmlReflectionMember(field.Name, field.FieldType, atts);
507                                 members.Add (member);
508                         }
509                         return members;
510                 }
511                 
512                 private XmlTypeMapMember CreateMapMember (XmlReflectionMember rmember, string defaultNamespace)
513                 {
514                         XmlTypeMapMember mapMember;
515                         XmlAttributes atts = rmember.XmlAttributes;
516                         TypeData typeData = TypeTranslator.GetTypeData (rmember.MemberType);
517
518                         if (atts.XmlAnyAttribute != null)
519                         {
520                                 if ( (rmember.MemberType.FullName == "System.Xml.XmlAttribute[]") ||
521                                          (rmember.MemberType.FullName == "System.Xml.XmlNode[]") )
522                                 {
523                                         mapMember = new XmlTypeMapMemberAnyAttribute();
524                                 }
525                                 else
526                                         throw new InvalidOperationException ("XmlAnyAttributeAttribute can only be applied to members of type XmlAttribute[] or XmlNode[]");
527                         }
528                         else if (atts.XmlAnyElements != null && atts.XmlAnyElements.Count > 0)
529                         {
530                                 if ( (rmember.MemberType.FullName == "System.Xml.XmlElement[]") ||
531                                          (rmember.MemberType.FullName == "System.Xml.XmlNode[]") ||
532                                          (rmember.MemberType.FullName == "System.Xml.XmlElement"))
533                                 {
534                                         XmlTypeMapMemberAnyElement member = new XmlTypeMapMemberAnyElement();
535                                         member.ElementInfo = ImportAnyElementInfo (defaultNamespace, rmember.MemberType, member, atts);
536                                         mapMember = member;
537                                 }
538                                 else
539                                         throw new InvalidOperationException ("XmlAnyElementAttribute can only be applied to members of type XmlElement, XmlElement[] or XmlNode[]");
540                         }
541                         else if (atts.Xmlns)
542                         {
543                                 XmlTypeMapMemberNamespaces mapNamespaces = new XmlTypeMapMemberNamespaces ();
544                                 mapMember = mapNamespaces;
545                         }
546                         else if (atts.XmlAttribute != null)
547                         {
548                                 // An attribute
549
550                                 if (atts.XmlElements != null && atts.XmlElements.Count > 0)
551                                         throw new Exception ("XmlAttributeAttribute and XmlElementAttribute cannot be applied to the same member");
552
553                                 XmlTypeMapMemberAttribute mapAttribute = new XmlTypeMapMemberAttribute ();
554                                 if (atts.XmlAttribute.AttributeName == null) 
555                                         mapAttribute.AttributeName = rmember.MemberName;
556                                 else 
557                                         mapAttribute.AttributeName = atts.XmlAttribute.AttributeName;
558
559                                 mapAttribute.Form = atts.XmlAttribute.Form;
560                                 mapAttribute.Namespace = (atts.XmlAttribute.Namespace != null) ? atts.XmlAttribute.Namespace : "";
561                                 if (typeData.IsComplexType)
562                                         mapAttribute.MappedType = ImportTypeMapping (typeData.Type, null, mapAttribute.Namespace);
563
564                                 typeData = TypeTranslator.GetTypeData(rmember.MemberType, atts.XmlAttribute.DataType);
565                                 mapMember = mapAttribute;
566                         }
567                         else if (typeData.SchemaType == SchemaTypes.Array)
568                         {
569                                 // If the member has a single XmlElementAttribute and the type is the type of the member,
570                                 // then it is not a flat list
571                                 
572                                 if (atts.XmlElements.Count > 1 ||
573                                    (atts.XmlElements.Count == 1 && atts.XmlElements[0].Type != typeData.Type) ||
574                                    (atts.XmlText != null))
575                                 {
576                                         // A flat list
577
578                                         // TODO: check that it does not have XmlArrayAttribute
579                                         XmlTypeMapMemberFlatList member = new XmlTypeMapMemberFlatList ();
580                                         member.ListMap = new ListMap ();
581                                         member.ListMap.ItemInfo = ImportElementInfo (rmember.MemberName, defaultNamespace, typeData.ListItemType, member, atts);
582                                         member.ElementInfo = member.ListMap.ItemInfo;
583                                         mapMember = member;
584                                 }
585                                 else
586                                 {
587                                         // A list
588
589                                         XmlTypeMapMemberList member = new XmlTypeMapMemberList ();
590
591                                         // Creates an ElementInfo that identifies the array instance. 
592                                         member.ElementInfo = new XmlTypeMapElementInfoList();
593                                         XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, typeData);
594                                         elem.ElementName = (atts.XmlArray != null && atts.XmlArray.ElementName != null) ? atts.XmlArray.ElementName : rmember.MemberName;
595                                         elem.Namespace = (atts.XmlArray != null && atts.XmlArray.Namespace != null) ? atts.XmlArray.Namespace : defaultNamespace;
596                                         elem.MappedType = ImportListMapping (rmember.MemberType, null, elem.Namespace, atts, 0);
597                                         elem.IsNullable = (atts.XmlArray != null) ? atts.XmlArray.IsNullable : false;
598                                         member.ElementInfo.Add (elem);
599                                         mapMember = member;
600                                 }
601                         }
602                         else
603                         {
604                                 // An element
605
606                                 XmlTypeMapMemberElement member = new XmlTypeMapMemberElement ();
607                                 member.ElementInfo = ImportElementInfo (rmember.MemberName, defaultNamespace, rmember.MemberType, member, atts);
608                                 mapMember = member;
609                         }
610
611                         mapMember.DefaultValue = atts.XmlDefaultValue;
612                         mapMember.TypeData = typeData;
613                         mapMember.Name = rmember.MemberName;
614                         return mapMember;
615                 }
616
617                 XmlTypeMapElementInfoList ImportElementInfo (string defaultName, string defaultNamespace, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts)
618                 {
619                         XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
620
621                         ImportTextElementInfo (list, defaultType, member, atts);
622                         
623                         if (atts.XmlElements.Count == 0 && list.Count == 0)
624                         {
625                                 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType));
626                                 elem.ElementName = defaultName;
627                                 elem.Namespace = defaultNamespace;
628                                 if (elem.TypeData.IsComplexType)
629                                         elem.MappedType = ImportTypeMapping (defaultType, null, defaultNamespace);
630                                 list.Add (elem);
631                         }
632
633                         bool multiType = (atts.XmlElements.Count > 1);
634                         foreach (XmlElementAttribute att in atts.XmlElements)
635                         {
636                                 Type elemType = (att.Type != null) ? att.Type : defaultType;
637                                 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(elemType, att.DataType));
638                                 elem.ElementName = (att.ElementName != null) ? att.ElementName : defaultName;
639                                 elem.Namespace = (att.Namespace != null) ? att.Namespace : defaultNamespace;
640                                 elem.Form = att.Form;
641                                 elem.IsNullable = att.IsNullable;
642                                 
643                                 if (elem.IsNullable && elem.TypeData.IsValueType)
644                                         throw new InvalidOperationException ("IsNullable may not be 'true' for value type " + elem.TypeData.FullTypeName);
645                                         
646                                 if (elem.TypeData.IsComplexType)
647                                 {
648                                         if (att.DataType != null) throw new InvalidOperationException ("'" + att.DataType + "' is an invalid value for the XmlElementAttribute.DateTime property. The property may only be specified for primitive types.");
649                                         elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
650                                 }
651
652                                 if (att.ElementName != null) 
653                                         elem.ElementName = att.ElementName;
654                                 else if (multiType) {
655                                         if (elem.MappedType != null) elem.ElementName = elem.MappedType.ElementName;
656                                         else elem.ElementName = TypeTranslator.GetTypeData(elemType).XmlType;
657                                 }
658                                 else
659                                         elem.ElementName = defaultName;
660
661                                 list.Add (elem);
662                         }
663                         return list;
664                 }
665
666                 XmlTypeMapElementInfoList ImportAnyElementInfo (string defaultNamespace, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts)
667                 {
668                         XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
669
670                         ImportTextElementInfo (list, defaultType, member, atts);
671
672                         foreach (XmlAnyElementAttribute att in atts.XmlAnyElements)
673                         {
674                                 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(typeof(XmlElement)));
675                                 if (att.Name != null && att.Name != string.Empty) elem.ElementName = att.Name;
676                                 else elem.IsUnnamedAnyElement = true;
677                                 elem.Namespace = (att.Namespace != null) ? att.Namespace : "";
678                                 list.Add (elem);
679                         }
680                         return list;
681                 }
682
683                 void ImportTextElementInfo (XmlTypeMapElementInfoList list, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts)
684                 {
685                         if (atts.XmlText != null)
686                         {
687                                 member.IsXmlTextCollector = true;
688                                 if (atts.XmlText.Type != null) defaultType = atts.XmlText.Type;
689                                 if (defaultType == typeof(XmlNode)) defaultType = typeof(XmlText);      // Nodes must be text nodes
690
691                                 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType, atts.XmlText.DataType));
692
693                                 if (elem.TypeData.SchemaType != SchemaTypes.Primitive &&
694                                         elem.TypeData.SchemaType != SchemaTypes.Enum &&
695                                     elem.TypeData.SchemaType != SchemaTypes.XmlNode &&
696                                     !(elem.TypeData.SchemaType == SchemaTypes.Array && elem.TypeData.ListItemTypeData.SchemaType == SchemaTypes.XmlNode)
697                                  )
698                                         throw new InvalidOperationException ("XmlText cannot be used to encode complex types");
699
700                                 elem.IsTextElement = true;
701                                 elem.WrappedElement = false;
702                                 list.Add (elem);
703                         }
704                 }
705                 
706                 bool CanBeNull (TypeData type)
707                 {
708                         return (type.SchemaType != SchemaTypes.Primitive || type.Type == typeof (string));
709                 }
710                 
711                 public void IncludeType (Type type)
712                 {
713                         if (type == null)
714                                 throw new ArgumentNullException ("type");
715
716                         if (includedTypes == null) includedTypes = new ArrayList ();
717                         includedTypes.Add (type);
718                 }
719
720                 #endregion // Methods
721         }
722 }