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