|
#region Copyright (c) Microsoft Corporation
/// <copyright company='Microsoft Corporation'>
/// Copyright (c) Microsoft Corporation. All Rights Reserved.
/// Information Contained Herein is Proprietary and Confidential.
/// </copyright>
#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
{
/// <summary>
/// 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.
/// </summary>
internal class WsdlInspector
{
private IList<ProxyGenerationError> importErrors;
private Dictionary<XmlQualifiedName, PortType> portTypes;
private Dictionary<XmlQualifiedName, Message> messages;
/// <summary>
/// constructor
/// </summary>
/// <remarks></remarks>
private WsdlInspector(IList<ProxyGenerationError> importErrors)
{
this.importErrors = importErrors;
this.portTypes = new Dictionary<XmlQualifiedName, PortType>();
this.messages = new Dictionary<XmlQualifiedName, Message>();
}
/// <summary>
/// function to check duplicated items
/// </summary>
/// <param name="wsdlFiles"></param>
/// <param name="importErrors"></param>
/// <remarks></remarks>
internal static void CheckDuplicatedWsdlItems(ICollection<ServiceDescription> wsdlFiles, IList<ProxyGenerationError> importErrors)
{
WsdlInspector inspector = new WsdlInspector(importErrors);
inspector.CheckServiceDescriptions(wsdlFiles);
}
/// <summary>
/// check all duplicated items in a collection of files
/// </summary>
/// <remarks></remarks>
private void CheckServiceDescriptions(ICollection<ServiceDescription> 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);
}
}
}
}
/// <summary>
/// Compare two port type (with same targetNamespace/name)
/// </summary>
/// <remarks></remarks>
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<Operation>(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;
}
);
}
/// <summary>
/// Compare two operations (with same name)
/// </summary>
/// <remarks></remarks>
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<OperationFault>(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;
}
/// <summary>
/// Compare two messages in operations (with same name)
/// </summary>
/// <remarks></remarks>
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);
}
/// <summary>
/// Compare two messages defined in wsdl (with same name/targetNamespace)
/// </summary>
/// <remarks></remarks>
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<MessagePart>(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;
}
);
}
/// <summary>
/// Compare two message parts (with same name)
/// </summary>
/// <remarks></remarks>
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;
}
/// <summary>
/// compare two XmlQualifiedName
/// </summary>
/// <remarks></remarks>
private bool MatchXmlQualifiedNames(XmlQualifiedName x, XmlQualifiedName y)
{
if (x != null && y != null)
{
return x == y; // XmlQualifiedName
}
return x == null && y == null;
}
/// <summary>
/// Report an error when we find operation defined in one place but not another
/// </summary>
/// <remarks></remarks>
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)
)
)
);
}
/// <summary>
/// Report an error when we find operation defined in two places differently
/// </summary>
/// <remarks></remarks>
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)
)
)
);
}
/// <summary>
/// Report an error when we find a part of message defined in one place but not another
/// </summary>
/// <remarks></remarks>
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)
)
)
);
}
/// <summary>
/// Report an error when we find message defined in two places differently
/// </summary>
/// <remarks></remarks>
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)
)
)
);
}
/// <summary>
/// Helper class to sort Operations
/// </summary>
/// <remarks></remarks>
private class OperationComparer : System.Collections.Generic.IComparer<Operation>
{
public int Compare(Operation x, Operation y)
{
return String.Compare(x.Name, y.Name, StringComparison.Ordinal);
}
}
/// <summary>
/// Helper class to sort OperationFaults
/// </summary>
/// <remarks></remarks>
private class OperationFaultComparer : System.Collections.Generic.IComparer<OperationFault>
{
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);
}
}
/// <summary>
/// Helper class to sort MessageParts
/// </summary>
/// <remarks></remarks>
private class MessagePartComparer : System.Collections.Generic.IComparer<MessagePart>
{
public int Compare(MessagePart x, MessagePart y)
{
return String.Compare(x.Name, y.Name, StringComparison.Ordinal);
}
}
/// <summary>
/// Helper function to compare two collections
/// </summary>
/// <remarks></remarks>
private delegate bool MatchCollectionItemDelegate<T>(T x, T y);
private bool MatchCollections<T>(T[] x, T[] y, MatchCollectionItemDelegate<T> 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);
}
}
}
|