2005-01-13 Atsushi Enomoto <atsushi@ximian.com>
[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 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System.Reflection;
35 using System.Collections;
36 using System.Xml.Schema;
37
38 namespace System.Xml.Serialization {
39         public class XmlReflectionImporter {
40
41                 string initialDefaultNamespace;
42                 XmlAttributeOverrides attributeOverrides;
43                 ArrayList includedTypes;
44                 ReflectionHelper helper = new ReflectionHelper();
45                 int arrayChoiceCount = 1;
46                 ArrayList relatedMaps = new ArrayList ();
47                 bool allowPrivateTypes = false;
48
49                 static readonly string errSimple = "Cannot serialize object of type '{0}'. Base " +
50                         "type '{1}' has simpleContent and can be only extended by adding XmlAttribute " +
51                         "elements. Please consider changing XmlText member of the base class to string array";
52
53                 static readonly string errSimple2 = "Cannot serialize object of type '{0}'. " +
54                         "Consider changing type of XmlText member '{1}' from '{2}' to string or string array";
55
56                 #region Constructors
57
58                 public XmlReflectionImporter ()
59                         : this (null, null)
60                 {
61                 }
62
63                 public XmlReflectionImporter (string defaultNamespace)
64                         : this (null, defaultNamespace)
65                 {
66                 }
67
68                 public XmlReflectionImporter (XmlAttributeOverrides attributeOverrides)
69                         : this (attributeOverrides, null)
70                 {
71                 }
72
73                 public XmlReflectionImporter (XmlAttributeOverrides attributeOverrides, string defaultNamespace)
74                 {
75                         if (defaultNamespace == null)
76                                 this.initialDefaultNamespace = String.Empty;
77                         else
78                                 this.initialDefaultNamespace = defaultNamespace;
79
80                         if (attributeOverrides == null)
81                                 this.attributeOverrides = new XmlAttributeOverrides();
82                         else
83                                 this.attributeOverrides = attributeOverrides;
84                 }
85
86                 void Reset ()
87                 {
88                         helper = new ReflectionHelper();
89                         arrayChoiceCount = 1;
90                 }
91                 
92                 internal bool AllowPrivateTypes
93                 {
94                         get { return allowPrivateTypes; }
95                         set { allowPrivateTypes = value; }
96                 }
97
98                 #endregion // Constructors
99
100                 #region Methods
101
102                 public XmlMembersMapping ImportMembersMapping (string elementName,
103                         string ns,
104                         XmlReflectionMember [] members,
105                         bool hasWrapperElement)
106                 {
107 //                      Reset ();       Disabled. See ChangeLog
108
109                         XmlMemberMapping[] mapping = new XmlMemberMapping[members.Length];
110                         for (int n=0; n<members.Length; n++)
111                         {
112                                 XmlTypeMapMember mapMem = CreateMapMember (members[n], ns);
113                                 mapping[n] = new XmlMemberMapping (members[n].MemberName, ns, mapMem, false);
114                         }
115                         XmlMembersMapping mps = new XmlMembersMapping (elementName, ns, hasWrapperElement, false, mapping);
116                         mps.RelatedMaps = relatedMaps;
117                         mps.Format = SerializationFormat.Literal;
118                         Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
119                         mps.Source = new MembersSerializationSource (elementName, hasWrapperElement, members, false, true, ns, extraTypes);
120                         if (allowPrivateTypes) mps.Source.CanBeGenerated = false;
121                         return mps;
122                 }
123
124 #if NET_2_0
125                 [MonoTODO]
126                 public XmlMembersMapping ImportMembersMapping (string elementName, 
127                         string ns, 
128                         XmlReflectionMember[] members, 
129                         bool hasWrapperElement, 
130                         bool rpc)
131                 {
132                         throw new NotImplementedException ();
133                 }
134
135                 [MonoTODO]
136                 public XmlMembersMapping ImportMembersMapping (string elementName, 
137                         string ns, 
138                         XmlReflectionMember[] members, 
139                         bool hasWrapperElement, 
140                         bool rpc, 
141                         bool openModel)
142                 {
143                         throw new NotImplementedException ();
144                 }
145 #endif
146
147                 public XmlTypeMapping ImportTypeMapping (Type type)
148                 {
149                         return ImportTypeMapping (type, null, null);
150                 }
151
152                 public XmlTypeMapping ImportTypeMapping (Type type, string defaultNamespace)
153                 {
154                         return ImportTypeMapping (type, null, defaultNamespace);
155                 }
156
157                 public XmlTypeMapping ImportTypeMapping (Type type, XmlRootAttribute group)
158                 {
159                         return ImportTypeMapping (type, group, null);
160                 }
161
162                 public XmlTypeMapping ImportTypeMapping (Type type, XmlRootAttribute root, string defaultNamespace)
163                 {
164                         if (type == null)
165                                 throw new ArgumentNullException ("type");
166
167                         if (type == typeof (void))
168                                 throw new InvalidOperationException ("Type " + type.Name + " may not be serialized.");
169
170                         if (defaultNamespace == null) defaultNamespace = initialDefaultNamespace;
171                         if (defaultNamespace == null) defaultNamespace = string.Empty;
172
173                         XmlTypeMapping map;
174
175                         switch (TypeTranslator.GetTypeData(type).SchemaType)
176                         {
177                                 case SchemaTypes.Class: map = ImportClassMapping (type, root, defaultNamespace); break;
178                                 case SchemaTypes.Array: map = ImportListMapping (type, root, defaultNamespace, null, 0); break;
179                                 case SchemaTypes.XmlNode: map = ImportXmlNodeMapping (type, root, defaultNamespace); break;
180                                 case SchemaTypes.Primitive: map = ImportPrimitiveMapping (type, root, defaultNamespace); break;
181                                 case SchemaTypes.Enum: map = ImportEnumMapping (type, root, defaultNamespace); break;
182                                 case SchemaTypes.XmlSerializable: map = ImportXmlSerializableMapping (type, root, defaultNamespace); break;
183                                 default: throw new NotSupportedException ("Type " + type.FullName + " not supported for XML stialization");
184                         }
185
186                         map.RelatedMaps = relatedMaps;
187                         map.Format = SerializationFormat.Literal;
188                         Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
189                         map.Source = new XmlTypeSerializationSource (type, root, attributeOverrides, defaultNamespace, extraTypes);
190                         if (allowPrivateTypes) map.Source.CanBeGenerated = false;
191                         return map;
192                 }
193
194                 XmlTypeMapping CreateTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultXmlType, string defaultNamespace)
195                 {
196                         string rootNamespace = defaultNamespace;
197                         string typeNamespace = null;
198                         string elementName;
199                         bool includeInSchema = true;
200                         XmlAttributes atts = null;
201                         bool nullable = true;
202
203                         if (defaultXmlType == null) defaultXmlType = typeData.XmlType;
204
205                         if (!typeData.IsListType)
206                         {
207                                 if (attributeOverrides != null) 
208                                         atts = attributeOverrides[typeData.Type];
209
210                                 if (atts != null && typeData.SchemaType == SchemaTypes.Primitive)
211                                         throw new InvalidOperationException ("XmlRoot and XmlType attributes may not be specified for the type " + typeData.FullTypeName);
212                         }
213
214                         if (atts == null) 
215                                 atts = new XmlAttributes (typeData.Type);
216
217                         if (atts.XmlRoot != null && root == null)
218                                 root = atts.XmlRoot;
219
220                         if (atts.XmlType != null)
221                         {
222                                 if (atts.XmlType.Namespace != null && atts.XmlType.Namespace != string.Empty && typeData.SchemaType != SchemaTypes.Enum)
223                                         typeNamespace = atts.XmlType.Namespace;
224
225                                 if (atts.XmlType.TypeName != null && atts.XmlType.TypeName != string.Empty)
226                                         defaultXmlType = atts.XmlType.TypeName;
227                                         
228                                 includeInSchema = atts.XmlType.IncludeInSchema;
229                         }
230
231                         elementName = defaultXmlType;
232
233                         if (root != null)
234                         {
235                                 if (root.ElementName != null && root.ElementName != String.Empty)
236                                         elementName = root.ElementName;
237                                 if (root.Namespace != null && root.Namespace != String.Empty)
238                                         rootNamespace = root.Namespace;
239                                 nullable = root.IsNullable;
240                         }
241
242                         if (rootNamespace == null) rootNamespace = "";
243                         if (typeNamespace == null || typeNamespace.Length == 0) typeNamespace = rootNamespace;
244                         
245                         XmlTypeMapping map = new XmlTypeMapping (elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
246                         map.IncludeInSchema = includeInSchema;
247                         map.IsNullable = nullable;
248                         relatedMaps.Add (map);
249                         
250                         return map;
251                 }
252
253                 XmlTypeMapping ImportClassMapping (Type type, XmlRootAttribute root, string defaultNamespace)
254                 {
255                         TypeData typeData = TypeTranslator.GetTypeData (type);
256                         XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
257                         if (map != null) return map;
258
259                         if (!allowPrivateTypes)
260                                 ReflectionHelper.CheckSerializableType (type, false);
261                         
262                         map = CreateTypeMapping (typeData, root, null, defaultNamespace);
263                         helper.RegisterClrType (map, type, map.XmlTypeNamespace);
264                         helper.RegisterSchemaType (map, map.XmlType, map.XmlTypeNamespace);
265
266                         // Import members
267
268                         ClassMap classMap = new ClassMap ();
269                         map.ObjectMap = classMap;
270
271 //                      try
272 //                      {
273                                 ICollection members = GetReflectionMembers (type);
274                                 foreach (XmlReflectionMember rmember in members)
275                                 {
276                                         string ns = map.XmlTypeNamespace;
277                                         if (rmember.XmlAttributes.XmlIgnore) continue;
278                                         if (rmember.DeclaringType != null && rmember.DeclaringType != type) {
279                                                 XmlTypeMapping bmap = ImportClassMapping (rmember.DeclaringType, root, defaultNamespace);
280                                                 ns = bmap.XmlTypeNamespace;
281                                         }
282                                         
283                                         XmlTypeMapMember mem = CreateMapMember (rmember, ns);
284                                         mem.CheckOptionalValueType (type);
285                                         classMap.AddMember (mem);
286                                 }
287 //                      }
288 //                      catch (Exception ex) {
289 //                              throw helper.CreateError (map, ex.Message);
290 //                      }
291
292                         // Import extra classes
293
294                         if (type == typeof (object) && includedTypes != null)
295                         {
296                                 foreach (Type intype in includedTypes)
297                                         map.DerivedTypes.Add (ImportTypeMapping (intype, defaultNamespace));
298                         }
299
300                         // Register inheritance relations
301
302                         if (type.BaseType != null)
303                         {
304                                 XmlTypeMapping bmap = ImportClassMapping (type.BaseType, root, defaultNamespace);
305                                 ClassMap cbmap = bmap.ObjectMap as ClassMap;
306                                 
307                                 if (type.BaseType != typeof (object)) {
308                                         map.BaseMap = bmap;
309                                         if (!cbmap.HasSimpleContent)
310                                                 classMap.SetCanBeSimpleType (false);
311                                 }
312                                 
313                                 // At this point, derived classes of this map must be already registered
314                                 
315                                 RegisterDerivedMap (bmap, map);
316                                 
317                                 if (cbmap.HasSimpleContent && classMap.ElementMembers != null && classMap.ElementMembers.Count != 1)
318                                         throw new InvalidOperationException (String.Format (errSimple, map.TypeData.TypeName, map.BaseMap.TypeData.TypeName));
319                         }
320                         
321                         ImportIncludedTypes (type, defaultNamespace);
322                         
323                         if (classMap.XmlTextCollector != null && !classMap.HasSimpleContent)
324                         {
325                                 XmlTypeMapMember mem = classMap.XmlTextCollector;
326                                 if (mem.TypeData.Type != typeof(string) && 
327                                    mem.TypeData.Type != typeof(string[]) && 
328                                    mem.TypeData.Type != typeof(object[]) && 
329                                    mem.TypeData.Type != typeof(XmlNode[]))
330                                    
331                                         throw new InvalidOperationException (String.Format (errSimple2, map.TypeData.TypeName, mem.Name, mem.TypeData.TypeName));
332                         }
333                         
334                         return map;
335                 }
336                 
337                 void RegisterDerivedMap (XmlTypeMapping map, XmlTypeMapping derivedMap)
338                 {
339                         map.DerivedTypes.Add (derivedMap);
340                         map.DerivedTypes.AddRange (derivedMap.DerivedTypes);
341                         
342                         if (map.BaseMap != null)
343                                 RegisterDerivedMap (map.BaseMap, derivedMap);
344                         else {
345                                 XmlTypeMapping obmap = ImportTypeMapping (typeof(object));
346                                 if (obmap != map)
347                                         obmap.DerivedTypes.Add (derivedMap);
348                         }
349                 }
350
351                 string GetTypeNamespace (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
352                 {
353                         string typeNamespace = null;
354                         
355                         XmlAttributes atts = null;
356                         if (!typeData.IsListType)
357                         {
358                                 if (attributeOverrides != null)
359                                         atts = attributeOverrides[typeData.Type];
360                         }
361
362                         if (atts == null)
363                                 atts = new XmlAttributes (typeData.Type);
364
365                         if (atts.XmlType != null)
366                         {
367                                 if (atts.XmlType.Namespace != null && atts.XmlType.Namespace.Length != 0 && typeData.SchemaType != SchemaTypes.Enum)
368                                         typeNamespace = atts.XmlType.Namespace;
369                         }
370
371                         if (typeNamespace != null && typeNamespace.Length != 0) return typeNamespace;
372                         
373                         if (atts.XmlRoot != null && root == null)
374                                 root = atts.XmlRoot;
375
376                         if (root != null)
377                         {
378                                 if (root.Namespace != null && root.Namespace.Length != 0)
379                                         return root.Namespace;
380                         }
381
382                         if (defaultNamespace == null) return "";
383                         else return defaultNamespace;
384                 }
385
386                 XmlTypeMapping ImportListMapping (Type type, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
387                 {
388                         TypeData typeData = TypeTranslator.GetTypeData (type);
389                         ListMap obmap = new ListMap ();
390
391                         if (!allowPrivateTypes)
392                                 ReflectionHelper.CheckSerializableType (type, true);
393                         
394                         if (atts == null) atts = new XmlAttributes();
395                         Type itemType = typeData.ListItemType;
396
397                         // warning: byte[][] should not be considered multiarray
398                         bool isMultiArray = (type.IsArray && (TypeTranslator.GetTypeData(itemType).SchemaType == SchemaTypes.Array) && itemType.IsArray);
399
400                         XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
401
402                         foreach (XmlArrayItemAttribute att in atts.XmlArrayItems)
403                         {
404                                 if (att.NestingLevel != nestingLevel) continue;
405                                 Type elemType = (att.Type != null) ? att.Type : itemType;
406                                 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData(elemType, att.DataType));
407                                 elem.Namespace = att.Namespace != null ? att.Namespace : defaultNamespace;
408                                 if (elem.Namespace == null) elem.Namespace = "";
409                                 elem.Form = att.Form;
410                                 elem.IsNullable = att.IsNullable && CanBeNull (elem.TypeData);
411                                 elem.NestingLevel = att.NestingLevel;
412
413                                 if (isMultiArray)
414                                         elem.MappedType = ImportListMapping (elemType, null, elem.Namespace, atts, nestingLevel + 1);
415                                 else if (elem.TypeData.IsComplexType)
416                                         elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
417
418                                 if (att.ElementName != null) elem.ElementName = att.ElementName;
419                                 else if (elem.MappedType != null) elem.ElementName = elem.MappedType.ElementName;
420                                 else elem.ElementName = TypeTranslator.GetTypeData(elemType).XmlType;
421
422                                 list.Add (elem);
423                         }
424
425                         if (list.Count == 0)
426                         {
427                                 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData (itemType));
428                                 if (isMultiArray)
429                                         elem.MappedType = ImportListMapping (itemType, null, defaultNamespace, atts, nestingLevel + 1);
430                                 else if (elem.TypeData.IsComplexType)
431                                         elem.MappedType = ImportTypeMapping (itemType, null, defaultNamespace);
432
433                                 if (elem.MappedType != null) elem.ElementName = elem.MappedType.XmlType;
434                                 else elem.ElementName = TypeTranslator.GetTypeData(itemType).XmlType ;
435
436                                 elem.Namespace = (defaultNamespace != null) ? defaultNamespace : "";
437                                 elem.IsNullable = CanBeNull (elem.TypeData);
438                                 list.Add (elem);
439                         }
440
441                         obmap.ItemInfo = list;
442
443                         // If there can be different element names (types) in the array, then its name cannot
444                         // be "ArrayOfXXX" it must be something like ArrayOfChoiceNNN
445
446                         string baseName;
447                         if (list.Count > 1)
448                                 baseName = "ArrayOfChoice" + (arrayChoiceCount++);
449                         else
450                         {
451                                 XmlTypeMapElementInfo elem = ((XmlTypeMapElementInfo)list[0]);
452                                 if (elem.MappedType != null) baseName = TypeTranslator.GetArrayName (elem.MappedType.XmlType);
453                                 else baseName = TypeTranslator.GetArrayName (elem.ElementName);
454                         }
455
456                         // Avoid name colisions
457
458                         int nameCount = 1;
459                         string name = baseName;
460
461                         do {
462                                 XmlTypeMapping foundMap = helper.GetRegisteredSchemaType (name, defaultNamespace);
463                                 if (foundMap == null) nameCount = -1;
464                                 else if (obmap.Equals (foundMap.ObjectMap) && typeData.Type == foundMap.TypeData.Type) return foundMap;
465                                 else name = baseName + (nameCount++);
466                         }
467                         while (nameCount != -1);
468
469                         XmlTypeMapping map = CreateTypeMapping (typeData, root, name, defaultNamespace);
470                         map.ObjectMap = obmap;
471                         
472                         // Register any of the including types as a derived class of object
473                         XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
474                         
475                         XmlTypeMapping objectMapping = ImportTypeMapping (typeof(object));
476                         for (int i = 0; i < includes.Length; i++)
477                         {
478                                 Type includedType = includes[i].Type;
479                                 objectMapping.DerivedTypes.Add(ImportTypeMapping (includedType, null, defaultNamespace));
480                         }
481                         
482                         // Register this map as a derived class of object
483
484                         helper.RegisterSchemaType (map, name, defaultNamespace);
485                         ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
486
487                         return map;
488                 }
489
490                 XmlTypeMapping ImportXmlNodeMapping (Type type, XmlRootAttribute root, string defaultNamespace)
491                 {
492                         XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (TypeTranslator.GetTypeData (type), root, defaultNamespace));
493                         if (map != null) return map;
494
495                         // Registers the maps for XmlNode and XmlElement
496
497                         XmlTypeMapping nodeMap = CreateTypeMapping (TypeTranslator.GetTypeData (typeof(XmlNode)), root, null, defaultNamespace);
498                         helper.RegisterClrType (nodeMap, typeof(XmlNode), nodeMap.XmlTypeNamespace);
499
500                         XmlTypeMapping elemMap = CreateTypeMapping (TypeTranslator.GetTypeData (typeof(XmlElement)), root, null, defaultNamespace);
501                         helper.RegisterClrType (elemMap, typeof(XmlElement), elemMap.XmlTypeNamespace);
502
503                         XmlTypeMapping textMap = CreateTypeMapping (TypeTranslator.GetTypeData (typeof(XmlText)), root, null, defaultNamespace);
504                         helper.RegisterClrType (textMap, typeof(XmlText), textMap.XmlTypeNamespace);
505
506                         XmlTypeMapping docMap = CreateTypeMapping (TypeTranslator.GetTypeData (typeof(XmlDocument)), root, null, defaultNamespace);
507                         helper.RegisterClrType (docMap, typeof(XmlDocument), textMap.XmlTypeNamespace);
508
509                         XmlTypeMapping obmap = ImportTypeMapping (typeof(object));
510                         obmap.DerivedTypes.Add (nodeMap);
511                         obmap.DerivedTypes.Add (elemMap);
512                         obmap.DerivedTypes.Add (textMap);
513                         obmap.DerivedTypes.Add (docMap);
514                         nodeMap.DerivedTypes.Add (elemMap);
515                         nodeMap.DerivedTypes.Add (textMap);
516                         nodeMap.DerivedTypes.Add (docMap);
517
518                         map = helper.GetRegisteredClrType (type, GetTypeNamespace (TypeTranslator.GetTypeData (type), root, defaultNamespace));
519                         if (map == null) throw new InvalidOperationException ("Objects of type '" + type + "' can't be serialized");
520                         return map;
521                 }
522
523                 XmlTypeMapping ImportPrimitiveMapping (Type type, XmlRootAttribute root, string defaultNamespace)
524                 {
525                         TypeData typeData = TypeTranslator.GetTypeData (type);
526                         XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
527                         if (map != null) return map;
528                         map = CreateTypeMapping (typeData, root, null, defaultNamespace);
529                         helper.RegisterClrType (map, type, map.XmlTypeNamespace);
530                         return map;
531                 }
532
533                 XmlTypeMapping ImportEnumMapping (Type type, XmlRootAttribute root, string defaultNamespace)
534                 {
535                         TypeData typeData = TypeTranslator.GetTypeData (type);
536                         XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
537                         if (map != null) return map;
538                         
539                         if (!allowPrivateTypes)
540                                 ReflectionHelper.CheckSerializableType (type, false);
541                                 
542                         map = CreateTypeMapping (typeData, root, null, defaultNamespace);
543                         helper.RegisterClrType (map, type, map.XmlTypeNamespace);
544
545                         string [] names = Enum.GetNames (type);
546                         ArrayList members = new ArrayList();
547                         foreach (string name in names)
548                         {
549                                 MemberInfo[] mem = type.GetMember (name);
550                                 string xmlName = null;
551                                 object[] atts = mem[0].GetCustomAttributes (typeof(XmlIgnoreAttribute), false);
552                                 if (atts.Length > 0) continue;
553                                 atts = mem[0].GetCustomAttributes (typeof(XmlEnumAttribute), false);
554                                 if (atts.Length > 0) xmlName = ((XmlEnumAttribute)atts[0]).Name;
555                                 if (xmlName == null) xmlName = name;
556                                 members.Add (new EnumMap.EnumMapMember (xmlName, name));
557                         }
558
559                         bool isFlags = type.GetCustomAttributes (typeof(FlagsAttribute),false).Length > 0;
560                         map.ObjectMap = new EnumMap ((EnumMap.EnumMapMember[])members.ToArray (typeof(EnumMap.EnumMapMember)), isFlags);
561                         ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
562                         return map;
563                 }
564
565                 XmlTypeMapping ImportXmlSerializableMapping (Type type, XmlRootAttribute root, string defaultNamespace)
566                 {
567                         TypeData typeData = TypeTranslator.GetTypeData (type);
568                         XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
569                         if (map != null) return map;
570                         
571                         if (!allowPrivateTypes)
572                                 ReflectionHelper.CheckSerializableType (type, false);
573                                 
574                         map = CreateTypeMapping (typeData, root, null, defaultNamespace);
575                         helper.RegisterClrType (map, type, map.XmlTypeNamespace);
576                         return map;
577                 }
578
579                 void ImportIncludedTypes (Type type, string defaultNamespace)
580                 {
581                         XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
582                         for (int n=0; n<includes.Length; n++)
583                         {
584                                 Type includedType = includes[n].Type;
585                                 ImportTypeMapping (includedType, null, defaultNamespace);
586                         }
587                 }
588
589                 ICollection GetReflectionMembers (Type type)
590                 {
591                         // First we want to find the inheritance hierarchy in reverse order.
592                         Type currentType = type;
593                         ArrayList typeList = new ArrayList();
594                         typeList.Add(currentType);
595                         while (currentType != typeof(object))
596                         {
597                                 currentType = currentType.BaseType; // Read the base type.
598                                 typeList.Insert(0, currentType); // Insert at 0 to reverse the order.
599                         }
600
601                         // Read all Fields via reflection.
602                         ArrayList fieldList = new ArrayList();
603                         FieldInfo[] tfields = type.GetFields (BindingFlags.Instance | BindingFlags.Public);
604                         currentType = null;
605                         int currentIndex = 0;
606                         foreach (FieldInfo field in tfields)
607                         {
608                                 // This statement ensures fields are ordered starting from the base type.
609                                 if (currentType != field.DeclaringType)
610                                 {
611                                         currentType = field.DeclaringType;
612                                         currentIndex=0;
613                                 }
614                                 fieldList.Insert(currentIndex++, field);
615                         }
616
617                         // Read all Properties via reflection.
618                         ArrayList propList = new ArrayList();
619                         PropertyInfo[] tprops = type.GetProperties (BindingFlags.Instance | BindingFlags.Public);
620                         currentType = null;
621                         currentIndex = 0;
622                         foreach (PropertyInfo prop in tprops)
623                         {
624                                 // This statement ensures properties are ordered starting from the base type.
625                                 if (currentType != prop.DeclaringType)
626                                 {
627                                         currentType = prop.DeclaringType;
628                                         currentIndex = 0;
629                                 }
630                                 if (!prop.CanRead) continue;
631                                 if (!prop.CanWrite && TypeTranslator.GetTypeData (prop.PropertyType).SchemaType != SchemaTypes.Array) continue;
632                                 if (prop.GetIndexParameters().Length > 0) continue;
633                                 propList.Insert(currentIndex++, prop);
634                         }
635
636                         ArrayList members = new ArrayList();
637                         int fieldIndex=0;
638                         int propIndex=0;
639                         // We now step through the type hierarchy from the base (object) through
640                         // to the supplied class, as each step outputting all Fields, and then
641                         // all Properties.  This is the exact same ordering as .NET 1.0/1.1.
642                         foreach (Type t in typeList)
643                         {
644                                 // Add any fields matching the current DeclaringType.
645                                 while (fieldIndex < fieldList.Count)
646                                 {
647                                         FieldInfo field = (FieldInfo)fieldList[fieldIndex];
648                                         if (field.DeclaringType==t)
649                                         {
650                                                 fieldIndex++;
651                                                 XmlAttributes atts = attributeOverrides[type, field.Name];
652                                                 if (atts == null) atts = new XmlAttributes (field);
653                                                 if (atts.XmlIgnore) continue;
654                                                 XmlReflectionMember member = new XmlReflectionMember(field.Name, field.FieldType, atts);
655                                                 member.DeclaringType = field.DeclaringType;
656                                                 members.Add(member);
657                                         }
658                                         else break;
659                                 }
660
661                                 // Add any properties matching the current DeclaringType.
662                                 while (propIndex < propList.Count)
663                                 {
664                                         PropertyInfo prop = (PropertyInfo)propList[propIndex];
665                                         if (prop.DeclaringType==t)
666                                         {
667                                                 propIndex++;
668                                                 XmlAttributes atts = attributeOverrides[type, prop.Name];
669                                                 if (atts == null) atts = new XmlAttributes (prop);
670                                                 if (atts.XmlIgnore) continue;
671                                                 XmlReflectionMember member = new XmlReflectionMember(prop.Name, prop.PropertyType, atts);
672                                                 member.DeclaringType = prop.DeclaringType;
673                                                 members.Add(member);
674                                         }
675                                         else break;
676                                 }
677                         }
678                         return members;         
679                 }
680                 
681                 private XmlTypeMapMember CreateMapMember (XmlReflectionMember rmember, string defaultNamespace)
682                 {
683                         XmlTypeMapMember mapMember;
684                         XmlAttributes atts = rmember.XmlAttributes;
685                         TypeData typeData = TypeTranslator.GetTypeData (rmember.MemberType);
686
687                         if (atts.XmlAnyAttribute != null)
688                         {
689                                 if ( (rmember.MemberType.FullName == "System.Xml.XmlAttribute[]") ||
690                                          (rmember.MemberType.FullName == "System.Xml.XmlNode[]") )
691                                 {
692                                         mapMember = new XmlTypeMapMemberAnyAttribute();
693                                 }
694                                 else
695                                         throw new InvalidOperationException ("XmlAnyAttributeAttribute can only be applied to members of type XmlAttribute[] or XmlNode[]");
696                         }
697                         else if (atts.XmlAnyElements != null && atts.XmlAnyElements.Count > 0)
698                         {
699                                 if ( (rmember.MemberType.FullName == "System.Xml.XmlElement[]") ||
700                                          (rmember.MemberType.FullName == "System.Xml.XmlNode[]") ||
701                                          (rmember.MemberType.FullName == "System.Xml.XmlElement"))
702                                 {
703                                         XmlTypeMapMemberAnyElement member = new XmlTypeMapMemberAnyElement();
704                                         member.ElementInfo = ImportAnyElementInfo (defaultNamespace, rmember, member, atts);
705                                         mapMember = member;
706                                 }
707                                 else
708                                         throw new InvalidOperationException ("XmlAnyElementAttribute can only be applied to members of type XmlElement, XmlElement[] or XmlNode[]");
709                         }
710                         else if (atts.Xmlns)
711                         {
712                                 XmlTypeMapMemberNamespaces mapNamespaces = new XmlTypeMapMemberNamespaces ();
713                                 mapMember = mapNamespaces;
714                         }
715                         else if (atts.XmlAttribute != null)
716                         {
717                                 // An attribute
718
719                                 if (atts.XmlElements != null && atts.XmlElements.Count > 0)
720                                         throw new Exception ("XmlAttributeAttribute and XmlElementAttribute cannot be applied to the same member");
721
722                                 XmlTypeMapMemberAttribute mapAttribute = new XmlTypeMapMemberAttribute ();
723                                 if (atts.XmlAttribute.AttributeName == null) 
724                                         mapAttribute.AttributeName = rmember.MemberName;
725                                 else 
726                                         mapAttribute.AttributeName = atts.XmlAttribute.AttributeName;
727
728                                 if (typeData.IsComplexType)
729                                         mapAttribute.MappedType = ImportTypeMapping (typeData.Type, null, mapAttribute.Namespace);
730                                 
731                                 if (atts.XmlAttribute.Namespace != null && atts.XmlAttribute.Namespace != defaultNamespace)
732                                 {
733                                         if (atts.XmlAttribute.Form == XmlSchemaForm.Unqualified)
734                                                 throw new InvalidOperationException ("The Form property may not be 'Unqualified' when an explicit Namespace property is present");
735                                         mapAttribute.Form = XmlSchemaForm.Qualified;
736                                         mapAttribute.Namespace = atts.XmlAttribute.Namespace;
737                                 }
738                                 else
739                                 {
740                                         mapAttribute.Form = atts.XmlAttribute.Form;
741                                         if (atts.XmlAttribute.Form == XmlSchemaForm.Qualified)
742                                                 mapAttribute.Namespace = defaultNamespace;
743                                         else
744                                                 mapAttribute.Namespace = "";
745                                 }
746                                 
747                                 typeData = TypeTranslator.GetTypeData(rmember.MemberType, atts.XmlAttribute.DataType);
748                                 mapMember = mapAttribute;
749                         }
750                         else if (typeData.SchemaType == SchemaTypes.Array)
751                         {
752                                 // If the member has a single XmlElementAttribute and the type is the type of the member,
753                                 // then it is not a flat list
754                                 
755                                 if (atts.XmlElements.Count > 1 ||
756                                    (atts.XmlElements.Count == 1 && atts.XmlElements[0].Type != typeData.Type) ||
757                                    (atts.XmlText != null))
758                                 {
759                                         // A flat list
760
761                                         // TODO: check that it does not have XmlArrayAttribute
762                                         XmlTypeMapMemberFlatList member = new XmlTypeMapMemberFlatList ();
763                                         member.ListMap = new ListMap ();
764                                         member.ListMap.ItemInfo = ImportElementInfo (rmember.MemberName, defaultNamespace, typeData.ListItemType, member, atts);
765                                         member.ElementInfo = member.ListMap.ItemInfo;
766                                         mapMember = member;
767                                 }
768                                 else
769                                 {
770                                         // A list
771
772                                         XmlTypeMapMemberList member = new XmlTypeMapMemberList ();
773
774                                         // Creates an ElementInfo that identifies the array instance. 
775                                         member.ElementInfo = new XmlTypeMapElementInfoList();
776                                         XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, typeData);
777                                         elem.ElementName = (atts.XmlArray != null && atts.XmlArray.ElementName != null) ? atts.XmlArray.ElementName : rmember.MemberName;
778                                         elem.Namespace = (atts.XmlArray != null && atts.XmlArray.Namespace != null) ? atts.XmlArray.Namespace : defaultNamespace;
779                                         elem.MappedType = ImportListMapping (rmember.MemberType, null, elem.Namespace, atts, 0);
780                                         elem.IsNullable = (atts.XmlArray != null) ? atts.XmlArray.IsNullable : false;
781                                         elem.Form = (atts.XmlArray != null) ? atts.XmlArray.Form : XmlSchemaForm.Qualified;
782
783                                         member.ElementInfo.Add (elem);
784                                         mapMember = member;
785                                 }
786                         }
787                         else
788                         {
789                                 // An element
790
791                                 XmlTypeMapMemberElement member = new XmlTypeMapMemberElement ();
792                                 member.ElementInfo = ImportElementInfo (rmember.MemberName, defaultNamespace, rmember.MemberType, member, atts);
793                                 mapMember = member;
794                         }
795
796                         mapMember.DefaultValue = atts.XmlDefaultValue;
797                         mapMember.TypeData = typeData;
798                         mapMember.Name = rmember.MemberName;
799                         mapMember.IsReturnValue = rmember.IsReturnValue;
800                         return mapMember;
801                 }
802
803                 XmlTypeMapElementInfoList ImportElementInfo (string defaultName, string defaultNamespace, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts)
804                 {
805                         XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
806
807                         ImportTextElementInfo (list, defaultType, member, atts);
808                         
809                         if (atts.XmlElements.Count == 0 && list.Count == 0)
810                         {
811                                 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType));
812                                 elem.ElementName = defaultName;
813                                 elem.Namespace = defaultNamespace;
814                                 if (elem.TypeData.IsComplexType)
815                                         elem.MappedType = ImportTypeMapping (defaultType, null, defaultNamespace);
816                                 list.Add (elem);
817                         }
818
819                         bool multiType = (atts.XmlElements.Count > 1);
820                         foreach (XmlElementAttribute att in atts.XmlElements)
821                         {
822                                 Type elemType = (att.Type != null) ? att.Type : defaultType;
823                                 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(elemType, att.DataType));
824                                 elem.ElementName = (att.ElementName != null) ? att.ElementName : defaultName;
825                                 elem.Namespace = (att.Namespace != null) ? att.Namespace : defaultNamespace;
826                                 elem.Form = att.Form;
827                                 elem.IsNullable = att.IsNullable;
828                                 
829                                 if (elem.IsNullable && elem.TypeData.IsValueType)
830                                         throw new InvalidOperationException ("IsNullable may not be 'true' for value type " + elem.TypeData.FullTypeName + " in member '" + defaultName + "'");
831                                         
832                                 if (elem.TypeData.IsComplexType)
833                                 {
834                                         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.");
835                                         elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
836                                 }
837
838                                 if (att.ElementName != null) 
839                                         elem.ElementName = att.ElementName;
840                                 else if (multiType) {
841                                         if (elem.MappedType != null) elem.ElementName = elem.MappedType.ElementName;
842                                         else elem.ElementName = TypeTranslator.GetTypeData(elemType).XmlType;
843                                 }
844                                 else
845                                         elem.ElementName = defaultName;
846
847                                 list.Add (elem);
848                         }
849                         return list;
850                 }
851
852                 XmlTypeMapElementInfoList ImportAnyElementInfo (string defaultNamespace, XmlReflectionMember rmember, XmlTypeMapMemberElement member, XmlAttributes atts)
853                 {
854                         XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
855
856                         ImportTextElementInfo (list, rmember.MemberType, member, atts);
857
858                         foreach (XmlAnyElementAttribute att in atts.XmlAnyElements)
859                         {
860                                 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(typeof(XmlElement)));
861                                 if (att.Name != null && att.Name != string.Empty) 
862                                 {
863                                         elem.ElementName = att.Name;
864                                         elem.Namespace = (att.Namespace != null) ? att.Namespace : "";
865                                 }
866                                 else 
867                                 {
868                                         elem.IsUnnamedAnyElement = true;
869                                         elem.Namespace = defaultNamespace;
870                                         if (att.Namespace != null) 
871                                                 throw new InvalidOperationException ("The element " + rmember.MemberName + " has been attributed with an XmlAnyElementAttribute and a namespace '" + att.Namespace + "', but no name. When a namespace is supplied, a name is also required. Supply a name or remove the namespace.");
872                                 }
873                                 list.Add (elem);
874                         }
875                         return list;
876                 }
877
878                 void ImportTextElementInfo (XmlTypeMapElementInfoList list, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts)
879                 {
880                         if (atts.XmlText != null)
881                         {
882                                 member.IsXmlTextCollector = true;
883                                 if (atts.XmlText.Type != null) defaultType = atts.XmlText.Type;
884                                 if (defaultType == typeof(XmlNode)) defaultType = typeof(XmlText);      // Nodes must be text nodes
885
886                                 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType, atts.XmlText.DataType));
887
888                                 if (elem.TypeData.SchemaType != SchemaTypes.Primitive &&
889                                         elem.TypeData.SchemaType != SchemaTypes.Enum &&
890                                     elem.TypeData.SchemaType != SchemaTypes.XmlNode &&
891                                     !(elem.TypeData.SchemaType == SchemaTypes.Array && elem.TypeData.ListItemTypeData.SchemaType == SchemaTypes.XmlNode)
892                                  )
893                                         throw new InvalidOperationException ("XmlText cannot be used to encode complex types");
894
895                                 elem.IsTextElement = true;
896                                 elem.WrappedElement = false;
897                                 list.Add (elem);
898                         }
899                 }
900                 
901                 bool CanBeNull (TypeData type)
902                 {
903                         return (type.SchemaType != SchemaTypes.Primitive || type.Type == typeof (string));
904                 }
905                 
906                 public void IncludeType (Type type)
907                 {
908                         if (type == null)
909                                 throw new ArgumentNullException ("type");
910
911                         if (includedTypes == null) includedTypes = new ArrayList ();
912                         if (!includedTypes.Contains (type))
913                                 includedTypes.Add (type);
914                 }
915
916                 public void IncludeTypes (ICustomAttributeProvider provider)
917                 { 
918                         object[] ats = provider.GetCustomAttributes (typeof(XmlIncludeAttribute), true);
919                         foreach (XmlIncludeAttribute at in ats)
920                                 IncludeType (at.Type);
921                 }
922
923                 #endregion // Methods
924         }
925 }