4a690af482afe7f34b5d7dbb6f32a9ceef173644
[mono.git] / mcs / class / System.XML / System.Xml.Serialization / XmlTypeMapping.cs
1 //
2 // XmlTypeMapping.cs: 
3 //
4 // Author:
5 //   John Donagher (john@webmeta.com)
6 //   Lluis Sanchez Gual (lluis@ximian.com)
7 //
8 // (C) 2002 John Donagher
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.Xml;
33 using System;
34 using System.Collections;
35 using System.Globalization;
36 using System.Xml.Schema;
37 using System.Reflection;
38
39 namespace System.Xml.Serialization
40 {
41         public class XmlTypeMapping : XmlMapping
42         {
43                 private string xmlType;
44                 private string xmlTypeNamespace;
45                 TypeData type;
46                 XmlTypeMapping baseMap;
47                 bool multiReferenceType = false;
48                 bool isSimpleType;
49                 string documentation;
50                 bool includeInSchema;
51                 bool isNullable = true;
52
53                 ArrayList _derivedTypes = new ArrayList();
54
55                 internal XmlTypeMapping(string elementName, string ns, TypeData typeData, string xmlType, string xmlTypeNamespace)
56                 : base (elementName, ns)
57                 {
58                         this.type = typeData;
59                         this.xmlType = xmlType;
60                         this.xmlTypeNamespace = xmlTypeNamespace;
61                 }
62
63 #if !NET_2_0
64                 public string ElementName
65                 {
66                         get { return _elementName; }
67                 }
68
69                 public string Namespace
70                 {
71                         get { return _namespace; }
72                 }
73 #endif
74
75                 public string TypeFullName
76                 {
77                         get { return type.FullTypeName; }
78                 }
79
80                 public string TypeName
81                 {
82                         get { return type.TypeName; }
83                 }
84
85 #if NET_2_0
86                 public string XsdTypeName
87                 {
88                         get { return XmlType; }
89                 }
90
91                 public string XsdTypeNamespace
92                 {
93                         get { return XmlTypeNamespace; }
94                 }
95 #endif
96
97                 internal TypeData TypeData
98                 {
99                         get { return type; }
100                 }
101
102                 internal string XmlType
103                 {
104                         get { return xmlType; }
105                         set { xmlType = value; }
106                 }
107
108                 internal string XmlTypeNamespace
109                 {
110                         get { return xmlTypeNamespace ?? string.Empty; }
111                         set { xmlTypeNamespace = value; }
112                 }
113
114                 internal bool HasXmlTypeNamespace
115                 {
116                         get { return xmlTypeNamespace != null; }
117                 }
118
119                 internal ArrayList DerivedTypes
120                 {
121                         get { return _derivedTypes; }
122                         set { _derivedTypes = value; }
123                 }
124
125                 internal bool MultiReferenceType
126                 {
127                         get { return multiReferenceType; }
128                         set { multiReferenceType = value; }
129                 }
130
131                 internal XmlTypeMapping BaseMap
132                 {
133                         get { return baseMap; }
134                         set { baseMap = value; }
135                 }
136
137                 internal bool IsSimpleType
138                 {
139                         get { return isSimpleType; }
140                         set { isSimpleType = value; }
141                 }
142
143                 internal string Documentation
144                 {
145                         set { documentation = value; }
146                         get { return documentation; }
147                 }
148
149                 internal bool IncludeInSchema
150                 {
151                         get { return includeInSchema; }
152                         set { includeInSchema = value; }
153                 }
154                 
155                 internal bool IsNullable
156                 {
157                         get { return isNullable; }
158                         set { isNullable = value; }
159                 }
160
161                 internal XmlTypeMapping GetRealTypeMap (Type objectType)
162                 {
163                         if (TypeData.SchemaType == SchemaTypes.Enum)
164                                 return this;
165
166                         // Returns the map for a subtype of this map's type
167                         if (TypeData.Type == objectType) return this;
168                         for (int n=0; n<_derivedTypes.Count; n++) {
169                                 XmlTypeMapping map = (XmlTypeMapping) _derivedTypes[n];
170                                 if (map.TypeData.Type == objectType) return map;
171                         }
172                         
173                         return null;
174                 }
175
176                 internal XmlTypeMapping GetRealElementMap (string name, string ens)
177                 {
178                         if (xmlType == name && XmlTypeNamespace == ens) return this;
179                         foreach (XmlTypeMapping map in _derivedTypes)
180                                 if (map.xmlType == name && map.XmlTypeNamespace == ens) return map;
181                         
182                         return null;
183                 }
184                 
185                 internal void UpdateRoot (XmlQualifiedName qname)
186                 {
187                         if (qname != null) {
188                                 this._elementName = qname.Name;
189                                 this._namespace = qname.Namespace;
190                         }
191                 }
192         }
193
194  
195         // Mapping info for XmlSerializable
196         internal class XmlSerializableMapping : XmlTypeMapping
197         {
198                 XmlSchema _schema;
199                 XmlSchemaComplexType _schemaType;
200                 XmlQualifiedName _schemaTypeName;
201
202                 internal XmlSerializableMapping(XmlRootAttribute root, string elementName, string ns, TypeData typeData, string xmlType, string xmlTypeNamespace)
203                         : base(elementName, ns, typeData, xmlType, xmlTypeNamespace)
204                 {
205                         XmlSchemaProviderAttribute schemaProvider = (XmlSchemaProviderAttribute) Attribute.GetCustomAttribute (typeData.Type, typeof (XmlSchemaProviderAttribute));
206
207                         if (schemaProvider != null) {
208                                 string method = schemaProvider.MethodName;
209                                 MethodInfo mi = typeData.Type.GetMethod (method, BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
210                                 if (mi == null)
211                                         throw new InvalidOperationException (String.Format ("Type '{0}' must implement public static method '{1}'", typeData.Type, method));
212                                 if (!typeof (XmlQualifiedName).IsAssignableFrom (mi.ReturnType) &&
213                                     // LAMESPEC: it is undocumented. (We don't have to tell users about it in the error message.)
214                                     // Also do not add such a silly compatibility test to assert that it does not raise an error.
215                                     !typeof (XmlSchemaComplexType).IsAssignableFrom (mi.ReturnType))
216                                         throw new InvalidOperationException (String.Format ("Method '{0}' indicated by XmlSchemaProviderAttribute must have its return type as XmlQualifiedName", method));
217                                 XmlSchemaSet xs = new XmlSchemaSet ();
218                                 object retVal = mi.Invoke (null, new object [] { xs });
219                                 _schemaTypeName = XmlQualifiedName.Empty;
220                                 if (retVal == null)
221                                         return;
222
223                                 if (retVal is XmlSchemaComplexType) {
224                                         _schemaType = (XmlSchemaComplexType) retVal;
225                                         if (!_schemaType.QualifiedName.IsEmpty)
226                                                 _schemaTypeName = _schemaType.QualifiedName;
227                                         else
228                                                 _schemaTypeName = new XmlQualifiedName (xmlType, xmlTypeNamespace);
229                                 }
230                                 else if (retVal is XmlQualifiedName) {
231                                         _schemaTypeName = (XmlQualifiedName)retVal;
232                                 }
233                                 else
234                                         throw new InvalidOperationException (
235                                                 String.Format ("Method {0}.{1}() specified by XmlSchemaProviderAttribute has invalid signature: return type must be compatible with System.Xml.XmlQualifiedName.", typeData.Type.Name, method));
236
237                                 // defaultNamespace at XmlReflectionImporter takes precedence for Namespace, but not for XsdTypeNamespace.
238                                 UpdateRoot (new XmlQualifiedName (root != null ? root.ElementName : _schemaTypeName.Name, root != null ? root.Namespace : Namespace ?? _schemaTypeName.Namespace));
239                                 XmlTypeNamespace = _schemaTypeName.Namespace;
240                                 XmlType = _schemaTypeName.Name;
241
242                                 if (!_schemaTypeName.IsEmpty && xs.Count > 0) {
243                                         XmlSchema [] schemas = new XmlSchema [xs.Count];
244                                         xs.CopyTo (schemas, 0);
245                                         _schema = schemas [0];
246                                 }
247
248                                 return;
249                         }
250
251                         IXmlSerializable serializable = (IXmlSerializable)Activator.CreateInstance (typeData.Type, true);
252                         try {
253                                 _schema = serializable.GetSchema();
254                         } catch (Exception) {
255                                 // LAMESPEC: .NET has a bad exception catch and swallows it silently.
256                         }
257
258                         if (_schema != null) 
259                         {
260                                 if (_schema.Id == null || _schema.Id.Length == 0) 
261                                         throw new InvalidOperationException("Schema Id is missing. The schema returned from " + typeData.Type.FullName + ".GetSchema() must have an Id.");
262                         }
263                 }
264
265                 internal XmlSchema Schema
266                 {
267                         get { return _schema; }
268                 }
269
270                 internal XmlSchemaType SchemaType {
271                         get { return _schemaType; }
272                 }
273
274                 internal XmlQualifiedName SchemaTypeName {
275                         get { return _schemaTypeName; }
276                 }
277         }
278  
279
280         // Mapping info for classes and structs
281
282         internal class ClassMap: ObjectMap
283         {
284                 Hashtable _elements = new Hashtable ();
285                 ArrayList _elementMembers;
286                 Hashtable _attributeMembers;
287                 XmlTypeMapMemberAttribute[] _attributeMembersArray;
288                 XmlTypeMapElementInfo[] _elementsByIndex;
289                 ArrayList _flatLists;
290                 ArrayList _allMembers = new ArrayList ();
291                 ArrayList _membersWithDefault;
292                 ArrayList _listMembers;
293                 XmlTypeMapMemberAnyElement _defaultAnyElement;
294                 XmlTypeMapMemberAnyAttribute _defaultAnyAttribute;
295                 XmlTypeMapMemberNamespaces _namespaceDeclarations;
296                 XmlTypeMapMember _xmlTextCollector;
297                 XmlTypeMapMember _returnMember;
298                 bool _ignoreMemberNamespace;
299                 bool _canBeSimpleType = true;
300                 bool? _isOrderDependentMap;
301
302                 public void AddMember (XmlTypeMapMember member)
303                 {
304                         // If GlobalIndex has not been set, set it now
305                         if (member.GlobalIndex == -1)
306                                 member.GlobalIndex = _allMembers.Count;
307                         
308                         _allMembers.Add (member);
309                         
310                         if (!(member.DefaultValue is System.DBNull) && member.DefaultValue != null) {
311                                 if (_membersWithDefault == null) _membersWithDefault = new ArrayList ();
312                                 _membersWithDefault.Add (member);
313                         }
314                         
315                         if (member.IsReturnValue)
316                                 _returnMember = member;
317                         
318                         if (member is XmlTypeMapMemberAttribute)
319                         {
320                                 XmlTypeMapMemberAttribute atm = (XmlTypeMapMemberAttribute)member;
321                                 if (_attributeMembers == null) _attributeMembers = new Hashtable();
322                                 string key = BuildKey (atm.AttributeName, atm.Namespace, -1);
323                                 if (_attributeMembers.ContainsKey (key))
324                                         throw new InvalidOperationException ("The XML attribute named '" + atm.AttributeName + "' from namespace '" + atm.Namespace + "' is already present in the current scope. Use XML attributes to specify another XML name or namespace for the attribute.");
325                                 member.Index = _attributeMembers.Count;
326                                 _attributeMembers.Add (key, member);
327                                 return;
328                         }
329                         else if (member is XmlTypeMapMemberFlatList)
330                         {
331                                 RegisterFlatList ((XmlTypeMapMemberFlatList)member);
332                         }
333                         else if (member is XmlTypeMapMemberAnyElement)
334                         {
335                                 XmlTypeMapMemberAnyElement mem = (XmlTypeMapMemberAnyElement) member;
336                                 if (mem.IsDefaultAny) _defaultAnyElement = mem;
337                                 if (mem.TypeData.IsListType) RegisterFlatList (mem);
338                         }
339                         else if (member is XmlTypeMapMemberAnyAttribute)
340                         {
341                                 _defaultAnyAttribute = (XmlTypeMapMemberAnyAttribute) member;
342                                 return;
343                         }
344                         else if (member is XmlTypeMapMemberNamespaces)
345                         {
346                                 _namespaceDeclarations = (XmlTypeMapMemberNamespaces) member;
347                                 return;
348                         }
349
350                         if (member is XmlTypeMapMemberElement && ((XmlTypeMapMemberElement)member).IsXmlTextCollector)
351                         {
352                                 if (_xmlTextCollector != null) throw new InvalidOperationException ("XmlTextAttribute can only be applied once in a class");
353                                 _xmlTextCollector = member;
354                         }
355
356                         if (_elementMembers == null) {
357                                 _elementMembers = new ArrayList();
358                                 _elements = new Hashtable();
359                         }
360
361                         member.Index = _elementMembers.Count;
362                         _elementMembers.Add (member);
363
364                         ICollection elemsInfo = ((XmlTypeMapMemberElement)member).ElementInfo;
365                         foreach (XmlTypeMapElementInfo elem in elemsInfo)
366                         {
367                                 string key = BuildKey (elem.ElementName, elem.Namespace, elem.ExplicitOrder);
368                                 if (_elements.ContainsKey (key)) 
369                                         throw new InvalidOperationException ("The XML element named '" + elem.ElementName + "' from namespace '" + elem.Namespace + "' is already present in the current scope. Use XML attributes to specify another XML name or namespace for the element.");
370                                 _elements.Add (key, elem);
371                         }
372                         
373                         if (member.TypeData.IsListType && member.TypeData.Type != null && !member.TypeData.Type.IsArray) {
374                                 if (_listMembers == null) _listMembers = new ArrayList ();
375                                 _listMembers.Add (member);
376                         }
377                 }
378
379                 void RegisterFlatList (XmlTypeMapMemberExpandable member)
380                 {
381                         if (_flatLists == null) _flatLists = new ArrayList ();
382                         member.FlatArrayIndex = _flatLists.Count;
383                         _flatLists.Add (member);
384                 }
385
386                 public XmlTypeMapMemberAttribute GetAttribute (string name, string ns)
387                 {
388                         if (_attributeMembers == null) return null;
389                         return (XmlTypeMapMemberAttribute)_attributeMembers [BuildKey (name,ns, -1)];
390                 }
391
392                 public XmlTypeMapElementInfo GetElement(string name, string ns, int minimalOrder)
393                 {
394                         if (_elements == null) return null;
395
396                         foreach (XmlTypeMapElementInfo info in _elements.Values)
397                                 if (info.ElementName == name && info.Namespace == ns && info.ExplicitOrder >= minimalOrder)
398                                         return info;
399
400                         return null;
401                 }
402
403                 public XmlTypeMapElementInfo GetElement(string name, string ns)
404                 {
405                         if (_elements == null) return null;
406
407                         foreach (XmlTypeMapElementInfo info in _elements.Values)
408                                 if (info.ElementName == name && info.Namespace == ns)
409                                         return info;
410
411                         return null;
412                 }
413                 
414                 public XmlTypeMapElementInfo GetElement (int index)
415                 {
416                         if (_elements == null) return null;
417                         
418                         if (_elementsByIndex == null)
419                         {
420                                 _elementsByIndex = new XmlTypeMapElementInfo [_elementMembers.Count];
421                                 foreach (XmlTypeMapMemberElement mem in _elementMembers)
422                                 {
423                                         if (mem.ElementInfo.Count != 1) 
424                                                 throw new InvalidOperationException ("Read by order only possible for encoded/bare format");
425                                                 
426                                         _elementsByIndex [mem.Index] = (XmlTypeMapElementInfo) mem.ElementInfo [0];
427                                 }
428                         }
429                         if (index >= _elementMembers.Count)
430                                 return null;
431                         return _elementsByIndex [index];
432                 }
433                 
434                 private string BuildKey (string name, string ns, int explicitOrder)
435                 {
436                         if (_ignoreMemberNamespace) return name;
437                         else return name + " / " + ns + (explicitOrder < 0 ? "" : "/" + explicitOrder);
438                 }
439                 
440                 public ICollection AllElementInfos
441                 {
442                         get { return _elements.Values; }
443                 }
444                 
445                 
446                 public bool IgnoreMemberNamespace
447                 {
448                         get { return _ignoreMemberNamespace; }
449                         set { _ignoreMemberNamespace = value; }
450                 }
451
452                 public bool IsOrderDependentMap {
453                         get {
454                                 if (_isOrderDependentMap == null) {
455                                         _isOrderDependentMap = false;
456                                         foreach (XmlTypeMapElementInfo ei in _elements.Values)
457                                                 if (ei.ExplicitOrder >= 0) {
458                                                         _isOrderDependentMap = true;
459                                                         break;
460                                                 }
461                                 }
462                                 return (bool) _isOrderDependentMap;
463                         }
464                 }
465
466                 public XmlTypeMapMember FindMember (string name)
467                 {
468                         for (int n=0; n<_allMembers.Count; n++)
469                                 if (((XmlTypeMapMember)_allMembers[n]).Name == name) return (XmlTypeMapMember)_allMembers[n];
470                         return null;
471                 }
472
473                 public XmlTypeMapMemberAnyElement DefaultAnyElementMember
474                 {
475                         get { return _defaultAnyElement; }
476                 }
477
478                 public XmlTypeMapMemberAnyAttribute DefaultAnyAttributeMember
479                 {
480                         get { return _defaultAnyAttribute; }
481                 }
482
483                 public XmlTypeMapMemberNamespaces NamespaceDeclarations
484                 {
485                         get { return _namespaceDeclarations; }
486                 }
487
488                 public ICollection AttributeMembers
489                 {
490                         get 
491                         {
492                                 if (_attributeMembers == null) return null;
493                                 if (_attributeMembersArray != null) return _attributeMembersArray;
494                                 
495                                 _attributeMembersArray = new XmlTypeMapMemberAttribute[_attributeMembers.Count];
496                                 foreach (XmlTypeMapMemberAttribute mem in _attributeMembers.Values)
497                                         _attributeMembersArray [mem.Index] = mem;
498                                 return _attributeMembersArray;
499                         }
500                 }
501
502                 public ICollection ElementMembers
503                 {
504                         get { return _elementMembers; }
505                 }
506
507                 public ArrayList AllMembers
508                 {
509                         get { return _allMembers; }
510                 }
511
512                 public ArrayList FlatLists
513                 {
514                         get { return _flatLists; }
515                 }
516                 
517                 public ArrayList MembersWithDefault
518                 {
519                         get { return _membersWithDefault; }
520                 }
521                 
522                 public ArrayList ListMembers
523                 {
524                         get { return _listMembers; }
525                 }
526
527                 public XmlTypeMapMember XmlTextCollector
528                 {
529                         get { return _xmlTextCollector; }
530                 }
531                 
532                 public XmlTypeMapMember ReturnMember
533                 {
534                         get { return _returnMember; }
535                 }
536
537                 public XmlQualifiedName SimpleContentBaseType
538                 {
539                         get
540                         {
541                                 if (!_canBeSimpleType || _elementMembers == null || _elementMembers.Count != 1) return null;
542                                 XmlTypeMapMemberElement member = (XmlTypeMapMemberElement) _elementMembers[0];
543                                 if (member.ElementInfo.Count != 1) return null;
544                                 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) member.ElementInfo[0];
545                                 if (!einfo.IsTextElement) return null;
546                                 if (member.TypeData.SchemaType == SchemaTypes.Primitive || member.TypeData.SchemaType == SchemaTypes.Enum)
547                                         return new XmlQualifiedName (einfo.TypeData.XmlType, einfo.DataTypeNamespace);
548                                 return null;
549                         }
550                 }
551                 
552                 public void SetCanBeSimpleType (bool can)
553                 {
554                         _canBeSimpleType = can;
555                 }
556
557                 public bool HasSimpleContent
558                 {
559                         get
560                         {
561                                 return SimpleContentBaseType != null;
562                         }
563                 }
564
565         }
566
567         // Mapping info for arrays and lists
568
569         internal class ListMap: ObjectMap
570         {
571                 XmlTypeMapElementInfoList _itemInfo;
572                 bool _gotNestedMapping;
573                 XmlTypeMapping _nestedArrayMapping;
574                 string _choiceMember;
575
576                 public bool IsMultiArray
577                 {
578                         get
579                         {
580                                 return (NestedArrayMapping != null);
581                         }
582                 }
583
584                 public string ChoiceMember
585                 {
586                         get { return _choiceMember; }
587                         set { _choiceMember = value; }
588                 }
589
590                 public XmlTypeMapping NestedArrayMapping
591                 {
592                         get
593                         {
594                                 if (_gotNestedMapping) return _nestedArrayMapping;
595                                 _gotNestedMapping = true;
596
597                                 _nestedArrayMapping = ((XmlTypeMapElementInfo)_itemInfo[0]).MappedType;
598
599                                 if (_nestedArrayMapping == null) return null;
600                                 
601                                 if (_nestedArrayMapping.TypeData.SchemaType != SchemaTypes.Array) {
602                                         _nestedArrayMapping = null; return null;
603                                 }
604
605                                 foreach (XmlTypeMapElementInfo elem in _itemInfo)
606                                         if (elem.MappedType != _nestedArrayMapping) {
607                                                 _nestedArrayMapping = null;
608                                                 return null;
609                                         }
610
611                                 return _nestedArrayMapping;
612                         }
613                 }
614
615                 public XmlTypeMapElementInfoList ItemInfo
616                 {
617
618                         get { return _itemInfo; }
619                         set { _itemInfo = value; }
620                 }
621
622                 public XmlTypeMapElementInfo FindElement (object ob, int index, object memberValue)
623                 {
624                         if (_itemInfo.Count == 1) 
625                                 return (XmlTypeMapElementInfo) _itemInfo[0];
626                         else if (_choiceMember != null && index != -1)
627                         {
628                                 Array values = (Array) XmlTypeMapMember.GetValue (ob, _choiceMember);
629                                 if (values == null || index >= values.Length)
630                                         throw new InvalidOperationException ("Invalid or missing choice enum value in member '" + _choiceMember + "'.");
631                                 object val = values.GetValue (index);
632                                 foreach (XmlTypeMapElementInfo elem in _itemInfo)
633                                         if (elem.ChoiceValue != null && elem.ChoiceValue.Equals (val))
634                                                 return elem;
635                         }
636                         else
637                         {
638                                 if (memberValue == null) return null;
639                                 Type type = memberValue.GetType();
640                                 foreach (XmlTypeMapElementInfo elem in _itemInfo)
641                                         if (elem.TypeData.Type == type) return elem;
642                         }
643                         return null;
644                 }       
645
646                 public XmlTypeMapElementInfo FindElement (string elementName, string ns)
647                 {
648                         foreach (XmlTypeMapElementInfo elem in _itemInfo)
649                                 if (elem.ElementName == elementName && elem.Namespace == ns) return elem;
650                         return null;
651                 }
652                 
653                 public XmlTypeMapElementInfo FindTextElement ()
654                 {
655                         foreach (XmlTypeMapElementInfo elem in _itemInfo)
656                                 if (elem.IsTextElement) return elem;
657                         return null;
658                 }
659                 
660                 public string GetSchemaArrayName ()
661                 {
662                         XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) _itemInfo[0];
663                         if (einfo.MappedType != null) return TypeTranslator.GetArrayName (einfo.MappedType.XmlType);
664                         else return TypeTranslator.GetArrayName (einfo.TypeData.XmlType);
665                 }
666
667                 public void GetArrayType (int itemCount, out string localName, out string ns)
668                 {
669                         string arrayDim;
670                         if (itemCount != -1) arrayDim = "[" + itemCount + "]";
671                         else arrayDim = "[]";
672
673                         XmlTypeMapElementInfo info = (XmlTypeMapElementInfo) _itemInfo[0];
674                         if (info.TypeData.SchemaType == SchemaTypes.Array)
675                         {
676                                 string nm;
677                                 ((ListMap)info.MappedType.ObjectMap).GetArrayType (-1, out nm, out ns);
678                                 localName = nm + arrayDim;
679                         }
680                         else 
681                         {
682                                 if (info.MappedType != null)
683                                 {
684                                         localName = info.MappedType.XmlType + arrayDim;
685                                         ns = info.MappedType.Namespace;
686                                 }
687                                 else 
688                                 {
689                                         localName = info.TypeData.XmlType + arrayDim;
690                                         ns = info.DataTypeNamespace;
691                                 }
692                         }
693                 }
694
695                 public override bool Equals (object other)
696                 {
697                         ListMap lmap = other as ListMap;
698                         if (lmap == null) return false;
699
700                         if (_itemInfo.Count != lmap._itemInfo.Count) return false;
701                         for (int n=0; n<_itemInfo.Count; n++)
702                                 if (!_itemInfo[n].Equals (lmap._itemInfo[n])) return false;
703                         return true;
704                 }
705
706                 public override int GetHashCode ()
707                 {
708                         return base.GetHashCode ();
709                 }
710         }
711
712         internal class EnumMap: ObjectMap
713         {
714                 readonly EnumMapMember[] _members;
715                 readonly bool _isFlags;
716                 readonly string[] _enumNames = null;
717                 readonly string[] _xmlNames = null;
718                 readonly long[] _values = null;
719
720                 public class EnumMapMember
721                 {
722                         readonly string _xmlName;
723                         readonly string _enumName;
724                         readonly long _value;
725                         string _documentation;
726
727                         public EnumMapMember (string xmlName, string enumName)
728                                 : this (xmlName, enumName, 0)
729                         {
730                         }
731
732                         public EnumMapMember (string xmlName, string enumName, long value)
733                         {
734                                 _xmlName = xmlName;
735                                 _enumName = enumName;
736                                 _value = value;
737                         }
738
739                         public string XmlName
740                         {
741                                 get { return _xmlName; }
742                         }
743
744                         public string EnumName
745                         {
746                                 get { return _enumName; }
747                         }
748
749                         public long Value
750                         {
751                                 get { return _value; }
752                         }
753
754                         public string Documentation
755                         {
756                                 get { return _documentation; }
757                                 set { _documentation = value; }
758                         }
759                 }
760
761                 public EnumMap (EnumMapMember[] members, bool isFlags)
762                 {
763                         _members = members;
764                         _isFlags = isFlags;
765
766                         _enumNames = new string[_members.Length];
767                         _xmlNames = new string[_members.Length];
768                         _values = new long[_members.Length];
769
770                         for (int i = 0; i < _members.Length; i++) {
771                                 EnumMapMember mem = _members[i];
772                                 _enumNames[i] = mem.EnumName;
773                                 _xmlNames[i] = mem.XmlName;
774                                 _values[i] = mem.Value;
775                         }
776                 }
777                 
778                 public bool IsFlags
779                 {
780                         get { return _isFlags; }
781                 }
782
783                 public EnumMapMember[] Members
784                 {
785                         get { return _members; }
786                 }
787
788                 public string[] EnumNames
789                 {
790                         get {
791                                 return _enumNames;
792                         }
793                 }
794
795                 public string[] XmlNames
796                 {
797                         get {
798                                 return _xmlNames;
799                         }
800                 }
801
802                 public long[] Values
803                 {
804                         get {
805                                 return _values;
806                         }
807                 }
808
809                 public string GetXmlName (string typeName, object enumValue)
810                 {
811                         if (enumValue is string) {
812                                 throw new InvalidCastException ();
813                         }
814
815                         long value = 0;
816
817                         try {
818                                 value = ((IConvertible) enumValue).ToInt64 (CultureInfo.CurrentCulture);
819                         } catch (FormatException) {
820                                 throw new InvalidCastException ();
821                         }
822
823                         for (int i = 0; i < Values.Length; i++) {
824                                 if (Values[i] == value)
825                                         return XmlNames[i];
826                         }
827
828                         if (IsFlags && value == 0)
829                                 return string.Empty;
830
831                         string xmlName = string.Empty;
832                         if (IsFlags) {
833 #if NET_2_0
834                                 xmlName = XmlCustomFormatter.FromEnum (value, XmlNames, Values, typeName);
835 #else
836                                 xmlName = XmlCustomFormatter.FromEnum (value, XmlNames, Values);
837 #endif
838                         }
839
840                         if (xmlName.Length == 0) {
841 #if NET_2_0
842                                 throw new InvalidOperationException (string.Format(CultureInfo.CurrentCulture,
843                                         "'{0}' is not a valid value for {1}.", value, typeName));
844 #else
845                                 return value.ToString (CultureInfo.InvariantCulture);
846 #endif
847                         }
848                         return xmlName;
849                 }
850
851                 public string GetEnumName (string typeName, string xmlName)
852                 {
853                         if (_isFlags) {
854                                 xmlName = xmlName.Trim ();
855                                 if (xmlName.Length == 0)
856                                         return "0";
857
858                                 System.Text.StringBuilder sb = new System.Text.StringBuilder ();
859                                 string[] enumNames = xmlName.Split (null);
860                                 foreach (string name in enumNames) {
861                                         if (name == string.Empty) continue;
862                                         string foundEnumValue = null;
863                                         for (int i = 0; i < XmlNames.Length; i++)
864                                                 if (XmlNames[i] == name) {
865                                                         foundEnumValue = EnumNames[i];
866                                                         break;
867                                                 }
868
869                                         if (foundEnumValue != null) {
870                                                 if (sb.Length > 0)
871                                                         sb.Append (',');
872                                                 sb.Append (foundEnumValue);
873                                         } else {
874                                                 throw new InvalidOperationException (string.Format (CultureInfo.CurrentCulture,
875                                                         "'{0}' is not a valid value for {1}.", name, typeName));
876                                         }
877                                 }
878                                 return sb.ToString ();
879                         }
880
881                         foreach (EnumMapMember mem in _members)
882                                 if (mem.XmlName == xmlName) return mem.EnumName;
883                                 
884                         return null;
885                 }
886         }
887 }