SchemaMerger.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / xsp / System / Web / Extensions / Compilation / WCFModel / SchemaMerger.cs / 1 / SchemaMerger.cs

                            //------------------------------------------------------------------------------ 
// 
//   Copyright (C) Microsoft Corporation. All Rights Reserved.
// 
//----------------------------------------------------------------------------- 

using System; 
using System.Collections.Generic; 
using System.Collections.ObjectModel;
using System.Diagnostics; 
using System.Globalization;
using System.Reflection;
using System.Web.Services.Description;
using System.Xml; 
using System.Xml.Schema;
#if WEB_EXTENSIONS_CODE 
using System.Web.Resources; 
#else
using Microsoft.VSDesigner.Resources.Microsoft.VSDesigner; 
#endif


 
#if WEB_EXTENSIONS_CODE
namespace System.Web.Compilation.WCFModel 
#else 
namespace Microsoft.VSDesigner.WCFModel
#endif 
{
    /// 
    /// a utility class to merge schema files, and remove duplicated part
    ///  
    internal class SchemaMerger {
 
        // xml serializable attributes 
        private static Type[] xmlSerializationAttributes = new Type[] {
                            typeof(System.Xml.Serialization.XmlElementAttribute), 
                            typeof(System.Xml.Serialization.XmlAttributeAttribute),
                            typeof(System.Xml.Serialization.XmlAnyAttributeAttribute),
                            typeof(System.Xml.Serialization.XmlAnyElementAttribute),
                            typeof(System.Xml.Serialization.XmlTextAttribute), 
        };
 
        // elements in the schema (we don't process annotation node) 
        private static SchemaTopLevelItemType[] schemaTopLevelItemTypes = new SchemaTopLevelItemType[] {
            new SchemaTopLevelItemType(typeof(XmlSchemaType), "type"), 
            new SchemaTopLevelItemType(typeof(XmlSchemaElement), "element"),
            new SchemaTopLevelItemType(typeof(XmlSchemaAttribute), "attribute"),
            new SchemaTopLevelItemType(typeof(XmlSchemaGroup), "group"),
            new SchemaTopLevelItemType(typeof(XmlSchemaAttributeGroup), "attributeGroup"), 
        };
 
        // when properties defined in those types are different, we only report warnings, but not error messages 
        private static Type[] ignorablePropertyTypes = new Type[] {
            typeof(XmlAttribute[]), 
            typeof(XmlElement[]),
            typeof(XmlNode[]),
            typeof(XmlSchemaAnnotation),
        }; 

        private readonly static XmlAttribute[] emptyXmlAttributeCollection = new XmlAttribute[0]; 
        private readonly static object[] emptyCollection = new object[0]; 

        ///  
        /// Merge and remove duplicated part from the schema list
        /// 
        /// schemas with names
        /// error messages 
        /// error messages
        ///  
        internal static void MergeSchemas(IEnumerable schemaList, IList importErrors, out IEnumerable duplicatedSchemas) { 
            if (schemaList == null) {
                throw new ArgumentNullException("schemaList"); 
            }
            if (importErrors == null) {
                throw new ArgumentNullException("importErrors");
            } 

            List duplicatedSchemaList = new List(); 
            duplicatedSchemas = duplicatedSchemaList; 

            // types, elements, groups have their own name space 
            Dictionary[] knownItemTables = new Dictionary[schemaTopLevelItemTypes.Length];
            for (int i = 0; i < schemaTopLevelItemTypes.Length; i++) {
                knownItemTables[i] = new Dictionary();
            } 

            foreach (XmlSchema schema in schemaList) { 
 
                bool hasNewDefinedItems = false;
                List duplicatedItems = new List(); 

                for (int i = 0; i < schemaTopLevelItemTypes.Length; i++) {
                    Dictionary knownItemTable = knownItemTables[i];
                    int knownItemCount = knownItemTable.Count; 
                    FindDuplicatedItems(schema, schemaTopLevelItemTypes[i].ItemType, schemaTopLevelItemTypes[i].Name, knownItemTable, duplicatedItems, importErrors);
                    if (knownItemTable.Count > knownItemCount) { 
                        hasNewDefinedItems = true; 
                    }
                } 

                if (duplicatedItems.Count > 0) {
                    if (!hasNewDefinedItems) {
                        // remove the whole schema... 
                        duplicatedSchemaList.Add(schema);
                    } 
                    else { 
                        // remove duplicated items only
                        foreach (XmlSchemaObject item in duplicatedItems) { 
                            schema.Items.Remove(item);
                        }
                    }
                } 
            }
        } 
 
        /// 
        /// Find duplicated items in a schema 
        /// 
        /// 
        private static void FindDuplicatedItems(
                    XmlSchema schema, 
                    Type itemType,
                    string itemTypeName, 
                    Dictionary knownItemTable, 
                    List duplicatedItems,
                    IList importErrors) { 

            string targetNamespace = schema.TargetNamespace;
            if (String.IsNullOrEmpty(targetNamespace)) {
                targetNamespace = String.Empty; 
            }
 
            foreach (XmlSchemaObject item in schema.Items) { 
                if (itemType.IsInstanceOfType(item)) {
 
                    XmlQualifiedName combinedName = new XmlQualifiedName(GetSchemaItemName(item), targetNamespace);

                    XmlSchemaObject originalItem = null;
                    if (knownItemTable.TryGetValue(combinedName, out originalItem)) { 
                        string differentLocation;
 
                        if (!AreSchemaObjectsEquivalent(originalItem, item, out differentLocation)) { 
                            differentLocation = CombinePath(".", differentLocation);
                            importErrors.Add( 
                                    new ProxyGenerationError(
                                        ProxyGenerationError.GeneratorState.MergeMetadata,
                                        String.Empty,
                                        new InvalidOperationException( 
                                            String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_DuplicatedSchemaItems, itemTypeName, combinedName.ToString(), schema.SourceUri, originalItem.SourceUri, differentLocation)
                                        ) 
                                    ) 
                            );
                        } 
                        else if (!String.IsNullOrEmpty(differentLocation)) {
                            // warning: ignorable difference found
                            differentLocation = CombinePath(".", differentLocation);
                            importErrors.Add( 
                                    new ProxyGenerationError(
                                        ProxyGenerationError.GeneratorState.MergeMetadata, 
                                        String.Empty, 
                                        new InvalidOperationException(
                                            String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_DuplicatedSchemaItemsIgnored, itemTypeName, combinedName.ToString(), schema.SourceUri, originalItem.SourceUri, differentLocation) 
                                        ),
                                        true        // isWarning = true
                                    )
                            ); 
                        }
                        duplicatedItems.Add(item); 
                    } 
                    else {
                        item.SourceUri = schema.SourceUri; 
                        knownItemTable.Add(combinedName, item);
                    }
                }
            } 
        }
 
        ///  
        /// Compare two schema objects
        ///  
        /// 
        /// 
        /// For all those functions, we follow the same pattern:
        ///  return false: find not-ignorable difference between them.  differentLocation will contain the path 
        ///  return true, with empty differentLocation -- no difference found
        ///  return true, with non-empty differentLocation -- ignorable difference found under that location 
        ///  
        private static bool AreSchemaObjectsEquivalent(XmlSchemaObject originalItem, XmlSchemaObject item, out string differentLocation) {
            differentLocation = String.Empty; 

            Type itemType = originalItem.GetType();
            if (itemType != item.GetType()) {
                return false; 
            }
 
            string ignorableDifferenceLocation = String.Empty; 

            PropertyInfo[] properties = itemType.GetProperties(); 
            foreach (PropertyInfo property in properties) {
                if (IsPersistedProperty(property)) {

                    bool ignorableProperty = ShouldIgnoreSchemaProperty(property); 

                    object originalValue = property.GetValue(originalItem, new object[] {}); 
                    object newValue = property.GetValue(item, new object[] {}); 

                    if (!CompareSchemaPropertyValues(property, originalValue, newValue, out differentLocation) && !ignorableProperty) { 
                        return false;
                    }
                    if (String.IsNullOrEmpty(ignorableDifferenceLocation)) {
                        ignorableDifferenceLocation = differentLocation; 
                    }
                } 
            } 

            differentLocation = ignorableDifferenceLocation; 
            return true;
        }

        ///  
        /// Compare two property of a schema object
        ///  
        /// true: if the value are same 
        /// 
        private static bool CompareSchemaPropertyValues(PropertyInfo propertyInfo, object originalValue, object newValue, out string differentLocation) { 
            differentLocation = String.Empty;

            if (originalValue == null && newValue == null) {
                return true; 
            }
 
            // we create empty collection so a meaningful differentLocation could be generated 
            if (typeof(XmlAttribute[]) == propertyInfo.PropertyType) {
                if (originalValue == null) { 
                    originalValue = emptyXmlAttributeCollection;
                }
                if (newValue == null) {
                    newValue = emptyXmlAttributeCollection; 
                }
 
                XmlAttribute differentAttribute1, differentAttribute2; 
                if (!CompareXmlAttributeCollections((XmlAttribute[])originalValue, (XmlAttribute[])newValue, out differentAttribute1, out differentAttribute2)) {
                    differentLocation = GetSchemaPropertyNameInXml(propertyInfo, differentAttribute1, differentAttribute2); 
                    return false;
                }
                return true;
            } 

            if (typeof(System.Collections.ICollection).IsAssignableFrom(propertyInfo.PropertyType)) { 
                if (originalValue == null) { 
                    originalValue = emptyCollection;
                } 
                if (newValue == null) {
                    newValue = emptyCollection;
                }
 
                object differentItem1, differentItem2;
                if (!CompareSchemaCollections((System.Collections.ICollection)originalValue, (System.Collections.ICollection)newValue, out differentItem1, out differentItem2, out differentLocation)) { 
                    differentLocation = CombinePath(GetSchemaPropertyNameInXml(propertyInfo, differentItem1, differentItem2), differentLocation); 
                    return false;
                } 
                else {
                    if (!String.IsNullOrEmpty(differentLocation)) {
                        // ignorable difference...
                        differentLocation = CombinePath(GetSchemaPropertyNameInXml(propertyInfo, differentItem1, differentItem2), differentLocation); 
                    }
                    return true; 
                } 
            }
 
            if (originalValue == null || newValue == null) {
                differentLocation = CombinePath(GetSchemaPropertyNameInXml(propertyInfo, originalValue, newValue), differentLocation);
                return false;
            } 

            if (originalValue.GetType() != newValue.GetType()) { 
                differentLocation = CombinePath(GetSchemaPropertyNameInXml(propertyInfo, originalValue, newValue), differentLocation); 
                return false;
            } 

            if (!CompareSchemaValues(originalValue, newValue, out differentLocation)) {
                differentLocation = CombinePath(GetSchemaPropertyNameInXml(propertyInfo, originalValue, newValue), differentLocation);
                return false; 
            }
            else { 
                // ignorable difference... 
                if (!String.IsNullOrEmpty(differentLocation)) {
                    differentLocation = CombinePath(GetSchemaPropertyNameInXml(propertyInfo, originalValue, newValue), differentLocation); 
                }
                return true;
            }
        } 

        ///  
        /// Compare two schema values 
        /// 
        ///  
        /// 
        private static bool CompareSchemaValues(object originalValue, object newValue, out string differentLocation) {
            differentLocation = String.Empty;
 
            if (originalValue == null || newValue == null) {
                return (originalValue == null && newValue == null); 
            } 

            if (originalValue.GetType() != newValue.GetType()) { 
                return false;
            }

            if (originalValue is XmlSchemaObject) { 
                return AreSchemaObjectsEquivalent((XmlSchemaObject)originalValue, (XmlSchemaObject)newValue, out differentLocation);
            } 
 
            if (originalValue is XmlAttribute) {
                return CompareXmlAttributes((XmlAttribute)originalValue, (XmlAttribute)newValue); 
            }

            if (originalValue is XmlElement) {
                return CompareXmlElements((XmlElement)originalValue, (XmlElement)newValue, out differentLocation); 
            }
 
            if (originalValue is XmlText) { 
                return CompareXmlTexts((XmlText)originalValue, (XmlText)newValue);
            } 

            return originalValue.Equals(newValue);
        }
 
        /// 
        /// Compare two collections of items 
        ///  
        /// 
        ///  
        private static bool CompareSchemaCollections(System.Collections.IEnumerable originalCollection, System.Collections.IEnumerable newCollection,
                out object differentItem1, out object differentItem2, out string differentLocation) {

            differentLocation = String.Empty; 

            System.Collections.IEnumerator list1 = originalCollection.GetEnumerator(); 
            System.Collections.IEnumerator list2 = newCollection.GetEnumerator(); 

            string ignorableDifferenceLocation = String.Empty; 
            object ignorableDifferenceItem1 = null;
            object ignorableDifferenceItem2 = null;

            do { 
                differentItem1 = list1.MoveNext() ? list1.Current : null;
                differentItem2 = list2.MoveNext() ? list2.Current : null; 
 
                if (!CompareSchemaValues(differentItem1, differentItem2, out differentLocation)) {
                    return false; 
                }

                if (String.IsNullOrEmpty(ignorableDifferenceLocation)) {
                    ignorableDifferenceItem1 = differentItem1; 
                    ignorableDifferenceItem2 = differentItem2;
                    ignorableDifferenceLocation = differentLocation; 
                } 
            }
            while (differentItem1 != null && differentItem2 != null); 

            Debug.Assert(differentItem1 == null && differentItem2 == null);

            differentLocation = ignorableDifferenceLocation; 
            differentItem1 = ignorableDifferenceItem1;
            differentItem2 = ignorableDifferenceItem2; 
 
            return true;
        } 

        /// 
        /// Compare two attributes
        ///  
        /// 
        ///  
        private static bool CompareXmlAttributes(XmlAttribute attribute1, XmlAttribute attribute2) { 
            return String.Equals(attribute1.LocalName, attribute2.LocalName, StringComparison.Ordinal) &&
                    String.Equals(attribute1.NamespaceURI, attribute2.NamespaceURI, StringComparison.Ordinal) && 
                    String.Equals(attribute1.Value, attribute2.Value, StringComparison.Ordinal);
        }

        ///  
        /// Compare two attribute collections
        ///  
        ///  
        /// 
        private static bool CompareXmlAttributeCollections(System.Collections.ICollection attributeCollection1, System.Collections.ICollection attributeCollection2, out XmlAttribute differentAttribute1, out XmlAttribute differentAttribute2) { 
            differentAttribute1 = null;
            differentAttribute2 = null;

            XmlAttribute[] attributeArray1 = GetSortedAttributeArray(attributeCollection1); 
            XmlAttribute[] attributeArray2 = GetSortedAttributeArray(attributeCollection2);
 
            object differentItem1, differentItem2; 
            string differentLocation;
            if (!CompareSchemaCollections(attributeArray1, attributeArray2, out differentItem1, out differentItem2, out differentLocation)) { 
                differentAttribute1 = (XmlAttribute)differentItem1;
                differentAttribute2 = (XmlAttribute)differentItem2;
                return false;
            } 
            return true;
        } 
 
        /// 
        /// sort XmlAttribute array, so we can compare two collections without being affected by the order 
        /// 
        /// 
        /// 
        private static XmlAttribute[] GetSortedAttributeArray(System.Collections.ICollection attributeCollection) { 
            XmlAttribute[] attributeArray = new XmlAttribute[attributeCollection.Count];
            int index = 0; 
            foreach (XmlAttribute attribute in attributeCollection) { 
                attributeArray[index++] = attribute;
            } 

            Array.Sort(attributeArray, new AttributeComparer());
            return attributeArray;
        } 

        ///  
        /// Compare two elements 
        /// 
        ///  
        /// 
        private static bool CompareXmlElements(XmlElement element1, XmlElement element2, out string differentLocation) {
            differentLocation = String.Empty;
 
            if (!String.Equals(element1.LocalName, element2.LocalName, StringComparison.Ordinal) ||
                    !String.Equals(element1.NamespaceURI, element2.NamespaceURI, StringComparison.Ordinal)) { 
                return false; 
            }
 
            XmlAttribute differentAttribute1, differentAttribute2;
            if (!CompareXmlAttributeCollections(element1.Attributes, element2.Attributes, out differentAttribute1, out differentAttribute2)) {
                string attributeName1 = differentAttribute1 != null ? "@" + differentAttribute1.LocalName : String.Empty;
                string attributeName2 = differentAttribute2 != null ? "@" + differentAttribute2.LocalName : String.Empty; 
                differentLocation = CombineTwoNames(attributeName1, attributeName2);
                return false; 
            } 

            object differentChild1, differentChild2; 
            if (!CompareSchemaCollections(element1.ChildNodes, element2.ChildNodes, out differentChild1, out differentChild2, out differentLocation)) {
                string child1Name = differentChild1 != null ? ((XmlNode)differentChild1).LocalName : String.Empty;
                string child2Name = differentChild2 != null ? ((XmlNode)differentChild2).LocalName : String.Empty;
                differentLocation = CombinePath(CombineTwoNames(child1Name, child2Name), differentLocation); 
                return false;
            } 
            return true; 
        }
 
        /// 
        /// Compare two text nodes
        /// 
        ///  
        /// 
        private static bool CompareXmlTexts(XmlText text1, XmlText text2) { 
            return String.Equals(text1.Value, text2.Value, StringComparison.Ordinal); 
        }
 
        /// 
        /// Combine two path (similar to xpath) in error messages
        /// 
        ///  
        /// 
        private static string CombinePath(string path1, string path2) { 
            if (String.IsNullOrEmpty(path1)) { 
                return path2;
            } 
            else if (String.IsNullOrEmpty(path2)) {
                return path1;
            }
 
            return path1 + "/" + path2;
        } 
 
        /// 
        /// Get Name of a top level schema item 
        /// 
        /// 
        /// 
        ///  
        private static string GetSchemaItemName(XmlSchemaObject item) {
            if (item == null) { 
                throw new ArgumentNullException("item"); 
            }
 
            Type itemType = item.GetType();
            PropertyInfo nameProperty = itemType.GetProperty("Name");
            if (nameProperty != null) {
                object nameValue = nameProperty.GetValue(item, new object[]{}); 
                if (nameValue is string) {
                    return (string)nameValue; 
                } 
                return String.Empty;
            } 

            return String.Empty;
        }
 
        /// 
        /// Generate end-user unstandable property name -- we will use name in the schema file, but not name in object model 
        ///  
        /// 
        ///  
        private static string GetSchemaPropertyNameInXml(PropertyInfo property, object value1, object value2) {
            object[] propertyAttributes = property.GetCustomAttributes(true);
            string name = String.Empty;
 
            if (propertyAttributes != null) {
                string name1 = GetSchemaPropertyNameInXmlHelper(propertyAttributes, value1); 
                string name2 = GetSchemaPropertyNameInXmlHelper(propertyAttributes, value2); 

                name = CombineTwoNames(name1, name2); 
            }

            if (String.IsNullOrEmpty(name)) {
                Debug.Fail("Why we didn't get a property name with normal routine?"); 
                name = property.Name;
            } 
 
            return name;
        } 

        /// 
        /// Combine names of two properties in error messages
        ///  
        /// 
        private static string CombineTwoNames(string name1, string name2) { 
            string name = String.Empty; 
            if (name1.Length > 0) {
                if (name2.Length > 0) { 
                    if (String.Equals(name1, name2, StringComparison.Ordinal)) {
                        name = name1;
                    }
                    else { 
                        name = name1 + "|" + name2;
                    } 
                } 
                else {
                    name = name1; 
                }
            }
            else if (name2.Length > 0) {
                name = name2; 
            }
            return name; 
        } 

        ///  
        /// a helper function to generate names
        /// 
        /// 
        private static string GetSchemaPropertyNameInXmlHelper(object[] propertyAttributes, object value) { 
            if (value != null) {
                foreach (object attribute in propertyAttributes) { 
                    if (attribute is System.Xml.Serialization.XmlAttributeAttribute) { 
                        return "@" + ((System.Xml.Serialization.XmlAttributeAttribute)attribute).AttributeName;
                    } 
                    if (attribute is System.Xml.Serialization.XmlElementAttribute) {
                        System.Xml.Serialization.XmlElementAttribute elementAttribute = (System.Xml.Serialization.XmlElementAttribute)attribute;
                        Type elementType = elementAttribute.Type;
                        if (elementType == null || elementType.IsInstanceOfType(value)) { 
                            if (value is XmlSchemaObject) {
                                string itemName = GetSchemaItemName((XmlSchemaObject)value); 
                                if (itemName.Length > 0) { 
                                    return String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}[@name='{1}']", elementAttribute.ElementName, itemName);
                                } 
                            }
                            return elementAttribute.ElementName;
                        }
                    } 
                    if (attribute is System.Xml.Serialization.XmlAnyAttributeAttribute) {
                        if (value is XmlAttribute) { 
                            return "@" + ((XmlAttribute)value).LocalName; 
                        }
                    } 
                    if (attribute is System.Xml.Serialization.XmlAnyElementAttribute) {
                        if (value is XmlElement) {
                            return ((XmlElement)value).LocalName;
                        } 
                    }
                    if (attribute is System.Xml.Serialization.XmlTextAttribute) { 
                        if (value is XmlText) { 
                            return ((XmlText)value).Name;
                        } 
                    }
                }
            }
 
            return String.Empty;
        } 
 
        /// 
        /// Check whether a property is persisted with XmlSerialization 
        /// 
        /// 
        /// 
        ///  
        private static bool IsPersistedProperty(PropertyInfo property) {
            object[] propertyAttributes = property.GetCustomAttributes(true); 
            if (propertyAttributes != null) { 
                foreach (object attribute in propertyAttributes) {
                    foreach (Type serializationAttibuteType in xmlSerializationAttributes) { 
                        if (serializationAttibuteType.IsInstanceOfType(attribute)) {
                            return true;
                        }
                    } 
                }
            } 
            return false; 
        }
 
        /// 
        /// check whether we should report warning but not error messages, when the property is different
        /// 
        ///  
        private static bool ShouldIgnoreSchemaProperty(PropertyInfo property) {
            Type propertyType = property.PropertyType; 
            foreach (Type ignoreableType in ignorablePropertyTypes) { 
                if (propertyType == ignoreableType || propertyType.IsSubclassOf(ignoreableType)) {
                    return true; 
                }
            }

            // special case constraints... 
            if (String.Equals(property.Name, "Constraints", StringComparison.Ordinal)) {
                return true; 
            } 
            return false;
        } 

        /// 
        /// a helper structure to hold top level items we want to scan
        ///  
        /// 
        private struct SchemaTopLevelItemType { 
            public Type ItemType; 
            public string Name;
 
            public SchemaTopLevelItemType(Type itemType, string name) {
                this.ItemType = itemType;
                this.Name = name;
            } 
        };
 
        ///  
        /// Helper class to compare two XmlAttributes
        ///  
        /// 
        private class AttributeComparer : System.Collections.Generic.IComparer {

            public int Compare(System.Xml.XmlAttribute x, System.Xml.XmlAttribute y) { 
                int namespaceResult = String.Compare(x.NamespaceURI, y.NamespaceURI, StringComparison.Ordinal);
                if (namespaceResult != 0) { 
                    return namespaceResult; 
                }
 
                return String.Compare(x.Name, y.Name, StringComparison.Ordinal);
            }
        }
    } 
}
 
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------ 
// 
//   Copyright (C) Microsoft Corporation. All Rights Reserved.
// 
//----------------------------------------------------------------------------- 

