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