#region Copyright (c) Microsoft Corporation /// /// Copyright (c) Microsoft Corporation. All Rights Reserved. /// Information Contained Herein is Proprietary and Confidential. /// #endregion using System; using System.Collections.Generic; using System.Globalization; using System.Web.Services.Description; using System.Xml; #if WEB_EXTENSIONS_CODE using System.Web.Resources; #else using Microsoft.VSDesigner.WCF.Resources; #endif #if WEB_EXTENSIONS_CODE namespace System.Web.Compilation.WCFModel #else namespace Microsoft.VSDesigner.WCFModel #endif { /// /// This class check whether there are duplicated wsdl files in the metadata collection, and report error messages, if any contract is /// defined differently in two wsdl files. /// internal class WsdlInspector { private IList importErrors; private Dictionary portTypes; private Dictionary messages; /// /// constructor /// /// private WsdlInspector(IList importErrors) { this.importErrors = importErrors; this.portTypes = new Dictionary(); this.messages = new Dictionary(); } /// /// function to check duplicated items /// /// /// /// internal static void CheckDuplicatedWsdlItems(ICollection wsdlFiles, IList importErrors) { WsdlInspector inspector = new WsdlInspector(importErrors); inspector.CheckServiceDescriptions(wsdlFiles); } /// /// check all duplicated items in a collection of files /// /// private void CheckServiceDescriptions(ICollection wsdlFiles) { foreach (System.Web.Services.Description.ServiceDescription wsdl in wsdlFiles) { string targetNamespace = wsdl.TargetNamespace; if (String.IsNullOrEmpty(targetNamespace)) { targetNamespace = String.Empty; } // check all portTypes... foreach (PortType portType in wsdl.PortTypes) { XmlQualifiedName portTypeName = new XmlQualifiedName(portType.Name, targetNamespace); PortType definedPortType; if (portTypes.TryGetValue(portTypeName, out definedPortType)) { MatchPortTypes(definedPortType, portType); } else { portTypes.Add(portTypeName, portType); } } // check all messages... foreach (Message message in wsdl.Messages) { XmlQualifiedName messageName = new XmlQualifiedName(message.Name, targetNamespace); Message definedMessage; if (messages.TryGetValue(messageName, out definedMessage)) { MatchMessages(definedMessage, message); } else { messages.Add(messageName, message); } } } } /// /// Compare two port type (with same targetNamespace/name) /// /// private void MatchPortTypes(PortType x, PortType y) { Operation[] operationsX = new Operation[x.Operations.Count]; x.Operations.CopyTo(operationsX, 0); Array.Sort(operationsX, new OperationComparer()); Operation[] operationsY = new Operation[y.Operations.Count]; y.Operations.CopyTo(operationsY, 0); Array.Sort(operationsY, new OperationComparer()); MatchCollections(operationsX, operationsY, delegate(Operation operationX, Operation operationY) { if (operationX != null && operationY != null) { int nameDifferent = String.Compare(operationX.Name, operationY.Name, StringComparison.Ordinal); if (nameDifferent < 0) { ReportUniqueOperation(operationX, x, y); return false; } else if (nameDifferent > 0) { ReportUniqueOperation(operationY, y, x); return false; } else if (!MatchOperations(operationX, operationY)) { return false; } return true; } else if (operationX != null) { ReportUniqueOperation(operationX, x, y); return false; } else if (operationY != null) { ReportUniqueOperation(operationY, y, x); return false; } return true; } ); } /// /// Compare two operations (with same name) /// /// private bool MatchOperations(Operation x, Operation y) { if (!MatchOperationMessages(x.Messages.Input, y.Messages.Input)) { ReportOperationDefinedDifferently(x, y); return false; } if (!MatchOperationMessages(x.Messages.Output, y.Messages.Output)) { ReportOperationDefinedDifferently(x, y); return false; } OperationFault[] faultsX = new OperationFault[x.Faults.Count]; x.Faults.CopyTo(faultsX, 0); Array.Sort(faultsX, new OperationFaultComparer()); OperationFault[] faultsY = new OperationFault[y.Faults.Count]; y.Faults.CopyTo(faultsY, 0); Array.Sort(faultsY, new OperationFaultComparer()); if (!MatchCollections(faultsX, faultsY, delegate(OperationFault faultX, OperationFault faultY) { if (faultX != null && faultY != null) { return MatchXmlQualifiedNames(faultX.Message, faultY.Message); } else if (faultX != null || faultY != null) { return false; } return true; } )) { ReportOperationDefinedDifferently(x, y); return false; } return true; } /// /// Compare two messages in operations (with same name) /// /// private bool MatchOperationMessages(OperationMessage x, OperationMessage y) { if (x == null && y == null) { return true; } else if (x == null || y == null) { return false; } return MatchXmlQualifiedNames(x.Message, y.Message); } /// /// Compare two messages defined in wsdl (with same name/targetNamespace) /// /// private void MatchMessages(Message x, Message y) { MessagePart[] partsX = new MessagePart[x.Parts.Count]; x.Parts.CopyTo(partsX, 0); Array.Sort(partsX, new MessagePartComparer()); MessagePart[] partsY = new MessagePart[y.Parts.Count]; y.Parts.CopyTo(partsY, 0); Array.Sort(partsY, new MessagePartComparer()); MatchCollections(partsX, partsY, delegate(MessagePart partX, MessagePart partY) { if (partX != null && partY != null) { int nameDifferent = String.Compare(partX.Name, partY.Name, StringComparison.Ordinal); if (nameDifferent < 0) { ReportUniqueMessagePart(partX, x, y); return false; } else if (nameDifferent > 0) { ReportUniqueMessagePart(partY, y, x); return false; } else if (!MatchMessageParts(partX, partY)) { return false; } return true; } else if (partX != null) { ReportUniqueMessagePart(partX, x, y); return false; } else if (partY != null) { ReportUniqueMessagePart(partY, y, x); return false; } return true; } ); } /// /// Compare two message parts (with same name) /// /// private bool MatchMessageParts(MessagePart partX, MessagePart partY) { if (!MatchXmlQualifiedNames(partX.Type, partY.Type) || !MatchXmlQualifiedNames(partX.Element, partY.Element)) { ReportMessageDefinedDifferently(partX, partX.Message, partY.Message); return false; } return true; } /// /// compare two XmlQualifiedName /// /// private bool MatchXmlQualifiedNames(XmlQualifiedName x, XmlQualifiedName y) { if (x != null && y != null) { return x == y; // XmlQualifiedName } return x == null && y == null; } /// /// Report an error when we find operation defined in one place but not another /// /// private void ReportUniqueOperation(Operation operation, PortType portType1, PortType portType2) { importErrors.Add(new ProxyGenerationError( ProxyGenerationError.GeneratorState.MergeMetadata, String.Empty, new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_OperationDefinedInOneOfDuplicatedServiceContract, portType1.Name, portType1.ServiceDescription.RetrievalUrl, portType2.ServiceDescription.RetrievalUrl, operation.Name) ) ) ); } /// /// Report an error when we find operation defined in two places differently /// /// private void ReportOperationDefinedDifferently(Operation x, Operation y) { importErrors.Add(new ProxyGenerationError( ProxyGenerationError.GeneratorState.MergeMetadata, String.Empty, new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_OperationDefinedDifferently, x.Name, x.PortType.Name, x.PortType.ServiceDescription.RetrievalUrl, y.PortType.ServiceDescription.RetrievalUrl) ) ) ); } /// /// Report an error when we find a part of message defined in one place but not another /// /// private void ReportUniqueMessagePart(MessagePart part, Message message1, Message message2) { importErrors.Add(new ProxyGenerationError( ProxyGenerationError.GeneratorState.MergeMetadata, String.Empty, new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_FieldDefinedInOneOfDuplicatedMessage, message1.Name, message1.ServiceDescription.RetrievalUrl, message2.ServiceDescription.RetrievalUrl, part.Name) ) ) ); } /// /// Report an error when we find message defined in two places differently /// /// private void ReportMessageDefinedDifferently(MessagePart part, Message x, Message y) { importErrors.Add(new ProxyGenerationError( ProxyGenerationError.GeneratorState.MergeMetadata, String.Empty, new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_FieldDefinedDifferentlyInDuplicatedMessage, part.Name, x.Name, x.ServiceDescription.RetrievalUrl, y.ServiceDescription.RetrievalUrl) ) ) ); } /// /// Helper class to sort Operations /// /// private class OperationComparer : System.Collections.Generic.IComparer { public int Compare(Operation x, Operation y) { return String.Compare(x.Name, y.Name, StringComparison.Ordinal); } } /// /// Helper class to sort OperationFaults /// /// private class OperationFaultComparer : System.Collections.Generic.IComparer { public int Compare(OperationFault x, OperationFault y) { int namespaceResult = String.Compare(x.Message.Namespace, y.Message.Namespace, StringComparison.Ordinal); if (namespaceResult != 0) { return namespaceResult; } return String.Compare(x.Message.Name, y.Message.Name, StringComparison.Ordinal); } } /// /// Helper class to sort MessageParts /// /// private class MessagePartComparer : System.Collections.Generic.IComparer { public int Compare(MessagePart x, MessagePart y) { return String.Compare(x.Name, y.Name, StringComparison.Ordinal); } } /// /// Helper function to compare two collections /// /// private delegate bool MatchCollectionItemDelegate(T x, T y); private bool MatchCollections(T[] x, T[] y, MatchCollectionItemDelegate compareItems) where T : class { System.Collections.IEnumerator enumeratorX = x.GetEnumerator(); System.Collections.IEnumerator enumeratorY = y.GetEnumerator(); T tX; T tY; do { tX = enumeratorX.MoveNext() ? (T)enumeratorX.Current : null; tY = enumeratorY.MoveNext() ? (T)enumeratorY.Current : null; if (tX != null && tY != null) { if (!compareItems(tX, tY)) { return false; } } } while (tX != null && tY != null); return compareItems(tX, tY); } } }