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