using System; 
using System.Collections.Generic; 
using System.Collections.ObjectModel;
using System.Diagnostics; 
using System.Globalization;
using System.Reflection;
using System.Web.Services.Description;
using System.Xml; 
using System.Xml.Schema;
#if WEB_EXTENSIONS_CODE 
using System.Web.Resources; 
#else
using Microsoft.VSDesigner.Resources.Microsoft.VSDesigner; 
#endif


 
#if WEB_EXTENSIONS_CODE
namespace System.Web.Compilation.WCFModel 
#else 
namespace Microsoft.VSDesigner.WCFModel
#endif 
{
    /// 
    /// a utility class to merge schema files, and remove duplicated part
    ///  
    internal class SchemaMerger {
 
        // xml serializable attributes 
        private static Type[] xmlSerializationAttributes = new Type[] {
                            typeof(System.Xml.Serialization.XmlElementAttribute), 
                            typeof(System.Xml.Serialization.XmlAttributeAttribute),
                            typeof(System.Xml.Serialization.XmlAnyAttributeAttribute),
                            typeof(System.Xml.Serialization.XmlAnyElementAttribute),
                            typeof(System.Xml.Serialization.XmlTextAttribute), 
        };
 
        // elements in the schema (we don't process annotation node) 
        private static SchemaTopLevelItemType[] schemaTopLevelItemTypes = new SchemaTopLevelItemType[] {
            new SchemaTopLevelItemType(typeof(XmlSchemaType), "type"), 
            new SchemaTopLevelItemType(typeof(XmlSchemaElement), "element"),
            new SchemaTopLevelItemType(typeof(XmlSchemaAttribute), "attribute"),
            new SchemaTopLevelItemType(typeof(XmlSchemaGroup), "group"),
            new SchemaTopLevelItemType(typeof(XmlSchemaAttributeGroup), "attributeGroup"), 
        };
 
        // when properties defined in those types are different, we only report warnings, but not error messages 
        private static Type[] ignorablePropertyTypes = new Type[] {
            typeof(XmlAttribute[]), 
            typeof(XmlElement[]),
            typeof(XmlNode[]),
            typeof(XmlSchemaAnnotation),
        }; 

        private readonly static XmlAttribute[] emptyXmlAttributeCollection = new XmlAttribute[0]; 
        private readonly static object[] emptyCollection = new object[0]; 

        ///  
        /// Merge and remove duplicated part from the schema list
        /// 
        /// schemas with names
        /// error messages 
        /// error messages
        ///  
        internal static void MergeSchemas(IEnumerable schemaList, IList importErrors, out IEnumerable duplicatedSchemas) { 
            if (schemaList == null) {
                throw new ArgumentNullException("schemaList"); 
            }
            if (importErrors == null) {
                throw new ArgumentNullException("importErrors");
            } 

            List duplicatedSchemaList = new List(); 
            duplicatedSchemas = duplicatedSchemaList; 

            // types, elements, groups have their own name space 
            Dictionary[] knownItemTables = new Dictionary[schemaTopLevelItemTypes.Length];
            for (int i = 0; i < schemaTopLevelItemTypes.Length; i++) {
                knownItemTables[i] = new Dictionary();
            } 

            foreach (XmlSchema schema in schemaList) { 
 
                bool hasNewDefinedItems = false;
                List duplicatedItems = new List(); 

                for (int i = 0; i < schemaTopLevelItemTypes.Length; i++) {
                    Dictionary knownItemTable = knownItemTables[i];
                    int knownItemCount = knownItemTable.Count; 
                    FindDuplicatedItems(schema, schemaTopLevelItemTypes[i].ItemType, schemaTopLevelItemTypes[i].Name, knownItemTable, duplicatedItems, importErrors);
                    if (knownItemTable.Count > knownItemCount) { 
                        hasNewDefinedItems = true; 
                    }
                } 

                if (duplicatedItems.Count > 0) {
                    if (!hasNewDefinedItems) {
                        // remove the whole schema... 
                        duplicatedSchemaList.Add(schema);
                    } 
                    else { 
                        // remove duplicated items only
                        foreach (XmlSchemaObject item in duplicatedItems) { 
                            schema.Items.Remove(item);
                        }
                    }
                } 
            }
        } 
 
        /// 
        /// Find duplicated items in a schema 
        /// 
        /// 
        private static void FindDuplicatedItems(
                    XmlSchema schema, 
                    Type itemType,
                    string itemTypeName, 
                    Dictionary knownItemTable, 
                    List duplicatedItems,
                    IList importErrors) { 

            string targetNamespace = schema.TargetNamespace;
            if (String.IsNullOrEmpty(targetNamespace)) {
                targetNamespace = String.Empty; 
            }
 
            foreach (XmlSchemaObject item in schema.Items) { 
                if (itemType.IsInstanceOfType(item)) {
 
                    XmlQualifiedName combinedName = new XmlQualifiedName(GetSchemaItemName(item), targetNamespace);

                    XmlSchemaObject originalItem = null;
                    if (knownItemTable.TryGetValue(combinedName, out originalItem)) { 
                        string differentLocation;
 
                        if (!AreSchemaObjectsEquivalent(originalItem, item, out differentLocation)) { 
                            differentLocation = CombinePath(".", differentLocation);
                            importErrors.Add( 
                                    new ProxyGenerationError(
                                        ProxyGenerationError.GeneratorState.MergeMetadata,
                                        String.Empty,
                                        new InvalidOperationException( 
                                            String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_DuplicatedSchemaItems, itemTypeName, combinedName.ToString(), schema.SourceUri, originalItem.SourceUri, differentLocation)
                                        ) 
                                    ) 
                            );
                        } 
                        else if (!String.IsNullOrEmpty(differentLocation)) {
                            // warning: ignorable difference found
                            differentLocation = CombinePath(".", differentLocation);
                            importErrors.Add( 
                                    new ProxyGenerationError(
                                        ProxyGenerationError.GeneratorState.MergeMetadata, 
                                        String.Empty, 
                                        new InvalidOperationException(
                                            String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_DuplicatedSchemaItemsIgnored, itemTypeName, combinedName.ToString(), schema.SourceUri, originalItem.SourceUri, differentLocation) 
                                        ),
                                        true        // isWarning = true
                                    )
                            ); 
                        }
                        duplicatedItems.Add(item); 
                    } 
                    else {
                        item.SourceUri = schema.SourceUri; 
                        knownItemTable.Add(combinedName, item);
                    }
                }
            } 
        }
 
        ///  
        /// Compare two schema objects
        ///  
        /// 
        /// 
        /// For all those functions, we follow the same pattern:
        ///  return false: find not-ignorable difference between them.  differentLocation will contain the path 
        ///  return true, with empty differentLocation -- no difference found
        ///  return true, with non-empty differentLocation -- ignorable difference found under that location 
        ///  
        private static bool AreSchemaObjectsEquivalent(XmlSchemaObject originalItem, XmlSchemaObject item, out string differentLocation) {
            differentLocation = String.Empty; 

            Type itemType = originalItem.GetType();
            if (itemType != item.GetType()) {
                return false; 
            }
 
            string ignorableDifferenceLocation = String.Empty; 

            PropertyInfo[] properties = itemType.GetProperties(); 
            foreach (PropertyInfo property in properties) {
                if (IsPersistedProperty(property)) {

                    bool ignorableProperty = ShouldIgnoreSchemaProperty(property); 

                    object originalValue = property.GetValue(originalItem, new object[] {}); 
                    object newValue = property.GetValue(item, new object[] {}); 

                    if (!CompareSchemaPropertyValues(property, originalValue, newValue, out differentLocation) && !ignorableProperty) { 
                        return false;
                    }
                    if (String.IsNullOrEmpty(ignorableDifferenceLocation)) {
                        ignorableDifferenceLocation = differentLocation; 
                    }
                } 
            } 

            differentLocation = ignorableDifferenceLocation; 
            return true;
        }

        ///  
        /// Compare two property of a schema object
        ///  
        /// true: if the value are same 
        /// 
        private static bool CompareSchemaPropertyValues(PropertyInfo propertyInfo, object originalValue, object newValue, out string differentLocation) { 
            differentLocation = String.Empty;

            if (originalValue == null && newValue == null) {
                return true; 
            }
 
            // we create empty collection so a meaningful differentLocation could be generated 
            if (typeof(XmlAttribute[]) == propertyInfo.PropertyType) {
                if (originalValue == null) { 
                    originalValue = emptyXmlAttributeCollection;
                }
                if (newValue == null) {
                    newValue = emptyXmlAttributeCollection; 
                }
 
                XmlAttribute differentAttribute1, differentAttribute2; 
                if (!CompareXmlAttributeCollections((XmlAttribute[])originalValue, (XmlAttribute[])newValue, out differentAttribute1, out differentAttribute2)) {
                    differentLocation = GetSchemaPropertyNameInXml(propertyInfo, differentAttribute1, differentAttribute2); 
                    return false;
                }
                return true;
            } 

            if (typeof(System.Collections.ICollection).IsAssignableFrom(propertyInfo.PropertyType)) { 
                if (originalValue == null) { 
                    originalValue = emptyCollection;
                } 
                if (newValue == null) {
                    newValue = emptyCollection;
                }
 
                object differentItem1, differentItem2;
                if (!CompareSchemaCollections((System.Collections.ICollection)originalValue, (System.Collections.ICollection)newValue, out differentItem1, out differentItem2, out differentLocation)) { 
                    differentLocation = CombinePath(GetSchemaPropertyNameInXml(propertyInfo, differentItem1, differentItem2), differentLocation); 
                    return false;
                } 
                else {
                    if (!String.IsNullOrEmpty(differentLocation)) {
                        // ignorable difference...
                        differentLocation = CombinePath(GetSchemaPropertyNameInXml(propertyInfo, differentItem1, differentItem2), differentLocation); 
                    }
                    return true; 
                } 
            }
 
            if (originalValue == null || newValue == null) {
                differentLocation = CombinePath(GetSchemaPropertyNameInXml(propertyInfo, originalValue, newValue), differentLocation);
                return false;
            } 

            if (originalValue.GetType() != newValue.GetType()) { 
                differentLocation = CombinePath(GetSchemaPropertyNameInXml(propertyInfo, originalValue, newValue), differentLocation); 
                return false;
            } 

            if (!CompareSchemaValues(originalValue, newValue, out differentLocation)) {
                differentLocation = CombinePath(GetSchemaPropertyNameInXml(propertyInfo, originalValue, newValue), differentLocation);
                return false; 
            }
            else { 
                // ignorable difference... 
                if (!String.IsNullOrEmpty(differentLocation)) {
                    differentLocation = CombinePath(GetSchemaPropertyNameInXml(propertyInfo, originalValue, newValue), differentLocation); 
                }
                return true;
            }
        } 

        ///  
        /// Compare two schema values 
        /// 
        ///  
        /// 
        private static bool CompareSchemaValues(object originalValue, object newValue, out string differentLocation) {
            differentLocation = String.Empty;
 
            if (originalValue == null || newValue == null) {
                return (originalValue == null && newValue == null); 
            } 

            if (originalValue.GetType() != newValue.GetType()) { 
                return false;
            }

            if (originalValue is XmlSchemaObject) { 
                return AreSchemaObjectsEquivalent((XmlSchemaObject)originalValue, (XmlSchemaObject)newValue, out differentLocation);
            } 
 
            if (originalValue is XmlAttribute) {
                return CompareXmlAttributes((XmlAttribute)originalValue, (XmlAttribute)newValue); 
            }

            if (originalValue is XmlElement) {
                return CompareXmlElements((XmlElement)originalValue, (XmlElement)newValue, out differentLocation); 
            }
 
            if (originalValue is XmlText) { 
                return CompareXmlTexts((XmlText)originalValue, (XmlText)newValue);
            } 

            return originalValue.Equals(newValue);
        }
 
        /// 
        /// Compare two collections of items 
        ///  
        /// 
        ///  
        private static bool CompareSchemaCollections(System.Collections.IEnumerable originalCollection, System.Collections.IEnumerable newCollection,
                out object differentItem1, out object differentItem2, out string differentLocation) {

            differentLocation = String.Empty; 

            System.Collections.IEnumerator list1 = originalCollection.GetEnumerator(); 
            System.Collections.IEnumerator list2 = newCollection.GetEnumerator(); 

            string ignorableDifferenceLocation = String.Empty; 
            object ignorableDifferenceItem1 = null;
            object ignorableDifferenceItem2 = null;

            do { 
                differentItem1 = list1.MoveNext() ? list1.Current : null;
                differentItem2 = list2.MoveNext() ? list2.Current : null; 
 
                if (!CompareSchemaValues(differentItem1, differentItem2, out differentLocation)) {
                    return false; 
                }

                if (String.IsNullOrEmpty(ignorableDifferenceLocation)) {
                    ignorableDifferenceItem1 = differentItem1; 
                    ignorableDifferenceItem2 = differentItem2;
                    ignorableDifferenceLocation = differentLocation; 
                } 
            }
            while (differentItem1 != null && differentItem2 != null); 

            Debug.Assert(differentItem1 == null && differentItem2 == null);

            differentLocation = ignorableDifferenceLocation; 
            differentItem1 = ignorableDifferenceItem1;
            differentItem2 = ignorableDifferenceItem2; 
 
            return true;
        } 

        /// 
        /// Compare two attributes
        ///  
        /// 
        ///  
        private static bool CompareXmlAttributes(XmlAttribute attribute1, XmlAttribute attribute2) { 
            return String.Equals(attribute1.LocalName, attribute2.LocalName, StringComparison.Ordinal) &&
                    String.Equals(attribute1.NamespaceURI, attribute2.NamespaceURI, StringComparison.Ordinal) && 
                    String.Equals(attribute1.Value, attribute2.Value, StringComparison.Ordinal);
        }

        ///  
        /// Compare two attribute collections
        ///  
        ///  
        /// 
        private static bool CompareXmlAttributeCollections(System.Collections.ICollection attributeCollection1, System.Collections.ICollection attributeCollection2, out XmlAttribute differentAttribute1, out XmlAttribute differentAttribute2) { 
            differentAttribute1 = null;
            differentAttribute2 = null;

            XmlAttribute[] attributeArray1 = GetSortedAttributeArray(attributeCollection1); 
            XmlAttribute[] attributeArray2 = GetSortedAttributeArray(attributeCollection2);
 
            object differentItem1, differentItem2; 
            string differentLocation;
            if (!CompareSchemaCollections(attributeArray1, attributeArray2, out differentItem1, out differentItem2, out differentLocation)) { 
                differentAttribute1 = (XmlAttribute)differentItem1;
                differentAttribute2 = (XmlAttribute)differentItem2;
                return false;
            } 
            return true;
        } 
 
        /// 
        /// sort XmlAttribute array, so we can compare two collections without being affected by the order 
        /// 
        /// 
        /// 
        private static XmlAttribute[] GetSortedAttributeArray(System.Collections.ICollection attributeCollection) { 
            XmlAttribute[] attributeArray = new XmlAttribute[attributeCollection.Count];
            int index = 0; 
            foreach (XmlAttribute attribute in attributeCollection) { 
                attributeArray[index++] = attribute;
            } 

            Array.Sort(attributeArray, new AttributeComparer());
            return attributeArray;
        } 

        ///  
        /// Compare two elements 
        /// 
        ///  
        /// 
        private static bool CompareXmlElements(XmlElement element1, XmlElement element2, out string differentLocation) {
            differentLocation = String.Empty;
 
            if (!String.Equals(element1.LocalName, element2.LocalName, StringComparison.Ordinal) ||
                    !String.Equals(element1.NamespaceURI, element2.NamespaceURI, StringComparison.Ordinal)) { 
                return false; 
            }
 
            XmlAttribute differentAttribute1, differentAttribute2;
            if (!CompareXmlAttributeCollections(element1.Attributes, element2.Attributes, out differentAttribute1, out differentAttribute2)) {
                string attributeName1 = differentAttribute1 != null ? "@" + differentAttribute1.LocalName : String.Empty;
                string attributeName2 = differentAttribute2 != null ? "@" + differentAttribute2.LocalName : String.Empty; 
                differentLocation = CombineTwoNames(attributeName1, attributeName2);
                return false; 
            } 

            object differentChild1, differentChild2; 
            if (!CompareSchemaCollections(element1.ChildNodes, element2.ChildNodes, out differentChild1, out differentChild2, out differentLocation)) {
                string child1Name = differentChild1 != null ? ((XmlNode)differentChild1).LocalName : String.Empty;
                string child2Name = differentChild2 != null ? ((XmlNode)differentChild2).LocalName : String.Empty;
                differentLocation = CombinePath(CombineTwoNames(child1Name, child2Name), differentLocation); 
                return false;
            } 
            return true; 
        }
 
        /// 
        /// Compare two text nodes
        /// 
        ///  
        /// 
        private static bool CompareXmlTexts(XmlText text1, XmlText text2) { 
            return String.Equals(text1.Value, text2.Value, StringComparison.Ordinal); 
        }
 
        /// 
        /// Combine two path (similar to xpath) in error messages
        /// 
        ///  
        /// 
        private static string CombinePath(string path1, string path2) { 
            if (String.IsNullOrEmpty(path1)) { 
                return path2;
            } 
            else if (String.IsNullOrEmpty(path2)) {
                return path1;
            }
 
            return path1 + "/" + path2;
        } 
 
        /// 
        /// Get Name of a top level schema item 
        /// 
        /// 
        /// 
        ///  
        private static string GetSchemaItemName(XmlSchemaObject item) {
            if (item == null) { 
                throw new ArgumentNullException("item"); 
            }
 
            Type itemType = item.GetType();
            PropertyInfo nameProperty = itemType.GetProperty("Name");
            if (nameProperty != null) {
                object nameValue = nameProperty.GetValue(item, new object[]{}); 
                if (nameValue is string) {
                    return (string)nameValue; 
                } 
                return String.Empty;
            } 

            return String.Empty;
        }
 
        /// 
        /// Generate end-user unstandable property name -- we will use name in the schema file, but not name in object model 
        ///  
        /// 
        ///  
        private static string GetSchemaPropertyNameInXml(PropertyInfo property, object value1, object value2) {
            object[] propertyAttributes = property.GetCustomAttributes(true);
            string name = String.Empty;
 
            if (propertyAttributes != null) {
                string name1 = GetSchemaPropertyNameInXmlHelper(propertyAttributes, value1); 
                string name2 = GetSchemaPropertyNameInXmlHelper(propertyAttributes, value2); 

                name = CombineTwoNames(name1, name2); 
            }

            if (String.IsNullOrEmpty(name)) {
                Debug.Fail("Why we didn't get a property name with normal routine?"); 
                name = property.Name;
            } 
 
            return name;
        } 

        /// 
        /// Combine names of two properties in error messages
        ///  
        /// 
        private static string CombineTwoNames(string name1, string name2) { 
            string name = String.Empty; 
            if (name1.Length > 0) {
                if (name2.Length > 0) { 
                    if (String.Equals(name1, name2, StringComparison.Ordinal)) {
                        name = name1;
                    }
                    else { 
                        name = name1 + "|" + name2;
                    } 
                } 
                else {
                    name = name1; 
                }
            }
            else if (name2.Length > 0) {
                name = name2; 
            }
            return name; 
        } 

        ///  
        /// a helper function to generate names
        /// 
        /// 
        private static string GetSchemaPropertyNameInXmlHelper(object[] propertyAttributes, object value) { 
            if (value != null) {
                foreach (object attribute in propertyAttributes) { 
                    if (attribute is System.Xml.Serialization.XmlAttributeAttribute) { 
                        return "@" + ((System.Xml.Serialization.XmlAttributeAttribute)attribute).AttributeName;
                    } 
                    if (attribute is System.Xml.Serialization.XmlElementAttribute) {
                        System.Xml.Serialization.XmlElementAttribute elementAttribute = (System.Xml.Serialization.XmlElementAttribute)attribute;
                        Type elementType = elementAttribute.Type;
                        if (elementType == null || elementType.IsInstanceOfType(value)) { 
                            if (value is XmlSchemaObject) {
                                string itemName = GetSchemaItemName((XmlSchemaObject)value); 
                                if (itemName.Length > 0) { 
                                    return String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}[@name='{1}']", elementAttribute.ElementName, itemName);
                                } 
                            }
                            return elementAttribute.ElementName;
                        }
                    } 
                    if (attribute is System.Xml.Serialization.XmlAnyAttributeAttribute) {
                        if (value is XmlAttribute) { 
                            return "@" + ((XmlAttribute)value).LocalName; 
                        }
                    } 
                    if (attribute is System.Xml.Serialization.XmlAnyElementAttribute) {
                        if (value is XmlElement) {
                            return ((XmlElement)value).LocalName;
                        } 
                    }
                    if (attribute is System.Xml.Serialization.XmlTextAttribute) { 
                        if (value is XmlText) { 
                            return ((XmlText)value).Name;
                        } 
                    }
                }
            }
 
            return String.Empty;
        } 
 
        /// 
        /// Check whether a property is persisted with XmlSerialization 
        /// 
        /// 
        /// 
        ///  
        private static bool IsPersistedProperty(PropertyInfo property) {
            object[] propertyAttributes = property.GetCustomAttributes(true); 
            if (propertyAttributes != null) { 
                foreach (object attribute in propertyAttributes) {
                    foreach (Type serializationAttibuteType in xmlSerializationAttributes) { 
                        if (serializationAttibuteType.IsInstanceOfType(attribute)) {
                            return true;
                        }
                    } 
                }
            } 
            return false; 
        }
 
        /// 
        /// check whether we should report warning but not error messages, when the property is different
        /// 
        ///  
        private static bool ShouldIgnoreSchemaProperty(PropertyInfo property) {
            Type propertyType = property.PropertyType; 
            foreach (Type ignoreableType in ignorablePropertyTypes) { 
                if (propertyType == ignoreableType || propertyType.IsSubclassOf(ignoreableType)) {
                    return true; 
                }
            }

            // special case constraints... 
            if (String.Equals(property.Name, "Constraints", StringComparison.Ordinal)) {
                return true; 
            } 
            return false;
        } 

        /// 
        /// a helper structure to hold top level items we want to scan
        ///  
        /// 
        private struct SchemaTopLevelItemType { 
            public Type ItemType; 
            public string Name;
 
            public SchemaTopLevelItemType(Type itemType, string name) {
                this.ItemType = itemType;
                this.Name = name;
            } 
        };
 
        ///  
        /// Helper class to compare two XmlAttributes
        ///  
        /// 
        private class AttributeComparer : System.Collections.Generic.IComparer {

            public int Compare(System.Xml.XmlAttribute x, System.Xml.XmlAttribute y) { 
                int namespaceResult = String.Compare(x.NamespaceURI, y.NamespaceURI, StringComparison.Ordinal);
                if (namespaceResult != 0) { 
                    return namespaceResult; 
                }
 
                return String.Compare(x.Name, y.Name, StringComparison.Ordinal);
            }
        }
    } 
}
 
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK