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