Merge pull request #495 from nicolas-raoul/fix-for-issue2907-with-no-formatting-changes
[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 #if NET_2_0 && !MOONLIGHT
200                 XmlSchemaComplexType _schemaType;
201                 XmlQualifiedName _schemaTypeName;
202 #endif
203
204                 internal XmlSerializableMapping(XmlRootAttribute root, string elementName, string ns, TypeData typeData, string xmlType, string xmlTypeNamespace)
205                         : base(elementName, ns, typeData, xmlType, xmlTypeNamespace)
206                 {
207 #if NET_2_0 && !MOONLIGHT
208                         XmlSchemaProviderAttribute schemaProvider = (XmlSchemaProviderAttribute) Attribute.GetCustomAttribute (typeData.Type, typeof (XmlSchemaProviderAttribute));
209
210                         if (schemaProvider != null) {
211                                 string method = schemaProvider.MethodName;
212                                 MethodInfo mi = typeData.Type.GetMethod (method, BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
213                                 if (mi == null)
214                                         throw new InvalidOperationException (String.Format ("Type '{0}' must implement public static method '{1}'", typeData.Type, method));
215                                 if (!typeof (XmlQualifiedName).IsAssignableFrom (mi.ReturnType) &&
216                                     // LAMESPEC: it is undocumented. (We don't have to tell users about it in the error message.)
217                                     // Also do not add such a silly compatibility test to assert that it does not raise an error.
218                                     !typeof (XmlSchemaComplexType).IsAssignableFrom (mi.ReturnType))
219                                         throw new InvalidOperationException (String.Format ("Method '{0}' indicated by XmlSchemaProviderAttribute must have its return type as XmlQualifiedName", method));
220                                 XmlSchemaSet xs = new XmlSchemaSet ();
221                                 object retVal = mi.Invoke (null, new object [] { xs });
222                                 _schemaTypeName = XmlQualifiedName.Empty;
223                                 if (retVal == null)
224                                         return;
225
226                                 if (retVal is XmlSchemaComplexType) {
227                                         _schemaType = (XmlSchemaComplexType) retVal;
228                                         if (!_schemaType.QualifiedName.IsEmpty)
229                                                 _schemaTypeName = _schemaType.QualifiedName;
230                                         else
231                                                 _schemaTypeName = new XmlQualifiedName (xmlType, xmlTypeNamespace);
232                                 }
233                                 else if (retVal is XmlQualifiedName) {
234                                         _schemaTypeName = (XmlQualifiedName)retVal;
235                                 }
236                                 else
237                                         throw new InvalidOperationException (
238                                                 String.Format ("Method {0}.{1}() specified by XmlSchemaProviderAttribute has invalid signature: return type must be compatible with System.Xml.XmlQualifiedName.", typeData.Type.Name, method));
239
240                                 // defaultNamespace at XmlReflectionImporter takes precedence for Namespace, but not for XsdTypeNamespace.
241                                 UpdateRoot (new XmlQualifiedName (root != null ? root.ElementName : _schemaTypeName.Name, root != null ? root.Namespace : Namespace ?? _schemaTypeName.Namespace));
242                                 XmlTypeNamespace = _schemaTypeName.Namespace;
243                                 XmlType = _schemaTypeName.Name;
244
245                                 if (!_schemaTypeName.IsEmpty && xs.Count > 0) {
246                                         XmlSchema [] schemas = new XmlSchema [xs.Count];
247                                         xs.CopyTo (schemas, 0);
248                                         _schema = schemas [0];
249                                 }
250
251                                 return;
252                         }
253 #endif
254 #if NET_2_0 && !MOONLIGHT
255                         IXmlSerializable serializable = (IXmlSerializable)Activator.CreateInstance (typeData.Type, true);
256                         try {
257                                 _schema = serializable.GetSchema();
258                         } catch (Exception) {
259                                 // LAMESPEC: .NET has a bad exception catch and swallows it silently.
260                         }
261 #else
262                         IXmlSerializable serializable = (IXmlSerializable)Activator.CreateInstance (typeData.Type);
263                         _schema = serializable.GetSchema();
264 #endif
265 #if !MOONLIGHT
266                         if (_schema != null) 
267                         {
268                                 if (_schema.Id == null || _schema.Id.Length == 0) 
269                                         throw new InvalidOperationException("Schema Id is missing. The schema returned from " + typeData.Type.FullName + ".GetSchema() must have an Id.");
270                         }
271 #endif
272                 }
273
274                 internal XmlSchema Schema
275                 {
276                         get { return _schema; }
277                 }
278
279 #if NET_2_0 && !MOONLIGHT
280                 internal XmlSchemaType SchemaType {
281                         get { return _schemaType; }
282                 }
283
284                 internal XmlQualifiedName SchemaTypeName {
285                         get { return _schemaTypeName; }
286                 }
287 #endif
288         }
289  
290
291         // Mapping info for classes and structs
292
293         internal class ClassMap: ObjectMap
294         {
295                 Hashtable _elements = new Hashtable ();
296                 ArrayList _elementMembers;
297                 Hashtable _attributeMembers;
298                 XmlTypeMapMemberAttribute[] _attributeMembersArray;
299                 XmlTypeMapElementInfo[] _elementsByIndex;
300                 ArrayList _flatLists;
301                 ArrayList _allMembers = new ArrayList ();
302                 ArrayList _membersWithDefault;
303                 ArrayList _listMembers;
304                 XmlTypeMapMemberAnyElement _defaultAnyElement;
305                 XmlTypeMapMemberAnyAttribute _defaultAnyAttribute;
306                 XmlTypeMapMemberNamespaces _namespaceDeclarations;
307                 XmlTypeMapMember _xmlTextCollector;
308                 XmlTypeMapMember _returnMember;
309                 bool _ignoreMemberNamespace;
310                 bool _canBeSimpleType = true;
311                 bool? _isOrderDependentMap;
312
313                 public void AddMember (XmlTypeMapMember member)
314                 {
315                         // If GlobalIndex has not been set, set it now
316                         if (member.GlobalIndex == -1)
317                                 member.GlobalIndex = _allMembers.Count;
318                         
319                         _allMembers.Add (member);
320                         
321                         if (!(member.DefaultValue is System.DBNull) && member.DefaultValue != null) {
322                                 if (_membersWithDefault == null) _membersWithDefault = new ArrayList ();
323                                 _membersWithDefault.Add (member);
324                         }
325                         
326                         if (member.IsReturnValue)
327                                 _returnMember = member;
328                         
329                         if (member is XmlTypeMapMemberAttribute)
330                         {
331                                 XmlTypeMapMemberAttribute atm = (XmlTypeMapMemberAttribute)member;
332                                 if (_attributeMembers == null) _attributeMembers = new Hashtable();
333                                 string key = BuildKey (atm.AttributeName, atm.Namespace, -1);
334                                 if (_attributeMembers.ContainsKey (key))
335                                         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.");
336                                 member.Index = _attributeMembers.Count;
337                                 _attributeMembers.Add (key, member);
338                                 return;
339                         }
340                         else if (member is XmlTypeMapMemberFlatList)
341                         {
342                                 RegisterFlatList ((XmlTypeMapMemberFlatList)member);
343                         }
344                         else if (member is XmlTypeMapMemberAnyElement)
345                         {
346                                 XmlTypeMapMemberAnyElement mem = (XmlTypeMapMemberAnyElement) member;
347                                 if (mem.IsDefaultAny) _defaultAnyElement = mem;
348                                 if (mem.TypeData.IsListType) RegisterFlatList (mem);
349                         }
350                         else if (member is XmlTypeMapMemberAnyAttribute)
351                         {
352                                 _defaultAnyAttribute = (XmlTypeMapMemberAnyAttribute) member;
353                                 return;
354                         }
355                         else if (member is XmlTypeMapMemberNamespaces)
356                         {
357                                 _namespaceDeclarations = (XmlTypeMapMemberNamespaces) member;
358                                 return;
359                         }
360
361                         if (member is XmlTypeMapMemberElement && ((XmlTypeMapMemberElement)member).IsXmlTextCollector)
362                         {
363                                 if (_xmlTextCollector != null) throw new InvalidOperationException ("XmlTextAttribute can only be applied once in a class");
364                                 _xmlTextCollector = member;
365                         }
366
367                         if (_elementMembers == null) {
368                                 _elementMembers = new ArrayList();
369                                 _elements = new Hashtable();
370                         }
371
372                         member.Index = _elementMembers.Count;
373                         _elementMembers.Add (member);
374
375                         ICollection elemsInfo = ((XmlTypeMapMemberElement)member).ElementInfo;
376                         foreach (XmlTypeMapElementInfo elem in elemsInfo)
377                         {
378                                 string key = BuildKey (elem.ElementName, elem.Namespace, elem.ExplicitOrder);
379                                 if (_elements.ContainsKey (key)) 
380                                         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.");
381                                 _elements.Add (key, elem);
382                         }
383                         
384                         if (member.TypeData.IsListType && member.TypeData.Type != null && !member.TypeData.Type.IsArray) {
385                                 if (_listMembers == null) _listMembers = new ArrayList ();
386                                 _listMembers.Add (member);
387                         }
388                 }
389
390                 void RegisterFlatList (XmlTypeMapMemberExpandable member)
391                 {
392                         if (_flatLists == null) _flatLists = new ArrayList ();
393                         member.FlatArrayIndex = _flatLists.Count;
394                         _flatLists.Add (member);
395                 }
396
397                 public XmlTypeMapMemberAttribute GetAttribute (string name, string ns)
398                 {
399                         if (_attributeMembers == null) return null;
400                         return (XmlTypeMapMemberAttribute)_attributeMembers [BuildKey (name,ns, -1)];
401                 }
402
403                 public XmlTypeMapElementInfo GetElement (string name, string ns, int order)
404                 {
405                         if (_elements == null) return null;
406                         return (XmlTypeMapElementInfo)_elements [BuildKey (name,ns, order)];
407                 }
408
409                 public XmlTypeMapElementInfo GetElement(string name, string ns)
410                 {
411                         if (_elements == null) return null;
412
413                         foreach (XmlTypeMapElementInfo info in _elements.Values)
414                                 if (info.ElementName == name && info.Namespace == ns)
415                                         return info;
416
417                         return null;
418                 }
419                 
420                 public XmlTypeMapElementInfo GetElement (int index)
421                 {
422                         if (_elements == null) return null;
423                         
424                         if (_elementsByIndex == null)
425                         {
426                                 _elementsByIndex = new XmlTypeMapElementInfo [_elementMembers.Count];
427                                 foreach (XmlTypeMapMemberElement mem in _elementMembers)
428                                 {
429                                         if (mem.ElementInfo.Count != 1) 
430                                                 throw new InvalidOperationException ("Read by order only possible for encoded/bare format");
431                                                 
432                                         _elementsByIndex [mem.Index] = (XmlTypeMapElementInfo) mem.ElementInfo [0];
433                                 }
434                         }
435                         if (index >= _elementMembers.Count)
436                                 return null;
437                         return _elementsByIndex [index];
438                 }
439                 
440                 private string BuildKey (string name, string ns, int explicitOrder)
441                 {
442                         if (_ignoreMemberNamespace) return name;
443                         else return name + " / " + ns + (explicitOrder < 0 ? "" : "/" + explicitOrder);
444                 }
445                 
446                 public ICollection AllElementInfos
447                 {
448                         get { return _elements.Values; }
449                 }
450                 
451                 
452                 public bool IgnoreMemberNamespace
453                 {
454                         get { return _ignoreMemberNamespace; }
455                         set { _ignoreMemberNamespace = value; }
456                 }
457
458                 public bool IsOrderDependentMap {
459                         get {
460                                 if (_isOrderDependentMap == null) {
461                                         _isOrderDependentMap = false;
462                                         foreach (XmlTypeMapElementInfo ei in _elements.Values)
463                                                 if (ei.ExplicitOrder >= 0) {
464                                                         _isOrderDependentMap = true;
465                                                         break;
466                                                 }
467                                 }
468                                 return (bool) _isOrderDependentMap;
469                         }
470                 }
471
472                 public XmlTypeMapMember FindMember (string name)
473                 {
474                         for (int n=0; n<_allMembers.Count; n++)
475                                 if (((XmlTypeMapMember)_allMembers[n]).Name == name) return (XmlTypeMapMember)_allMembers[n];
476                         return null;
477                 }
478
479                 public XmlTypeMapMemberAnyElement DefaultAnyElementMember
480                 {
481                         get { return _defaultAnyElement; }
482                 }
483
484                 public XmlTypeMapMemberAnyAttribute DefaultAnyAttributeMember
485                 {
486                         get { return _defaultAnyAttribute; }
487                 }
488
489                 public XmlTypeMapMemberNamespaces NamespaceDeclarations
490                 {
491                         get { return _namespaceDeclarations; }
492                 }
493
494                 public ICollection AttributeMembers
495                 {
496                         get 
497                         {
498                                 if (_attributeMembers == null) return null;
499                                 if (_attributeMembersArray != null) return _attributeMembersArray;
500                                 
501                                 _attributeMembersArray = new XmlTypeMapMemberAttribute[_attributeMembers.Count];
502                                 foreach (XmlTypeMapMemberAttribute mem in _attributeMembers.Values)
503                                         _attributeMembersArray [mem.Index] = mem;
504                                 return _attributeMembersArray;
505                         }
506                 }
507
508                 public ICollection ElementMembers
509                 {
510                         get { return _elementMembers; }
511                 }
512
513                 public ArrayList AllMembers
514                 {
515                         get { return _allMembers; }
516                 }
517
518                 public ArrayList FlatLists
519                 {
520                         get { return _flatLists; }
521                 }
522                 
523                 public ArrayList MembersWithDefault
524                 {
525                         get { return _membersWithDefault; }
526                 }
527                 
528                 public ArrayList ListMembers
529                 {
530                         get { return _listMembers; }
531                 }
532
533                 public XmlTypeMapMember XmlTextCollector
534                 {
535                         get { return _xmlTextCollector; }
536                 }
537                 
538                 public XmlTypeMapMember ReturnMember
539                 {
540                         get { return _returnMember; }
541                 }
542
543                 public XmlQualifiedName SimpleContentBaseType
544                 {
545                         get
546                         {
547                                 if (!_canBeSimpleType || _elementMembers == null || _elementMembers.Count != 1) return null;
548                                 XmlTypeMapMemberElement member = (XmlTypeMapMemberElement) _elementMembers[0];
549                                 if (member.ElementInfo.Count != 1) return null;
550                                 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) member.ElementInfo[0];
551                                 if (!einfo.IsTextElement) return null;
552                                 if (member.TypeData.SchemaType == SchemaTypes.Primitive || member.TypeData.SchemaType == SchemaTypes.Enum)
553                                         return new XmlQualifiedName (einfo.TypeData.XmlType, einfo.DataTypeNamespace);
554                                 return null;
555                         }
556                 }
557                 
558                 public void SetCanBeSimpleType (bool can)
559                 {
560                         _canBeSimpleType = can;
561                 }
562
563                 public bool HasSimpleContent
564                 {
565                         get
566                         {
567                                 return SimpleContentBaseType != null;
568                         }
569                 }
570
571         }
572
573         // Mapping info for arrays and lists
574
575         internal class ListMap: ObjectMap
576         {
577                 XmlTypeMapElementInfoList _itemInfo;
578                 bool _gotNestedMapping;
579                 XmlTypeMapping _nestedArrayMapping;
580                 string _choiceMember;
581
582                 public bool IsMultiArray
583                 {
584                         get
585                         {
586                                 return (NestedArrayMapping != null);
587                         }
588                 }
589
590                 public string ChoiceMember
591                 {
592                         get { return _choiceMember; }
593                         set { _choiceMember = value; }
594                 }
595
596                 public XmlTypeMapping NestedArrayMapping
597                 {
598                         get
599                         {
600                                 if (_gotNestedMapping) return _nestedArrayMapping;
601                                 _gotNestedMapping = true;
602
603                                 _nestedArrayMapping = ((XmlTypeMapElementInfo)_itemInfo[0]).MappedType;
604
605                                 if (_nestedArrayMapping == null) return null;
606                                 
607                                 if (_nestedArrayMapping.TypeData.SchemaType != SchemaTypes.Array) {
608                                         _nestedArrayMapping = null; return null;
609                                 }
610
611                                 foreach (XmlTypeMapElementInfo elem in _itemInfo)
612                                         if (elem.MappedType != _nestedArrayMapping) {
613                                                 _nestedArrayMapping = null;
614                                                 return null;
615                                         }
616
617                                 return _nestedArrayMapping;
618                         }
619                 }
620
621                 public XmlTypeMapElementInfoList ItemInfo
622                 {
623
624                         get { return _itemInfo; }
625                         set { _itemInfo = value; }
626                 }
627
628                 public XmlTypeMapElementInfo FindElement (object ob, int index, object memberValue)
629                 {
630                         if (_itemInfo.Count == 1) 
631                                 return (XmlTypeMapElementInfo) _itemInfo[0];
632                         else if (_choiceMember != null && index != -1)
633                         {
634                                 Array values = (Array) XmlTypeMapMember.GetValue (ob, _choiceMember);
635                                 if (values == null || index >= values.Length)
636                                         throw new InvalidOperationException ("Invalid or missing choice enum value in member '" + _choiceMember + "'.");
637                                 object val = values.GetValue (index);
638                                 foreach (XmlTypeMapElementInfo elem in _itemInfo)
639                                         if (elem.ChoiceValue != null && elem.ChoiceValue.Equals (val))
640                                                 return elem;
641                         }
642                         else
643                         {
644                                 if (memberValue == null) return null;
645                                 Type type = memberValue.GetType();
646                                 foreach (XmlTypeMapElementInfo elem in _itemInfo)
647                                         if (elem.TypeData.Type == type) return elem;
648                         }
649                         return null;
650                 }       
651
652                 public XmlTypeMapElementInfo FindElement (string elementName, string ns)
653                 {
654                         foreach (XmlTypeMapElementInfo elem in _itemInfo)
655                                 if (elem.ElementName == elementName && elem.Namespace == ns) return elem;
656                         return null;
657                 }
658                 
659                 public XmlTypeMapElementInfo FindTextElement ()
660                 {
661                         foreach (XmlTypeMapElementInfo elem in _itemInfo)
662                                 if (elem.IsTextElement) return elem;
663                         return null;
664                 }
665                 
666                 public string GetSchemaArrayName ()
667                 {
668                         XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) _itemInfo[0];
669                         if (einfo.MappedType != null) return TypeTranslator.GetArrayName (einfo.MappedType.XmlType);
670                         else return TypeTranslator.GetArrayName (einfo.TypeData.XmlType);
671                 }
672
673                 public void GetArrayType (int itemCount, out string localName, out string ns)
674                 {
675                         string arrayDim;
676                         if (itemCount != -1) arrayDim = "[" + itemCount + "]";
677                         else arrayDim = "[]";
678
679                         XmlTypeMapElementInfo info = (XmlTypeMapElementInfo) _itemInfo[0];
680                         if (info.TypeData.SchemaType == SchemaTypes.Array)
681                         {
682                                 string nm;
683                                 ((ListMap)info.MappedType.ObjectMap).GetArrayType (-1, out nm, out ns);
684                                 localName = nm + arrayDim;
685                         }
686                         else 
687                         {
688                                 if (info.MappedType != null)
689                                 {
690                                         localName = info.MappedType.XmlType + arrayDim;
691                                         ns = info.MappedType.Namespace;
692                                 }
693                                 else 
694                                 {
695                                         localName = info.TypeData.XmlType + arrayDim;
696                                         ns = info.DataTypeNamespace;
697                                 }
698                         }
699                 }
700
701                 public override bool Equals (object other)
702                 {
703                         ListMap lmap = other as ListMap;
704                         if (lmap == null) return false;
705
706                         if (_itemInfo.Count != lmap._itemInfo.Count) return false;
707                         for (int n=0; n<_itemInfo.Count; n++)
708                                 if (!_itemInfo[n].Equals (lmap._itemInfo[n])) return false;
709                         return true;
710                 }
711
712                 public override int GetHashCode ()
713                 {
714                         return base.GetHashCode ();
715                 }
716         }
717
718         internal class EnumMap: ObjectMap
719         {
720                 readonly EnumMapMember[] _members;
721                 readonly bool _isFlags;
722                 readonly string[] _enumNames = null;
723                 readonly string[] _xmlNames = null;
724                 readonly long[] _values = null;
725
726                 public class EnumMapMember
727                 {
728                         readonly string _xmlName;
729                         readonly string _enumName;
730                         readonly long _value;
731                         string _documentation;
732
733                         public EnumMapMember (string xmlName, string enumName)
734                                 : this (xmlName, enumName, 0)
735                         {
736                         }
737
738                         public EnumMapMember (string xmlName, string enumName, long value)
739                         {
740                                 _xmlName = xmlName;
741                                 _enumName = enumName;
742                                 _value = value;
743                         }
744
745                         public string XmlName
746                         {
747                                 get { return _xmlName; }
748                         }
749
750                         public string EnumName
751                         {
752                                 get { return _enumName; }
753                         }
754
755                         public long Value
756                         {
757                                 get { return _value; }
758                         }
759
760                         public string Documentation
761                         {
762                                 get { return _documentation; }
763                                 set { _documentation = value; }
764                         }
765                 }
766
767                 public EnumMap (EnumMapMember[] members, bool isFlags)
768                 {
769                         _members = members;
770                         _isFlags = isFlags;
771
772                         _enumNames = new string[_members.Length];
773                         _xmlNames = new string[_members.Length];
774                         _values = new long[_members.Length];
775
776                         for (int i = 0; i < _members.Length; i++) {
777                                 EnumMapMember mem = _members[i];
778                                 _enumNames[i] = mem.EnumName;
779                                 _xmlNames[i] = mem.XmlName;
780                                 _values[i] = mem.Value;
781                         }
782                 }
783                 
784                 public bool IsFlags
785                 {
786                         get { return _isFlags; }
787                 }
788
789                 public EnumMapMember[] Members
790                 {
791                         get { return _members; }
792                 }
793
794                 public string[] EnumNames
795                 {
796                         get {
797                                 return _enumNames;
798                         }
799                 }
800
801                 public string[] XmlNames
802                 {
803                         get {
804                                 return _xmlNames;
805                         }
806                 }
807
808                 public long[] Values
809                 {
810                         get {
811                                 return _values;
812                         }
813                 }
814
815                 public string GetXmlName (string typeName, object enumValue)
816                 {
817                         if (enumValue is string) {
818                                 throw new InvalidCastException ();
819                         }
820
821                         long value = 0;
822
823                         try {
824                                 value = ((IConvertible) enumValue).ToInt64 (CultureInfo.CurrentCulture);
825                         } catch (FormatException) {
826                                 throw new InvalidCastException ();
827                         }
828
829                         for (int i = 0; i < Values.Length; i++) {
830                                 if (Values[i] == value)
831                                         return XmlNames[i];
832                         }
833
834                         if (IsFlags && value == 0)
835                                 return string.Empty;
836
837                         string xmlName = string.Empty;
838                         if (IsFlags) {
839 #if NET_2_0
840                                 xmlName = XmlCustomFormatter.FromEnum (value, XmlNames, Values, typeName);
841 #else
842                                 xmlName = XmlCustomFormatter.FromEnum (value, XmlNames, Values);
843 #endif
844                         }
845
846                         if (xmlName.Length == 0) {
847 #if NET_2_0
848                                 throw new InvalidOperationException (string.Format(CultureInfo.CurrentCulture,
849                                         "'{0}' is not a valid value for {1}.", value, typeName));
850 #else
851                                 return value.ToString (CultureInfo.InvariantCulture);
852 #endif
853                         }
854                         return xmlName;
855                 }
856
857                 public string GetEnumName (string typeName, string xmlName)
858                 {
859                         if (_isFlags) {
860                                 xmlName = xmlName.Trim ();
861                                 if (xmlName.Length == 0)
862                                         return "0";
863
864                                 System.Text.StringBuilder sb = new System.Text.StringBuilder ();
865                                 string[] enumNames = xmlName.Split (null);
866                                 foreach (string name in enumNames) {
867                                         if (name == string.Empty) continue;
868                                         string foundEnumValue = null;
869                                         for (int i = 0; i < XmlNames.Length; i++)
870                                                 if (XmlNames[i] == name) {
871                                                         foundEnumValue = EnumNames[i];
872                                                         break;
873                                                 }
874
875                                         if (foundEnumValue != null) {
876                                                 if (sb.Length > 0)
877                                                         sb.Append (',');
878                                                 sb.Append (foundEnumValue);
879                                         } else {
880                                                 throw new InvalidOperationException (string.Format (CultureInfo.CurrentCulture,
881                                                         "'{0}' is not a valid value for {1}.", name, typeName));
882                                         }
883                                 }
884                                 return sb.ToString ();
885                         }
886
887                         foreach (EnumMapMember mem in _members)
888                                 if (mem.XmlName == xmlName) return mem.EnumName;
889                                 
890                         return null;
891                 }
892         }
893 }