5 // John Donagher (john@webmeta.com)
6 // Lluis Sanchez Gual (lluis@ximian.com)
8 // (C) 2002 John Donagher
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;
35 using System.Globalization;
36 using System.Xml.Schema;
38 namespace System.Xml.Serialization
40 public class XmlTypeMapping : XmlMapping
42 private string xmlType;
43 private string xmlTypeNamespace;
45 XmlTypeMapping baseMap;
46 bool multiReferenceType = false;
50 bool isNullable = true;
52 ArrayList _derivedTypes = new ArrayList();
54 internal XmlTypeMapping(string elementName, string ns, TypeData typeData, string xmlType, string xmlTypeNamespace)
55 : base (elementName, ns)
58 this.xmlType = xmlType;
59 this.xmlTypeNamespace = xmlTypeNamespace;
63 public string ElementName
65 get { return _elementName; }
68 public string Namespace
70 get { return _namespace; }
74 public string TypeFullName
76 get { return type.FullTypeName; }
79 public string TypeName
81 get { return type.TypeName; }
85 public string XsdTypeName
87 get { return XmlType; }
90 public string XsdTypeNamespace
92 get { return XmlTypeNamespace; }
96 internal TypeData TypeData
101 internal string XmlType
103 get { return xmlType; }
106 internal string XmlTypeNamespace
108 get { return xmlTypeNamespace; }
111 internal ArrayList DerivedTypes
113 get { return _derivedTypes; }
114 set { _derivedTypes = value; }
117 internal bool MultiReferenceType
119 get { return multiReferenceType; }
120 set { multiReferenceType = value; }
123 internal XmlTypeMapping BaseMap
125 get { return baseMap; }
126 set { baseMap = value; }
129 internal bool IsSimpleType
131 get { return isSimpleType; }
132 set { isSimpleType = value; }
135 internal string Documentation
137 set { documentation = value; }
138 get { return documentation; }
141 internal bool IncludeInSchema
143 get { return includeInSchema; }
144 set { includeInSchema = value; }
147 internal bool IsNullable
149 get { return isNullable; }
150 set { isNullable = value; }
153 internal XmlTypeMapping GetRealTypeMap (string objectFullTypeName)
155 if (TypeData.SchemaType == SchemaTypes.Enum)
158 // Returns the map for a subtype of this map's type
159 objectFullTypeName = objectFullTypeName.Replace ('+','.');
160 if (TypeFullName == objectFullTypeName) return this;
161 for (int n=0; n<_derivedTypes.Count; n++) {
162 XmlTypeMapping map = (XmlTypeMapping) _derivedTypes[n];
163 if (map.TypeFullName == objectFullTypeName) return map;
169 internal XmlTypeMapping GetRealElementMap (string name, string ens)
171 if (xmlType == name && xmlTypeNamespace == ens) return this;
172 foreach (XmlTypeMapping map in _derivedTypes)
173 if (map.xmlType == name && map.xmlTypeNamespace == ens) return map;
178 internal void UpdateRoot (XmlQualifiedName qname)
181 this._elementName = qname.Name;
182 this._namespace = qname.Namespace;
188 // Mapping info for XmlSerializable
189 internal class XmlSerializableMapping : XmlTypeMapping
193 internal XmlSerializableMapping(string elementName, string ns, TypeData typeData, string xmlType, string xmlTypeNamespace)
194 : base(elementName, ns, typeData, xmlType, xmlTypeNamespace)
196 IXmlSerializable serializable = (IXmlSerializable)Activator.CreateInstance(typeData.Type);
197 _schema = serializable.GetSchema();
200 if (_schema.Id == null || _schema.Id.Length == 0)
201 throw new InvalidOperationException("Schema Id is missing. The schema returned from " + typeData.Type.FullName + ".GetSchema() must have an Id.");
205 internal XmlSchema Schema
207 get { return _schema; }
212 // Mapping info for classes and structs
214 internal class ClassMap: ObjectMap
216 Hashtable _elements = new Hashtable ();
217 ArrayList _elementMembers;
218 Hashtable _attributeMembers;
219 XmlTypeMapMemberAttribute[] _attributeMembersArray;
220 XmlTypeMapElementInfo[] _elementsByIndex;
221 ArrayList _flatLists;
222 ArrayList _allMembers = new ArrayList ();
223 ArrayList _membersWithDefault;
224 ArrayList _listMembers;
225 XmlTypeMapMemberAnyElement _defaultAnyElement;
226 XmlTypeMapMemberAnyAttribute _defaultAnyAttribute;
227 XmlTypeMapMemberNamespaces _namespaceDeclarations;
228 XmlTypeMapMember _xmlTextCollector;
229 XmlTypeMapMember _returnMember;
230 bool _ignoreMemberNamespace;
231 bool _canBeSimpleType = true;
233 public void AddMember (XmlTypeMapMember member)
235 member.GlobalIndex = _allMembers.Count;
236 _allMembers.Add (member);
238 if (!(member.DefaultValue is System.DBNull) && member.DefaultValue != null) {
239 if (_membersWithDefault == null) _membersWithDefault = new ArrayList ();
240 _membersWithDefault.Add (member);
243 if (member.IsReturnValue)
244 _returnMember = member;
246 if (member is XmlTypeMapMemberAttribute)
248 XmlTypeMapMemberAttribute atm = (XmlTypeMapMemberAttribute)member;
249 if (_attributeMembers == null) _attributeMembers = new Hashtable();
250 string key = BuildKey (atm.AttributeName, atm.Namespace);
251 if (_attributeMembers.ContainsKey (key))
252 throw new InvalidOperationException ("The XML attribute named '" + atm.AttributeName + "' from namespace '" + atm.Namespace + "' is already present in the current scope. Use XML attributes to specify another XML name or namespace for the attribute.");
253 member.Index = _attributeMembers.Count;
254 _attributeMembers.Add (key, member);
257 else if (member is XmlTypeMapMemberFlatList)
259 RegisterFlatList ((XmlTypeMapMemberFlatList)member);
261 else if (member is XmlTypeMapMemberAnyElement)
263 XmlTypeMapMemberAnyElement mem = (XmlTypeMapMemberAnyElement) member;
264 if (mem.IsDefaultAny) _defaultAnyElement = mem;
265 if (mem.TypeData.IsListType) RegisterFlatList (mem);
267 else if (member is XmlTypeMapMemberAnyAttribute)
269 _defaultAnyAttribute = (XmlTypeMapMemberAnyAttribute) member;
272 else if (member is XmlTypeMapMemberNamespaces)
274 _namespaceDeclarations = (XmlTypeMapMemberNamespaces) member;
278 if (member is XmlTypeMapMemberElement && ((XmlTypeMapMemberElement)member).IsXmlTextCollector)
280 if (_xmlTextCollector != null) throw new InvalidOperationException ("XmlTextAttribute can only be applied once in a class");
281 _xmlTextCollector = member;
284 if (_elementMembers == null) {
285 _elementMembers = new ArrayList();
286 _elements = new Hashtable();
289 member.Index = _elementMembers.Count;
290 _elementMembers.Add (member);
292 ICollection elemsInfo = ((XmlTypeMapMemberElement)member).ElementInfo;
293 foreach (XmlTypeMapElementInfo elem in elemsInfo)
295 string key = BuildKey (elem.ElementName, elem.Namespace);
296 if (_elements.ContainsKey (key))
297 throw new InvalidOperationException ("The XML element named '" + elem.ElementName + "' from namespace '" + elem.Namespace + "' is already present in the current scope. Use XML attributes to specify another XML name or namespace for the element.");
298 _elements.Add (key, elem);
301 if (member.TypeData.IsListType && member.TypeData.Type != null && !member.TypeData.Type.IsArray) {
302 if (_listMembers == null) _listMembers = new ArrayList ();
303 _listMembers.Add (member);
307 void RegisterFlatList (XmlTypeMapMemberExpandable member)
309 if (_flatLists == null) _flatLists = new ArrayList ();
310 member.FlatArrayIndex = _flatLists.Count;
311 _flatLists.Add (member);
314 public XmlTypeMapMemberAttribute GetAttribute (string name, string ns)
316 if (_attributeMembers == null) return null;
317 return (XmlTypeMapMemberAttribute)_attributeMembers [BuildKey(name,ns)];
320 public XmlTypeMapElementInfo GetElement (string name, string ns)
322 if (_elements == null) return null;
323 return (XmlTypeMapElementInfo)_elements [BuildKey(name,ns)];
326 public XmlTypeMapElementInfo GetElement (int index)
328 if (_elements == null) return null;
330 if (_elementsByIndex == null)
332 _elementsByIndex = new XmlTypeMapElementInfo [_elementMembers.Count];
333 foreach (XmlTypeMapMemberElement mem in _elementMembers)
335 if (mem.ElementInfo.Count != 1)
336 throw new InvalidOperationException ("Read by order only possible for encoded/bare format");
338 _elementsByIndex [mem.Index] = (XmlTypeMapElementInfo) mem.ElementInfo [0];
342 return _elementsByIndex [index];
345 private string BuildKey (string name, string ns)
347 if (_ignoreMemberNamespace) return name;
348 else return name + " / " + ns;
351 public ICollection AllElementInfos
353 get { return _elements.Values; }
357 public bool IgnoreMemberNamespace
359 get { return _ignoreMemberNamespace; }
360 set { _ignoreMemberNamespace = value; }
363 public XmlTypeMapMember FindMember (string name)
365 for (int n=0; n<_allMembers.Count; n++)
366 if (((XmlTypeMapMember)_allMembers[n]).Name == name) return (XmlTypeMapMember)_allMembers[n];
370 public XmlTypeMapMemberAnyElement DefaultAnyElementMember
372 get { return _defaultAnyElement; }
375 public XmlTypeMapMemberAnyAttribute DefaultAnyAttributeMember
377 get { return _defaultAnyAttribute; }
380 public XmlTypeMapMemberNamespaces NamespaceDeclarations
382 get { return _namespaceDeclarations; }
385 public ICollection AttributeMembers
389 if (_attributeMembers == null) return null;
390 if (_attributeMembersArray != null) return _attributeMembersArray;
392 _attributeMembersArray = new XmlTypeMapMemberAttribute[_attributeMembers.Count];
393 foreach (XmlTypeMapMemberAttribute mem in _attributeMembers.Values)
394 _attributeMembersArray [mem.Index] = mem;
395 return _attributeMembersArray;
399 public ICollection ElementMembers
401 get { return _elementMembers; }
404 public ArrayList AllMembers
406 get { return _allMembers; }
409 public ArrayList FlatLists
411 get { return _flatLists; }
414 public ArrayList MembersWithDefault
416 get { return _membersWithDefault; }
419 public ArrayList ListMembers
421 get { return _listMembers; }
424 public XmlTypeMapMember XmlTextCollector
426 get { return _xmlTextCollector; }
429 public XmlTypeMapMember ReturnMember
431 get { return _returnMember; }
434 public XmlQualifiedName SimpleContentBaseType
438 if (!_canBeSimpleType || _elementMembers == null || _elementMembers.Count != 1) return null;
439 XmlTypeMapMemberElement member = (XmlTypeMapMemberElement) _elementMembers[0];
440 if (member.ElementInfo.Count != 1) return null;
441 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) member.ElementInfo[0];
442 if (!einfo.IsTextElement) return null;
443 if (member.TypeData.SchemaType == SchemaTypes.Primitive || member.TypeData.SchemaType == SchemaTypes.Enum)
444 return new XmlQualifiedName (einfo.TypeData.XmlType, einfo.DataTypeNamespace);
449 public void SetCanBeSimpleType (bool can)
451 _canBeSimpleType = can;
454 public bool HasSimpleContent
458 return SimpleContentBaseType != null;
464 // Mapping info for arrays and lists
466 internal class ListMap: ObjectMap
468 XmlTypeMapElementInfoList _itemInfo;
469 bool _gotNestedMapping;
470 XmlTypeMapping _nestedArrayMapping;
471 string _choiceMember;
473 public bool IsMultiArray
477 return (NestedArrayMapping != null);
481 public string ChoiceMember
483 get { return _choiceMember; }
484 set { _choiceMember = value; }
487 public XmlTypeMapping NestedArrayMapping
491 if (_gotNestedMapping) return _nestedArrayMapping;
492 _gotNestedMapping = true;
494 _nestedArrayMapping = ((XmlTypeMapElementInfo)_itemInfo[0]).MappedType;
496 if (_nestedArrayMapping == null) return null;
498 if (_nestedArrayMapping.TypeData.SchemaType != SchemaTypes.Array) {
499 _nestedArrayMapping = null; return null;
502 foreach (XmlTypeMapElementInfo elem in _itemInfo)
503 if (elem.MappedType != _nestedArrayMapping) {
504 _nestedArrayMapping = null;
508 return _nestedArrayMapping;
512 public XmlTypeMapElementInfoList ItemInfo
515 get { return _itemInfo; }
516 set { _itemInfo = value; }
519 public XmlTypeMapElementInfo FindElement (object ob, int index, object memberValue)
521 if (_itemInfo.Count == 1)
522 return (XmlTypeMapElementInfo) _itemInfo[0];
523 else if (_choiceMember != null && index != -1)
525 Array values = (Array) XmlTypeMapMember.GetValue (ob, _choiceMember);
526 if (values == null || index >= values.Length)
527 throw new InvalidOperationException ("Invalid or missing choice enum value in member '" + _choiceMember + "'.");
528 object val = values.GetValue (index);
529 foreach (XmlTypeMapElementInfo elem in _itemInfo)
530 if (elem.ChoiceValue != null && elem.ChoiceValue.Equals (val))
535 if (memberValue == null) return null;
536 Type type = memberValue.GetType();
537 foreach (XmlTypeMapElementInfo elem in _itemInfo)
538 if (elem.TypeData.Type == type) return elem;
543 public XmlTypeMapElementInfo FindElement (string elementName, string ns)
545 foreach (XmlTypeMapElementInfo elem in _itemInfo)
546 if (elem.ElementName == elementName && elem.Namespace == ns) return elem;
550 public XmlTypeMapElementInfo FindTextElement ()
552 foreach (XmlTypeMapElementInfo elem in _itemInfo)
553 if (elem.IsTextElement) return elem;
557 public string GetSchemaArrayName ()
559 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) _itemInfo[0];
560 if (einfo.MappedType != null) return TypeTranslator.GetArrayName (einfo.MappedType.XmlType);
561 else return TypeTranslator.GetArrayName (einfo.TypeData.XmlType);
564 public void GetArrayType (int itemCount, out string localName, out string ns)
567 if (itemCount != -1) arrayDim = "[" + itemCount + "]";
568 else arrayDim = "[]";
570 XmlTypeMapElementInfo info = (XmlTypeMapElementInfo) _itemInfo[0];
571 if (info.TypeData.SchemaType == SchemaTypes.Array)
574 ((ListMap)info.MappedType.ObjectMap).GetArrayType (-1, out nm, out ns);
575 localName = nm + arrayDim;
579 if (info.MappedType != null)
581 localName = info.MappedType.XmlType + arrayDim;
582 ns = info.MappedType.Namespace;
586 localName = info.TypeData.XmlType + arrayDim;
587 ns = info.DataTypeNamespace;
592 public override bool Equals (object other)
594 ListMap lmap = other as ListMap;
595 if (lmap == null) return false;
597 if (_itemInfo.Count != lmap._itemInfo.Count) return false;
598 for (int n=0; n<_itemInfo.Count; n++)
599 if (!_itemInfo[n].Equals (lmap._itemInfo[n])) return false;
603 public override int GetHashCode ()
605 return base.GetHashCode ();
609 internal class EnumMap: ObjectMap
611 readonly EnumMapMember[] _members;
612 readonly bool _isFlags;
613 readonly string[] _enumNames = null;
614 readonly string[] _xmlNames = null;
615 readonly long[] _values = null;
617 public class EnumMapMember
619 readonly string _xmlName;
620 readonly string _enumName;
621 readonly long _value;
622 string _documentation;
624 public EnumMapMember (string xmlName, string enumName)
625 : this (xmlName, enumName, 0)
629 public EnumMapMember (string xmlName, string enumName, long value)
632 _enumName = enumName;
636 public string XmlName
638 get { return _xmlName; }
641 public string EnumName
643 get { return _enumName; }
648 get { return _value; }
651 public string Documentation
653 get { return _documentation; }
654 set { _documentation = value; }
658 public EnumMap (EnumMapMember[] members, bool isFlags)
663 _enumNames = new string[_members.Length];
664 _xmlNames = new string[_members.Length];
665 _values = new long[_members.Length];
667 for (int i = 0; i < _members.Length; i++) {
668 EnumMapMember mem = _members[i];
669 _enumNames[i] = mem.EnumName;
670 _xmlNames[i] = mem.XmlName;
671 _values[i] = mem.Value;
677 get { return _isFlags; }
680 public EnumMapMember[] Members
682 get { return _members; }
685 public string[] EnumNames
692 public string[] XmlNames
706 public string GetXmlName (string typeName, object enumValue)
708 if (enumValue is string) {
709 throw new InvalidCastException ();
715 value = ((IConvertible) enumValue).ToInt64 (CultureInfo.CurrentCulture);
716 } catch (FormatException) {
717 throw new InvalidCastException ();
720 for (int i = 0; i < Values.Length; i++) {
721 if (Values[i] == value)
725 if (IsFlags && value == 0)
728 string xmlName = string.Empty;
731 xmlName = XmlCustomFormatter.FromEnum (value, XmlNames, Values, typeName);
733 xmlName = XmlCustomFormatter.FromEnum (value, XmlNames, Values);
737 if (xmlName.Length == 0) {
739 throw new InvalidOperationException (string.Format(CultureInfo.CurrentCulture,
740 "'{0}' is not a valid value for {1}.", value, typeName));
742 return value.ToString (CultureInfo.InvariantCulture);
748 public string GetEnumName (string typeName, string xmlName)
751 xmlName = xmlName.Trim ();
752 if (xmlName.Length == 0)
755 System.Text.StringBuilder sb = new System.Text.StringBuilder ();
756 string[] enumNames = xmlName.Split (null);
757 foreach (string name in enumNames) {
758 if (name == string.Empty) continue;
759 string foundEnumValue = null;
760 for (int i = 0; i < XmlNames.Length; i++)
761 if (XmlNames[i] == name) {
762 foundEnumValue = EnumNames[i];
766 if (foundEnumValue != null) {
769 sb.Append (foundEnumValue);
771 throw new InvalidOperationException (string.Format (CultureInfo.CurrentCulture,
772 "'{0}' is not a valid value for {1}.", name, typeName));
775 return sb.ToString ();
778 foreach (EnumMapMember mem in _members)
779 if (mem.XmlName == xmlName) return mem.EnumName;