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; }
84 internal TypeData TypeData
89 internal string XmlType
91 get { return xmlType; }
94 internal string XmlTypeNamespace
96 get { return xmlTypeNamespace; }
99 internal ArrayList DerivedTypes
101 get { return _derivedTypes; }
102 set { _derivedTypes = value; }
105 internal bool MultiReferenceType
107 get { return multiReferenceType; }
108 set { multiReferenceType = value; }
111 internal XmlTypeMapping BaseMap
113 get { return baseMap; }
114 set { baseMap = value; }
117 internal bool IsSimpleType
119 get { return isSimpleType; }
120 set { isSimpleType = value; }
123 internal string Documentation
125 set { documentation = value; }
126 get { return documentation; }
129 internal bool IncludeInSchema
131 get { return includeInSchema; }
132 set { includeInSchema = value; }
135 internal bool IsNullable
137 get { return isNullable; }
138 set { isNullable = value; }
141 internal XmlTypeMapping GetRealTypeMap (string objectFullTypeName)
143 if (TypeData.SchemaType == SchemaTypes.Enum)
146 // Returns the map for a subtype of this map's type
147 objectFullTypeName = objectFullTypeName.Replace ('+','.');
148 if (TypeFullName == objectFullTypeName) return this;
149 for (int n=0; n<_derivedTypes.Count; n++) {
150 XmlTypeMapping map = (XmlTypeMapping) _derivedTypes[n];
151 if (map.TypeFullName == objectFullTypeName) return map;
157 internal XmlTypeMapping GetRealElementMap (string name, string ens)
159 if (xmlType == name && xmlTypeNamespace == ens) return this;
160 foreach (XmlTypeMapping map in _derivedTypes)
161 if (map.xmlType == name && map.xmlTypeNamespace == ens) return map;
166 internal void UpdateRoot (XmlQualifiedName qname)
169 this._elementName = qname.Name;
170 this._namespace = qname.Namespace;
176 // Mapping info for XmlSerializable
177 internal class XmlSerializableMapping : XmlTypeMapping
181 internal XmlSerializableMapping(string elementName, string ns, TypeData typeData, string xmlType, string xmlTypeNamespace)
182 : base(elementName, ns, typeData, xmlType, xmlTypeNamespace)
184 IXmlSerializable serializable = (IXmlSerializable)Activator.CreateInstance(typeData.Type);
185 _schema = serializable.GetSchema();
188 if (_schema.Id == null || _schema.Id.Length == 0)
189 throw new InvalidOperationException("Schema Id is missing. The schema returned from " + typeData.Type.FullName + ".GetSchema() must have an Id.");
193 internal XmlSchema Schema
195 get { return _schema; }
200 // Mapping info for classes and structs
202 internal class ClassMap: ObjectMap
204 Hashtable _elements = new Hashtable ();
205 ArrayList _elementMembers;
206 Hashtable _attributeMembers;
207 XmlTypeMapMemberAttribute[] _attributeMembersArray;
208 XmlTypeMapElementInfo[] _elementsByIndex;
209 ArrayList _flatLists;
210 ArrayList _allMembers = new ArrayList ();
211 ArrayList _membersWithDefault;
212 ArrayList _listMembers;
213 XmlTypeMapMemberAnyElement _defaultAnyElement;
214 XmlTypeMapMemberAnyAttribute _defaultAnyAttribute;
215 XmlTypeMapMemberNamespaces _namespaceDeclarations;
216 XmlTypeMapMember _xmlTextCollector;
217 XmlTypeMapMember _returnMember;
218 bool _ignoreMemberNamespace;
219 bool _canBeSimpleType = true;
221 public void AddMember (XmlTypeMapMember member)
223 member.GlobalIndex = _allMembers.Count;
224 _allMembers.Add (member);
226 if (!(member.DefaultValue is System.DBNull) && member.DefaultValue != null) {
227 if (_membersWithDefault == null) _membersWithDefault = new ArrayList ();
228 _membersWithDefault.Add (member);
231 if (member.IsReturnValue)
232 _returnMember = member;
234 if (member is XmlTypeMapMemberAttribute)
236 XmlTypeMapMemberAttribute atm = (XmlTypeMapMemberAttribute)member;
237 if (_attributeMembers == null) _attributeMembers = new Hashtable();
238 string key = BuildKey (atm.AttributeName, atm.Namespace);
239 if (_attributeMembers.ContainsKey (key))
240 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.");
241 member.Index = _attributeMembers.Count;
242 _attributeMembers.Add (key, member);
245 else if (member is XmlTypeMapMemberFlatList)
247 RegisterFlatList ((XmlTypeMapMemberFlatList)member);
249 else if (member is XmlTypeMapMemberAnyElement)
251 XmlTypeMapMemberAnyElement mem = (XmlTypeMapMemberAnyElement) member;
252 if (mem.IsDefaultAny) _defaultAnyElement = mem;
253 if (mem.TypeData.IsListType) RegisterFlatList (mem);
255 else if (member is XmlTypeMapMemberAnyAttribute)
257 _defaultAnyAttribute = (XmlTypeMapMemberAnyAttribute) member;
260 else if (member is XmlTypeMapMemberNamespaces)
262 _namespaceDeclarations = (XmlTypeMapMemberNamespaces) member;
266 if (member is XmlTypeMapMemberElement && ((XmlTypeMapMemberElement)member).IsXmlTextCollector)
268 if (_xmlTextCollector != null) throw new InvalidOperationException ("XmlTextAttribute can only be applied once in a class");
269 _xmlTextCollector = member;
272 if (_elementMembers == null) {
273 _elementMembers = new ArrayList();
274 _elements = new Hashtable();
277 member.Index = _elementMembers.Count;
278 _elementMembers.Add (member);
280 ICollection elemsInfo = ((XmlTypeMapMemberElement)member).ElementInfo;
281 foreach (XmlTypeMapElementInfo elem in elemsInfo)
283 string key = BuildKey (elem.ElementName, elem.Namespace);
284 if (_elements.ContainsKey (key))
285 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.");
286 _elements.Add (key, elem);
289 if (member.TypeData.IsListType && member.TypeData.Type != null && !member.TypeData.Type.IsArray) {
290 if (_listMembers == null) _listMembers = new ArrayList ();
291 _listMembers.Add (member);
295 void RegisterFlatList (XmlTypeMapMemberExpandable member)
297 if (_flatLists == null) _flatLists = new ArrayList ();
298 member.FlatArrayIndex = _flatLists.Count;
299 _flatLists.Add (member);
302 public XmlTypeMapMemberAttribute GetAttribute (string name, string ns)
304 if (_attributeMembers == null) return null;
305 return (XmlTypeMapMemberAttribute)_attributeMembers [BuildKey(name,ns)];
308 public XmlTypeMapElementInfo GetElement (string name, string ns)
310 if (_elements == null) return null;
311 return (XmlTypeMapElementInfo)_elements [BuildKey(name,ns)];
314 public XmlTypeMapElementInfo GetElement (int index)
316 if (_elements == null) return null;
318 if (_elementsByIndex == null)
320 _elementsByIndex = new XmlTypeMapElementInfo [_elementMembers.Count];
321 foreach (XmlTypeMapMemberElement mem in _elementMembers)
323 if (mem.ElementInfo.Count != 1)
324 throw new InvalidOperationException ("Read by order only possible for encoded/bare format");
326 _elementsByIndex [mem.Index] = (XmlTypeMapElementInfo) mem.ElementInfo [0];
330 return _elementsByIndex [index];
333 private string BuildKey (string name, string ns)
335 if (_ignoreMemberNamespace) return name;
336 else return name + " / " + ns;
339 public ICollection AllElementInfos
341 get { return _elements.Values; }
345 public bool IgnoreMemberNamespace
347 get { return _ignoreMemberNamespace; }
348 set { _ignoreMemberNamespace = value; }
351 public XmlTypeMapMember FindMember (string name)
353 for (int n=0; n<_allMembers.Count; n++)
354 if (((XmlTypeMapMember)_allMembers[n]).Name == name) return (XmlTypeMapMember)_allMembers[n];
358 public XmlTypeMapMemberAnyElement DefaultAnyElementMember
360 get { return _defaultAnyElement; }
363 public XmlTypeMapMemberAnyAttribute DefaultAnyAttributeMember
365 get { return _defaultAnyAttribute; }
368 public XmlTypeMapMemberNamespaces NamespaceDeclarations
370 get { return _namespaceDeclarations; }
373 public ICollection AttributeMembers
377 if (_attributeMembers == null) return null;
378 if (_attributeMembersArray != null) return _attributeMembersArray;
380 _attributeMembersArray = new XmlTypeMapMemberAttribute[_attributeMembers.Count];
381 foreach (XmlTypeMapMemberAttribute mem in _attributeMembers.Values)
382 _attributeMembersArray [mem.Index] = mem;
383 return _attributeMembersArray;
387 public ICollection ElementMembers
389 get { return _elementMembers; }
392 public ArrayList AllMembers
394 get { return _allMembers; }
397 public ArrayList FlatLists
399 get { return _flatLists; }
402 public ArrayList MembersWithDefault
404 get { return _membersWithDefault; }
407 public ArrayList ListMembers
409 get { return _listMembers; }
412 public XmlTypeMapMember XmlTextCollector
414 get { return _xmlTextCollector; }
417 public XmlTypeMapMember ReturnMember
419 get { return _returnMember; }
422 public XmlQualifiedName SimpleContentBaseType
426 if (!_canBeSimpleType || _elementMembers == null || _elementMembers.Count != 1) return null;
427 XmlTypeMapMemberElement member = (XmlTypeMapMemberElement) _elementMembers[0];
428 if (member.ElementInfo.Count != 1) return null;
429 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) member.ElementInfo[0];
430 if (!einfo.IsTextElement) return null;
431 if (member.TypeData.SchemaType == SchemaTypes.Primitive || member.TypeData.SchemaType == SchemaTypes.Enum)
432 return new XmlQualifiedName (einfo.TypeData.XmlType, einfo.DataTypeNamespace);
437 public void SetCanBeSimpleType (bool can)
439 _canBeSimpleType = can;
442 public bool HasSimpleContent
446 return SimpleContentBaseType != null;
452 // Mapping info for arrays and lists
454 internal class ListMap: ObjectMap
456 XmlTypeMapElementInfoList _itemInfo;
457 bool _gotNestedMapping;
458 XmlTypeMapping _nestedArrayMapping;
459 string _choiceMember;
461 public bool IsMultiArray
465 return (NestedArrayMapping != null);
469 public string ChoiceMember
471 get { return _choiceMember; }
472 set { _choiceMember = value; }
475 public XmlTypeMapping NestedArrayMapping
479 if (_gotNestedMapping) return _nestedArrayMapping;
480 _gotNestedMapping = true;
482 _nestedArrayMapping = ((XmlTypeMapElementInfo)_itemInfo[0]).MappedType;
484 if (_nestedArrayMapping == null) return null;
486 if (_nestedArrayMapping.TypeData.SchemaType != SchemaTypes.Array) {
487 _nestedArrayMapping = null; return null;
490 foreach (XmlTypeMapElementInfo elem in _itemInfo)
491 if (elem.MappedType != _nestedArrayMapping) {
492 _nestedArrayMapping = null;
496 return _nestedArrayMapping;
500 public XmlTypeMapElementInfoList ItemInfo
503 get { return _itemInfo; }
504 set { _itemInfo = value; }
507 public XmlTypeMapElementInfo FindElement (object ob, int index, object memberValue)
509 if (_itemInfo.Count == 1)
510 return (XmlTypeMapElementInfo) _itemInfo[0];
511 else if (_choiceMember != null && index != -1)
513 Array values = (Array) XmlTypeMapMember.GetValue (ob, _choiceMember);
514 if (values == null || index >= values.Length)
515 throw new InvalidOperationException ("Invalid or missing choice enum value in member '" + _choiceMember + "'.");
516 object val = values.GetValue (index);
517 foreach (XmlTypeMapElementInfo elem in _itemInfo)
518 if (elem.ChoiceValue != null && elem.ChoiceValue.Equals (val))
523 if (memberValue == null) return null;
524 Type type = memberValue.GetType();
525 foreach (XmlTypeMapElementInfo elem in _itemInfo)
526 if (elem.TypeData.Type == type) return elem;
531 public XmlTypeMapElementInfo FindElement (string elementName, string ns)
533 foreach (XmlTypeMapElementInfo elem in _itemInfo)
534 if (elem.ElementName == elementName && elem.Namespace == ns) return elem;
538 public XmlTypeMapElementInfo FindTextElement ()
540 foreach (XmlTypeMapElementInfo elem in _itemInfo)
541 if (elem.IsTextElement) return elem;
545 public string GetSchemaArrayName ()
547 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) _itemInfo[0];
548 if (einfo.MappedType != null) return TypeTranslator.GetArrayName (einfo.MappedType.XmlType);
549 else return TypeTranslator.GetArrayName (einfo.TypeData.XmlType);
552 public void GetArrayType (int itemCount, out string localName, out string ns)
555 if (itemCount != -1) arrayDim = "[" + itemCount + "]";
556 else arrayDim = "[]";
558 XmlTypeMapElementInfo info = (XmlTypeMapElementInfo) _itemInfo[0];
559 if (info.TypeData.SchemaType == SchemaTypes.Array)
562 ((ListMap)info.MappedType.ObjectMap).GetArrayType (-1, out nm, out ns);
563 localName = nm + arrayDim;
567 if (info.MappedType != null)
569 localName = info.MappedType.XmlType + arrayDim;
570 ns = info.MappedType.Namespace;
574 localName = info.TypeData.XmlType + arrayDim;
575 ns = info.DataTypeNamespace;
580 public override bool Equals (object other)
582 ListMap lmap = other as ListMap;
583 if (lmap == null) return false;
585 if (_itemInfo.Count != lmap._itemInfo.Count) return false;
586 for (int n=0; n<_itemInfo.Count; n++)
587 if (!_itemInfo[n].Equals (lmap._itemInfo[n])) return false;
591 public override int GetHashCode ()
593 return base.GetHashCode ();
597 internal class EnumMap: ObjectMap
599 readonly EnumMapMember[] _members;
600 readonly bool _isFlags;
601 readonly string[] _enumNames = null;
602 readonly string[] _xmlNames = null;
603 readonly long[] _values = null;
605 public class EnumMapMember
607 readonly string _xmlName;
608 readonly string _enumName;
609 readonly long _value;
610 string _documentation;
612 public EnumMapMember (string xmlName, string enumName)
613 : this (xmlName, enumName, 0)
617 public EnumMapMember (string xmlName, string enumName, long value)
620 _enumName = enumName;
624 public string XmlName
626 get { return _xmlName; }
629 public string EnumName
631 get { return _enumName; }
636 get { return _value; }
639 public string Documentation
641 get { return _documentation; }
642 set { _documentation = value; }
646 public EnumMap (EnumMapMember[] members, bool isFlags)
651 _enumNames = new string[_members.Length];
652 _xmlNames = new string[_members.Length];
653 _values = new long[_members.Length];
655 for (int i = 0; i < _members.Length; i++) {
656 EnumMapMember mem = _members[i];
657 _enumNames[i] = mem.EnumName;
658 _xmlNames[i] = mem.XmlName;
659 _values[i] = mem.Value;
665 get { return _isFlags; }
668 public EnumMapMember[] Members
670 get { return _members; }
673 public string[] EnumNames
680 public string[] XmlNames
694 public string GetXmlName (string typeName, object enumValue)
696 if (enumValue is string) {
697 throw new InvalidCastException ();
703 value = ((IConvertible) enumValue).ToInt64 (CultureInfo.CurrentCulture);
704 } catch (FormatException) {
705 throw new InvalidCastException ();
708 for (int i = 0; i < Values.Length; i++) {
709 if (Values[i] == value)
713 if (IsFlags && value == 0)
716 string xmlName = string.Empty;
719 xmlName = XmlCustomFormatter.FromEnum (value, XmlNames, Values, typeName);
721 xmlName = XmlCustomFormatter.FromEnum (value, XmlNames, Values);
725 if (xmlName.Length == 0) {
727 throw new InvalidOperationException (string.Format(CultureInfo.CurrentCulture,
728 "'{0}' is not a valid value for {1}.", value, typeName));
730 return value.ToString (CultureInfo.InvariantCulture);
736 public string GetEnumName (string typeName, string xmlName)
739 xmlName = xmlName.Trim ();
740 if (xmlName.Length == 0)
743 System.Text.StringBuilder sb = new System.Text.StringBuilder ();
744 string[] enumNames = xmlName.Split (null);
745 foreach (string name in enumNames) {
746 if (name == string.Empty) continue;
747 string foundEnumValue = null;
748 for (int i = 0; i < XmlNames.Length; i++)
749 if (XmlNames[i] == name) {
750 foundEnumValue = EnumNames[i];
754 if (foundEnumValue != null) {
757 sb.Append (foundEnumValue);
759 throw new InvalidOperationException (string.Format (CultureInfo.CurrentCulture,
760 "'{0}' is not a valid value for {1}.", name, typeName));
763 return sb.ToString ();
766 foreach (EnumMapMember mem in _members)
767 if (mem.XmlName == xmlName) return mem.EnumName;