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