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