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