2 // XmlTypeMapping.cs:
\r
5 // John Donagher (john@webmeta.com)
\r
6 // Lluis Sanchez Gual (lluis@ximian.com)
\r
8 // (C) 2002 John Donagher
\r
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections;
\r
35 using System.Globalization;
\r
37 namespace System.Xml.Serialization
\r
39 public class XmlTypeMapping : XmlMapping
\r
41 private string elementName;
\r
43 private string xmlType;
\r
44 private string xmlTypeNamespace;
\r
46 XmlTypeMapping baseMap;
\r
47 bool multiReferenceType = false;
\r
49 string documentation;
\r
50 bool includeInSchema;
\r
51 bool isNullable = true;
\r
53 ArrayList _derivedTypes = new ArrayList();
\r
55 internal XmlTypeMapping(string elementName, string ns, TypeData typeData, string xmlType, string xmlTypeNamespace)
\r
57 this.elementName = elementName;
\r
59 this.type = typeData;
\r
60 this.xmlType = xmlType;
\r
61 this.xmlTypeNamespace = xmlTypeNamespace;
\r
64 public string ElementName
\r
66 get { return elementName; }
\r
69 public string Namespace
\r
74 public string TypeFullName
\r
76 get { return type.FullTypeName; }
\r
79 public string TypeName
\r
81 get { return type.TypeName; }
\r
84 internal TypeData TypeData
\r
86 get { return type; }
\r
89 internal string XmlType
\r
91 get { return xmlType; }
\r
94 internal string XmlTypeNamespace
\r
96 get { return xmlTypeNamespace; }
\r
99 internal ArrayList DerivedTypes
\r
101 get { return _derivedTypes; }
\r
102 set { _derivedTypes = value; }
\r
105 internal bool MultiReferenceType
\r
107 get { return multiReferenceType; }
\r
108 set { multiReferenceType = value; }
\r
111 internal XmlTypeMapping BaseMap
\r
113 get { return baseMap; }
\r
114 set { baseMap = value; }
\r
117 internal bool IsSimpleType
\r
119 get { return isSimpleType; }
\r
120 set { isSimpleType = value; }
\r
123 internal string Documentation
\r
125 set { documentation = value; }
\r
126 get { return documentation; }
\r
129 internal bool IncludeInSchema
\r
131 get { return includeInSchema; }
\r
132 set { includeInSchema = value; }
\r
135 internal bool IsNullable
\r
137 get { return isNullable; }
\r
138 set { isNullable = value; }
\r
141 internal XmlTypeMapping GetRealTypeMap (string objectFullTypeName)
\r
143 // Returns the map for a subtype of this map's type
\r
145 objectFullTypeName = objectFullTypeName.Replace ('+','.');
\r
146 if (TypeFullName == objectFullTypeName) return this;
\r
147 for (int n=0; n<_derivedTypes.Count; n++) {
\r
148 XmlTypeMapping map = (XmlTypeMapping) _derivedTypes[n];
\r
149 if (map.TypeFullName == objectFullTypeName) return map;
\r
155 internal XmlTypeMapping GetRealElementMap (string name, string ens)
\r
157 if (xmlType == name && xmlTypeNamespace == ens) return this;
\r
158 foreach (XmlTypeMapping map in _derivedTypes)
\r
159 if (map.xmlType == name && map.xmlTypeNamespace == ens) return map;
\r
164 internal void UpdateRoot (XmlQualifiedName qname)
\r
166 if (qname != null) {
\r
167 this.elementName = qname.Name;
\r
168 this.ns = qname.Namespace;
\r
173 // Mapping info for classes and structs
\r
175 internal class ClassMap: ObjectMap
\r
177 Hashtable _elements = new Hashtable ();
\r
178 ArrayList _elementMembers;
\r
179 Hashtable _attributeMembers;
\r
180 XmlTypeMapMemberAttribute[] _attributeMembersArray;
\r
181 XmlTypeMapElementInfo[] _elementsByIndex;
\r
182 ArrayList _flatLists;
\r
183 ArrayList _allMembers = new ArrayList ();
\r
184 ArrayList _membersWithDefault;
\r
185 XmlTypeMapMemberAnyElement _defaultAnyElement;
\r
186 XmlTypeMapMemberAnyAttribute _defaultAnyAttribute;
\r
187 XmlTypeMapMemberNamespaces _namespaceDeclarations;
\r
188 XmlTypeMapMember _xmlTextCollector;
\r
189 XmlTypeMapMember _returnMember;
\r
190 bool _ignoreMemberNamespace;
\r
191 bool _canBeSimpleType = true;
\r
193 public void AddMember (XmlTypeMapMember member)
\r
195 _allMembers.Add (member);
\r
197 if (!(member.DefaultValue is System.DBNull)) {
\r
198 if (_membersWithDefault == null) _membersWithDefault = new ArrayList ();
\r
199 _membersWithDefault.Add (member);
\r
202 if (member.IsReturnValue)
\r
203 _returnMember = member;
\r
205 if (member is XmlTypeMapMemberAttribute)
\r
207 XmlTypeMapMemberAttribute atm = (XmlTypeMapMemberAttribute)member;
\r
208 if (_attributeMembers == null) _attributeMembers = new Hashtable();
\r
209 string key = BuildKey (atm.AttributeName, atm.Namespace);
\r
210 if (_attributeMembers.ContainsKey (key))
\r
211 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
212 member.Index = _attributeMembers.Count;
\r
213 _attributeMembers.Add (key, member);
\r
216 else if (member is XmlTypeMapMemberFlatList)
\r
218 RegisterFlatList ((XmlTypeMapMemberFlatList)member);
\r
220 else if (member is XmlTypeMapMemberAnyElement)
\r
222 XmlTypeMapMemberAnyElement mem = (XmlTypeMapMemberAnyElement) member;
\r
223 if (mem.IsDefaultAny) _defaultAnyElement = mem;
\r
224 if (mem.TypeData.IsListType) RegisterFlatList (mem);
\r
226 else if (member is XmlTypeMapMemberAnyAttribute)
\r
228 _defaultAnyAttribute = (XmlTypeMapMemberAnyAttribute) member;
\r
231 else if (member is XmlTypeMapMemberNamespaces)
\r
233 _namespaceDeclarations = (XmlTypeMapMemberNamespaces) member;
\r
237 if (member is XmlTypeMapMemberElement && ((XmlTypeMapMemberElement)member).IsXmlTextCollector)
\r
239 if (_xmlTextCollector != null) throw new InvalidOperationException ("XmlTextAttribute can only be applied once in a class");
\r
240 _xmlTextCollector = member;
\r
243 if (_elementMembers == null) {
\r
244 _elementMembers = new ArrayList();
\r
245 _elements = new Hashtable();
\r
248 member.Index = _elementMembers.Count;
\r
249 _elementMembers.Add (member);
\r
251 ICollection elemsInfo = ((XmlTypeMapMemberElement)member).ElementInfo;
\r
252 foreach (XmlTypeMapElementInfo elem in elemsInfo)
\r
254 string key = BuildKey (elem.ElementName, elem.Namespace);
\r
255 if (_elements.ContainsKey (key))
\r
256 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
257 _elements.Add (key, elem);
\r
261 void RegisterFlatList (XmlTypeMapMemberExpandable member)
\r
263 if (_flatLists == null) _flatLists = new ArrayList ();
\r
264 member.FlatArrayIndex = _flatLists.Count;
\r
265 _flatLists.Add (member);
\r
268 public XmlTypeMapMemberAttribute GetAttribute (string name, string ns)
\r
270 if (_attributeMembers == null) return null;
\r
271 return (XmlTypeMapMemberAttribute)_attributeMembers [BuildKey(name,ns)];
\r
274 public XmlTypeMapElementInfo GetElement (string name, string ns)
\r
276 if (_elements == null) return null;
\r
277 return (XmlTypeMapElementInfo)_elements [BuildKey(name,ns)];
\r
280 public XmlTypeMapElementInfo GetElement (int index)
\r
282 if (_elements == null) return null;
\r
284 if (_elementsByIndex == null)
\r
286 _elementsByIndex = new XmlTypeMapElementInfo [_elementMembers.Count];
\r
287 foreach (XmlTypeMapMemberElement mem in _elementMembers)
\r
289 if (mem.ElementInfo.Count != 1)
\r
290 throw new InvalidOperationException ("Read by order only possible for encoded/bare format");
\r
292 _elementsByIndex [mem.Index] = (XmlTypeMapElementInfo) mem.ElementInfo [0];
\r
296 return _elementsByIndex [index];
\r
299 private string BuildKey (string name, string ns)
\r
301 if (_ignoreMemberNamespace) return name;
\r
302 else return name + " / " + ns;
\r
305 public ICollection AllElementInfos
\r
307 get { return _elements.Values; }
\r
311 public bool IgnoreMemberNamespace
\r
313 get { return _ignoreMemberNamespace; }
\r
314 set { _ignoreMemberNamespace = value; }
\r
317 public XmlTypeMapMember FindMember (string name)
\r
319 for (int n=0; n<_allMembers.Count; n++)
\r
320 if (((XmlTypeMapMember)_allMembers[n]).Name == name) return (XmlTypeMapMember)_allMembers[n];
\r
324 public XmlTypeMapMemberAnyElement DefaultAnyElementMember
\r
326 get { return _defaultAnyElement; }
\r
329 public XmlTypeMapMemberAnyAttribute DefaultAnyAttributeMember
\r
331 get { return _defaultAnyAttribute; }
\r
334 public XmlTypeMapMemberNamespaces NamespaceDeclarations
\r
336 get { return _namespaceDeclarations; }
\r
339 public ICollection AttributeMembers
\r
343 if (_attributeMembers == null) return null;
\r
344 if (_attributeMembersArray != null) return _attributeMembersArray;
\r
346 _attributeMembersArray = new XmlTypeMapMemberAttribute[_attributeMembers.Count];
\r
347 foreach (XmlTypeMapMemberAttribute mem in _attributeMembers.Values)
\r
348 _attributeMembersArray [mem.Index] = mem;
\r
349 return _attributeMembersArray;
\r
353 public ICollection ElementMembers
\r
355 get { return _elementMembers; }
\r
358 public ArrayList AllMembers
\r
360 get { return _allMembers; }
\r
363 public ArrayList FlatLists
\r
365 get { return _flatLists; }
\r
368 public ArrayList MembersWithDefault
\r
370 get { return _membersWithDefault; }
\r
373 public XmlTypeMapMember XmlTextCollector
\r
375 get { return _xmlTextCollector; }
\r
378 public XmlTypeMapMember ReturnMember
\r
380 get { return _returnMember; }
\r
383 public XmlQualifiedName SimpleContentBaseType
\r
387 if (!_canBeSimpleType || _elementMembers == null || _elementMembers.Count != 1) return null;
\r
388 XmlTypeMapMemberElement member = (XmlTypeMapMemberElement) _elementMembers[0];
\r
389 if (member.ElementInfo.Count != 1) return null;
\r
390 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) member.ElementInfo[0];
\r
391 if (!einfo.IsTextElement) return null;
\r
392 if (member.TypeData.SchemaType == SchemaTypes.Primitive || member.TypeData.SchemaType == SchemaTypes.Enum)
\r
393 return new XmlQualifiedName (einfo.TypeData.XmlType, einfo.DataTypeNamespace);
\r
398 public void SetCanBeSimpleType (bool can)
\r
400 _canBeSimpleType = can;
\r
403 public bool HasSimpleContent
\r
407 return SimpleContentBaseType != null;
\r
413 // Mapping info for arrays and lists
\r
415 internal class ListMap: ObjectMap
\r
417 XmlTypeMapElementInfoList _itemInfo;
\r
418 bool _gotNestedMapping;
\r
419 XmlTypeMapping _nestedArrayMapping;
\r
421 public bool IsMultiArray
\r
425 return (NestedArrayMapping != null);
\r
429 public XmlTypeMapping NestedArrayMapping
\r
433 if (_gotNestedMapping) return _nestedArrayMapping;
\r
434 _gotNestedMapping = true;
\r
436 _nestedArrayMapping = ((XmlTypeMapElementInfo)_itemInfo[0]).MappedType;
\r
438 if (_nestedArrayMapping == null) return null;
\r
440 if (_nestedArrayMapping.TypeData.SchemaType != SchemaTypes.Array) {
\r
441 _nestedArrayMapping = null; return null;
\r
444 foreach (XmlTypeMapElementInfo elem in _itemInfo)
\r
445 if (elem.MappedType != _nestedArrayMapping) {
\r
446 _nestedArrayMapping = null;
\r
450 return _nestedArrayMapping;
\r
454 public XmlTypeMapElementInfoList ItemInfo
\r
457 get { return _itemInfo; }
\r
458 set { _itemInfo = value; }
\r
461 public XmlTypeMapElementInfo FindElement (object memberValue)
\r
463 if (_itemInfo.Count == 1)
\r
464 return (XmlTypeMapElementInfo) _itemInfo[0];
\r
467 if (memberValue == null) return null;
\r
468 Type type = memberValue.GetType();
\r
469 foreach (XmlTypeMapElementInfo elem in _itemInfo)
\r
470 if (elem.TypeData.Type == type) return elem;
\r
475 public XmlTypeMapElementInfo FindElement (string elementName, string ns)
\r
477 foreach (XmlTypeMapElementInfo elem in _itemInfo)
\r
478 if (elem.ElementName == elementName && elem.Namespace == ns) return elem;
\r
482 public XmlTypeMapElementInfo FindTextElement ()
\r
484 foreach (XmlTypeMapElementInfo elem in _itemInfo)
\r
485 if (elem.IsTextElement) return elem;
\r
489 public string GetSchemaArrayName ()
\r
491 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) _itemInfo[0];
\r
492 if (einfo.MappedType != null) return TypeTranslator.GetArrayName (einfo.MappedType.XmlType);
\r
493 else return TypeTranslator.GetArrayName (einfo.TypeData.XmlType);
\r
496 public void GetArrayType (int itemCount, out string localName, out string ns)
\r
499 if (itemCount != -1) arrayDim = "[" + itemCount + "]";
\r
500 else arrayDim = "[]";
\r
502 XmlTypeMapElementInfo info = (XmlTypeMapElementInfo) _itemInfo[0];
\r
503 if (info.TypeData.SchemaType == SchemaTypes.Array)
\r
506 ((ListMap)info.MappedType.ObjectMap).GetArrayType (-1, out nm, out ns);
\r
507 localName = nm + arrayDim;
\r
511 if (info.MappedType != null)
\r
513 localName = info.MappedType.XmlType + arrayDim;
\r
514 ns = info.MappedType.Namespace;
\r
518 localName = info.TypeData.XmlType + arrayDim;
\r
519 ns = info.DataTypeNamespace;
\r
524 public override bool Equals (object other)
\r
526 ListMap lmap = other as ListMap;
\r
527 if (lmap == null) return false;
\r
529 if (_itemInfo.Count != lmap._itemInfo.Count) return false;
\r
530 for (int n=0; n<_itemInfo.Count; n++)
\r
531 if (!_itemInfo[n].Equals (lmap._itemInfo[n])) return false;
\r
535 public override int GetHashCode ()
\r
537 return base.GetHashCode ();
\r
541 internal class EnumMap: ObjectMap
\r
543 EnumMapMember[] _members;
\r
546 public class EnumMapMember
\r
550 string _documentation;
\r
552 public EnumMapMember (string xmlName, string enumName)
\r
554 _xmlName = xmlName;
\r
555 _enumName = enumName;
\r
558 public string XmlName
\r
560 get { return _xmlName; }
\r
563 public string EnumName
\r
565 get { return _enumName; }
\r
568 public string Documentation
\r
570 get { return _documentation; }
\r
571 set { _documentation = value; }
\r
575 public EnumMap (EnumMapMember[] members, bool isFlags)
\r
577 _members = members;
\r
578 _isFlags = isFlags;
\r
581 public bool IsFlags
\r
583 get { return _isFlags; }
\r
586 public EnumMapMember[] Members
\r
588 get { return _members; }
\r
591 public string GetXmlName (object enumValue)
\r
593 string enumName = enumValue.ToString();
\r
595 if (_isFlags && enumName.IndexOf (',') != -1)
\r
597 System.Text.StringBuilder sb = new System.Text.StringBuilder ();
\r
598 string[] enumNames = enumValue.ToString().Split (',');
\r
599 foreach (string name in enumNames)
\r
601 string tname = name.Trim();
\r
602 foreach (EnumMapMember mem in _members)
\r
603 if (mem.EnumName == tname) {
\r
604 sb.Append (mem.XmlName).Append (' ');
\r
608 sb.Remove (sb.Length-1, 1);
\r
609 return sb.ToString ();
\r
612 foreach (EnumMapMember mem in _members)
\r
613 if (mem.EnumName == enumName) return mem.XmlName;
\r
615 return Convert.ToInt64(enumValue).ToString(CultureInfo.InvariantCulture);
\r
618 public string GetEnumName (string xmlName)
\r
620 if (_isFlags && xmlName.Length == 0)
\r
623 if (_isFlags && xmlName.Trim().IndexOf (' ') != -1)
\r
625 System.Text.StringBuilder sb = new System.Text.StringBuilder ();
\r
626 string[] enumNames = xmlName.ToString().Split (' ');
\r
627 foreach (string name in enumNames)
\r
629 if (name == string.Empty) continue;
\r
630 string foundEnumValue = null;
\r
631 foreach (EnumMapMember mem in _members)
\r
632 if (mem.XmlName == name) { foundEnumValue = mem.EnumName; break; }
\r
634 if (foundEnumValue != null) sb.Append (foundEnumValue).Append (',');
\r
637 sb.Remove (sb.Length-1, 1);
\r
638 return sb.ToString ();
\r
641 foreach (EnumMapMember mem in _members)
\r
642 if (mem.XmlName == xmlName) return mem.EnumName;
\r