|
//------------------------------------------------------------------------------
// <copyright file="Mappings.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
namespace System.Xml.Serialization {
using System.Reflection;
using System.Collections;
using System.Xml.Schema;
using System;
using System.Text;
using System.ComponentModel;
using System.Xml;
using System.CodeDom.Compiler;
// These classes represent a mapping between classes and a particular XML format.
// There are two class of mapping information: accessors (such as elements and
// attributes), and mappings (which specify the type of an accessor).
internal abstract class Accessor {
string name;
object defaultValue = null;
string ns;
TypeMapping mapping;
bool any;
string anyNs;
bool topLevelInSchema;
bool isFixed;
bool isOptional;
XmlSchemaForm form = XmlSchemaForm.None;
internal Accessor() { }
internal TypeMapping Mapping {
get { return mapping; }
set { mapping = value; }
}
internal object Default {
get { return defaultValue; }
set { defaultValue = value; }
}
internal bool HasDefault {
get { return defaultValue != null && defaultValue != DBNull.Value; }
}
internal virtual string Name {
get { return name == null ? string.Empty : name; }
set { name = value; }
}
internal bool Any {
get { return any; }
set { any = value; }
}
internal string AnyNamespaces {
get { return anyNs; }
set { anyNs = value; }
}
internal string Namespace {
get { return ns; }
set { ns = value; }
}
internal XmlSchemaForm Form {
get { return form; }
set { form = value; }
}
internal bool IsFixed {
get { return isFixed; }
set { isFixed = value; }
}
internal bool IsOptional {
get { return isOptional; }
set { isOptional = value; }
}
internal bool IsTopLevelInSchema {
get { return topLevelInSchema; }
set { topLevelInSchema = value; }
}
internal static string EscapeName(string name) {
if (name == null || name.Length == 0) return name;
return XmlConvert.EncodeLocalName(name);
}
internal static string EscapeQName(string name) {
if (name == null || name.Length == 0) return name;
int colon = name.LastIndexOf(':');
if (colon < 0)
return XmlConvert.EncodeLocalName(name);
else {
if (colon == 0 || colon == name.Length - 1)
throw new ArgumentException(Res.GetString(Res.Xml_InvalidNameChars, name), "name");
return new XmlQualifiedName(XmlConvert.EncodeLocalName(name.Substring(colon + 1)), XmlConvert.EncodeLocalName(name.Substring(0, colon))).ToString();
}
}
internal static string UnescapeName(string name) {
return XmlConvert.DecodeName(name);
}
internal string ToString(string defaultNs) {
if (Any) {
return (Namespace == null ? "##any" : Namespace) + ":" + Name;
}
else {
return Namespace == defaultNs ? Name : Namespace + ":" + Name;
}
}
}
internal class ElementAccessor : Accessor {
bool nullable;
bool isSoap;
bool unbounded = false;
internal bool IsSoap {
get { return isSoap; }
set { isSoap = value; }
}
internal bool IsNullable {
get { return nullable; }
set { nullable = value; }
}
internal bool IsUnbounded {
get { return unbounded; }
set { unbounded = value; }
}
internal ElementAccessor Clone() {
ElementAccessor newAccessor = new ElementAccessor();
newAccessor.nullable = this.nullable;
newAccessor.IsTopLevelInSchema = this.IsTopLevelInSchema;
newAccessor.Form = this.Form;
newAccessor.isSoap = this.isSoap;
newAccessor.Name = this.Name;
newAccessor.Default = this.Default;
newAccessor.Namespace = this.Namespace;
newAccessor.Mapping = this.Mapping;
newAccessor.Any = this.Any;
return newAccessor;
}
}
internal class ChoiceIdentifierAccessor : Accessor {
string memberName;
string[] memberIds;
MemberInfo memberInfo;
internal string MemberName {
get { return memberName; }
set { memberName = value; }
}
internal string[] MemberIds {
get { return memberIds; }
set { memberIds = value; }
}
internal MemberInfo MemberInfo {
get { return memberInfo; }
set { memberInfo = value; }
}
}
internal class TextAccessor : Accessor {
}
internal class XmlnsAccessor : Accessor {
}
internal class AttributeAccessor : Accessor {
bool isSpecial;
bool isList;
internal bool IsSpecialXmlNamespace {
get { return isSpecial; }
}
internal bool IsList {
get { return isList; }
set { isList = value; }
}
internal void CheckSpecial() {
int colon = Name.LastIndexOf(':');
if (colon >= 0) {
if (!Name.StartsWith("xml:", StringComparison.Ordinal)) {
throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidNameChars, Name));
}
Name = Name.Substring("xml:".Length);
Namespace = XmlReservedNs.NsXml;
isSpecial = true;
}
else {
if (Namespace == XmlReservedNs.NsXml) {
isSpecial = true;
}
else {
isSpecial = false;
}
}
if (isSpecial) {
Form = XmlSchemaForm.Qualified;
}
}
}
internal abstract class Mapping {
bool isSoap;
internal Mapping() { }
protected Mapping(Mapping mapping)
{
this.isSoap = mapping.isSoap;
}
internal bool IsSoap {
get { return isSoap; }
set { isSoap = value; }
}
}
internal abstract class TypeMapping : Mapping {
TypeDesc typeDesc;
string typeNs;
string typeName;
bool referencedByElement;
bool referencedByTopLevelElement;
bool includeInSchema = true;
bool reference = false;
internal bool ReferencedByTopLevelElement {
get { return referencedByTopLevelElement; }
set { referencedByTopLevelElement = value; }
}
internal bool ReferencedByElement {
get { return referencedByElement || referencedByTopLevelElement; }
set { referencedByElement = value; }
}
internal string Namespace {
get { return typeNs; }
set { typeNs = value; }
}
internal string TypeName {
get { return typeName; }
set { typeName = value; }
}
internal TypeDesc TypeDesc {
get { return typeDesc; }
set { typeDesc = value; }
}
internal bool IncludeInSchema {
get { return includeInSchema; }
set { includeInSchema = value; }
}
internal virtual bool IsList {
get { return false; }
set { }
}
internal bool IsReference {
get { return reference; }
set { reference = value; }
}
internal bool IsAnonymousType {
get { return typeName == null || typeName.Length == 0; }
}
internal virtual string DefaultElementName {
get { return IsAnonymousType ? XmlConvert.EncodeLocalName(typeDesc.Name) : typeName; }
}
}
internal class PrimitiveMapping : TypeMapping {
bool isList;
internal override bool IsList {
get { return isList; }
set { isList = value; }
}
}
internal class NullableMapping : TypeMapping {
TypeMapping baseMapping;
internal TypeMapping BaseMapping {
get { return baseMapping; }
set { baseMapping = value; }
}
internal override string DefaultElementName {
get { return BaseMapping.DefaultElementName; }
}
}
internal class ArrayMapping : TypeMapping {
ElementAccessor[] elements;
ElementAccessor[] sortedElements;
ArrayMapping next;
StructMapping topLevelMapping;
internal ElementAccessor[] Elements {
get { return elements; }
set { elements = value; sortedElements = null; }
}
internal ElementAccessor[] ElementsSortedByDerivation {
get {
if (sortedElements != null)
return sortedElements;
if (elements == null)
return null;
sortedElements = new ElementAccessor[elements.Length];
Array.Copy(elements, 0, sortedElements, 0, elements.Length);
AccessorMapping.SortMostToLeastDerived(sortedElements);
return sortedElements;
}
}
internal ArrayMapping Next {
get { return next; }
set { next = value; }
}
internal StructMapping TopLevelMapping {
get { return topLevelMapping; }
set { topLevelMapping = value; }
}
}
internal class EnumMapping : PrimitiveMapping {
ConstantMapping[] constants;
bool isFlags;
internal bool IsFlags {
get { return isFlags; }
set { isFlags = value; }
}
internal ConstantMapping[] Constants {
get { return constants; }
set { constants = value; }
}
}
internal class ConstantMapping : Mapping {
string xmlName;
string name;
long value;
internal string XmlName {
get { return xmlName == null ? string.Empty : xmlName; }
set { xmlName = value; }
}
internal string Name {
get { return name == null ? string.Empty : name; }
set { this.name = value; }
}
internal long Value {
get { return value; }
set { this.value = value; }
}
}
internal class StructMapping : TypeMapping, INameScope {
MemberMapping[] members;
StructMapping baseMapping;
StructMapping derivedMappings;
StructMapping nextDerivedMapping;
MemberMapping xmlnsMember = null;
bool hasSimpleContent;
bool openModel;
bool isSequence;
NameTable elements;
NameTable attributes;
CodeIdentifiers scope;
internal StructMapping BaseMapping {
get { return baseMapping; }
set {
baseMapping = value;
if (!IsAnonymousType && baseMapping != null) {
nextDerivedMapping = baseMapping.derivedMappings;
baseMapping.derivedMappings = this;
}
if (value.isSequence && !isSequence) {
isSequence = true;
if (baseMapping.IsSequence) {
for (StructMapping derived = derivedMappings; derived != null; derived = derived.NextDerivedMapping) {
derived.SetSequence();
}
}
}
}
}
internal StructMapping DerivedMappings {
get { return derivedMappings; }
}
internal bool IsFullyInitialized {
get { return baseMapping != null && Members != null; }
}
internal NameTable LocalElements {
get {
if (elements == null)
elements = new NameTable();
return elements;
}
}
internal NameTable LocalAttributes {
get {
if (attributes == null)
attributes = new NameTable();
return attributes;
}
}
object INameScope.this[string name, string ns] {
get {
object named = LocalElements[name, ns];
if (named != null)
return named;
if (baseMapping != null)
return ((INameScope)baseMapping)[name, ns];
return null;
}
set {
LocalElements[name, ns] = value;
}
}
internal StructMapping NextDerivedMapping {
get { return nextDerivedMapping; }
}
internal bool HasSimpleContent {
get { return hasSimpleContent; }
}
internal bool HasXmlnsMember {
get {
StructMapping mapping = this;
while (mapping != null) {
if (mapping.XmlnsMember != null)
return true;
mapping = mapping.BaseMapping;
}
return false;
}
}
internal MemberMapping[] Members {
get { return members; }
set { members = value; }
}
internal MemberMapping XmlnsMember {
get { return xmlnsMember; }
set { xmlnsMember = value; }
}
internal bool IsOpenModel {
get { return openModel; }
set { openModel = value; }
}
internal CodeIdentifiers Scope {
get {
if (scope == null)
scope = new CodeIdentifiers();
return scope;
}
set { scope = value; }
}
internal MemberMapping FindDeclaringMapping(MemberMapping member, out StructMapping declaringMapping, string parent) {
declaringMapping = null;
if (BaseMapping != null) {
MemberMapping baseMember = BaseMapping.FindDeclaringMapping(member, out declaringMapping, parent);
if (baseMember != null) return baseMember;
}
if (members == null) return null;
for (int i = 0; i < members.Length; i++) {
if (members[i].Name == member.Name) {
if (members[i].TypeDesc != member.TypeDesc)
throw new InvalidOperationException(Res.GetString(Res.XmlHiddenMember, parent, member.Name, member.TypeDesc.FullName, this.TypeName, members[i].Name, members[i].TypeDesc.FullName));
else if (!members[i].Match(member)) {
throw new InvalidOperationException(Res.GetString(Res.XmlInvalidXmlOverride, parent, member.Name, this.TypeName, members[i].Name));
}
declaringMapping = this;
return members[i];
}
}
return null;
}
internal bool Declares(MemberMapping member, string parent) {
StructMapping m;
return (FindDeclaringMapping(member, out m, parent) != null);
}
internal void SetContentModel(TextAccessor text, bool hasElements) {
if (BaseMapping == null || BaseMapping.TypeDesc.IsRoot) {
hasSimpleContent = !hasElements && text != null && !text.Mapping.IsList;
}
else if (BaseMapping.HasSimpleContent) {
if (text != null || hasElements) {
// we can only extent a simleContent type with attributes
throw new InvalidOperationException(Res.GetString(Res.XmlIllegalSimpleContentExtension, TypeDesc.FullName, BaseMapping.TypeDesc.FullName));
}
else {
hasSimpleContent = true;
}
}
else {
hasSimpleContent = false;
}
if (!hasSimpleContent && text != null && !text.Mapping.TypeDesc.CanBeTextValue) {
throw new InvalidOperationException(Res.GetString(Res.XmlIllegalTypedTextAttribute, TypeDesc.FullName, text.Name, text.Mapping.TypeDesc.FullName));
}
}
internal bool HasElements {
get { return elements != null && elements.Values.Count > 0; }
}
internal bool HasExplicitSequence() {
if (members != null) {
for (int i = 0; i < members.Length; i++) {
if (members[i].IsParticle && members[i].IsSequence) {
return true;
}
}
}
return (baseMapping != null && baseMapping.HasExplicitSequence());
}
internal void SetSequence() {
if (TypeDesc.IsRoot)
return;
StructMapping start = this;
// find first mapping that does not have the sequence set
while (!start.BaseMapping.IsSequence && start.BaseMapping != null && !start.BaseMapping.TypeDesc.IsRoot)
start = start.BaseMapping;
start.IsSequence = true;
for (StructMapping derived = start.DerivedMappings; derived != null; derived = derived.NextDerivedMapping) {
derived.SetSequence();
}
}
internal bool IsSequence {
get { return isSequence && !TypeDesc.IsRoot; }
set { isSequence = value; }
}
}
internal abstract class AccessorMapping : Mapping {
TypeDesc typeDesc;
AttributeAccessor attribute;
ElementAccessor[] elements;
ElementAccessor[] sortedElements;
TextAccessor text;
ChoiceIdentifierAccessor choiceIdentifier;
XmlnsAccessor xmlns;
bool ignore;
internal AccessorMapping()
{ }
protected AccessorMapping(AccessorMapping mapping)
: base(mapping)
{
this.typeDesc = mapping.typeDesc;
this.attribute = mapping.attribute;
this.elements = mapping.elements;
this.sortedElements = mapping.sortedElements;
this.text = mapping.text;
this.choiceIdentifier = mapping.choiceIdentifier;
this.xmlns = mapping.xmlns;
this.ignore = mapping.ignore;
}
internal bool IsAttribute {
get { return attribute != null; }
}
internal bool IsText {
get { return text != null && (elements == null || elements.Length == 0); }
}
internal bool IsParticle {
get { return (elements != null && elements.Length > 0); }
}
internal TypeDesc TypeDesc {
get { return typeDesc; }
set { typeDesc = value; }
}
internal AttributeAccessor Attribute {
get { return attribute; }
set { attribute = value; }
}
internal ElementAccessor[] Elements {
get { return elements; }
set { elements = value; sortedElements = null; }
}
internal static void SortMostToLeastDerived(ElementAccessor[] elements) {
Array.Sort(elements, new AccessorComparer());
}
internal class AccessorComparer : IComparer {
public int Compare(object o1, object o2) {
if (o1 == o2)
return 0;
Accessor a1 = (Accessor)o1;
Accessor a2 = (Accessor)o2;
int w1 = a1.Mapping.TypeDesc.Weight;
int w2 = a2.Mapping.TypeDesc.Weight;
if (w1 == w2)
return 0;
if (w1 < w2)
return 1;
return -1;
}
}
internal ElementAccessor[] ElementsSortedByDerivation {
get {
if (sortedElements != null)
return sortedElements;
if (elements == null)
return null;
sortedElements = new ElementAccessor[elements.Length];
Array.Copy(elements, 0, sortedElements, 0, elements.Length);
SortMostToLeastDerived(sortedElements);
return sortedElements;
}
}
internal TextAccessor Text {
get { return text; }
set { text = value; }
}
internal ChoiceIdentifierAccessor ChoiceIdentifier {
get { return choiceIdentifier; }
set { choiceIdentifier = value; }
}
internal XmlnsAccessor Xmlns {
get { return xmlns; }
set { xmlns = value; }
}
internal bool Ignore {
get { return ignore; }
set { ignore = value; }
}
internal Accessor Accessor {
get {
if (xmlns != null) return xmlns;
if (attribute != null) return attribute;
if (elements != null && elements.Length > 0) return elements[0];
return text;
}
}
static bool IsNeedNullableMember(ElementAccessor element) {
if (element.Mapping is ArrayMapping) {
ArrayMapping arrayMapping = (ArrayMapping)element.Mapping;
if (arrayMapping.Elements != null && arrayMapping.Elements.Length == 1) {
return IsNeedNullableMember(arrayMapping.Elements[0]);
}
return false;
}
else {
return element.IsNullable && element.Mapping.TypeDesc.IsValueType;
}
}
internal bool IsNeedNullable {
get {
if (xmlns != null) return false;
if (attribute != null) return false;
if (elements != null && elements.Length == 1) {
return IsNeedNullableMember(elements[0]);
}
return false;
}
}
internal static bool ElementsMatch(ElementAccessor[] a, ElementAccessor[] b) {
if (a == null) {
if (b == null)
return true;
return false;
}
if (b == null)
return false;
if (a.Length != b.Length)
return false;
for (int i = 0; i < a.Length; i++) {
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)
return false;
}
return true;
}
internal bool Match(AccessorMapping mapping) {
if (Elements != null && Elements.Length > 0) {
if (!ElementsMatch(Elements, mapping.Elements)) {
return false;
}
if (Text == null) {
return (mapping.Text == null);
}
}
if (Attribute != null) {
if (mapping.Attribute == null)
return false;
return (Attribute.Name == mapping.Attribute.Name && Attribute.Namespace == mapping.Attribute.Namespace && Attribute.Form == mapping.Attribute.Form);
}
if (Text != null) {
return (mapping.Text != null);
}
return (mapping.Accessor == null);
}
}
internal class MemberMappingComparer : IComparer {
public int Compare(object o1, object o2) {
MemberMapping m1 = (MemberMapping)o1;
MemberMapping m2 = (MemberMapping)o2;
bool m1Text = m1.IsText;
if (m1Text) {
if (m2.IsText)
return 0;
return 1;
}
else if (m2.IsText)
return -1;
if (m1.SequenceId < 0 && m2.SequenceId < 0)
return 0;
if (m1.SequenceId < 0)
return 1;
if (m2.SequenceId < 0)
return -1;
if (m1.SequenceId < m2.SequenceId)
return -1;
if (m1.SequenceId > m2.SequenceId)
return 1;
return 0;
}
}
internal class MemberMapping : AccessorMapping {
string name;
bool checkShouldPersist;
SpecifiedAccessor checkSpecified;
bool isReturnValue;
bool readOnly = false;
int sequenceId = -1;
MemberInfo memberInfo;
MemberInfo checkSpecifiedMemberInfo;
MethodInfo checkShouldPersistMethodInfo;
internal MemberMapping() { }
MemberMapping(MemberMapping mapping)
: base(mapping)
{
this.name = mapping.name;
this.checkShouldPersist = mapping.checkShouldPersist;
this.checkSpecified = mapping.checkSpecified;
this.isReturnValue = mapping.isReturnValue;
this.readOnly = mapping.readOnly;
this.sequenceId = mapping.sequenceId;
this.memberInfo = mapping.memberInfo;
this.checkSpecifiedMemberInfo = mapping.checkSpecifiedMemberInfo;
this.checkShouldPersistMethodInfo = mapping.checkShouldPersistMethodInfo;
}
internal bool CheckShouldPersist {
get { return checkShouldPersist; }
set { checkShouldPersist = value; }
}
internal SpecifiedAccessor CheckSpecified {
get { return checkSpecified; }
set { checkSpecified = value; }
}
internal string Name {
get { return name == null ? string.Empty : name; }
set { name = value; }
}
internal MemberInfo MemberInfo {
get { return memberInfo; }
set { memberInfo = value; }
}
internal MemberInfo CheckSpecifiedMemberInfo {
get { return checkSpecifiedMemberInfo; }
set { checkSpecifiedMemberInfo = value; }
}
internal MethodInfo CheckShouldPersistMethodInfo {
get { return checkShouldPersistMethodInfo; }
set { checkShouldPersistMethodInfo = value; }
}
internal bool IsReturnValue {
get { return isReturnValue; }
set { isReturnValue = value; }
}
internal bool ReadOnly {
get { return readOnly; }
set { readOnly = value; }
}
internal bool IsSequence {
get { return sequenceId >= 0; }
}
internal int SequenceId {
get { return sequenceId; }
set { sequenceId = value; }
}
string GetNullableType(TypeDesc td) {
// SOAP encoded arrays not mapped to Nullable<T> since they always derive from soapenc:Array
if (td.IsMappedType || (!td.IsValueType && (Elements[0].IsSoap || td.ArrayElementTypeDesc == null)))
return td.FullName;
if (td.ArrayElementTypeDesc != null) {
return GetNullableType(td.ArrayElementTypeDesc) + "[]";
}
return "System.Nullable`1[" + td.FullName + "]";
}
internal MemberMapping Clone()
{
return new MemberMapping(this);
}
internal string GetTypeName(CodeDomProvider codeProvider) {
if (IsNeedNullable && codeProvider.Supports(GeneratorSupport.GenericTypeReference)) {
return GetNullableType(TypeDesc);
}
return TypeDesc.FullName;
}
}
internal class MembersMapping : TypeMapping {
MemberMapping[] members;
bool hasWrapperElement = true;
bool validateRpcWrapperElement;
bool writeAccessors = true;
MemberMapping xmlnsMember = null;
internal MemberMapping[] Members {
get { return members; }
set { members = value; }
}
internal MemberMapping XmlnsMember {
get { return xmlnsMember; }
set { xmlnsMember = value; }
}
internal bool HasWrapperElement {
get { return hasWrapperElement; }
set { hasWrapperElement = value; }
}
internal bool ValidateRpcWrapperElement {
get { return validateRpcWrapperElement; }
set { validateRpcWrapperElement = value; }
}
internal bool WriteAccessors {
get { return writeAccessors; }
set { writeAccessors = value; }
}
}
internal class SpecialMapping : TypeMapping {
bool namedAny;
internal bool NamedAny {
get { return namedAny; }
set { namedAny = value; }
}
}
internal class SerializableMapping : SpecialMapping {
XmlSchema schema;
Type type;
bool needSchema = true;
// new implementation of the IXmlSerializable
MethodInfo getSchemaMethod;
XmlQualifiedName xsiType;
XmlSchemaType xsdType;
XmlSchemaSet schemas;
bool any;
string namespaces;
SerializableMapping baseMapping;
SerializableMapping derivedMappings;
SerializableMapping nextDerivedMapping;
SerializableMapping next; // all mappings with the same qname
internal SerializableMapping() { }
internal SerializableMapping(MethodInfo getSchemaMethod, bool any, string ns) {
this.getSchemaMethod = getSchemaMethod;
this.any = any;
this.Namespace = ns;
needSchema = getSchemaMethod != null;
}
internal SerializableMapping(XmlQualifiedName xsiType, XmlSchemaSet schemas) {
this.xsiType = xsiType;
this.schemas = schemas;
this.TypeName = xsiType.Name;
this.Namespace = xsiType.Namespace;
needSchema = false;
}
internal void SetBaseMapping(SerializableMapping mapping) {
baseMapping = mapping;
if (baseMapping != null) {
nextDerivedMapping = baseMapping.derivedMappings;
baseMapping.derivedMappings = this;
if (this == nextDerivedMapping) {
throw new InvalidOperationException(Res.GetString(Res.XmlCircularDerivation, TypeDesc.FullName));
}
}
}
internal bool IsAny {
get {
if (any)
return true;
if (getSchemaMethod == null)
return false;
if (needSchema && typeof(XmlSchemaType).IsAssignableFrom(getSchemaMethod.ReturnType))
return false;
RetrieveSerializableSchema();
return any;
}
}
internal string NamespaceList {
get {
RetrieveSerializableSchema();
if (namespaces == null) {
if (schemas != null) {
StringBuilder anyNamespaces = new StringBuilder();
foreach (XmlSchema s in schemas.Schemas()) {
if (s.TargetNamespace != null && s.TargetNamespace.Length > 0) {
if (anyNamespaces.Length > 0)
anyNamespaces.Append(" ");
anyNamespaces.Append(s.TargetNamespace);
}
}
namespaces = anyNamespaces.ToString();
}
else {
namespaces = string.Empty;
}
}
return namespaces;
}
}
internal SerializableMapping DerivedMappings {
get {
return derivedMappings;
}
}
internal SerializableMapping NextDerivedMapping {
get {
return nextDerivedMapping;
}
}
internal SerializableMapping Next {
get { return next; }
set { next = value; }
}
internal Type Type {
get { return type; }
set { type = value; }
}
internal XmlSchemaSet Schemas {
get {
RetrieveSerializableSchema();
return schemas;
}
}
internal XmlSchema Schema {
get {
RetrieveSerializableSchema();
return schema;
}
}
internal XmlQualifiedName XsiType {
get {
if (!needSchema)
return xsiType;
if (getSchemaMethod == null)
return null;
if (typeof(XmlSchemaType).IsAssignableFrom(getSchemaMethod.ReturnType))
return null;
RetrieveSerializableSchema();
return xsiType;
}
}
internal XmlSchemaType XsdType {
get {
RetrieveSerializableSchema();
return xsdType;
}
}
internal static void ValidationCallbackWithErrorCode(object sender, ValidationEventArgs args) {
//
if (args.Severity == XmlSeverityType.Error)
throw new InvalidOperationException(Res.GetString(Res.XmlSerializableSchemaError, typeof(IXmlSerializable).Name, args.Message));
}
internal void CheckDuplicateElement(XmlSchemaElement element, string elementNs) {
if (element == null)
return;
// only check duplicate definitions for top-level element
if (element.Parent == null || !(element.Parent is XmlSchema))
return;
XmlSchemaObjectTable elements = null;
if (Schema != null && Schema.TargetNamespace == elementNs) {
XmlSchemas.Preprocess(Schema);
elements = Schema.Elements;
}
else if (Schemas != null) {
elements = Schemas.GlobalElements;
}
else {
return;
}
foreach (XmlSchemaElement e in elements.Values) {
if (e.Name == element.Name && e.QualifiedName.Namespace == elementNs) {
if (Match(e, element))
return;
// 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}'.
throw new InvalidOperationException(Res.GetString(Res.XmlSerializableRootDupName, getSchemaMethod.DeclaringType.FullName, e.Name, elementNs));
}
}
}
bool Match(XmlSchemaElement e1, XmlSchemaElement e2) {
if (e1.IsNillable != e2.IsNillable)
return false;
if (e1.RefName != e2.RefName)
return false;
if (e1.SchemaType != e2.SchemaType)
return false;
if (e1.SchemaTypeName != e2.SchemaTypeName)
return false;
if (e1.MinOccurs != e2.MinOccurs)
return false;
if (e1.MaxOccurs != e2.MaxOccurs)
return false;
if (e1.IsAbstract != e2.IsAbstract)
return false;
if (e1.DefaultValue != e2.DefaultValue)
return false;
if (e1.SubstitutionGroup != e2.SubstitutionGroup)
return false;
return true;
}
void RetrieveSerializableSchema() {
if (needSchema) {
needSchema = false;
if (getSchemaMethod != null) {
// get the type info
if (schemas == null)
schemas = new XmlSchemaSet();
object typeInfo = getSchemaMethod.Invoke(null, new object[] { schemas });
xsiType = XmlQualifiedName.Empty;
if (typeInfo != null) {
if (typeof(XmlSchemaType).IsAssignableFrom(getSchemaMethod.ReturnType)) {
xsdType = (XmlSchemaType)typeInfo;
// check if type is named
xsiType = xsdType.QualifiedName;
}
else if (typeof(XmlQualifiedName).IsAssignableFrom(getSchemaMethod.ReturnType)) {
xsiType = (XmlQualifiedName)typeInfo;
if (xsiType.IsEmpty) {
throw new InvalidOperationException(Res.GetString(Res.XmlGetSchemaEmptyTypeName, type.FullName, getSchemaMethod.Name));
}
}
else {
throw new InvalidOperationException(Res.GetString(Res.XmlGetSchemaMethodReturnType, type.Name, getSchemaMethod.Name, typeof(XmlSchemaProviderAttribute).Name, typeof(XmlQualifiedName).FullName));
}
}
else {
any = true;
}
// make sure that user-specified schemas are valid
schemas.ValidationEventHandler += new ValidationEventHandler(ValidationCallbackWithErrorCode);
schemas.Compile();
// at this point we verified that the information returned by the IXmlSerializable is valid
// Now check to see if the type was referenced before:
//
if (!xsiType.IsEmpty) {
// try to find the type in the schemas collection
if (xsiType.Namespace != XmlSchema.Namespace) {
ArrayList srcSchemas = (ArrayList)schemas.Schemas(xsiType.Namespace);
if (srcSchemas.Count == 0) {
throw new InvalidOperationException(Res.GetString(Res.XmlMissingSchema, xsiType.Namespace));
}
if (srcSchemas.Count > 1) {
throw new InvalidOperationException(Res.GetString(Res.XmlGetSchemaInclude, xsiType.Namespace, getSchemaMethod.DeclaringType.FullName, getSchemaMethod.Name));
}
XmlSchema s = (XmlSchema)srcSchemas[0];
if (s == null) {
throw new InvalidOperationException(Res.GetString(Res.XmlMissingSchema, xsiType.Namespace));
}
xsdType = (XmlSchemaType)s.SchemaTypes[xsiType];
if (xsdType == null) {
throw new InvalidOperationException(Res.GetString(Res.XmlGetSchemaTypeMissing, getSchemaMethod.DeclaringType.FullName, getSchemaMethod.Name, xsiType.Name, xsiType.Namespace));
}
xsdType = xsdType.Redefined != null ? xsdType.Redefined : xsdType;
}
}
}
else {
IXmlSerializable serializable = (IXmlSerializable)Activator.CreateInstance(type);
schema = serializable.GetSchema();
if (schema != null) {
if (schema.Id == null || schema.Id.Length == 0) throw new InvalidOperationException(Res.GetString(Res.XmlSerializableNameMissing1, type.FullName));
}
}
}
}
}
}
|