add partial support for XmlSchemaProviderAttribute
[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; }
111                         set { xmlTypeNamespace = value; }
112                 }
113
114                 internal ArrayList DerivedTypes
115                 {
116                         get { return _derivedTypes; }
117                         set { _derivedTypes = value; }
118                 }
119
120                 internal bool MultiReferenceType
121                 {
122                         get { return multiReferenceType; }
123                         set { multiReferenceType = value; }
124                 }
125
126                 internal XmlTypeMapping BaseMap
127                 {
128                         get { return baseMap; }
129                         set { baseMap = value; }
130                 }
131
132                 internal bool IsSimpleType
133                 {
134                         get { return isSimpleType; }
135                         set { isSimpleType = value; }
136                 }
137
138                 internal string Documentation
139                 {
140                         set { documentation = value; }
141                         get { return documentation; }
142                 }
143
144                 internal bool IncludeInSchema
145                 {
146                         get { return includeInSchema; }
147                         set { includeInSchema = value; }
148                 }
149                 
150                 internal bool IsNullable
151                 {
152                         get { return isNullable; }
153                         set { isNullable = value; }
154                 }
155
156                 internal XmlTypeMapping GetRealTypeMap (Type objectType)
157                 {
158                         if (TypeData.SchemaType == SchemaTypes.Enum)
159                                 return this;
160
161                         // Returns the map for a subtype of this map's type
162                         if (TypeData.Type == objectType) return this;
163                         for (int n=0; n<_derivedTypes.Count; n++) {
164                                 XmlTypeMapping map = (XmlTypeMapping) _derivedTypes[n];
165                                 if (map.TypeData.Type == objectType) return map;
166                         }
167                         
168                         return null;
169                 }
170
171                 internal XmlTypeMapping GetRealElementMap (string name, string ens)
172                 {
173                         if (xmlType == name && xmlTypeNamespace == ens) return this;
174                         foreach (XmlTypeMapping map in _derivedTypes)
175                                 if (map.xmlType == name && map.xmlTypeNamespace == ens) return map;
176                         
177                         return null;
178                 }
179                 
180                 internal void UpdateRoot (XmlQualifiedName qname)
181                 {
182                         if (qname != null) {
183                                 this._elementName = qname.Name;
184                                 this._namespace = qname.Namespace;
185                         }
186                 }
187         }
188
189  
190         // Mapping info for XmlSerializable
191         internal class XmlSerializableMapping : XmlTypeMapping
192         {
193                 XmlSchema _schema;
194 #if NET_2_0
195                 XmlSchemaComplexType _schemaType;
196                 XmlQualifiedName _schemaTypeName;
197 #endif
198
199                 internal XmlSerializableMapping(string elementName, string ns, TypeData typeData, string xmlType, string xmlTypeNamespace)
200                         : base(elementName, ns, typeData, xmlType, xmlTypeNamespace)
201                 {
202 #if NET_2_0
203                         XmlSchemaProviderAttribute schemaProvider = (XmlSchemaProviderAttribute) Attribute.GetCustomAttribute (typeData.Type, typeof (XmlSchemaProviderAttribute));
204
205                         if (schemaProvider != null) {
206                                 string method = schemaProvider.MethodName;
207                                 MethodInfo mi = typeData.Type.GetMethod (method, BindingFlags.Static | BindingFlags.Public);
208                                 XmlSchemaSet xs = new XmlSchemaSet ();
209                                 object retVal = mi.Invoke (null, new object [] { xs });
210                                 if (retVal is XmlSchemaComplexType) {
211                                         _schemaType = (XmlSchemaComplexType) retVal;
212                                         if (_schemaType.Attributes.Count > 1) {
213                                                 XmlTypeNamespace = ((XmlSchemaAttribute) _schemaType.Attributes [0]).FixedValue;
214                                                 XmlType = ((XmlSchemaAttribute) _schemaType.Attributes [1]).FixedValue;
215                                         }
216                                 }
217                                 else if (retVal is XmlQualifiedName) {
218                                         _schemaTypeName = (XmlQualifiedName)retVal;
219                                         XmlTypeNamespace = _schemaTypeName.Namespace;
220                                         XmlType = _schemaTypeName.Name;
221                                 }
222                                 else
223                                         throw new InvalidOperationException (
224                                                 String.Format ("Method {0}.{1}() specified by XmlSchemaProviderAttribute has invalid signature: return type must be compatible with System.Xml.XmlQualifiedName.", typeData.Type.Name, method));
225
226                                 XmlSchema [] schemas = new XmlSchema [xs.Count];
227                                 xs.CopyTo (schemas, 0);
228                                 _schema = schemas [0];
229
230                                 return;
231                         }
232 #endif
233                         IXmlSerializable serializable = (IXmlSerializable)Activator.CreateInstance (typeData.Type, true);
234                         _schema = serializable.GetSchema();
235                         if (_schema != null) 
236                         {
237                                 if (_schema.Id == null || _schema.Id.Length == 0) 
238                                         throw new InvalidOperationException("Schema Id is missing. The schema returned from " + typeData.Type.FullName + ".GetSchema() must have an Id.");
239                         }
240                 }
241
242                 internal XmlSchema Schema
243                 {
244                         get { return _schema; }
245                 }
246
247 #if NET_2_0
248                 internal XmlSchemaType SchemaType {
249                         get { return _schemaType; }
250                 }
251
252                 internal XmlQualifiedName SchemaTypeName {
253                         get { return _schemaTypeName; }
254                 }
255 #endif
256         }
257  
258
259         // Mapping info for classes and structs
260
261         internal class ClassMap: ObjectMap
262         {
263                 Hashtable _elements = new Hashtable ();
264                 ArrayList _elementMembers;
265                 Hashtable _attributeMembers;
266                 XmlTypeMapMemberAttribute[] _attributeMembersArray;
267                 XmlTypeMapElementInfo[] _elementsByIndex;
268                 ArrayList _flatLists;
269                 ArrayList _allMembers = new ArrayList ();
270                 ArrayList _membersWithDefault;
271                 ArrayList _listMembers;
272                 XmlTypeMapMemberAnyElement _defaultAnyElement;
273                 XmlTypeMapMemberAnyAttribute _defaultAnyAttribute;
274                 XmlTypeMapMemberNamespaces _namespaceDeclarations;
275                 XmlTypeMapMember _xmlTextCollector;
276                 XmlTypeMapMember _returnMember;
277                 bool _ignoreMemberNamespace;
278                 bool _canBeSimpleType = true;
279
280                 public void AddMember (XmlTypeMapMember member)
281                 {
282                         member.GlobalIndex = _allMembers.Count;
283                         _allMembers.Add (member);
284                         
285                         if (!(member.DefaultValue is System.DBNull) && member.DefaultValue != null) {
286                                 if (_membersWithDefault == null) _membersWithDefault = new ArrayList ();
287                                 _membersWithDefault.Add (member);
288                         }
289                         
290                         if (member.IsReturnValue)
291                                 _returnMember = member;
292                         
293                         if (member is XmlTypeMapMemberAttribute)
294                         {
295                                 XmlTypeMapMemberAttribute atm = (XmlTypeMapMemberAttribute)member;
296                                 if (_attributeMembers == null) _attributeMembers = new Hashtable();
297                                 string key = BuildKey (atm.AttributeName, atm.Namespace);
298                                 if (_attributeMembers.ContainsKey (key))
299                                         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.");
300                                 member.Index = _attributeMembers.Count;
301                                 _attributeMembers.Add (key, member);
302                                 return;
303                         }
304                         else if (member is XmlTypeMapMemberFlatList)
305                         {
306                                 RegisterFlatList ((XmlTypeMapMemberFlatList)member);
307                         }
308                         else if (member is XmlTypeMapMemberAnyElement)
309                         {
310                                 XmlTypeMapMemberAnyElement mem = (XmlTypeMapMemberAnyElement) member;
311                                 if (mem.IsDefaultAny) _defaultAnyElement = mem;
312                                 if (mem.TypeData.IsListType) RegisterFlatList (mem);
313                         }
314                         else if (member is XmlTypeMapMemberAnyAttribute)
315                         {
316                                 _defaultAnyAttribute = (XmlTypeMapMemberAnyAttribute) member;
317                                 return;
318                         }
319                         else if (member is XmlTypeMapMemberNamespaces)
320                         {
321                                 _namespaceDeclarations = (XmlTypeMapMemberNamespaces) member;
322                                 return;
323                         }
324
325                         if (member is XmlTypeMapMemberElement && ((XmlTypeMapMemberElement)member).IsXmlTextCollector)
326                         {
327                                 if (_xmlTextCollector != null) throw new InvalidOperationException ("XmlTextAttribute can only be applied once in a class");
328                                 _xmlTextCollector = member;
329                         }
330
331                         if (_elementMembers == null) {
332                                 _elementMembers = new ArrayList();
333                                 _elements = new Hashtable();
334                         }
335
336                         member.Index = _elementMembers.Count;
337                         _elementMembers.Add (member);
338
339                         ICollection elemsInfo = ((XmlTypeMapMemberElement)member).ElementInfo;
340                         foreach (XmlTypeMapElementInfo elem in elemsInfo)
341                         {
342                                 string key = BuildKey (elem.ElementName, elem.Namespace);
343                                 if (_elements.ContainsKey (key)) 
344                                         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.");
345                                 _elements.Add (key, elem);
346                         }
347                         
348                         if (member.TypeData.IsListType && member.TypeData.Type != null && !member.TypeData.Type.IsArray) {
349                                 if (_listMembers == null) _listMembers = new ArrayList ();
350                                 _listMembers.Add (member);
351                         }
352                 }
353
354                 void RegisterFlatList (XmlTypeMapMemberExpandable member)
355                 {
356                         if (_flatLists == null) _flatLists = new ArrayList ();
357                         member.FlatArrayIndex = _flatLists.Count;
358                         _flatLists.Add (member);
359                 }
360
361                 public XmlTypeMapMemberAttribute GetAttribute (string name, string ns)
362                 {
363                         if (_attributeMembers == null) return null;
364                         return (XmlTypeMapMemberAttribute)_attributeMembers [BuildKey(name,ns)];
365                 }
366
367                 public XmlTypeMapElementInfo GetElement (string name, string ns)
368                 {
369                         if (_elements == null) return null;
370                         return (XmlTypeMapElementInfo)_elements [BuildKey(name,ns)];
371                 }
372                 
373                 public XmlTypeMapElementInfo GetElement (int index)
374                 {
375                         if (_elements == null) return null;
376                         
377                         if (_elementsByIndex == null)
378                         {
379                                 _elementsByIndex = new XmlTypeMapElementInfo [_elementMembers.Count];
380                                 foreach (XmlTypeMapMemberElement mem in _elementMembers)
381                                 {
382                                         if (mem.ElementInfo.Count != 1) 
383                                                 throw new InvalidOperationException ("Read by order only possible for encoded/bare format");
384                                                 
385                                         _elementsByIndex [mem.Index] = (XmlTypeMapElementInfo) mem.ElementInfo [0];
386                                 }
387                         }
388                         
389                         return _elementsByIndex [index];
390                 }
391                 
392                 private string BuildKey (string name, string ns)
393                 {
394                         if (_ignoreMemberNamespace) return name;
395                         else return name + " / " + ns;
396                 }
397                 
398                 public ICollection AllElementInfos
399                 {
400                         get { return _elements.Values; }
401                 }
402                 
403                 
404                 public bool IgnoreMemberNamespace
405                 {
406                         get { return _ignoreMemberNamespace; }
407                         set { _ignoreMemberNamespace = value; }
408                 }
409
410                 public XmlTypeMapMember FindMember (string name)
411                 {
412                         for (int n=0; n<_allMembers.Count; n++)
413                                 if (((XmlTypeMapMember)_allMembers[n]).Name == name) return (XmlTypeMapMember)_allMembers[n];
414                         return null;
415                 }
416
417                 public XmlTypeMapMemberAnyElement DefaultAnyElementMember
418                 {
419                         get { return _defaultAnyElement; }
420                 }
421
422                 public XmlTypeMapMemberAnyAttribute DefaultAnyAttributeMember
423                 {
424                         get { return _defaultAnyAttribute; }
425                 }
426
427                 public XmlTypeMapMemberNamespaces NamespaceDeclarations
428                 {
429                         get { return _namespaceDeclarations; }
430                 }
431
432                 public ICollection AttributeMembers
433                 {
434                         get 
435                         {
436                                 if (_attributeMembers == null) return null;
437                                 if (_attributeMembersArray != null) return _attributeMembersArray;
438                                 
439                                 _attributeMembersArray = new XmlTypeMapMemberAttribute[_attributeMembers.Count];
440                                 foreach (XmlTypeMapMemberAttribute mem in _attributeMembers.Values)
441                                         _attributeMembersArray [mem.Index] = mem;
442                                 return _attributeMembersArray;
443                         }
444                 }
445
446                 public ICollection ElementMembers
447                 {
448                         get { return _elementMembers; }
449                 }
450
451                 public ArrayList AllMembers
452                 {
453                         get { return _allMembers; }
454                 }
455
456                 public ArrayList FlatLists
457                 {
458                         get { return _flatLists; }
459                 }
460                 
461                 public ArrayList MembersWithDefault
462                 {
463                         get { return _membersWithDefault; }
464                 }
465                 
466                 public ArrayList ListMembers
467                 {
468                         get { return _listMembers; }
469                 }
470
471                 public XmlTypeMapMember XmlTextCollector
472                 {
473                         get { return _xmlTextCollector; }
474                 }
475                 
476                 public XmlTypeMapMember ReturnMember
477                 {
478                         get { return _returnMember; }
479                 }
480
481                 public XmlQualifiedName SimpleContentBaseType
482                 {
483                         get
484                         {
485                                 if (!_canBeSimpleType || _elementMembers == null || _elementMembers.Count != 1) return null;
486                                 XmlTypeMapMemberElement member = (XmlTypeMapMemberElement) _elementMembers[0];
487                                 if (member.ElementInfo.Count != 1) return null;
488                                 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) member.ElementInfo[0];
489                                 if (!einfo.IsTextElement) return null;
490                                 if (member.TypeData.SchemaType == SchemaTypes.Primitive || member.TypeData.SchemaType == SchemaTypes.Enum)
491                                         return new XmlQualifiedName (einfo.TypeData.XmlType, einfo.DataTypeNamespace);
492                                 return null;
493                         }
494                 }
495                 
496                 public void SetCanBeSimpleType (bool can)
497                 {
498                         _canBeSimpleType = can;
499                 }
500
501                 public bool HasSimpleContent
502                 {
503                         get
504                         {
505                                 return SimpleContentBaseType != null;
506                         }
507                 }
508
509         }
510
511         // Mapping info for arrays and lists
512
513         internal class ListMap: ObjectMap
514         {
515                 XmlTypeMapElementInfoList _itemInfo;
516                 bool _gotNestedMapping;
517                 XmlTypeMapping _nestedArrayMapping;
518                 string _choiceMember;
519
520                 public bool IsMultiArray
521                 {
522                         get
523                         {
524                                 return (NestedArrayMapping != null);
525                         }
526                 }
527
528                 public string ChoiceMember
529                 {
530                         get { return _choiceMember; }
531                         set { _choiceMember = value; }
532                 }
533
534                 public XmlTypeMapping NestedArrayMapping
535                 {
536                         get
537                         {
538                                 if (_gotNestedMapping) return _nestedArrayMapping;
539                                 _gotNestedMapping = true;
540
541                                 _nestedArrayMapping = ((XmlTypeMapElementInfo)_itemInfo[0]).MappedType;
542
543                                 if (_nestedArrayMapping == null) return null;
544                                 
545                                 if (_nestedArrayMapping.TypeData.SchemaType != SchemaTypes.Array) {
546                                         _nestedArrayMapping = null; return null;
547                                 }
548
549                                 foreach (XmlTypeMapElementInfo elem in _itemInfo)
550                                         if (elem.MappedType != _nestedArrayMapping) {
551                                                 _nestedArrayMapping = null;
552                                                 return null;
553                                         }
554
555                                 return _nestedArrayMapping;
556                         }
557                 }
558
559                 public XmlTypeMapElementInfoList ItemInfo
560                 {
561
562                         get { return _itemInfo; }
563                         set { _itemInfo = value; }
564                 }
565
566                 public XmlTypeMapElementInfo FindElement (object ob, int index, object memberValue)
567                 {
568                         if (_itemInfo.Count == 1) 
569                                 return (XmlTypeMapElementInfo) _itemInfo[0];
570                         else if (_choiceMember != null && index != -1)
571                         {
572                                 Array values = (Array) XmlTypeMapMember.GetValue (ob, _choiceMember);
573                                 if (values == null || index >= values.Length)
574                                         throw new InvalidOperationException ("Invalid or missing choice enum value in member '" + _choiceMember + "'.");
575                                 object val = values.GetValue (index);
576                                 foreach (XmlTypeMapElementInfo elem in _itemInfo)
577                                         if (elem.ChoiceValue != null && elem.ChoiceValue.Equals (val))
578                                                 return elem;
579                         }
580                         else
581                         {
582                                 if (memberValue == null) return null;
583                                 Type type = memberValue.GetType();
584                                 foreach (XmlTypeMapElementInfo elem in _itemInfo)
585                                         if (elem.TypeData.Type == type) return elem;
586                         }
587                         return null;
588                 }       
589
590                 public XmlTypeMapElementInfo FindElement (string elementName, string ns)
591                 {
592                         foreach (XmlTypeMapElementInfo elem in _itemInfo)
593                                 if (elem.ElementName == elementName && elem.Namespace == ns) return elem;
594                         return null;
595                 }
596                 
597                 public XmlTypeMapElementInfo FindTextElement ()
598                 {
599                         foreach (XmlTypeMapElementInfo elem in _itemInfo)
600                                 if (elem.IsTextElement) return elem;
601                         return null;
602                 }
603                 
604                 public string GetSchemaArrayName ()
605                 {
606                         XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) _itemInfo[0];
607                         if (einfo.MappedType != null) return TypeTranslator.GetArrayName (einfo.MappedType.XmlType);
608                         else return TypeTranslator.GetArrayName (einfo.TypeData.XmlType);
609                 }
610
611                 public void GetArrayType (int itemCount, out string localName, out string ns)
612                 {
613                         string arrayDim;
614                         if (itemCount != -1) arrayDim = "[" + itemCount + "]";
615                         else arrayDim = "[]";
616
617                         XmlTypeMapElementInfo info = (XmlTypeMapElementInfo) _itemInfo[0];
618                         if (info.TypeData.SchemaType == SchemaTypes.Array)
619                         {
620                                 string nm;
621                                 ((ListMap)info.MappedType.ObjectMap).GetArrayType (-1, out nm, out ns);
622                                 localName = nm + arrayDim;
623                         }
624                         else 
625                         {
626                                 if (info.MappedType != null)
627                                 {
628                                         localName = info.MappedType.XmlType + arrayDim;
629                                         ns = info.MappedType.Namespace;
630                                 }
631                                 else 
632                                 {
633                                         localName = info.TypeData.XmlType + arrayDim;
634                                         ns = info.DataTypeNamespace;
635                                 }
636                         }
637                 }
638
639                 public override bool Equals (object other)
640                 {
641                         ListMap lmap = other as ListMap;
642                         if (lmap == null) return false;
643
644                         if (_itemInfo.Count != lmap._itemInfo.Count) return false;
645                         for (int n=0; n<_itemInfo.Count; n++)
646                                 if (!_itemInfo[n].Equals (lmap._itemInfo[n])) return false;
647                         return true;
648                 }
649
650                 public override int GetHashCode ()
651                 {
652                         return base.GetHashCode ();
653                 }
654         }
655
656         internal class EnumMap: ObjectMap
657         {
658                 readonly EnumMapMember[] _members;
659                 readonly bool _isFlags;
660                 readonly string[] _enumNames = null;
661                 readonly string[] _xmlNames = null;
662                 readonly long[] _values = null;
663
664                 public class EnumMapMember
665                 {
666                         readonly string _xmlName;
667                         readonly string _enumName;
668                         readonly long _value;
669                         string _documentation;
670
671                         public EnumMapMember (string xmlName, string enumName)
672                                 : this (xmlName, enumName, 0)
673                         {
674                         }
675
676                         public EnumMapMember (string xmlName, string enumName, long value)
677                         {
678                                 _xmlName = xmlName;
679                                 _enumName = enumName;
680                                 _value = value;
681                         }
682
683                         public string XmlName
684                         {
685                                 get { return _xmlName; }
686                         }
687
688                         public string EnumName
689                         {
690                                 get { return _enumName; }
691                         }
692
693                         public long Value
694                         {
695                                 get { return _value; }
696                         }
697
698                         public string Documentation
699                         {
700                                 get { return _documentation; }
701                                 set { _documentation = value; }
702                         }
703                 }
704
705                 public EnumMap (EnumMapMember[] members, bool isFlags)
706                 {
707                         _members = members;
708                         _isFlags = isFlags;
709
710                         _enumNames = new string[_members.Length];
711                         _xmlNames = new string[_members.Length];
712                         _values = new long[_members.Length];
713
714                         for (int i = 0; i < _members.Length; i++) {
715                                 EnumMapMember mem = _members[i];
716                                 _enumNames[i] = mem.EnumName;
717                                 _xmlNames[i] = mem.XmlName;
718                                 _values[i] = mem.Value;
719                         }
720                 }
721                 
722                 public bool IsFlags
723                 {
724                         get { return _isFlags; }
725                 }
726
727                 public EnumMapMember[] Members
728                 {
729                         get { return _members; }
730                 }
731
732                 public string[] EnumNames
733                 {
734                         get {
735                                 return _enumNames;
736                         }
737                 }
738
739                 public string[] XmlNames
740                 {
741                         get {
742                                 return _xmlNames;
743                         }
744                 }
745
746                 public long[] Values
747                 {
748                         get {
749                                 return _values;
750                         }
751                 }
752
753                 public string GetXmlName (string typeName, object enumValue)
754                 {
755                         if (enumValue is string) {
756                                 throw new InvalidCastException ();
757                         }
758
759                         long value = 0;
760
761                         try {
762                                 value = ((IConvertible) enumValue).ToInt64 (CultureInfo.CurrentCulture);
763                         } catch (FormatException) {
764                                 throw new InvalidCastException ();
765                         }
766
767                         for (int i = 0; i < Values.Length; i++) {
768                                 if (Values[i] == value)
769                                         return XmlNames[i];
770                         }
771
772                         if (IsFlags && value == 0)
773                                 return string.Empty;
774
775                         string xmlName = string.Empty;
776                         if (IsFlags) {
777 #if NET_2_0
778                                 xmlName = XmlCustomFormatter.FromEnum (value, XmlNames, Values, typeName);
779 #else
780                                 xmlName = XmlCustomFormatter.FromEnum (value, XmlNames, Values);
781 #endif
782                         }
783
784                         if (xmlName.Length == 0) {
785 #if NET_2_0
786                                 throw new InvalidOperationException (string.Format(CultureInfo.CurrentCulture,
787                                         "'{0}' is not a valid value for {1}.", value, typeName));
788 #else
789                                 return value.ToString (CultureInfo.InvariantCulture);
790 #endif
791                         }
792                         return xmlName;
793                 }
794
795                 public string GetEnumName (string typeName, string xmlName)
796                 {
797                         if (_isFlags) {
798                                 xmlName = xmlName.Trim ();
799                                 if (xmlName.Length == 0)
800                                         return "0";
801
802                                 System.Text.StringBuilder sb = new System.Text.StringBuilder ();
803                                 string[] enumNames = xmlName.Split (null);
804                                 foreach (string name in enumNames) {
805                                         if (name == string.Empty) continue;
806                                         string foundEnumValue = null;
807                                         for (int i = 0; i < XmlNames.Length; i++)
808                                                 if (XmlNames[i] == name) {
809                                                         foundEnumValue = EnumNames[i];
810                                                         break;
811                                                 }
812
813                                         if (foundEnumValue != null) {
814                                                 if (sb.Length > 0)
815                                                         sb.Append (',');
816                                                 sb.Append (foundEnumValue);
817                                         } else {
818                                                 throw new InvalidOperationException (string.Format (CultureInfo.CurrentCulture,
819                                                         "'{0}' is not a valid value for {1}.", name, typeName));
820                                         }
821                                 }
822                                 return sb.ToString ();
823                         }
824
825                         foreach (EnumMapMember mem in _members)
826                                 if (mem.XmlName == xmlName) return mem.EnumName;
827                                 
828                         return null;
829                 }
830         }
831 }