File: System\Runtime\Serialization\SchemaExporter.cs
Project: ndp\cdf\src\WCF\Serialization\System.Runtime.Serialization.csproj (System.Runtime.Serialization)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.Runtime.Serialization
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Reflection;
    using System.Runtime.Diagnostics;
    using System.ServiceModel.Diagnostics;
    using System.Security;
    using System.Xml;
    using System.Xml.Schema;
    using System.Xml.Serialization;
    using System.Runtime.Serialization.Diagnostics;
 
    class SchemaExporter
    {
        XmlSchemaSet schemas;
        XmlDocument xmlDoc;
        DataContractSet dataContractSet;
 
        internal SchemaExporter(XmlSchemaSet schemas, DataContractSet dataContractSet)
        {
            this.schemas = schemas;
            this.dataContractSet = dataContractSet;
        }
 
        XmlSchemaSet Schemas
        {
            get { return schemas; }
        }
 
        XmlDocument XmlDoc
        {
            get
            {
                if (xmlDoc == null)
                    xmlDoc = new XmlDocument();
                return xmlDoc;
            }
        }
 
        internal void Export()
        {
            try
            {
                // Remove this if we decide to publish serialization schema at well-known location
                ExportSerializationSchema();
                foreach (KeyValuePair<XmlQualifiedName, DataContract> pair in dataContractSet)
                {
                    DataContract dataContract = pair.Value;
                    if (!dataContractSet.IsContractProcessed(dataContract))
                    {
                        ExportDataContract(dataContract);
                        dataContractSet.SetContractProcessed(dataContract);
                    }
                }
            }
            finally
            {
                xmlDoc = null;
                dataContractSet = null;
            }
        }
 
        void ExportSerializationSchema()
        {
            if (!Schemas.Contains(Globals.SerializationNamespace))
            {
                StringReader reader = new StringReader(Globals.SerializationSchema);
                XmlSchema schema = XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null);
                if (schema == null)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.CouldNotReadSerializationSchema, Globals.SerializationNamespace)));
                Schemas.Add(schema);
            }
        }
 
        void ExportDataContract(DataContract dataContract)
        {
            if (dataContract.IsBuiltInDataContract)
                return;
            else if (dataContract is XmlDataContract)
                ExportXmlDataContract((XmlDataContract)dataContract);
            else
            {
                XmlSchema schema = GetSchema(dataContract.StableName.Namespace);
 
                if (dataContract is ClassDataContract)
                {
                    ClassDataContract classDataContract = (ClassDataContract)dataContract;
                    if (classDataContract.IsISerializable)
                        ExportISerializableDataContract(classDataContract, schema);
                    else
                        ExportClassDataContract(classDataContract, schema);
                }
                else if (dataContract is CollectionDataContract)
                    ExportCollectionDataContract((CollectionDataContract)dataContract, schema);
                else if (dataContract is EnumDataContract)
                    ExportEnumDataContract((EnumDataContract)dataContract, schema);
                ExportTopLevelElement(dataContract, schema);
                Schemas.Reprocess(schema);
            }
        }
 
        XmlSchemaElement ExportTopLevelElement(DataContract dataContract, XmlSchema schema)
        {
            if (schema == null || dataContract.StableName.Namespace != dataContract.TopLevelElementNamespace.Value)
                schema = GetSchema(dataContract.TopLevelElementNamespace.Value);
 
            XmlSchemaElement topLevelElement = new XmlSchemaElement();
            topLevelElement.Name = dataContract.TopLevelElementName.Value;
            SetElementType(topLevelElement, dataContract, schema);
            topLevelElement.IsNillable = true;
            schema.Items.Add(topLevelElement);
            return topLevelElement;
        }
 
        void ExportClassDataContract(ClassDataContract classDataContract, XmlSchema schema)
        {
            XmlSchemaComplexType type = new XmlSchemaComplexType();
            type.Name = classDataContract.StableName.Name;
            schema.Items.Add(type);
            XmlElement genericInfoElement = null;
            if (classDataContract.UnderlyingType.IsGenericType)
                genericInfoElement = ExportGenericInfo(classDataContract.UnderlyingType, Globals.GenericTypeLocalName, Globals.SerializationNamespace);
 
            XmlSchemaSequence rootSequence = new XmlSchemaSequence();
            for (int i = 0; i < classDataContract.Members.Count; i++)
            {
                DataMember dataMember = classDataContract.Members[i];
 
                XmlSchemaElement element = new XmlSchemaElement();
                element.Name = dataMember.Name;
                XmlElement actualTypeElement = null;
                DataContract memberTypeContract = dataContractSet.GetMemberTypeDataContract(dataMember);
                if (CheckIfMemberHasConflict(dataMember))
                {
                    element.SchemaTypeName = AnytypeQualifiedName;
                    actualTypeElement = ExportActualType(memberTypeContract.StableName);
                    SchemaHelper.AddSchemaImport(memberTypeContract.StableName.Namespace, schema);
                }
                else
                    SetElementType(element, memberTypeContract, schema);
                SchemaHelper.AddElementForm(element, schema);
                if (dataMember.IsNullable)
                    element.IsNillable = true;
                if (!dataMember.IsRequired)
                    element.MinOccurs = 0;
 
                element.Annotation = GetSchemaAnnotation(actualTypeElement, ExportSurrogateData(dataMember), ExportEmitDefaultValue(dataMember));
                rootSequence.Items.Add(element);
            }
 
            XmlElement isValueTypeElement = null;
            if (classDataContract.BaseContract != null)
            {
                XmlSchemaComplexContentExtension extension = CreateTypeContent(type, classDataContract.BaseContract.StableName, schema);
                extension.Particle = rootSequence;
                if (classDataContract.IsReference && !classDataContract.BaseContract.IsReference)
                {
                    AddReferenceAttributes(extension.Attributes, schema);
                }
            }
            else
            {
                type.Particle = rootSequence;
                if (classDataContract.IsValueType)
                    isValueTypeElement = GetAnnotationMarkup(IsValueTypeName, XmlConvert.ToString(classDataContract.IsValueType), schema);
                if (classDataContract.IsReference)
                    AddReferenceAttributes(type.Attributes, schema);
            }
            type.Annotation = GetSchemaAnnotation(genericInfoElement, ExportSurrogateData(classDataContract), isValueTypeElement);
        }
 
        void AddReferenceAttributes(XmlSchemaObjectCollection attributes, XmlSchema schema)
        {
            SchemaHelper.AddSchemaImport(Globals.SerializationNamespace, schema);
            schema.Namespaces.Add(Globals.SerPrefixForSchema, Globals.SerializationNamespace);
            attributes.Add(IdAttribute);
            attributes.Add(RefAttribute);
        }
 
        void SetElementType(XmlSchemaElement element, DataContract dataContract, XmlSchema schema)
        {
            XmlDataContract xmlDataContract = dataContract as XmlDataContract;
            if (xmlDataContract != null && xmlDataContract.IsAnonymous)
            {
                element.SchemaType = xmlDataContract.XsdType;
            }
            else
            {
                element.SchemaTypeName = dataContract.StableName;
 
                if (element.SchemaTypeName.Namespace.Equals(Globals.SerializationNamespace))
                    schema.Namespaces.Add(Globals.SerPrefixForSchema, Globals.SerializationNamespace);
 
                SchemaHelper.AddSchemaImport(dataContract.StableName.Namespace, schema);
            }
        }
 
        bool CheckIfMemberHasConflict(DataMember dataMember)
        {
            if (dataMember.HasConflictingNameAndType)
                return true;
 
            DataMember conflictingMember = dataMember.ConflictingMember;
            while (conflictingMember != null)
            {
                if (conflictingMember.HasConflictingNameAndType)
                    return true;
                conflictingMember = conflictingMember.ConflictingMember;
            }
 
            return false;
        }
 
        private XmlElement ExportEmitDefaultValue(DataMember dataMember)
        {
            if (dataMember.EmitDefaultValue)
                return null;
            XmlElement defaultValueElement = XmlDoc.CreateElement(DefaultValueAnnotation.Name, DefaultValueAnnotation.Namespace);
            XmlAttribute emitDefaultValueAttribute = XmlDoc.CreateAttribute(Globals.EmitDefaultValueAttribute);
            emitDefaultValueAttribute.Value = Globals.False;
            defaultValueElement.Attributes.Append(emitDefaultValueAttribute);
            return defaultValueElement;
 
        }
 
        XmlElement ExportActualType(XmlQualifiedName typeName)
        {
            return ExportActualType(typeName, XmlDoc);
        }
 
        static XmlElement ExportActualType(XmlQualifiedName typeName, XmlDocument xmlDoc)
        {
            XmlElement actualTypeElement = xmlDoc.CreateElement(ActualTypeAnnotationName.Name, ActualTypeAnnotationName.Namespace);
 
            XmlAttribute nameAttribute = xmlDoc.CreateAttribute(Globals.ActualTypeNameAttribute);
            nameAttribute.Value = typeName.Name;
            actualTypeElement.Attributes.Append(nameAttribute);
 
            XmlAttribute nsAttribute = xmlDoc.CreateAttribute(Globals.ActualTypeNamespaceAttribute);
            nsAttribute.Value = typeName.Namespace;
            actualTypeElement.Attributes.Append(nsAttribute);
 
            return actualTypeElement;
        }
 
        XmlElement ExportGenericInfo(Type clrType, string elementName, string elementNs)
        {
            Type itemType;
            int nestedCollectionLevel = 0;
            while (CollectionDataContract.IsCollection(clrType, out itemType))
            {
                if (DataContract.GetBuiltInDataContract(clrType) != null
                    || CollectionDataContract.IsCollectionDataContract(clrType))
                {
                    break;
                }
                clrType = itemType;
                nestedCollectionLevel++;
            }
 
            Type[] genericArguments = null;
            IList<int> genericArgumentCounts = null;
            if (clrType.IsGenericType)
            {
                genericArguments = clrType.GetGenericArguments();
                string typeName;
                if (clrType.DeclaringType == null)
                    typeName = clrType.Name;
                else
                {
                    int nsLen = (clrType.Namespace == null) ? 0 : clrType.Namespace.Length;
                    if (nsLen > 0)
                        nsLen++; //include the . following namespace
                    typeName = DataContract.GetClrTypeFullName(clrType).Substring(nsLen).Replace('+', '.');
                }
                int iParam = typeName.IndexOf('[');
                if (iParam >= 0)
                    typeName = typeName.Substring(0, iParam);
                genericArgumentCounts = DataContract.GetDataContractNameForGenericName(typeName, null);
                clrType = clrType.GetGenericTypeDefinition();
            }
            XmlQualifiedName dcqname = DataContract.GetStableName(clrType);
            if (nestedCollectionLevel > 0)
            {
                string collectionName = dcqname.Name;
                for (int n = 0; n < nestedCollectionLevel; n++)
                    collectionName = Globals.ArrayPrefix + collectionName;
                dcqname = new XmlQualifiedName(collectionName, DataContract.GetCollectionNamespace(dcqname.Namespace));
            }
            XmlElement typeElement = XmlDoc.CreateElement(elementName, elementNs);
 
            XmlAttribute nameAttribute = XmlDoc.CreateAttribute(Globals.GenericNameAttribute);
            nameAttribute.Value = genericArguments != null ? XmlConvert.DecodeName(dcqname.Name) : dcqname.Name;
            //nameAttribute.Value = dcqname.Name;
            typeElement.Attributes.Append(nameAttribute);
 
            XmlAttribute nsAttribute = XmlDoc.CreateAttribute(Globals.GenericNamespaceAttribute);
            nsAttribute.Value = dcqname.Namespace;
            typeElement.Attributes.Append(nsAttribute);
 
            if (genericArguments != null)
            {
                int argIndex = 0;
                int nestedLevel = 0;
                foreach (int genericArgumentCount in genericArgumentCounts)
                {
                    for (int i = 0; i < genericArgumentCount; i++, argIndex++)
                    {
                        XmlElement argumentElement = ExportGenericInfo(genericArguments[argIndex], Globals.GenericParameterLocalName, Globals.SerializationNamespace);
                        if (nestedLevel > 0)
                        {
                            XmlAttribute nestedLevelAttribute = XmlDoc.CreateAttribute(Globals.GenericParameterNestedLevelAttribute);
                            nestedLevelAttribute.Value = nestedLevel.ToString(CultureInfo.InvariantCulture);
                            argumentElement.Attributes.Append(nestedLevelAttribute);
                        }
                        typeElement.AppendChild(argumentElement);
                    }
                    nestedLevel++;
                }
                if (genericArgumentCounts[nestedLevel - 1] == 0)
                {
                    XmlAttribute typeNestedLevelsAttribute = XmlDoc.CreateAttribute(Globals.GenericParameterNestedLevelAttribute);
                    typeNestedLevelsAttribute.Value = genericArgumentCounts.Count.ToString(CultureInfo.InvariantCulture);
                    typeElement.Attributes.Append(typeNestedLevelsAttribute);
                }
            }
 
            return typeElement;
        }
 
        XmlElement ExportSurrogateData(object key)
        {
            object surrogateData = dataContractSet.GetSurrogateData(key);
            if (surrogateData == null)
                return null;
            StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
            XmlWriterSettings writerSettings = new XmlWriterSettings();
            writerSettings.OmitXmlDeclaration = true;
            XmlWriter xmlWriter = XmlWriter.Create(stringWriter, writerSettings);
            Collection<Type> knownTypes = new Collection<Type>();
            DataContractSurrogateCaller.GetKnownCustomDataTypes(dataContractSet.DataContractSurrogate, knownTypes);
            DataContractSerializer serializer = new DataContractSerializer(Globals.TypeOfObject,
                SurrogateDataAnnotationName.Name, SurrogateDataAnnotationName.Namespace, knownTypes, Int32.MaxValue,
                false /*ignoreExtensionDataObject*/, true /*preserveObjectReferences*/, null /*dataContractSurrogate*/);
            serializer.WriteObject(xmlWriter, surrogateData);
            xmlWriter.Flush();
            return (XmlElement)XmlDoc.ReadNode(XmlReader.Create(new StringReader(stringWriter.ToString())));
        }
 
        void ExportCollectionDataContract(CollectionDataContract collectionDataContract, XmlSchema schema)
        {
            XmlSchemaComplexType type = new XmlSchemaComplexType();
            type.Name = collectionDataContract.StableName.Name;
            schema.Items.Add(type);
            XmlElement genericInfoElement = null, isDictionaryElement = null;
            if (collectionDataContract.UnderlyingType.IsGenericType && CollectionDataContract.IsCollectionDataContract(collectionDataContract.UnderlyingType))
                genericInfoElement = ExportGenericInfo(collectionDataContract.UnderlyingType, Globals.GenericTypeLocalName, Globals.SerializationNamespace);
            if (collectionDataContract.IsDictionary)
                isDictionaryElement = ExportIsDictionary();
            type.Annotation = GetSchemaAnnotation(isDictionaryElement, genericInfoElement, ExportSurrogateData(collectionDataContract));
 
            XmlSchemaSequence rootSequence = new XmlSchemaSequence();
 
            XmlSchemaElement element = new XmlSchemaElement();
            element.Name = collectionDataContract.ItemName;
            element.MinOccurs = 0;
            element.MaxOccursString = Globals.OccursUnbounded;
            if (collectionDataContract.IsDictionary)
            {
                ClassDataContract keyValueContract = collectionDataContract.ItemContract as ClassDataContract;
                XmlSchemaComplexType keyValueType = new XmlSchemaComplexType();
                XmlSchemaSequence keyValueSequence = new XmlSchemaSequence();
                foreach (DataMember dataMember in keyValueContract.Members)
                {
                    XmlSchemaElement keyValueElement = new XmlSchemaElement();
                    keyValueElement.Name = dataMember.Name;
                    SetElementType(keyValueElement, dataContractSet.GetMemberTypeDataContract(dataMember), schema);
                    SchemaHelper.AddElementForm(keyValueElement, schema);
                    if (dataMember.IsNullable)
                        keyValueElement.IsNillable = true;
                    keyValueElement.Annotation = GetSchemaAnnotation(ExportSurrogateData(dataMember));
                    keyValueSequence.Items.Add(keyValueElement);
                }
                keyValueType.Particle = keyValueSequence;
                element.SchemaType = keyValueType;
            }
            else
            {
                if (collectionDataContract.IsItemTypeNullable)
                    element.IsNillable = true;
                DataContract itemContract = dataContractSet.GetItemTypeDataContract(collectionDataContract);
                SetElementType(element, itemContract, schema);
            }
            SchemaHelper.AddElementForm(element, schema);
            rootSequence.Items.Add(element);
 
            type.Particle = rootSequence;
 
            if (collectionDataContract.IsReference)
                AddReferenceAttributes(type.Attributes, schema);
        }
 
        XmlElement ExportIsDictionary()
        {
            XmlElement isDictionaryElement = XmlDoc.CreateElement(IsDictionaryAnnotationName.Name, IsDictionaryAnnotationName.Namespace);
            isDictionaryElement.InnerText = Globals.True;
            return isDictionaryElement;
        }
 
        void ExportEnumDataContract(EnumDataContract enumDataContract, XmlSchema schema)
        {
            XmlSchemaSimpleType type = new XmlSchemaSimpleType();
            type.Name = enumDataContract.StableName.Name;
            XmlElement actualTypeElement = (enumDataContract.BaseContractName == DefaultEnumBaseTypeName) ? null : ExportActualType(enumDataContract.BaseContractName);
            type.Annotation = GetSchemaAnnotation(actualTypeElement, ExportSurrogateData(enumDataContract));
            schema.Items.Add(type);
 
            XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction();
            restriction.BaseTypeName = StringQualifiedName;
            SchemaHelper.AddSchemaImport(enumDataContract.BaseContractName.Namespace, schema);
            if (enumDataContract.Values != null)
            {
                for (int i = 0; i < enumDataContract.Values.Count; i++)
                {
                    XmlSchemaEnumerationFacet facet = new XmlSchemaEnumerationFacet();
                    facet.Value = enumDataContract.Members[i].Name;
                    if (enumDataContract.Values[i] != GetDefaultEnumValue(enumDataContract.IsFlags, i))
                        facet.Annotation = GetSchemaAnnotation(EnumerationValueAnnotationName, enumDataContract.GetStringFromEnumValue(enumDataContract.Values[i]), schema);
                    restriction.Facets.Add(facet);
                }
            }
            if (enumDataContract.IsFlags)
            {
                XmlSchemaSimpleTypeList list = new XmlSchemaSimpleTypeList();
                XmlSchemaSimpleType anonymousType = new XmlSchemaSimpleType();
                anonymousType.Content = restriction;
                list.ItemType = anonymousType;
                type.Content = list;
            }
            else
                type.Content = restriction;
        }
 
        internal static long GetDefaultEnumValue(bool isFlags, int index)
        {
            return isFlags ? (long)Math.Pow(2, index) : index;
        }
 
        void ExportISerializableDataContract(ClassDataContract dataContract, XmlSchema schema)
        {
            XmlSchemaComplexType type = new XmlSchemaComplexType();
            type.Name = dataContract.StableName.Name;
            schema.Items.Add(type);
            XmlElement genericInfoElement = null;
            if (dataContract.UnderlyingType.IsGenericType)
                genericInfoElement = ExportGenericInfo(dataContract.UnderlyingType, Globals.GenericTypeLocalName, Globals.SerializationNamespace);
 
            XmlElement isValueTypeElement = null;
            if (dataContract.BaseContract != null)
            {
                XmlSchemaComplexContentExtension extension = CreateTypeContent(type, dataContract.BaseContract.StableName, schema);
            }
            else
            {
                schema.Namespaces.Add(Globals.SerPrefixForSchema, Globals.SerializationNamespace);
                type.Particle = ISerializableSequence;
                XmlSchemaAttribute iSerializableFactoryTypeAttribute = ISerializableFactoryTypeAttribute;
                type.Attributes.Add(iSerializableFactoryTypeAttribute);
                SchemaHelper.AddSchemaImport(ISerializableFactoryTypeAttribute.RefName.Namespace, schema);
                if (dataContract.IsValueType)
                    isValueTypeElement = GetAnnotationMarkup(IsValueTypeName, XmlConvert.ToString(dataContract.IsValueType), schema);
            }
            type.Annotation = GetSchemaAnnotation(genericInfoElement, ExportSurrogateData(dataContract), isValueTypeElement);
        }
 
        XmlSchemaComplexContentExtension CreateTypeContent(XmlSchemaComplexType type, XmlQualifiedName baseTypeName, XmlSchema schema)
        {
            SchemaHelper.AddSchemaImport(baseTypeName.Namespace, schema);
 
            XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension();
            extension.BaseTypeName = baseTypeName;
            type.ContentModel = new XmlSchemaComplexContent();
            type.ContentModel.Content = extension;
 
            return extension;
        }
 
        void ExportXmlDataContract(XmlDataContract dataContract)
        {
            XmlQualifiedName typeQName;
            bool hasRoot;
            XmlSchemaType xsdType;
 
            Type clrType = dataContract.UnderlyingType;
            if (!IsSpecialXmlType(clrType, out typeQName, out xsdType, out hasRoot))
                if (!InvokeSchemaProviderMethod(clrType, schemas, out typeQName, out xsdType, out hasRoot))
                    InvokeGetSchemaMethod(clrType, schemas, typeQName);
 
            if (hasRoot)
            {
                if (!(typeQName.Equals(dataContract.StableName)))
                {
                    Fx.Assert("XML data contract type name does not match schema name");
                }
 
                XmlSchema schema;
                if (SchemaHelper.GetSchemaElement(Schemas,
                    new XmlQualifiedName(dataContract.TopLevelElementName.Value, dataContract.TopLevelElementNamespace.Value),
                    out schema) == null)
                {
                    XmlSchemaElement topLevelElement = ExportTopLevelElement(dataContract, schema);
                    topLevelElement.IsNillable = dataContract.IsTopLevelElementNullable;
                    ReprocessAll(schemas);
                }
 
                XmlSchemaType anonymousType = xsdType;
                xsdType = SchemaHelper.GetSchemaType(schemas, typeQName, out schema);
                if (anonymousType == null && xsdType == null && typeQName.Namespace != XmlSchema.Namespace)
                {
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.MissingSchemaType, typeQName, DataContract.GetClrTypeFullName(clrType))));
                }
                if (xsdType != null)
                    xsdType.Annotation = GetSchemaAnnotation(
                                           ExportSurrogateData(dataContract),
                                           dataContract.IsValueType ?
                                             GetAnnotationMarkup(IsValueTypeName, XmlConvert.ToString(dataContract.IsValueType), schema) :
                                             null
                                         );
                else if (DiagnosticUtility.ShouldTraceVerbose)
                {
                    TraceUtility.Trace(TraceEventType.Verbose, TraceCode.XsdExportAnnotationFailed,
                        SR.GetString(SR.TraceCodeXsdExportAnnotationFailed), new StringTraceRecord("Type", typeQName.Namespace + ":" + typeQName.Name));
                }
            }
        }
 
        static void ReprocessAll(XmlSchemaSet schemas)// and remove duplicate items
        {
            Hashtable elements = new Hashtable();
            Hashtable types = new Hashtable();
            XmlSchema[] schemaArray = new XmlSchema[schemas.Count];
            schemas.CopyTo(schemaArray, 0);
            for (int i = 0; i < schemaArray.Length; i++)
            {
                XmlSchema schema = schemaArray[i];
                XmlSchemaObject[] itemArray = new XmlSchemaObject[schema.Items.Count];
                schema.Items.CopyTo(itemArray, 0);
                for (int j = 0; j < itemArray.Length; j++)
                {
                    XmlSchemaObject item = itemArray[j];
                    Hashtable items;
                    XmlQualifiedName qname;
                    if (item is XmlSchemaElement)
                    {
                        items = elements;
                        qname = new XmlQualifiedName(((XmlSchemaElement)item).Name, schema.TargetNamespace);
                    }
                    else if (item is XmlSchemaType)
                    {
                        items = types;
                        qname = new XmlQualifiedName(((XmlSchemaType)item).Name, schema.TargetNamespace);
                    }
                    else
                        continue;
                    object otherItem = items[qname];
                    if (otherItem != null)
                    {
                        if (DiagnosticUtility.ShouldTraceWarning)
                        {
                            Dictionary<string, string> values = new Dictionary<string, string>(2)
                            {
                                { "ItemType", item.ToString() },
                                { "Name", qname.Namespace + ":" + qname.Name }
                            };
                            TraceUtility.Trace(TraceEventType.Warning, TraceCode.XsdExportDupItems,
                                SR.GetString(SR.TraceCodeXsdExportDupItems), new DictionaryTraceRecord(values));
                        }
                        schema.Items.Remove(item);
                    }
                    else
                        items.Add(qname, item);
                }
                schemas.Reprocess(schema);
            }
        }
 
        internal static void GetXmlTypeInfo(Type type, out XmlQualifiedName stableName, out XmlSchemaType xsdType, out bool hasRoot)
        {
            if (IsSpecialXmlType(type, out stableName, out xsdType, out hasRoot))
                return;
            XmlSchemaSet schemas = new XmlSchemaSet();
            schemas.XmlResolver = null;
            InvokeSchemaProviderMethod(type, schemas, out stableName, out xsdType, out hasRoot);
            if (stableName.Name == null || stableName.Name.Length == 0)
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.InvalidXmlDataContractName, DataContract.GetClrTypeFullName(type))));
        }
 
        static bool InvokeSchemaProviderMethod(Type clrType, XmlSchemaSet schemas, out XmlQualifiedName stableName, out XmlSchemaType xsdType, out bool hasRoot)
        {
            xsdType = null;
            hasRoot = true;
            object[] attrs = clrType.GetCustomAttributes(Globals.TypeOfXmlSchemaProviderAttribute, false);
            if (attrs == null || attrs.Length == 0)
            {
                stableName = DataContract.GetDefaultStableName(clrType);
                return false;
            }
 
            XmlSchemaProviderAttribute provider = (XmlSchemaProviderAttribute)attrs[0];
            if (provider.IsAny)
            {
                xsdType = CreateAnyElementType();
                hasRoot = false;
            }
            string methodName = provider.MethodName;
            if (methodName == null || methodName.Length == 0)
            {
                if (!provider.IsAny)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.InvalidGetSchemaMethod, DataContract.GetClrTypeFullName(clrType))));
                stableName = DataContract.GetDefaultStableName(clrType);
            }
            else
            {
                MethodInfo getMethod = clrType.GetMethod(methodName,  /*BindingFlags.DeclaredOnly |*/ BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, null, new Type[] { typeof(XmlSchemaSet) }, null);
                if (getMethod == null)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.MissingGetSchemaMethod, DataContract.GetClrTypeFullName(clrType), methodName)));
 
                if (!(Globals.TypeOfXmlQualifiedName.IsAssignableFrom(getMethod.ReturnType)) && !(Globals.TypeOfXmlSchemaType.IsAssignableFrom(getMethod.ReturnType)))
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.InvalidReturnTypeOnGetSchemaMethod, DataContract.GetClrTypeFullName(clrType), methodName, DataContract.GetClrTypeFullName(getMethod.ReturnType), DataContract.GetClrTypeFullName(Globals.TypeOfXmlQualifiedName), typeof(XmlSchemaType))));
 
                object typeInfo = getMethod.Invoke(null, new object[] { schemas });
 
                if (provider.IsAny)
                {
                    if (typeInfo != null)
                        throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.InvalidNonNullReturnValueByIsAny, DataContract.GetClrTypeFullName(clrType), methodName)));
                    stableName = DataContract.GetDefaultStableName(clrType);
                }
                else if (typeInfo == null)
                {
                    xsdType = CreateAnyElementType();
                    hasRoot = false;
                    stableName = DataContract.GetDefaultStableName(clrType);
                }
                else
                {
                    XmlSchemaType providerXsdType = typeInfo as XmlSchemaType;
                    if (providerXsdType != null)
                    {
                        string typeName = providerXsdType.Name;
                        string typeNs = null;
                        if (typeName == null || typeName.Length == 0)
                        {
                            DataContract.GetDefaultStableName(DataContract.GetClrTypeFullName(clrType), out typeName, out typeNs);
                            stableName = new XmlQualifiedName(typeName, typeNs);
                            providerXsdType.Annotation = GetSchemaAnnotation(ExportActualType(stableName, new XmlDocument()));
                            xsdType = providerXsdType;
                        }
                        else
                        {
                            foreach (XmlSchema schema in schemas.Schemas())
                            {
                                foreach (XmlSchemaObject schemaItem in schema.Items)
                                {
                                    if ((object)schemaItem == (object)providerXsdType)
                                    {
                                        typeNs = schema.TargetNamespace;
                                        if (typeNs == null)
                                            typeNs = String.Empty;
                                        break;
                                    }
                                }
                                if (typeNs != null)
                                    break;
                            }
                            if (typeNs == null)
                                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.MissingSchemaType, typeName, DataContract.GetClrTypeFullName(clrType))));
                            stableName = new XmlQualifiedName(typeName, typeNs);
                        }
                    }
                    else
                        stableName = (XmlQualifiedName)typeInfo;
                }
            }
            return true;
        }
 
        static void InvokeGetSchemaMethod(Type clrType, XmlSchemaSet schemas, XmlQualifiedName stableName)
        {
            IXmlSerializable ixmlSerializable = (IXmlSerializable)Activator.CreateInstance(clrType);
            XmlSchema schema = ixmlSerializable.GetSchema();
            if (schema == null)
            {
                AddDefaultDatasetType(schemas, stableName.Name, stableName.Namespace);
            }
            else
            {
                if (schema.Id == null || schema.Id.Length == 0)
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.InvalidReturnSchemaOnGetSchemaMethod, DataContract.GetClrTypeFullName(clrType))));
                AddDefaultTypedDatasetType(schemas, schema, stableName.Name, stableName.Namespace);
            }
        }
 
        internal static void AddDefaultXmlType(XmlSchemaSet schemas, string localName, string ns)
        {
            XmlSchemaComplexType defaultXmlType = CreateAnyType();
            defaultXmlType.Name = localName;
            XmlSchema schema = SchemaHelper.GetSchema(ns, schemas);
            schema.Items.Add(defaultXmlType);
            schemas.Reprocess(schema);
        }
 
        static XmlSchemaComplexType CreateAnyType()
        {
            XmlSchemaComplexType anyType = new XmlSchemaComplexType();
            anyType.IsMixed = true;
            anyType.Particle = new XmlSchemaSequence();
            XmlSchemaAny any = new XmlSchemaAny();
            any.MinOccurs = 0;
            any.MaxOccurs = Decimal.MaxValue;
            any.ProcessContents = XmlSchemaContentProcessing.Lax;
            ((XmlSchemaSequence)anyType.Particle).Items.Add(any);
            anyType.AnyAttribute = new XmlSchemaAnyAttribute();
            return anyType;
        }
 
        static XmlSchemaComplexType CreateAnyElementType()
        {
            XmlSchemaComplexType anyElementType = new XmlSchemaComplexType();
            anyElementType.IsMixed = false;
            anyElementType.Particle = new XmlSchemaSequence();
            XmlSchemaAny any = new XmlSchemaAny();
            any.MinOccurs = 0;
            any.ProcessContents = XmlSchemaContentProcessing.Lax;
            ((XmlSchemaSequence)anyElementType.Particle).Items.Add(any);
            return anyElementType;
        }
 
        internal static bool IsSpecialXmlType(Type type, out XmlQualifiedName typeName, out XmlSchemaType xsdType, out bool hasRoot)
        {
            xsdType = null;
            hasRoot = true;
            if (type == Globals.TypeOfXmlElement || type == Globals.TypeOfXmlNodeArray)
            {
                string name = null;
                if (type == Globals.TypeOfXmlElement)
                {
                    xsdType = CreateAnyElementType();
                    name = "XmlElement";
                    hasRoot = false;
                }
                else
                {
                    xsdType = CreateAnyType();
                    name = "ArrayOfXmlNode";
                    hasRoot = true;
                }
                typeName = new XmlQualifiedName(name, DataContract.GetDefaultStableNamespace(type));
                return true;
            }
            typeName = null;
            return false;
        }
 
        static void AddDefaultDatasetType(XmlSchemaSet schemas, string localName, string ns)
        {
            XmlSchemaComplexType type = new XmlSchemaComplexType();
            type.Name = localName;
            type.Particle = new XmlSchemaSequence();
            XmlSchemaElement schemaRefElement = new XmlSchemaElement();
            schemaRefElement.RefName = new XmlQualifiedName(Globals.SchemaLocalName, XmlSchema.Namespace);
            ((XmlSchemaSequence)type.Particle).Items.Add(schemaRefElement);
            XmlSchemaAny any = new XmlSchemaAny();
            ((XmlSchemaSequence)type.Particle).Items.Add(any);
            XmlSchema schema = SchemaHelper.GetSchema(ns, schemas);
            schema.Items.Add(type);
            schemas.Reprocess(schema);
        }
 
        static void AddDefaultTypedDatasetType(XmlSchemaSet schemas, XmlSchema datasetSchema, string localName, string ns)
        {
            XmlSchemaComplexType type = new XmlSchemaComplexType();
            type.Name = localName;
            type.Particle = new XmlSchemaSequence();
            XmlSchemaAny any = new XmlSchemaAny();
            any.Namespace = (datasetSchema.TargetNamespace == null) ? String.Empty : datasetSchema.TargetNamespace;
            ((XmlSchemaSequence)type.Particle).Items.Add(any);
            schemas.Add(datasetSchema);
            XmlSchema schema = SchemaHelper.GetSchema(ns, schemas);
            schema.Items.Add(type);
            schemas.Reprocess(datasetSchema);
            schemas.Reprocess(schema);
        }
 
        XmlSchemaAnnotation GetSchemaAnnotation(XmlQualifiedName annotationQualifiedName, string innerText, XmlSchema schema)
        {
            XmlSchemaAnnotation annotation = new XmlSchemaAnnotation();
            XmlSchemaAppInfo appInfo = new XmlSchemaAppInfo();
            XmlElement annotationElement = GetAnnotationMarkup(annotationQualifiedName, innerText, schema);
            appInfo.Markup = new XmlNode[1] { annotationElement };
            annotation.Items.Add(appInfo);
            return annotation;
        }
 
        static XmlSchemaAnnotation GetSchemaAnnotation(params XmlNode[] nodes)
        {
            if (nodes == null || nodes.Length == 0)
                return null;
            bool hasAnnotation = false;
            for (int i = 0; i < nodes.Length; i++)
                if (nodes[i] != null)
                {
                    hasAnnotation = true;
                    break;
                }
            if (!hasAnnotation)
                return null;
 
            XmlSchemaAnnotation annotation = new XmlSchemaAnnotation();
            XmlSchemaAppInfo appInfo = new XmlSchemaAppInfo();
            annotation.Items.Add(appInfo);
            appInfo.Markup = nodes;
            return annotation;
        }
 
        XmlElement GetAnnotationMarkup(XmlQualifiedName annotationQualifiedName, string innerText, XmlSchema schema)
        {
            XmlElement annotationElement = XmlDoc.CreateElement(annotationQualifiedName.Name, annotationQualifiedName.Namespace);
            SchemaHelper.AddSchemaImport(annotationQualifiedName.Namespace, schema);
            annotationElement.InnerText = innerText;
            return annotationElement;
        }
 
        XmlSchema GetSchema(string ns)
        {
            return SchemaHelper.GetSchema(ns, Schemas);
        }
 
        // Property is not stored in a local because XmlSchemaSequence is mutable.
        // The schema export process should not expose objects that may be modified later.
        internal static XmlSchemaSequence ISerializableSequence
        {
            get
            {
                XmlSchemaSequence iSerializableSequence = new XmlSchemaSequence();
                iSerializableSequence.Items.Add(ISerializableWildcardElement);
                return iSerializableSequence;
            }
        }
 
        // Property is not stored in a local because XmlSchemaAny is mutable.
        // The schema export process should not expose objects that may be modified later.
        internal static XmlSchemaAny ISerializableWildcardElement
        {
            get
            {
                XmlSchemaAny iSerializableWildcardElement = new XmlSchemaAny();
                iSerializableWildcardElement.MinOccurs = 0;
                iSerializableWildcardElement.MaxOccursString = Globals.OccursUnbounded;
                iSerializableWildcardElement.Namespace = "##local";
                iSerializableWildcardElement.ProcessContents = XmlSchemaContentProcessing.Skip;
                return iSerializableWildcardElement;
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Static fields are marked SecurityCritical or readonly to prevent"
            + " data from being modified or leaked to other components in appdomain.")]
        [SecurityCritical]
        static XmlQualifiedName anytypeQualifiedName;
        internal static XmlQualifiedName AnytypeQualifiedName
        {
            [Fx.Tag.SecurityNote(Critical = "Fetches the critical anytypeQualifiedName field.",
                Safe = "Get-only properties only needs to be protected for write; initialized in getter if null.")]
            [SecuritySafeCritical]
            get
            {
                if (anytypeQualifiedName == null)
                    anytypeQualifiedName = new XmlQualifiedName(Globals.AnyTypeLocalName, Globals.SchemaNamespace);
                return anytypeQualifiedName;
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Static fields are marked SecurityCritical or readonly to prevent"
            + " data from being modified or leaked to other components in appdomain.")]
        [SecurityCritical]
        static XmlQualifiedName stringQualifiedName;
        internal static XmlQualifiedName StringQualifiedName
        {
            [Fx.Tag.SecurityNote(Critical = "Fetches the critical stringQualifiedName field.",
                Safe = "Get-only properties only needs to be protected for write; initialized in getter if null.")]
            [SecuritySafeCritical]
            get
            {
                if (stringQualifiedName == null)
                    stringQualifiedName = new XmlQualifiedName(Globals.StringLocalName, Globals.SchemaNamespace);
                return stringQualifiedName;
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Static fields are marked SecurityCritical or readonly to prevent"
            + " data from being modified or leaked to other components in appdomain.")]
        [SecurityCritical]
        static XmlQualifiedName defaultEnumBaseTypeName;
        internal static XmlQualifiedName DefaultEnumBaseTypeName
        {
            [Fx.Tag.SecurityNote(Critical = "Fetches the critical defaultEnumBaseTypeName field.",
                Safe = "Get-only properties only needs to be protected for write; initialized in getter if null.")]
            [SecuritySafeCritical]
            get
            {
                if (defaultEnumBaseTypeName == null)
                    defaultEnumBaseTypeName = new XmlQualifiedName(Globals.IntLocalName, Globals.SchemaNamespace);
                return defaultEnumBaseTypeName;
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Static fields are marked SecurityCritical or readonly to prevent"
            + " data from being modified or leaked to other components in appdomain.")]
        [SecurityCritical]
        static XmlQualifiedName enumerationValueAnnotationName;
        internal static XmlQualifiedName EnumerationValueAnnotationName
        {
            [Fx.Tag.SecurityNote(Critical = "Fetches the critical enumerationValueAnnotationName field.",
                Safe = "Get-only properties only needs to be protected for write; initialized in getter if null.")]
            [SecuritySafeCritical]
            get
            {
                if (enumerationValueAnnotationName == null)
                    enumerationValueAnnotationName = new XmlQualifiedName(Globals.EnumerationValueLocalName, Globals.SerializationNamespace);
                return enumerationValueAnnotationName;
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Static fields are marked SecurityCritical or readonly to prevent"
            + " data from being modified or leaked to other components in appdomain.")]
        [SecurityCritical]
        static XmlQualifiedName surrogateDataAnnotationName;
        internal static XmlQualifiedName SurrogateDataAnnotationName
        {
            [Fx.Tag.SecurityNote(Critical = "Fetches the critical surrogateDataAnnotationName field.",
                Safe = "Get-only properties only needs to be protected for write; initialized in getter if null.")]
            [SecuritySafeCritical]
            get
            {
                if (surrogateDataAnnotationName == null)
                    surrogateDataAnnotationName = new XmlQualifiedName(Globals.SurrogateDataLocalName, Globals.SerializationNamespace);
                return surrogateDataAnnotationName;
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Static fields are marked SecurityCritical or readonly to prevent"
            + " data from being modified or leaked to other components in appdomain.")]
        [SecurityCritical]
        static XmlQualifiedName defaultValueAnnotation;
        internal static XmlQualifiedName DefaultValueAnnotation
        {
            [Fx.Tag.SecurityNote(Critical = "Fetches the critical defaultValueAnnotation field.",
                Safe = "Get-only properties only needs to be protected for write; initialized in getter if null.")]
            [SecuritySafeCritical]
            get
            {
                if (defaultValueAnnotation == null)
                    defaultValueAnnotation = new XmlQualifiedName(Globals.DefaultValueLocalName, Globals.SerializationNamespace);
                return defaultValueAnnotation;
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Static fields are marked SecurityCritical or readonly to prevent"
            + " data from being modified or leaked to other components in appdomain.")]
        [SecurityCritical]
        static XmlQualifiedName actualTypeAnnotationName;
        internal static XmlQualifiedName ActualTypeAnnotationName
        {
            [Fx.Tag.SecurityNote(Critical = "Fetches the critical actualTypeAnnotationName field.",
                Safe = "Get-only properties only needs to be protected for write; initialized in getter if null.")]
            [SecuritySafeCritical]
            get
            {
                if (actualTypeAnnotationName == null)
                    actualTypeAnnotationName = new XmlQualifiedName(Globals.ActualTypeLocalName, Globals.SerializationNamespace);
                return actualTypeAnnotationName;
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Static fields are marked SecurityCritical or readonly to prevent"
            + " data from being modified or leaked to other components in appdomain.")]
        [SecurityCritical]
        static XmlQualifiedName isDictionaryAnnotationName;
        internal static XmlQualifiedName IsDictionaryAnnotationName
        {
            [Fx.Tag.SecurityNote(Critical = "Fetches the critical isDictionaryAnnotationName field.",
                Safe = "Get-only properties only needs to be protected for write; initialized in getter if null.")]
            [SecuritySafeCritical]
            get
            {
                if (isDictionaryAnnotationName == null)
                    isDictionaryAnnotationName = new XmlQualifiedName(Globals.IsDictionaryLocalName, Globals.SerializationNamespace);
                return isDictionaryAnnotationName;
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Static fields are marked SecurityCritical or readonly to prevent"
            + " data from being modified or leaked to other components in appdomain.")]
        [SecurityCritical]
        static XmlQualifiedName isValueTypeName;
        internal static XmlQualifiedName IsValueTypeName
        {
            [Fx.Tag.SecurityNote(Critical = "Fetches the critical isValueTypeName field.",
                Safe = "Get-only properties only needs to be protected for write; initialized in getter if null.")]
            [SecuritySafeCritical]
            get
            {
                if (isValueTypeName == null)
                    isValueTypeName = new XmlQualifiedName(Globals.IsValueTypeLocalName, Globals.SerializationNamespace);
                return isValueTypeName;
            }
        }
 
        // Property is not stored in a local because XmlSchemaAttribute is mutable.
        // The schema export process should not expose objects that may be modified later.
        internal static XmlSchemaAttribute ISerializableFactoryTypeAttribute
        {
            get
            {
                XmlSchemaAttribute iSerializableFactoryTypeAttribute = new XmlSchemaAttribute();
                iSerializableFactoryTypeAttribute.RefName = new XmlQualifiedName(Globals.ISerializableFactoryTypeLocalName, Globals.SerializationNamespace);
                return iSerializableFactoryTypeAttribute;
            }
        }
 
        // Property is not stored in a local because XmlSchemaAttribute is mutable.
        // The schema export process should not expose objects that may be modified later.
        internal static XmlSchemaAttribute RefAttribute
        {
            get
            {
                XmlSchemaAttribute refAttribute = new XmlSchemaAttribute();
                refAttribute.RefName = Globals.RefQualifiedName;
                return refAttribute;
            }
        }
 
        // Property is not stored in a local because XmlSchemaAttribute is mutable.
        // The schema export process should not expose objects that may be modified later.
        internal static XmlSchemaAttribute IdAttribute
        {
            get
            {
                XmlSchemaAttribute idAttribute = new XmlSchemaAttribute();
                idAttribute.RefName = Globals.IdQualifiedName;
                return idAttribute;
            }
        }
    }
}