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;
37 using System.Reflection;
39 namespace System.Xml.Serialization
41 public class XmlTypeMapping : XmlMapping
43 private string xmlType;
44 private string xmlTypeNamespace;
46 XmlTypeMapping baseMap;
47 bool multiReferenceType = false;
51 bool isNullable = true;
53 ArrayList _derivedTypes = new ArrayList();
55 internal XmlTypeMapping(string elementName, string ns, TypeData typeData, string xmlType, string xmlTypeNamespace)
56 : base (elementName, ns)
59 this.xmlType = xmlType;
60 this.xmlTypeNamespace = xmlTypeNamespace;
64 public string ElementName
66 get { return _elementName; }
69 public string Namespace
71 get { return _namespace; }
75 public string TypeFullName
77 get { return type.FullTypeName; }
80 public string TypeName
82 get { return type.TypeName; }
86 public string XsdTypeName
88 get { return XmlType; }
91 public string XsdTypeNamespace
93 get { return XmlTypeNamespace; }
97 internal TypeData TypeData
102 internal string XmlType
104 get { return xmlType; }
105 set { xmlType = value; }
108 internal string XmlTypeNamespace
110 get { return xmlTypeNamespace; }
111 set { xmlTypeNamespace = value; }
114 internal ArrayList DerivedTypes
116 get { return _derivedTypes; }
117 set { _derivedTypes = value; }
120 internal bool MultiReferenceType
122 get { return multiReferenceType; }
123 set { multiReferenceType = value; }
126 internal XmlTypeMapping BaseMap
128 get { return baseMap; }
129 set { baseMap = value; }
132 internal bool IsSimpleType
134 get { return isSimpleType; }
135 set { isSimpleType = value; }
138 internal string Documentation
140 set { documentation = value; }
141 get { return documentation; }
144 internal bool IncludeInSchema
146 get { return includeInSchema; }
147 set { includeInSchema = value; }
150 internal bool IsNullable
152 get { return isNullable; }
153 set { isNullable = value; }
156 internal XmlTypeMapping GetRealTypeMap (Type objectType)
158 if (TypeData.SchemaType == SchemaTypes.Enum)
161 // Returns the map for a subtype of this map's type
162 if (TypeData.Type == objectType) return this;
163 for (int n=0; n<_derivedTypes.Count; n++) {
164 XmlTypeMapping map = (XmlTypeMapping) _derivedTypes[n];
165 if (map.TypeData.Type == objectType) return map;
171 internal XmlTypeMapping GetRealElementMap (string name, string ens)
173 if (xmlType == name && xmlTypeNamespace == ens) return this;
174 foreach (XmlTypeMapping map in _derivedTypes)
175 if (map.xmlType == name && map.xmlTypeNamespace == ens) return map;
180 internal void UpdateRoot (XmlQualifiedName qname)
183 this._elementName = qname.Name;
184 this._namespace = qname.Namespace;
190 // Mapping info for XmlSerializable
191 internal class XmlSerializableMapping : XmlTypeMapping
195 XmlSchemaComplexType _schemaType;
196 XmlQualifiedName _schemaTypeName;
199 internal XmlSerializableMapping(string elementName, string ns, TypeData typeData, string xmlType, string xmlTypeNamespace)
200 : base(elementName, ns, typeData, xmlType, xmlTypeNamespace)
203 XmlSchemaProviderAttribute schemaProvider = (XmlSchemaProviderAttribute) Attribute.GetCustomAttribute (typeData.Type, typeof (XmlSchemaProviderAttribute));
205 if (schemaProvider != null) {
206 string method = schemaProvider.MethodName;
207 MethodInfo mi = typeData.Type.GetMethod (method, BindingFlags.Static | BindingFlags.Public);
208 XmlSchemaSet xs = new XmlSchemaSet ();
209 object retVal = mi.Invoke (null, new object [] { xs });
210 if (retVal is XmlSchemaComplexType) {
211 _schemaType = (XmlSchemaComplexType) retVal;
212 if (_schemaType.Attributes.Count > 1) {
213 XmlTypeNamespace = ((XmlSchemaAttribute) _schemaType.Attributes [0]).FixedValue;
214 XmlType = ((XmlSchemaAttribute) _schemaType.Attributes [1]).FixedValue;
217 else if (retVal is XmlQualifiedName) {
218 _schemaTypeName = (XmlQualifiedName)retVal;
219 XmlTypeNamespace = _schemaTypeName.Namespace;
220 XmlType = _schemaTypeName.Name;
223 throw new InvalidOperationException (
224 String.Format ("Method {0}.{1}() specified by XmlSchemaProviderAttribute has invalid signature: return type must be compatible with System.Xml.XmlQualifiedName.", typeData.Type.Name, method));
226 XmlSchema [] schemas = new XmlSchema [xs.Count];
227 xs.CopyTo (schemas, 0);
228 _schema = schemas [0];
233 IXmlSerializable serializable = (IXmlSerializable)Activator.CreateInstance (typeData.Type, true);
234 _schema = serializable.GetSchema();
237 if (_schema.Id == null || _schema.Id.Length == 0)
238 throw new InvalidOperationException("Schema Id is missing. The schema returned from " + typeData.Type.FullName + ".GetSchema() must have an Id.");
242 internal XmlSchema Schema
244 get { return _schema; }
248 internal XmlSchemaType SchemaType {
249 get { return _schemaType; }
252 internal XmlQualifiedName SchemaTypeName {
253 get { return _schemaTypeName; }
259 // Mapping info for classes and structs
261 internal class ClassMap: ObjectMap
263 Hashtable _elements = new Hashtable ();
264 ArrayList _elementMembers;
265 Hashtable _attributeMembers;
266 XmlTypeMapMemberAttribute[] _attributeMembersArray;
267 XmlTypeMapElementInfo[] _elementsByIndex;
268 ArrayList _flatLists;
269 ArrayList _allMembers = new ArrayList ();
270 ArrayList _membersWithDefault;
271 ArrayList _listMembers;
272 XmlTypeMapMemberAnyElement _defaultAnyElement;
273 XmlTypeMapMemberAnyAttribute _defaultAnyAttribute;
274 XmlTypeMapMemberNamespaces _namespaceDeclarations;
275 XmlTypeMapMember _xmlTextCollector;
276 XmlTypeMapMember _returnMember;
277 bool _ignoreMemberNamespace;
278 bool _canBeSimpleType = true;
280 public void AddMember (XmlTypeMapMember member)
282 member.GlobalIndex = _allMembers.Count;
283 _allMembers.Add (member);
285 if (!(member.DefaultValue is System.DBNull) && member.DefaultValue != null) {
286 if (_membersWithDefault == null) _membersWithDefault = new ArrayList ();
287 _membersWithDefault.Add (member);
290 if (member.IsReturnValue)
291 _returnMember = member;
293 if (member is XmlTypeMapMemberAttribute)
295 XmlTypeMapMemberAttribute atm = (XmlTypeMapMemberAttribute)member;
296 if (_attributeMembers == null) _attributeMembers = new Hashtable();
297 string key = BuildKey (atm.AttributeName, atm.Namespace);
298 if (_attributeMembers.ContainsKey (key))
299 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.");
300 member.Index = _attributeMembers.Count;
301 _attributeMembers.Add (key, member);
304 else if (member is XmlTypeMapMemberFlatList)
306 RegisterFlatList ((XmlTypeMapMemberFlatList)member);
308 else if (member is XmlTypeMapMemberAnyElement)
310 XmlTypeMapMemberAnyElement mem = (XmlTypeMapMemberAnyElement) member;
311 if (mem.IsDefaultAny) _defaultAnyElement = mem;
312 if (mem.TypeData.IsListType) RegisterFlatList (mem);
314 else if (member is XmlTypeMapMemberAnyAttribute)
316 _defaultAnyAttribute = (XmlTypeMapMemberAnyAttribute) member;
319 else if (member is XmlTypeMapMemberNamespaces)
321 _namespaceDeclarations = (XmlTypeMapMemberNamespaces) member;
325 if (member is XmlTypeMapMemberElement && ((XmlTypeMapMemberElement)member).IsXmlTextCollector)
327 if (_xmlTextCollector != null) throw new InvalidOperationException ("XmlTextAttribute can only be applied once in a class");
328 _xmlTextCollector = member;
331 if (_elementMembers == null) {
332 _elementMembers = new ArrayList();
333 _elements = new Hashtable();
336 member.Index = _elementMembers.Count;
337 _elementMembers.Add (member);
339 ICollection elemsInfo = ((XmlTypeMapMemberElement)member).ElementInfo;
340 foreach (XmlTypeMapElementInfo elem in elemsInfo)
342 string key = BuildKey (elem.ElementName, elem.Namespace);
343 if (_elements.ContainsKey (key))
344 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.");
345 _elements.Add (key, elem);
348 if (member.TypeData.IsListType && member.TypeData.Type != null && !member.TypeData.Type.IsArray) {
349 if (_listMembers == null) _listMembers = new ArrayList ();
350 _listMembers.Add (member);
354 void RegisterFlatList (XmlTypeMapMemberExpandable member)
356 if (_flatLists == null) _flatLists = new ArrayList ();
357 member.FlatArrayIndex = _flatLists.Count;
358 _flatLists.Add (member);
361 public XmlTypeMapMemberAttribute GetAttribute (string name, string ns)
363 if (_attributeMembers == null) return null;
364 return (XmlTypeMapMemberAttribute)_attributeMembers [BuildKey(name,ns)];
367 public XmlTypeMapElementInfo GetElement (string name, string ns)
369 if (_elements == null) return null;
370 return (XmlTypeMapElementInfo)_elements [BuildKey(name,ns)];
373 public XmlTypeMapElementInfo GetElement (int index)
375 if (_elements == null) return null;
377 if (_elementsByIndex == null)
379 _elementsByIndex = new XmlTypeMapElementInfo [_elementMembers.Count];
380 foreach (XmlTypeMapMemberElement mem in _elementMembers)
382 if (mem.ElementInfo.Count != 1)
383 throw new InvalidOperationException ("Read by order only possible for encoded/bare format");
385 _elementsByIndex [mem.Index] = (XmlTypeMapElementInfo) mem.ElementInfo [0];
389 return _elementsByIndex [index];
392 private string BuildKey (string name, string ns)
394 if (_ignoreMemberNamespace) return name;
395 else return name + " / " + ns;
398 public ICollection AllElementInfos
400 get { return _elements.Values; }
404 public bool IgnoreMemberNamespace
406 get { return _ignoreMemberNamespace; }
407 set { _ignoreMemberNamespace = value; }
410 public XmlTypeMapMember FindMember (string name)
412 for (int n=0; n<_allMembers.Count; n++)
413 if (((XmlTypeMapMember)_allMembers[n]).Name == name) return (XmlTypeMapMember)_allMembers[n];
417 public XmlTypeMapMemberAnyElement DefaultAnyElementMember
419 get { return _defaultAnyElement; }
422 public XmlTypeMapMemberAnyAttribute DefaultAnyAttributeMember
424 get { return _defaultAnyAttribute; }
427 public XmlTypeMapMemberNamespaces NamespaceDeclarations
429 get { return _namespaceDeclarations; }
432 public ICollection AttributeMembers
436 if (_attributeMembers == null) return null;
437 if (_attributeMembersArray != null) return _attributeMembersArray;
439 _attributeMembersArray = new XmlTypeMapMemberAttribute[_attributeMembers.Count];
440 foreach (XmlTypeMapMemberAttribute mem in _attributeMembers.Values)
441 _attributeMembersArray [mem.Index] = mem;
442 return _attributeMembersArray;
446 public ICollection ElementMembers
448 get { return _elementMembers; }
451 public ArrayList AllMembers
453 get { return _allMembers; }
456 public ArrayList FlatLists
458 get { return _flatLists; }
461 public ArrayList MembersWithDefault
463 get { return _membersWithDefault; }
466 public ArrayList ListMembers
468 get { return _listMembers; }
471 public XmlTypeMapMember XmlTextCollector
473 get { return _xmlTextCollector; }
476 public XmlTypeMapMember ReturnMember
478 get { return _returnMember; }
481 public XmlQualifiedName SimpleContentBaseType
485 if (!_canBeSimpleType || _elementMembers == null || _elementMembers.Count != 1) return null;
486 XmlTypeMapMemberElement member = (XmlTypeMapMemberElement) _elementMembers[0];
487 if (member.ElementInfo.Count != 1) return null;
488 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) member.ElementInfo[0];
489 if (!einfo.IsTextElement) return null;
490 if (member.TypeData.SchemaType == SchemaTypes.Primitive || member.TypeData.SchemaType == SchemaTypes.Enum)
491 return new XmlQualifiedName (einfo.TypeData.XmlType, einfo.DataTypeNamespace);
496 public void SetCanBeSimpleType (bool can)
498 _canBeSimpleType = can;
501 public bool HasSimpleContent
505 return SimpleContentBaseType != null;
511 // Mapping info for arrays and lists
513 internal class ListMap: ObjectMap
515 XmlTypeMapElementInfoList _itemInfo;
516 bool _gotNestedMapping;
517 XmlTypeMapping _nestedArrayMapping;
518 string _choiceMember;
520 public bool IsMultiArray
524 return (NestedArrayMapping != null);
528 public string ChoiceMember
530 get { return _choiceMember; }
531 set { _choiceMember = value; }
534 public XmlTypeMapping NestedArrayMapping
538 if (_gotNestedMapping) return _nestedArrayMapping;
539 _gotNestedMapping = true;
541 _nestedArrayMapping = ((XmlTypeMapElementInfo)_itemInfo[0]).MappedType;
543 if (_nestedArrayMapping == null) return null;
545 if (_nestedArrayMapping.TypeData.SchemaType != SchemaTypes.Array) {
546 _nestedArrayMapping = null; return null;
549 foreach (XmlTypeMapElementInfo elem in _itemInfo)
550 if (elem.MappedType != _nestedArrayMapping) {
551 _nestedArrayMapping = null;
555 return _nestedArrayMapping;
559 public XmlTypeMapElementInfoList ItemInfo
562 get { return _itemInfo; }
563 set { _itemInfo = value; }
566 public XmlTypeMapElementInfo FindElement (object ob, int index, object memberValue)
568 if (_itemInfo.Count == 1)
569 return (XmlTypeMapElementInfo) _itemInfo[0];
570 else if (_choiceMember != null && index != -1)
572 Array values = (Array) XmlTypeMapMember.GetValue (ob, _choiceMember);
573 if (values == null || index >= values.Length)
574 throw new InvalidOperationException ("Invalid or missing choice enum value in member '" + _choiceMember + "'.");
575 object val = values.GetValue (index);
576 foreach (XmlTypeMapElementInfo elem in _itemInfo)
577 if (elem.ChoiceValue != null && elem.ChoiceValue.Equals (val))
582 if (memberValue == null) return null;
583 Type type = memberValue.GetType();
584 foreach (XmlTypeMapElementInfo elem in _itemInfo)
585 if (elem.TypeData.Type == type) return elem;
590 public XmlTypeMapElementInfo FindElement (string elementName, string ns)
592 foreach (XmlTypeMapElementInfo elem in _itemInfo)
593 if (elem.ElementName == elementName && elem.Namespace == ns) return elem;
597 public XmlTypeMapElementInfo FindTextElement ()
599 foreach (XmlTypeMapElementInfo elem in _itemInfo)
600 if (elem.IsTextElement) return elem;
604 public string GetSchemaArrayName ()
606 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) _itemInfo[0];
607 if (einfo.MappedType != null) return TypeTranslator.GetArrayName (einfo.MappedType.XmlType);
608 else return TypeTranslator.GetArrayName (einfo.TypeData.XmlType);
611 public void GetArrayType (int itemCount, out string localName, out string ns)
614 if (itemCount != -1) arrayDim = "[" + itemCount + "]";
615 else arrayDim = "[]";
617 XmlTypeMapElementInfo info = (XmlTypeMapElementInfo) _itemInfo[0];
618 if (info.TypeData.SchemaType == SchemaTypes.Array)
621 ((ListMap)info.MappedType.ObjectMap).GetArrayType (-1, out nm, out ns);
622 localName = nm + arrayDim;
626 if (info.MappedType != null)
628 localName = info.MappedType.XmlType + arrayDim;
629 ns = info.MappedType.Namespace;
633 localName = info.TypeData.XmlType + arrayDim;
634 ns = info.DataTypeNamespace;
639 public override bool Equals (object other)
641 ListMap lmap = other as ListMap;
642 if (lmap == null) return false;
644 if (_itemInfo.Count != lmap._itemInfo.Count) return false;
645 for (int n=0; n<_itemInfo.Count; n++)
646 if (!_itemInfo[n].Equals (lmap._itemInfo[n])) return false;
650 public override int GetHashCode ()
652 return base.GetHashCode ();
656 internal class EnumMap: ObjectMap
658 readonly EnumMapMember[] _members;
659 readonly bool _isFlags;
660 readonly string[] _enumNames = null;
661 readonly string[] _xmlNames = null;
662 readonly long[] _values = null;
664 public class EnumMapMember
666 readonly string _xmlName;
667 readonly string _enumName;
668 readonly long _value;
669 string _documentation;
671 public EnumMapMember (string xmlName, string enumName)
672 : this (xmlName, enumName, 0)
676 public EnumMapMember (string xmlName, string enumName, long value)
679 _enumName = enumName;
683 public string XmlName
685 get { return _xmlName; }
688 public string EnumName
690 get { return _enumName; }
695 get { return _value; }
698 public string Documentation
700 get { return _documentation; }
701 set { _documentation = value; }
705 public EnumMap (EnumMapMember[] members, bool isFlags)
710 _enumNames = new string[_members.Length];
711 _xmlNames = new string[_members.Length];
712 _values = new long[_members.Length];
714 for (int i = 0; i < _members.Length; i++) {
715 EnumMapMember mem = _members[i];
716 _enumNames[i] = mem.EnumName;
717 _xmlNames[i] = mem.XmlName;
718 _values[i] = mem.Value;
724 get { return _isFlags; }
727 public EnumMapMember[] Members
729 get { return _members; }
732 public string[] EnumNames
739 public string[] XmlNames
753 public string GetXmlName (string typeName, object enumValue)
755 if (enumValue is string) {
756 throw new InvalidCastException ();
762 value = ((IConvertible) enumValue).ToInt64 (CultureInfo.CurrentCulture);
763 } catch (FormatException) {
764 throw new InvalidCastException ();
767 for (int i = 0; i < Values.Length; i++) {
768 if (Values[i] == value)
772 if (IsFlags && value == 0)
775 string xmlName = string.Empty;
778 xmlName = XmlCustomFormatter.FromEnum (value, XmlNames, Values, typeName);
780 xmlName = XmlCustomFormatter.FromEnum (value, XmlNames, Values);
784 if (xmlName.Length == 0) {
786 throw new InvalidOperationException (string.Format(CultureInfo.CurrentCulture,
787 "'{0}' is not a valid value for {1}.", value, typeName));
789 return value.ToString (CultureInfo.InvariantCulture);
795 public string GetEnumName (string typeName, string xmlName)
798 xmlName = xmlName.Trim ();
799 if (xmlName.Length == 0)
802 System.Text.StringBuilder sb = new System.Text.StringBuilder ();
803 string[] enumNames = xmlName.Split (null);
804 foreach (string name in enumNames) {
805 if (name == string.Empty) continue;
806 string foundEnumValue = null;
807 for (int i = 0; i < XmlNames.Length; i++)
808 if (XmlNames[i] == name) {
809 foundEnumValue = EnumNames[i];
813 if (foundEnumValue != null) {
816 sb.Append (foundEnumValue);
818 throw new InvalidOperationException (string.Format (CultureInfo.CurrentCulture,
819 "'{0}' is not a valid value for {1}.", name, typeName));
822 return sb.ToString ();
825 foreach (EnumMapMember mem in _members)
826 if (mem.XmlName == xmlName) return mem.EnumName;