* MapCodeGenerator.cs, SoapCodeExporter.cs: Generate the same XmlInclude
[mono.git] / mcs / class / System.XML / System.Xml.Serialization / XmlTypeMapping.cs
1 //\r
2 // XmlTypeMapping.cs: \r
3 //\r
4 // Author:\r
5 //   John Donagher (john@webmeta.com)\r
6 //   Lluis Sanchez Gual (lluis@ximian.com)\r
7 //\r
8 // (C) 2002 John Donagher\r
9 //\r
10 \r
11 using System.Xml;\r
12 using System;\r
13 using System.Collections;\r
14 \r
15 namespace System.Xml.Serialization\r
16 {\r
17         public class XmlTypeMapping : XmlMapping\r
18         {\r
19                 private string elementName;\r
20                 private string ns;\r
21                 private string xmlType;\r
22                 private string xmlTypeNamespace;\r
23                 TypeData type;\r
24                 XmlTypeMapping baseMap;\r
25                 bool multiReferenceType = false;\r
26                 bool isSimpleType;\r
27                 string documentation;\r
28                 bool includeInSchema;\r
29                 bool isNullable = true;\r
30 \r
31                 ArrayList _derivedTypes = new ArrayList();\r
32 \r
33                 internal XmlTypeMapping(string elementName, string ns, TypeData typeData, string xmlType, string xmlTypeNamespace)\r
34                 {\r
35                         this.elementName = elementName;\r
36                         this.ns = ns;\r
37                         this.type = typeData;\r
38                         this.xmlType = xmlType;\r
39                         this.xmlTypeNamespace = xmlTypeNamespace;\r
40                 }\r
41 \r
42                 public string ElementName\r
43                 {\r
44                         get { return elementName; }\r
45                 }\r
46 \r
47                 public string Namespace\r
48                 {\r
49                         get { return ns; }\r
50                 }\r
51 \r
52                 public string TypeFullName\r
53                 {\r
54                         get { return type.FullTypeName; }\r
55                 }\r
56 \r
57                 public string TypeName\r
58                 {\r
59                         get { return type.TypeName; }\r
60                 }\r
61 \r
62                 internal TypeData TypeData\r
63                 {\r
64                         get { return type; }\r
65                 }\r
66 \r
67                 internal string XmlType\r
68                 {\r
69                         get { return xmlType; }\r
70                 }\r
71 \r
72                 internal string XmlTypeNamespace\r
73                 {\r
74                         get { return xmlTypeNamespace; }\r
75                 }\r
76 \r
77                 internal ArrayList DerivedTypes\r
78                 {\r
79                         get { return _derivedTypes; }\r
80                         set { _derivedTypes = value; }\r
81                 }\r
82 \r
83                 internal bool MultiReferenceType\r
84                 {\r
85                         get { return multiReferenceType; }\r
86                         set { multiReferenceType = value; }\r
87                 }\r
88 \r
89                 internal XmlTypeMapping BaseMap\r
90                 {\r
91                         get { return baseMap; }\r
92                         set { baseMap = value; }\r
93                 }\r
94 \r
95                 internal bool IsSimpleType\r
96                 {\r
97                         get { return isSimpleType; }\r
98                         set { isSimpleType = value; }\r
99                 }\r
100 \r
101                 internal string Documentation\r
102                 {\r
103                         set { documentation = value; }\r
104                         get { return documentation; }\r
105                 }\r
106 \r
107                 internal bool IncludeInSchema\r
108                 {\r
109                         get { return includeInSchema; }\r
110                         set { includeInSchema = value; }\r
111                 }\r
112                 \r
113                 internal bool IsNullable\r
114                 {\r
115                         get { return isNullable; }\r
116                         set { isNullable = value; }\r
117                 }\r
118 \r
119                 internal XmlTypeMapping GetRealTypeMap (string objectFullTypeName)\r
120                 {\r
121                         // Returns the map for a subtype of this map's type\r
122 \r
123                         objectFullTypeName = objectFullTypeName.Replace ('+','.');\r
124                         if (TypeFullName == objectFullTypeName) return this;\r
125                         for (int n=0; n<_derivedTypes.Count; n++) {\r
126                                 XmlTypeMapping map = (XmlTypeMapping) _derivedTypes[n];\r
127                                 if (map.TypeFullName == objectFullTypeName) return map;\r
128                         }\r
129                         \r
130                         return null;\r
131                 }\r
132 \r
133                 internal XmlTypeMapping GetRealElementMap (string name, string ens)\r
134                 {\r
135                         if (xmlType == name && xmlTypeNamespace == ens) return this;\r
136                         foreach (XmlTypeMapping map in _derivedTypes)\r
137                                 if (map.xmlType == name && map.xmlTypeNamespace == ens) return map;\r
138                         \r
139                         return null;\r
140                 }\r
141                 \r
142                 internal void UpdateRoot (XmlQualifiedName qname)\r
143                 {\r
144                         if (qname != null) {\r
145                                 this.elementName = qname.Name;\r
146                                 this.ns = qname.Namespace;\r
147                         }\r
148                 }\r
149         }\r
150 \r
151         // Mapping info for classes and structs\r
152 \r
153         internal class ClassMap: ObjectMap\r
154         {\r
155                 Hashtable _elements = new Hashtable ();\r
156                 ArrayList _elementMembers;\r
157                 Hashtable _attributeMembers;\r
158                 XmlTypeMapMemberAttribute[] _attributeMembersArray;\r
159                 XmlTypeMapElementInfo[] _elementsByIndex;\r
160                 ArrayList _flatLists;\r
161                 ArrayList _allMembers = new ArrayList ();\r
162                 ArrayList _membersWithDefault;\r
163                 XmlTypeMapMemberAnyElement _defaultAnyElement;\r
164                 XmlTypeMapMemberAnyAttribute _defaultAnyAttribute;\r
165                 XmlTypeMapMemberNamespaces _namespaceDeclarations;\r
166                 XmlTypeMapMember _xmlTextCollector;\r
167                 XmlTypeMapMember _returnMember;\r
168                 bool _ignoreMemberNamespace;\r
169                 bool _canBeSimpleType = true;\r
170 \r
171                 public void AddMember (XmlTypeMapMember member)\r
172                 {\r
173                         _allMembers.Add (member);\r
174                         \r
175                         if (!(member.DefaultValue is System.DBNull)) {\r
176                                 if (_membersWithDefault == null) _membersWithDefault = new ArrayList ();\r
177                                 _membersWithDefault.Add (member);\r
178                         }\r
179                         \r
180                         if (member.IsReturnValue)\r
181                                 _returnMember = member;\r
182                         \r
183                         if (member is XmlTypeMapMemberAttribute)\r
184                         {\r
185                                 XmlTypeMapMemberAttribute atm = (XmlTypeMapMemberAttribute)member;\r
186                                 if (_attributeMembers == null) _attributeMembers = new Hashtable();\r
187                                 string key = BuildKey (atm.AttributeName, atm.Namespace);\r
188                                 if (_attributeMembers.ContainsKey (key))\r
189                                         throw new InvalidOperationException ("The XML attribute named '" + atm.AttributeName + "' from namespace '" + atm.Namespace + "' already present in the current scope. Use XML attributes to specify another XML name or namespace for the attribute.");\r
190                                 member.Index = _attributeMembers.Count;\r
191                                 _attributeMembers.Add (key, member);\r
192                                 return;\r
193                         }\r
194                         else if (member is XmlTypeMapMemberFlatList)\r
195                         {\r
196                                 RegisterFlatList ((XmlTypeMapMemberFlatList)member);\r
197                         }\r
198                         else if (member is XmlTypeMapMemberAnyElement)\r
199                         {\r
200                                 XmlTypeMapMemberAnyElement mem = (XmlTypeMapMemberAnyElement) member;\r
201                                 if (mem.IsDefaultAny) _defaultAnyElement = mem;\r
202                                 if (mem.TypeData.IsListType) RegisterFlatList (mem);\r
203                         }\r
204                         else if (member is XmlTypeMapMemberAnyAttribute)\r
205                         {\r
206                                 _defaultAnyAttribute = (XmlTypeMapMemberAnyAttribute) member;\r
207                                 return;\r
208                         }\r
209                         else if (member is XmlTypeMapMemberNamespaces)\r
210                         {\r
211                                 _namespaceDeclarations = (XmlTypeMapMemberNamespaces) member;\r
212                                 return;\r
213                         }\r
214 \r
215                         if (member is XmlTypeMapMemberElement && ((XmlTypeMapMemberElement)member).IsXmlTextCollector)\r
216                         {\r
217                                 if (_xmlTextCollector != null) throw new InvalidOperationException ("XmlTextAttribute can only be applied once in a class");\r
218                                 _xmlTextCollector = member;\r
219                         }\r
220 \r
221                         if (_elementMembers == null) {\r
222                                 _elementMembers = new ArrayList();\r
223                                 _elements = new Hashtable();\r
224                         }\r
225 \r
226                         member.Index = _elementMembers.Count;\r
227                         _elementMembers.Add (member);\r
228 \r
229                         ICollection elemsInfo = ((XmlTypeMapMemberElement)member).ElementInfo;\r
230                         foreach (XmlTypeMapElementInfo elem in elemsInfo)\r
231                         {\r
232                                 string key = BuildKey (elem.ElementName, elem.Namespace);\r
233                                 if (_elements.ContainsKey (key)) \r
234                                         throw new InvalidOperationException ("The XML element named '" + elem.ElementName + "' from namespace '" + elem.Namespace + "' already present in the current scope. Use XML attributes to specify another XML name or namespace for the element.");\r
235                                 _elements.Add (key, elem);\r
236                         }\r
237                 }\r
238 \r
239                 void RegisterFlatList (XmlTypeMapMemberExpandable member)\r
240                 {\r
241                         if (_flatLists == null) _flatLists = new ArrayList ();\r
242                         member.FlatArrayIndex = _flatLists.Count;\r
243                         _flatLists.Add (member);\r
244                 }\r
245 \r
246                 public XmlTypeMapMemberAttribute GetAttribute (string name, string ns)\r
247                 {\r
248                         if (_attributeMembers == null) return null;\r
249                         return (XmlTypeMapMemberAttribute)_attributeMembers [BuildKey(name,ns)];\r
250                 }\r
251 \r
252                 public XmlTypeMapElementInfo GetElement (string name, string ns)\r
253                 {\r
254                         if (_elements == null) return null;\r
255                         return (XmlTypeMapElementInfo)_elements [BuildKey(name,ns)];\r
256                 }\r
257                 \r
258                 public XmlTypeMapElementInfo GetElement (int index)\r
259                 {\r
260                         if (_elements == null) return null;\r
261                         \r
262                         if (_elementsByIndex == null)\r
263                         {\r
264                                 _elementsByIndex = new XmlTypeMapElementInfo [_elementMembers.Count];\r
265                                 foreach (XmlTypeMapMemberElement mem in _elementMembers)\r
266                                 {\r
267                                         if (mem.ElementInfo.Count != 1) \r
268                                                 throw new InvalidOperationException ("Read by order only possible for encoded/bare format");\r
269                                                 \r
270                                         _elementsByIndex [mem.Index] = (XmlTypeMapElementInfo) mem.ElementInfo [0];\r
271                                 }\r
272                         }\r
273                         \r
274                         return _elementsByIndex [index];\r
275                 }\r
276                 \r
277                 private string BuildKey (string name, string ns)\r
278                 {\r
279                         if (_ignoreMemberNamespace) return name;\r
280                         else return name + " / " + ns;\r
281                 }\r
282                 \r
283                 public ICollection AllElementInfos\r
284                 {\r
285                         get { return _elements.Values; }\r
286                 }\r
287                 \r
288                 \r
289                 public bool IgnoreMemberNamespace\r
290                 {\r
291                         get { return _ignoreMemberNamespace; }\r
292                         set { _ignoreMemberNamespace = value; }\r
293                 }\r
294 \r
295                 public XmlTypeMapMember FindMember (string name)\r
296                 {\r
297                         for (int n=0; n<_allMembers.Count; n++)\r
298                                 if (((XmlTypeMapMember)_allMembers[n]).Name == name) return (XmlTypeMapMember)_allMembers[n];\r
299                         return null;\r
300                 }\r
301 \r
302                 public XmlTypeMapMemberAnyElement DefaultAnyElementMember\r
303                 {\r
304                         get { return _defaultAnyElement; }\r
305                 }\r
306 \r
307                 public XmlTypeMapMemberAnyAttribute DefaultAnyAttributeMember\r
308                 {\r
309                         get { return _defaultAnyAttribute; }\r
310                 }\r
311 \r
312                 public XmlTypeMapMemberNamespaces NamespaceDeclarations\r
313                 {\r
314                         get { return _namespaceDeclarations; }\r
315                 }\r
316 \r
317                 public ICollection AttributeMembers\r
318                 {\r
319                         get \r
320                         {\r
321                                 if (_attributeMembers == null) return null;\r
322                                 if (_attributeMembersArray != null) return _attributeMembersArray;\r
323                                 \r
324                                 _attributeMembersArray = new XmlTypeMapMemberAttribute[_attributeMembers.Count];\r
325                                 foreach (XmlTypeMapMemberAttribute mem in _attributeMembers.Values)\r
326                                         _attributeMembersArray [mem.Index] = mem;\r
327                                 return _attributeMembersArray;\r
328                         }\r
329                 }\r
330 \r
331                 public ICollection ElementMembers\r
332                 {\r
333                         get { return _elementMembers; }\r
334                 }\r
335 \r
336                 public ArrayList AllMembers\r
337                 {\r
338                         get { return _allMembers; }\r
339                 }\r
340 \r
341                 public ArrayList FlatLists\r
342                 {\r
343                         get { return _flatLists; }\r
344                 }\r
345                 \r
346                 public ArrayList MembersWithDefault\r
347                 {\r
348                         get { return _membersWithDefault; }\r
349                 }\r
350 \r
351                 public XmlTypeMapMember XmlTextCollector\r
352                 {\r
353                         get { return _xmlTextCollector; }\r
354                 }\r
355                 \r
356                 public XmlTypeMapMember ReturnMember\r
357                 {\r
358                         get { return _returnMember; }\r
359                 }\r
360 \r
361                 public XmlQualifiedName SimpleContentBaseType\r
362                 {\r
363                         get\r
364                         {\r
365                                 if (!_canBeSimpleType || _elementMembers == null || _elementMembers.Count != 1) return null;\r
366                                 XmlTypeMapMemberElement member = (XmlTypeMapMemberElement) _elementMembers[0];\r
367                                 if (member.ElementInfo.Count != 1) return null;\r
368                                 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) member.ElementInfo[0];\r
369                                 if (!einfo.IsTextElement) return null;\r
370                                 if (member.TypeData.SchemaType == SchemaTypes.Primitive || member.TypeData.SchemaType == SchemaTypes.Enum)\r
371                                         return new XmlQualifiedName (einfo.TypeData.XmlType, einfo.DataTypeNamespace);\r
372                                 return null;\r
373                         }\r
374                 }\r
375                 \r
376                 public void SetCanBeSimpleType (bool can)\r
377                 {\r
378                         _canBeSimpleType = can;\r
379                 }\r
380 \r
381                 public bool HasSimpleContent\r
382                 {\r
383                         get\r
384                         {\r
385                                 return SimpleContentBaseType != null;\r
386                         }\r
387                 }\r
388 \r
389         }\r
390 \r
391         // Mapping info for arrays and lists\r
392 \r
393         internal class ListMap: ObjectMap\r
394         {\r
395                 XmlTypeMapElementInfoList _itemInfo;\r
396                 bool _gotNestedMapping;\r
397                 XmlTypeMapping _nestedArrayMapping;\r
398 \r
399                 public bool IsMultiArray\r
400                 {\r
401                         get\r
402                         {\r
403                                 return (NestedArrayMapping != null);\r
404                         }\r
405                 }\r
406 \r
407                 public XmlTypeMapping NestedArrayMapping\r
408                 {\r
409                         get\r
410                         {\r
411                                 if (_gotNestedMapping) return _nestedArrayMapping;\r
412                                 _gotNestedMapping = true;\r
413 \r
414                                 _nestedArrayMapping = ((XmlTypeMapElementInfo)_itemInfo[0]).MappedType;\r
415 \r
416                                 if (_nestedArrayMapping == null) return null;\r
417                                 \r
418                                 if (_nestedArrayMapping.TypeData.SchemaType != SchemaTypes.Array) {\r
419                                         _nestedArrayMapping = null; return null;\r
420                                 }\r
421 \r
422                                 foreach (XmlTypeMapElementInfo elem in _itemInfo)\r
423                                         if (elem.MappedType != _nestedArrayMapping) {\r
424                                                 _nestedArrayMapping = null;\r
425                                                 return null;\r
426                                         }\r
427 \r
428                                 return _nestedArrayMapping;\r
429                         }\r
430                 }\r
431 \r
432                 public XmlTypeMapElementInfoList ItemInfo\r
433                 {\r
434 \r
435                         get { return _itemInfo; }\r
436                         set { _itemInfo = value; }\r
437                 }\r
438 \r
439                 public XmlTypeMapElementInfo FindElement (object memberValue)\r
440                 {\r
441                         if (_itemInfo.Count == 1) \r
442                                 return (XmlTypeMapElementInfo) _itemInfo[0];\r
443                         else\r
444                         {\r
445                                 if (memberValue == null) return null;\r
446                                 Type type = memberValue.GetType();\r
447                                 foreach (XmlTypeMapElementInfo elem in _itemInfo)\r
448                                         if (elem.TypeData.Type == type) return elem;\r
449                         }\r
450                         return null;\r
451                 }       \r
452 \r
453                 public XmlTypeMapElementInfo FindElement (string elementName, string ns)\r
454                 {\r
455                         foreach (XmlTypeMapElementInfo elem in _itemInfo)\r
456                                 if (elem.ElementName == elementName && elem.Namespace == ns) return elem;\r
457                         return null;\r
458                 }\r
459                 \r
460                 public XmlTypeMapElementInfo FindTextElement ()\r
461                 {\r
462                         foreach (XmlTypeMapElementInfo elem in _itemInfo)\r
463                                 if (elem.IsTextElement) return elem;\r
464                         return null;\r
465                 }\r
466                 \r
467                 public string GetSchemaArrayName ()\r
468                 {\r
469                         XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) _itemInfo[0];\r
470                         if (einfo.MappedType != null) return TypeTranslator.GetArrayName (einfo.MappedType.XmlType);\r
471                         else return TypeTranslator.GetArrayName (einfo.TypeData.XmlType);\r
472                 }\r
473 \r
474                 public void GetArrayType (int itemCount, out string localName, out string ns)\r
475                 {\r
476                         string arrayDim;\r
477                         if (itemCount != -1) arrayDim = "[" + itemCount + "]";\r
478                         else arrayDim = "[]";\r
479 \r
480                         XmlTypeMapElementInfo info = (XmlTypeMapElementInfo) _itemInfo[0];\r
481                         if (info.TypeData.SchemaType == SchemaTypes.Array)\r
482                         {\r
483                                 string nm;\r
484                                 ((ListMap)info.MappedType.ObjectMap).GetArrayType (-1, out nm, out ns);\r
485                                 localName = nm + arrayDim;\r
486                         }\r
487                         else \r
488                         {\r
489                                 if (info.MappedType != null)\r
490                                 {\r
491                                         localName = info.MappedType.XmlType + arrayDim;\r
492                                         ns = info.MappedType.Namespace;\r
493                                 }\r
494                                 else \r
495                                 {\r
496                                         localName = info.TypeData.XmlType + arrayDim;\r
497                                         ns = info.DataTypeNamespace;\r
498                                 }\r
499                         }\r
500                 }\r
501 \r
502                 public override bool Equals (object other)\r
503                 {\r
504                         ListMap lmap = other as ListMap;\r
505                         if (lmap == null) return false;\r
506 \r
507                         if (_itemInfo.Count != lmap._itemInfo.Count) return false;\r
508                         for (int n=0; n<_itemInfo.Count; n++)\r
509                                 if (!_itemInfo[n].Equals (lmap._itemInfo[n])) return false;\r
510                         return true;\r
511                 }\r
512 \r
513                 public override int GetHashCode ()\r
514                 {\r
515                         return base.GetHashCode ();\r
516                 }\r
517         }\r
518 \r
519         internal class EnumMap: ObjectMap\r
520         {\r
521                 EnumMapMember[] _members;\r
522                 bool _isFlags;\r
523 \r
524                 public class EnumMapMember\r
525                 {\r
526                         string _xmlName;\r
527                         string _enumName;\r
528                         string _documentation;\r
529 \r
530                         public EnumMapMember (string xmlName, string enumName)\r
531                         {\r
532                                 _xmlName = xmlName;\r
533                                 _enumName = enumName;\r
534                         }\r
535 \r
536                         public string XmlName\r
537                         {\r
538                                 get { return _xmlName; }\r
539                         }\r
540 \r
541                         public string EnumName\r
542                         {\r
543                                 get { return _enumName; }\r
544                         }\r
545 \r
546                         public string Documentation\r
547                         {\r
548                                 get { return _documentation; }\r
549                                 set { _documentation = value; }\r
550                         }\r
551                 }\r
552 \r
553                 public EnumMap (EnumMapMember[] members, bool isFlags)\r
554                 {\r
555                         _members = members;\r
556                         _isFlags = isFlags;\r
557                 }\r
558                 \r
559                 public bool IsFlags\r
560                 {\r
561                         get { return _isFlags; }\r
562                 }\r
563 \r
564                 public EnumMapMember[] Members\r
565                 {\r
566                         get { return _members; }\r
567                 }\r
568 \r
569                 public string GetXmlName (object enumValue)\r
570                 {\r
571                         string enumName = enumValue.ToString();\r
572 \r
573                         if (_isFlags && enumName.IndexOf (',') != -1)\r
574                         {\r
575                                 System.Text.StringBuilder sb = new System.Text.StringBuilder ();\r
576                                 string[] enumNames = enumValue.ToString().Split (',');\r
577                                 foreach (string name in enumNames)\r
578                                 {\r
579                                         string tname = name.Trim();\r
580                                         foreach (EnumMapMember mem in _members)\r
581                                                 if (mem.EnumName == tname) {\r
582                                                         sb.Append (mem.XmlName).Append (' ');\r
583                                                         break;\r
584                                                 }\r
585                                 }\r
586                                 sb.Remove (sb.Length-1, 1);\r
587                                 return sb.ToString ();\r
588                         }\r
589 \r
590                         foreach (EnumMapMember mem in _members)\r
591                                 if (mem.EnumName == enumName) return mem.XmlName;\r
592                         \r
593                         return Convert.ToInt64(enumValue).ToString();\r
594                 }\r
595 \r
596                 public string GetEnumName (string xmlName)\r
597                 {\r
598                         if (_isFlags && xmlName.Length == 0) \r
599                                 return "0";\r
600                         \r
601                         if (_isFlags && xmlName.Trim().IndexOf (' ') != -1)\r
602                         {\r
603                                 System.Text.StringBuilder sb = new System.Text.StringBuilder ();\r
604                                 string[] enumNames = xmlName.ToString().Split (' ');\r
605                                 foreach (string name in enumNames)\r
606                                 {\r
607                                         if (name == string.Empty) continue;\r
608                                         string foundEnumValue = null;\r
609                                         foreach (EnumMapMember mem in _members)\r
610                                                 if (mem.XmlName == name) { foundEnumValue = mem.EnumName; break; }\r
611 \r
612                                         if (foundEnumValue != null) sb.Append (foundEnumValue).Append (','); \r
613                                         else return null;\r
614                                 }\r
615                                 sb.Remove (sb.Length-1, 1);\r
616                                 return sb.ToString ();\r
617                         }\r
618 \r
619                         foreach (EnumMapMember mem in _members)\r
620                                 if (mem.XmlName == xmlName) return mem.EnumName;\r
621                                 \r
622                         return null;\r
623                 }\r
624         }\r
625 }