Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Xml / System / Xml / Serialization / Mappings.cs
blobb8be09a3a88d0299b8838f0151e2ae46617ffb72
1 //------------------------------------------------------------------------------
2 // <copyright file="Mappings.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
8 namespace System.Xml.Serialization {
10 using System.Reflection;
11 using System.Collections;
12 using System.Xml.Schema;
13 using System;
14 using System.Text;
15 using System.ComponentModel;
16 using System.Xml;
17 using System.CodeDom.Compiler;
19 // These classes represent a mapping between classes and a particular XML format.
20 // There are two class of mapping information: accessors (such as elements and
21 // attributes), and mappings (which specify the type of an accessor).
23 internal abstract class Accessor {
24 string name;
25 object defaultValue = null;
26 string ns;
27 TypeMapping mapping;
28 bool any;
29 string anyNs;
30 bool topLevelInSchema;
31 bool isFixed;
32 bool isOptional;
33 XmlSchemaForm form = XmlSchemaForm.None;
35 internal Accessor() { }
37 internal TypeMapping Mapping {
38 get { return mapping; }
39 set { mapping = value; }
42 internal object Default {
43 get { return defaultValue; }
44 set { defaultValue = value; }
47 internal bool HasDefault {
48 get { return defaultValue != null && defaultValue != DBNull.Value; }
51 internal virtual string Name {
52 get { return name == null ? string.Empty : name; }
53 set { name = value; }
56 internal bool Any {
57 get { return any; }
58 set { any = value; }
61 internal string AnyNamespaces {
62 get { return anyNs; }
63 set { anyNs = value; }
66 internal string Namespace {
67 get { return ns; }
68 set { ns = value; }
71 internal XmlSchemaForm Form {
72 get { return form; }
73 set { form = value; }
76 internal bool IsFixed {
77 get { return isFixed; }
78 set { isFixed = value; }
81 internal bool IsOptional {
82 get { return isOptional; }
83 set { isOptional = value; }
86 internal bool IsTopLevelInSchema {
87 get { return topLevelInSchema; }
88 set { topLevelInSchema = value; }
91 internal static string EscapeName(string name) {
92 if (name == null || name.Length == 0) return name;
93 return XmlConvert.EncodeLocalName(name);
96 internal static string EscapeQName(string name) {
97 if (name == null || name.Length == 0) return name;
98 int colon = name.LastIndexOf(':');
99 if (colon < 0)
100 return XmlConvert.EncodeLocalName(name);
101 else {
102 if (colon == 0 || colon == name.Length - 1)
103 throw new ArgumentException(Res.GetString(Res.Xml_InvalidNameChars, name), "name");
104 return new XmlQualifiedName(XmlConvert.EncodeLocalName(name.Substring(colon + 1)), XmlConvert.EncodeLocalName(name.Substring(0, colon))).ToString();
108 internal static string UnescapeName(string name) {
109 return XmlConvert.DecodeName(name);
112 internal string ToString(string defaultNs) {
113 if (Any) {
114 return (Namespace == null ? "##any" : Namespace) + ":" + Name;
116 else {
117 return Namespace == defaultNs ? Name : Namespace + ":" + Name;
122 internal class ElementAccessor : Accessor {
123 bool nullable;
124 bool isSoap;
125 bool unbounded = false;
127 internal bool IsSoap {
128 get { return isSoap; }
129 set { isSoap = value; }
132 internal bool IsNullable {
133 get { return nullable; }
134 set { nullable = value; }
137 internal bool IsUnbounded {
138 get { return unbounded; }
139 set { unbounded = value; }
142 internal ElementAccessor Clone() {
143 ElementAccessor newAccessor = new ElementAccessor();
144 newAccessor.nullable = this.nullable;
145 newAccessor.IsTopLevelInSchema = this.IsTopLevelInSchema;
146 newAccessor.Form = this.Form;
147 newAccessor.isSoap = this.isSoap;
148 newAccessor.Name = this.Name;
149 newAccessor.Default = this.Default;
150 newAccessor.Namespace = this.Namespace;
151 newAccessor.Mapping = this.Mapping;
152 newAccessor.Any = this.Any;
154 return newAccessor;
158 internal class ChoiceIdentifierAccessor : Accessor {
159 string memberName;
160 string[] memberIds;
161 MemberInfo memberInfo;
163 internal string MemberName {
164 get { return memberName; }
165 set { memberName = value; }
168 internal string[] MemberIds {
169 get { return memberIds; }
170 set { memberIds = value; }
173 internal MemberInfo MemberInfo {
174 get { return memberInfo; }
175 set { memberInfo = value; }
179 internal class TextAccessor : Accessor {
182 internal class XmlnsAccessor : Accessor {
185 internal class AttributeAccessor : Accessor {
186 bool isSpecial;
187 bool isList;
189 internal bool IsSpecialXmlNamespace {
190 get { return isSpecial; }
193 internal bool IsList {
194 get { return isList; }
195 set { isList = value; }
198 internal void CheckSpecial() {
199 int colon = Name.LastIndexOf(':');
201 if (colon >= 0) {
202 if (!Name.StartsWith("xml:", StringComparison.Ordinal)) {
203 throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidNameChars, Name));
205 Name = Name.Substring("xml:".Length);
206 Namespace = XmlReservedNs.NsXml;
207 isSpecial = true;
209 else {
210 if (Namespace == XmlReservedNs.NsXml) {
211 isSpecial = true;
213 else {
214 isSpecial = false;
217 if (isSpecial) {
218 Form = XmlSchemaForm.Qualified;
223 internal abstract class Mapping {
224 bool isSoap;
226 internal Mapping() { }
228 protected Mapping(Mapping mapping)
230 this.isSoap = mapping.isSoap;
233 internal bool IsSoap {
234 get { return isSoap; }
235 set { isSoap = value; }
239 internal abstract class TypeMapping : Mapping {
240 TypeDesc typeDesc;
241 string typeNs;
242 string typeName;
243 bool referencedByElement;
244 bool referencedByTopLevelElement;
245 bool includeInSchema = true;
246 bool reference = false;
248 internal bool ReferencedByTopLevelElement {
249 get { return referencedByTopLevelElement; }
250 set { referencedByTopLevelElement = value; }
253 internal bool ReferencedByElement {
254 get { return referencedByElement || referencedByTopLevelElement; }
255 set { referencedByElement = value; }
257 internal string Namespace {
258 get { return typeNs; }
259 set { typeNs = value; }
262 internal string TypeName {
263 get { return typeName; }
264 set { typeName = value; }
267 internal TypeDesc TypeDesc {
268 get { return typeDesc; }
269 set { typeDesc = value; }
272 internal bool IncludeInSchema {
273 get { return includeInSchema; }
274 set { includeInSchema = value; }
277 internal virtual bool IsList {
278 get { return false; }
279 set { }
282 internal bool IsReference {
283 get { return reference; }
284 set { reference = value; }
287 internal bool IsAnonymousType {
288 get { return typeName == null || typeName.Length == 0; }
291 internal virtual string DefaultElementName {
292 get { return IsAnonymousType ? XmlConvert.EncodeLocalName(typeDesc.Name) : typeName; }
296 internal class PrimitiveMapping : TypeMapping {
297 bool isList;
299 internal override bool IsList {
300 get { return isList; }
301 set { isList = value; }
305 internal class NullableMapping : TypeMapping {
306 TypeMapping baseMapping;
308 internal TypeMapping BaseMapping {
309 get { return baseMapping; }
310 set { baseMapping = value; }
313 internal override string DefaultElementName {
314 get { return BaseMapping.DefaultElementName; }
318 internal class ArrayMapping : TypeMapping {
319 ElementAccessor[] elements;
320 ElementAccessor[] sortedElements;
321 ArrayMapping next;
322 StructMapping topLevelMapping;
324 internal ElementAccessor[] Elements {
325 get { return elements; }
326 set { elements = value; sortedElements = null; }
329 internal ElementAccessor[] ElementsSortedByDerivation {
330 get {
331 if (sortedElements != null)
332 return sortedElements;
333 if (elements == null)
334 return null;
335 sortedElements = new ElementAccessor[elements.Length];
336 Array.Copy(elements, 0, sortedElements, 0, elements.Length);
337 AccessorMapping.SortMostToLeastDerived(sortedElements);
338 return sortedElements;
343 internal ArrayMapping Next {
344 get { return next; }
345 set { next = value; }
348 internal StructMapping TopLevelMapping {
349 get { return topLevelMapping; }
350 set { topLevelMapping = value; }
354 internal class EnumMapping : PrimitiveMapping {
355 ConstantMapping[] constants;
356 bool isFlags;
358 internal bool IsFlags {
359 get { return isFlags; }
360 set { isFlags = value; }
363 internal ConstantMapping[] Constants {
364 get { return constants; }
365 set { constants = value; }
369 internal class ConstantMapping : Mapping {
370 string xmlName;
371 string name;
372 long value;
374 internal string XmlName {
375 get { return xmlName == null ? string.Empty : xmlName; }
376 set { xmlName = value; }
379 internal string Name {
380 get { return name == null ? string.Empty : name; }
381 set { this.name = value; }
384 internal long Value {
385 get { return value; }
386 set { this.value = value; }
390 internal class StructMapping : TypeMapping, INameScope {
391 MemberMapping[] members;
392 StructMapping baseMapping;
393 StructMapping derivedMappings;
394 StructMapping nextDerivedMapping;
395 MemberMapping xmlnsMember = null;
396 bool hasSimpleContent;
397 bool openModel;
398 bool isSequence;
399 NameTable elements;
400 NameTable attributes;
401 CodeIdentifiers scope;
403 internal StructMapping BaseMapping {
404 get { return baseMapping; }
405 set {
406 baseMapping = value;
407 if (!IsAnonymousType && baseMapping != null) {
408 nextDerivedMapping = baseMapping.derivedMappings;
409 baseMapping.derivedMappings = this;
411 if (value.isSequence && !isSequence) {
412 isSequence = true;
413 if (baseMapping.IsSequence) {
414 for (StructMapping derived = derivedMappings; derived != null; derived = derived.NextDerivedMapping) {
415 derived.SetSequence();
422 internal StructMapping DerivedMappings {
423 get { return derivedMappings; }
426 internal bool IsFullyInitialized {
427 get { return baseMapping != null && Members != null; }
430 internal NameTable LocalElements {
431 get {
432 if (elements == null)
433 elements = new NameTable();
434 return elements;
437 internal NameTable LocalAttributes {
438 get {
439 if (attributes == null)
440 attributes = new NameTable();
441 return attributes;
444 object INameScope.this[string name, string ns] {
445 get {
446 object named = LocalElements[name, ns];
447 if (named != null)
448 return named;
449 if (baseMapping != null)
450 return ((INameScope)baseMapping)[name, ns];
451 return null;
453 set {
454 LocalElements[name, ns] = value;
457 internal StructMapping NextDerivedMapping {
458 get { return nextDerivedMapping; }
461 internal bool HasSimpleContent {
462 get { return hasSimpleContent; }
465 internal bool HasXmlnsMember {
466 get {
467 StructMapping mapping = this;
468 while (mapping != null) {
469 if (mapping.XmlnsMember != null)
470 return true;
471 mapping = mapping.BaseMapping;
473 return false;
477 internal MemberMapping[] Members {
478 get { return members; }
479 set { members = value; }
482 internal MemberMapping XmlnsMember {
483 get { return xmlnsMember; }
484 set { xmlnsMember = value; }
487 internal bool IsOpenModel {
488 get { return openModel; }
489 set { openModel = value; }
492 internal CodeIdentifiers Scope {
493 get {
494 if (scope == null)
495 scope = new CodeIdentifiers();
496 return scope;
498 set { scope = value; }
501 internal MemberMapping FindDeclaringMapping(MemberMapping member, out StructMapping declaringMapping, string parent) {
502 declaringMapping = null;
503 if (BaseMapping != null) {
504 MemberMapping baseMember = BaseMapping.FindDeclaringMapping(member, out declaringMapping, parent);
505 if (baseMember != null) return baseMember;
507 if (members == null) return null;
509 for (int i = 0; i < members.Length; i++) {
510 if (members[i].Name == member.Name) {
511 if (members[i].TypeDesc != member.TypeDesc)
512 throw new InvalidOperationException(Res.GetString(Res.XmlHiddenMember, parent, member.Name, member.TypeDesc.FullName, this.TypeName, members[i].Name, members[i].TypeDesc.FullName));
513 else if (!members[i].Match(member)) {
514 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidXmlOverride, parent, member.Name, this.TypeName, members[i].Name));
516 declaringMapping = this;
517 return members[i];
520 return null;
522 internal bool Declares(MemberMapping member, string parent) {
523 StructMapping m;
524 return (FindDeclaringMapping(member, out m, parent) != null);
527 internal void SetContentModel(TextAccessor text, bool hasElements) {
528 if (BaseMapping == null || BaseMapping.TypeDesc.IsRoot) {
529 hasSimpleContent = !hasElements && text != null && !text.Mapping.IsList;
531 else if (BaseMapping.HasSimpleContent) {
532 if (text != null || hasElements) {
533 // we can only extent a simleContent type with attributes
534 throw new InvalidOperationException(Res.GetString(Res.XmlIllegalSimpleContentExtension, TypeDesc.FullName, BaseMapping.TypeDesc.FullName));
536 else {
537 hasSimpleContent = true;
540 else {
541 hasSimpleContent = false;
543 if (!hasSimpleContent && text != null && !text.Mapping.TypeDesc.CanBeTextValue) {
544 throw new InvalidOperationException(Res.GetString(Res.XmlIllegalTypedTextAttribute, TypeDesc.FullName, text.Name, text.Mapping.TypeDesc.FullName));
548 internal bool HasElements {
549 get { return elements != null && elements.Values.Count > 0; }
552 internal bool HasExplicitSequence() {
553 if (members != null) {
554 for (int i = 0; i < members.Length; i++) {
555 if (members[i].IsParticle && members[i].IsSequence) {
556 return true;
560 return (baseMapping != null && baseMapping.HasExplicitSequence());
563 internal void SetSequence() {
564 if (TypeDesc.IsRoot)
565 return;
567 StructMapping start = this;
569 // find first mapping that does not have the sequence set
570 while (!start.BaseMapping.IsSequence && start.BaseMapping != null && !start.BaseMapping.TypeDesc.IsRoot)
571 start = start.BaseMapping;
573 start.IsSequence = true;
574 for (StructMapping derived = start.DerivedMappings; derived != null; derived = derived.NextDerivedMapping) {
575 derived.SetSequence();
579 internal bool IsSequence {
580 get { return isSequence && !TypeDesc.IsRoot; }
581 set { isSequence = value; }
585 internal abstract class AccessorMapping : Mapping {
586 TypeDesc typeDesc;
587 AttributeAccessor attribute;
588 ElementAccessor[] elements;
589 ElementAccessor[] sortedElements;
590 TextAccessor text;
591 ChoiceIdentifierAccessor choiceIdentifier;
592 XmlnsAccessor xmlns;
593 bool ignore;
595 internal AccessorMapping()
598 protected AccessorMapping(AccessorMapping mapping)
599 : base(mapping)
601 this.typeDesc = mapping.typeDesc;
602 this.attribute = mapping.attribute;
603 this.elements = mapping.elements;
604 this.sortedElements = mapping.sortedElements;
605 this.text = mapping.text;
606 this.choiceIdentifier = mapping.choiceIdentifier;
607 this.xmlns = mapping.xmlns;
608 this.ignore = mapping.ignore;
611 internal bool IsAttribute {
612 get { return attribute != null; }
615 internal bool IsText {
616 get { return text != null && (elements == null || elements.Length == 0); }
619 internal bool IsParticle {
620 get { return (elements != null && elements.Length > 0); }
623 internal TypeDesc TypeDesc {
624 get { return typeDesc; }
625 set { typeDesc = value; }
628 internal AttributeAccessor Attribute {
629 get { return attribute; }
630 set { attribute = value; }
633 internal ElementAccessor[] Elements {
634 get { return elements; }
635 set { elements = value; sortedElements = null; }
638 internal static void SortMostToLeastDerived(ElementAccessor[] elements) {
639 Array.Sort(elements, new AccessorComparer());
642 internal class AccessorComparer : IComparer {
643 public int Compare(object o1, object o2) {
644 if (o1 == o2)
645 return 0;
646 Accessor a1 = (Accessor)o1;
647 Accessor a2 = (Accessor)o2;
648 int w1 = a1.Mapping.TypeDesc.Weight;
649 int w2 = a2.Mapping.TypeDesc.Weight;
650 if (w1 == w2)
651 return 0;
652 if (w1 < w2)
653 return 1;
654 return -1;
658 internal ElementAccessor[] ElementsSortedByDerivation {
659 get {
660 if (sortedElements != null)
661 return sortedElements;
662 if (elements == null)
663 return null;
664 sortedElements = new ElementAccessor[elements.Length];
665 Array.Copy(elements, 0, sortedElements, 0, elements.Length);
666 SortMostToLeastDerived(sortedElements);
667 return sortedElements;
671 internal TextAccessor Text {
672 get { return text; }
673 set { text = value; }
676 internal ChoiceIdentifierAccessor ChoiceIdentifier {
677 get { return choiceIdentifier; }
678 set { choiceIdentifier = value; }
681 internal XmlnsAccessor Xmlns {
682 get { return xmlns; }
683 set { xmlns = value; }
686 internal bool Ignore {
687 get { return ignore; }
688 set { ignore = value; }
691 internal Accessor Accessor {
692 get {
693 if (xmlns != null) return xmlns;
694 if (attribute != null) return attribute;
695 if (elements != null && elements.Length > 0) return elements[0];
696 return text;
700 static bool IsNeedNullableMember(ElementAccessor element) {
701 if (element.Mapping is ArrayMapping) {
702 ArrayMapping arrayMapping = (ArrayMapping)element.Mapping;
703 if (arrayMapping.Elements != null && arrayMapping.Elements.Length == 1) {
704 return IsNeedNullableMember(arrayMapping.Elements[0]);
706 return false;
708 else {
709 return element.IsNullable && element.Mapping.TypeDesc.IsValueType;
713 internal bool IsNeedNullable {
714 get {
715 if (xmlns != null) return false;
716 if (attribute != null) return false;
717 if (elements != null && elements.Length == 1) {
718 return IsNeedNullableMember(elements[0]);
720 return false;
724 internal static bool ElementsMatch(ElementAccessor[] a, ElementAccessor[] b) {
725 if (a == null) {
726 if (b == null)
727 return true;
728 return false;
730 if (b == null)
731 return false;
732 if (a.Length != b.Length)
733 return false;
734 for (int i = 0; i < a.Length; i++) {
735 if (a[i].Name != b[i].Name || a[i].Namespace != b[i].Namespace || a[i].Form != b[i].Form || a[i].IsNullable != b[i].IsNullable)
736 return false;
738 return true;
741 internal bool Match(AccessorMapping mapping) {
742 if (Elements != null && Elements.Length > 0) {
743 if (!ElementsMatch(Elements, mapping.Elements)) {
744 return false;
746 if (Text == null) {
747 return (mapping.Text == null);
750 if (Attribute != null) {
751 if (mapping.Attribute == null)
752 return false;
753 return (Attribute.Name == mapping.Attribute.Name && Attribute.Namespace == mapping.Attribute.Namespace && Attribute.Form == mapping.Attribute.Form);
755 if (Text != null) {
756 return (mapping.Text != null);
758 return (mapping.Accessor == null);
762 internal class MemberMappingComparer : IComparer {
763 public int Compare(object o1, object o2) {
764 MemberMapping m1 = (MemberMapping)o1;
765 MemberMapping m2 = (MemberMapping)o2;
767 bool m1Text = m1.IsText;
768 if (m1Text) {
769 if (m2.IsText)
770 return 0;
771 return 1;
773 else if (m2.IsText)
774 return -1;
776 if (m1.SequenceId < 0 && m2.SequenceId < 0)
777 return 0;
778 if (m1.SequenceId < 0)
779 return 1;
780 if (m2.SequenceId < 0)
781 return -1;
782 if (m1.SequenceId < m2.SequenceId)
783 return -1;
784 if (m1.SequenceId > m2.SequenceId)
785 return 1;
786 return 0;
790 internal class MemberMapping : AccessorMapping {
791 string name;
792 bool checkShouldPersist;
793 SpecifiedAccessor checkSpecified;
794 bool isReturnValue;
795 bool readOnly = false;
796 int sequenceId = -1;
797 MemberInfo memberInfo;
798 MemberInfo checkSpecifiedMemberInfo;
799 MethodInfo checkShouldPersistMethodInfo;
801 internal MemberMapping() { }
803 MemberMapping(MemberMapping mapping)
804 : base(mapping)
806 this.name = mapping.name;
807 this.checkShouldPersist = mapping.checkShouldPersist;
808 this.checkSpecified = mapping.checkSpecified;
809 this.isReturnValue = mapping.isReturnValue;
810 this.readOnly = mapping.readOnly;
811 this.sequenceId = mapping.sequenceId;
812 this.memberInfo = mapping.memberInfo;
813 this.checkSpecifiedMemberInfo = mapping.checkSpecifiedMemberInfo;
814 this.checkShouldPersistMethodInfo = mapping.checkShouldPersistMethodInfo;
817 internal bool CheckShouldPersist {
818 get { return checkShouldPersist; }
819 set { checkShouldPersist = value; }
822 internal SpecifiedAccessor CheckSpecified {
823 get { return checkSpecified; }
824 set { checkSpecified = value; }
827 internal string Name {
828 get { return name == null ? string.Empty : name; }
829 set { name = value; }
832 internal MemberInfo MemberInfo {
833 get { return memberInfo; }
834 set { memberInfo = value; }
837 internal MemberInfo CheckSpecifiedMemberInfo {
838 get { return checkSpecifiedMemberInfo; }
839 set { checkSpecifiedMemberInfo = value; }
842 internal MethodInfo CheckShouldPersistMethodInfo {
843 get { return checkShouldPersistMethodInfo; }
844 set { checkShouldPersistMethodInfo = value; }
847 internal bool IsReturnValue {
848 get { return isReturnValue; }
849 set { isReturnValue = value; }
852 internal bool ReadOnly {
853 get { return readOnly; }
854 set { readOnly = value; }
857 internal bool IsSequence {
858 get { return sequenceId >= 0; }
861 internal int SequenceId {
862 get { return sequenceId; }
863 set { sequenceId = value; }
866 string GetNullableType(TypeDesc td) {
867 // SOAP encoded arrays not mapped to Nullable<T> since they always derive from soapenc:Array
868 if (td.IsMappedType || (!td.IsValueType && (Elements[0].IsSoap || td.ArrayElementTypeDesc == null)))
869 return td.FullName;
870 if (td.ArrayElementTypeDesc != null) {
871 return GetNullableType(td.ArrayElementTypeDesc) + "[]";
873 return "System.Nullable`1[" + td.FullName + "]";
876 internal MemberMapping Clone()
878 return new MemberMapping(this);
881 internal string GetTypeName(CodeDomProvider codeProvider) {
882 if (IsNeedNullable && codeProvider.Supports(GeneratorSupport.GenericTypeReference)) {
883 return GetNullableType(TypeDesc);
885 return TypeDesc.FullName;
889 internal class MembersMapping : TypeMapping {
890 MemberMapping[] members;
891 bool hasWrapperElement = true;
892 bool validateRpcWrapperElement;
893 bool writeAccessors = true;
894 MemberMapping xmlnsMember = null;
896 internal MemberMapping[] Members {
897 get { return members; }
898 set { members = value; }
901 internal MemberMapping XmlnsMember {
902 get { return xmlnsMember; }
903 set { xmlnsMember = value; }
906 internal bool HasWrapperElement {
907 get { return hasWrapperElement; }
908 set { hasWrapperElement = value; }
911 internal bool ValidateRpcWrapperElement {
912 get { return validateRpcWrapperElement; }
913 set { validateRpcWrapperElement = value; }
916 internal bool WriteAccessors {
917 get { return writeAccessors; }
918 set { writeAccessors = value; }
922 internal class SpecialMapping : TypeMapping {
923 bool namedAny;
925 internal bool NamedAny {
926 get { return namedAny; }
927 set { namedAny = value; }
931 internal class SerializableMapping : SpecialMapping {
932 XmlSchema schema;
933 Type type;
934 bool needSchema = true;
936 // new implementation of the IXmlSerializable
937 MethodInfo getSchemaMethod;
938 XmlQualifiedName xsiType;
939 XmlSchemaType xsdType;
940 XmlSchemaSet schemas;
941 bool any;
942 string namespaces;
944 SerializableMapping baseMapping;
945 SerializableMapping derivedMappings;
946 SerializableMapping nextDerivedMapping;
947 SerializableMapping next; // all mappings with the same qname
949 internal SerializableMapping() { }
950 internal SerializableMapping(MethodInfo getSchemaMethod, bool any, string ns) {
951 this.getSchemaMethod = getSchemaMethod;
952 this.any = any;
953 this.Namespace = ns;
954 needSchema = getSchemaMethod != null;
957 internal SerializableMapping(XmlQualifiedName xsiType, XmlSchemaSet schemas) {
958 this.xsiType = xsiType;
959 this.schemas = schemas;
960 this.TypeName = xsiType.Name;
961 this.Namespace = xsiType.Namespace;
962 needSchema = false;
965 internal void SetBaseMapping(SerializableMapping mapping) {
966 baseMapping = mapping;
967 if (baseMapping != null) {
968 nextDerivedMapping = baseMapping.derivedMappings;
969 baseMapping.derivedMappings = this;
970 if (this == nextDerivedMapping) {
971 throw new InvalidOperationException(Res.GetString(Res.XmlCircularDerivation, TypeDesc.FullName));
976 internal bool IsAny {
977 get {
978 if (any)
979 return true;
980 if (getSchemaMethod == null)
981 return false;
982 if (needSchema && typeof(XmlSchemaType).IsAssignableFrom(getSchemaMethod.ReturnType))
983 return false;
984 RetrieveSerializableSchema();
985 return any;
989 internal string NamespaceList {
990 get {
991 RetrieveSerializableSchema();
992 if (namespaces == null) {
993 if (schemas != null) {
994 StringBuilder anyNamespaces = new StringBuilder();
995 foreach (XmlSchema s in schemas.Schemas()) {
996 if (s.TargetNamespace != null && s.TargetNamespace.Length > 0) {
997 if (anyNamespaces.Length > 0)
998 anyNamespaces.Append(" ");
999 anyNamespaces.Append(s.TargetNamespace);
1002 namespaces = anyNamespaces.ToString();
1004 else {
1005 namespaces = string.Empty;
1008 return namespaces;
1012 internal SerializableMapping DerivedMappings {
1013 get {
1014 return derivedMappings;
1018 internal SerializableMapping NextDerivedMapping {
1019 get {
1020 return nextDerivedMapping;
1024 internal SerializableMapping Next {
1025 get { return next; }
1026 set { next = value; }
1029 internal Type Type {
1030 get { return type; }
1031 set { type = value; }
1034 internal XmlSchemaSet Schemas {
1035 get {
1036 RetrieveSerializableSchema();
1037 return schemas;
1041 internal XmlSchema Schema {
1042 get {
1043 RetrieveSerializableSchema();
1044 return schema;
1048 internal XmlQualifiedName XsiType {
1049 get {
1050 if (!needSchema)
1051 return xsiType;
1052 if (getSchemaMethod == null)
1053 return null;
1054 if (typeof(XmlSchemaType).IsAssignableFrom(getSchemaMethod.ReturnType))
1055 return null;
1056 RetrieveSerializableSchema();
1057 return xsiType;
1061 internal XmlSchemaType XsdType {
1062 get {
1063 RetrieveSerializableSchema();
1064 return xsdType;
1068 internal static void ValidationCallbackWithErrorCode(object sender, ValidationEventArgs args) {
1070 if (args.Severity == XmlSeverityType.Error)
1071 throw new InvalidOperationException(Res.GetString(Res.XmlSerializableSchemaError, typeof(IXmlSerializable).Name, args.Message));
1074 internal void CheckDuplicateElement(XmlSchemaElement element, string elementNs) {
1075 if (element == null)
1076 return;
1078 // only check duplicate definitions for top-level element
1079 if (element.Parent == null || !(element.Parent is XmlSchema))
1080 return;
1082 XmlSchemaObjectTable elements = null;
1083 if (Schema != null && Schema.TargetNamespace == elementNs) {
1084 XmlSchemas.Preprocess(Schema);
1085 elements = Schema.Elements;
1087 else if (Schemas != null) {
1088 elements = Schemas.GlobalElements;
1090 else {
1091 return;
1093 foreach (XmlSchemaElement e in elements.Values) {
1094 if (e.Name == element.Name && e.QualifiedName.Namespace == elementNs) {
1095 if (Match(e, element))
1096 return;
1097 // XmlSerializableRootDupName=Cannot reconcile schema for '{0}'. Please use [XmlRoot] attribute to change name or namepace of the top-level element to avoid duplicate element declarations: element name='{1} namespace='{2}'.
1098 throw new InvalidOperationException(Res.GetString(Res.XmlSerializableRootDupName, getSchemaMethod.DeclaringType.FullName, e.Name, elementNs));
1103 bool Match(XmlSchemaElement e1, XmlSchemaElement e2) {
1104 if (e1.IsNillable != e2.IsNillable)
1105 return false;
1106 if (e1.RefName != e2.RefName)
1107 return false;
1108 if (e1.SchemaType != e2.SchemaType)
1109 return false;
1110 if (e1.SchemaTypeName != e2.SchemaTypeName)
1111 return false;
1112 if (e1.MinOccurs != e2.MinOccurs)
1113 return false;
1114 if (e1.MaxOccurs != e2.MaxOccurs)
1115 return false;
1116 if (e1.IsAbstract != e2.IsAbstract)
1117 return false;
1118 if (e1.DefaultValue != e2.DefaultValue)
1119 return false;
1120 if (e1.SubstitutionGroup != e2.SubstitutionGroup)
1121 return false;
1122 return true;
1125 void RetrieveSerializableSchema() {
1126 if (needSchema) {
1127 needSchema = false;
1128 if (getSchemaMethod != null) {
1129 // get the type info
1130 if (schemas == null)
1131 schemas = new XmlSchemaSet();
1132 object typeInfo = getSchemaMethod.Invoke(null, new object[] { schemas });
1133 xsiType = XmlQualifiedName.Empty;
1135 if (typeInfo != null) {
1136 if (typeof(XmlSchemaType).IsAssignableFrom(getSchemaMethod.ReturnType)) {
1137 xsdType = (XmlSchemaType)typeInfo;
1138 // check if type is named
1139 xsiType = xsdType.QualifiedName;
1141 else if (typeof(XmlQualifiedName).IsAssignableFrom(getSchemaMethod.ReturnType)) {
1142 xsiType = (XmlQualifiedName)typeInfo;
1143 if (xsiType.IsEmpty) {
1144 throw new InvalidOperationException(Res.GetString(Res.XmlGetSchemaEmptyTypeName, type.FullName, getSchemaMethod.Name));
1147 else {
1148 throw new InvalidOperationException(Res.GetString(Res.XmlGetSchemaMethodReturnType, type.Name, getSchemaMethod.Name, typeof(XmlSchemaProviderAttribute).Name, typeof(XmlQualifiedName).FullName));
1151 else {
1152 any = true;
1155 // make sure that user-specified schemas are valid
1156 schemas.ValidationEventHandler += new ValidationEventHandler(ValidationCallbackWithErrorCode);
1157 schemas.Compile();
1158 // at this point we verified that the information returned by the IXmlSerializable is valid
1159 // Now check to see if the type was referenced before:
1161 if (!xsiType.IsEmpty) {
1162 // try to find the type in the schemas collection
1163 if (xsiType.Namespace != XmlSchema.Namespace) {
1164 ArrayList srcSchemas = (ArrayList)schemas.Schemas(xsiType.Namespace);
1166 if (srcSchemas.Count == 0) {
1167 throw new InvalidOperationException(Res.GetString(Res.XmlMissingSchema, xsiType.Namespace));
1169 if (srcSchemas.Count > 1) {
1170 throw new InvalidOperationException(Res.GetString(Res.XmlGetSchemaInclude, xsiType.Namespace, getSchemaMethod.DeclaringType.FullName, getSchemaMethod.Name));
1172 XmlSchema s = (XmlSchema)srcSchemas[0];
1173 if (s == null) {
1174 throw new InvalidOperationException(Res.GetString(Res.XmlMissingSchema, xsiType.Namespace));
1176 xsdType = (XmlSchemaType)s.SchemaTypes[xsiType];
1177 if (xsdType == null) {
1178 throw new InvalidOperationException(Res.GetString(Res.XmlGetSchemaTypeMissing, getSchemaMethod.DeclaringType.FullName, getSchemaMethod.Name, xsiType.Name, xsiType.Namespace));
1180 xsdType = xsdType.Redefined != null ? xsdType.Redefined : xsdType;
1184 else {
1185 IXmlSerializable serializable = (IXmlSerializable)Activator.CreateInstance(type);
1186 schema = serializable.GetSchema();
1188 if (schema != null) {
1189 if (schema.Id == null || schema.Id.Length == 0) throw new InvalidOperationException(Res.GetString(Res.XmlSerializableNameMissing1, type.FullName));