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