File: System\ServiceModel\Discovery\FindCriteria.cs
Project: ndp\cdf\src\NetFx40\System.ServiceModel.Discovery\System.ServiceModel.Discovery.csproj (System.ServiceModel.Discovery)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Discovery
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Runtime;
    using System.ServiceModel.Description;
    using System.Xml;
    using System.Xml.Linq;
    using System.Xml.Schema;
    using SR2 = System.ServiceModel.Discovery.SR;
 
    [Fx.Tag.XamlVisible(false)]
    public class FindCriteria
    {
        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
        public static readonly Uri ScopeMatchByExact = new Uri(ProtocolStrings.VersionInternal.ScopeMatchByExact);
 
        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
        public static readonly Uri ScopeMatchByLdap = new Uri(ProtocolStrings.VersionInternal.ScopeMatchByLdap);
 
        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
        public static readonly Uri ScopeMatchByPrefix = new Uri(ProtocolStrings.VersionInternal.ScopeMatchByPrefix);
 
        [SuppressMessage(FxCop.Category.Naming, FxCop.Rule.IdentifiersShouldBeSpelledCorrectly)]
        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
        public static readonly Uri ScopeMatchByUuid = new Uri(ProtocolStrings.VersionInternal.ScopeMatchByUuid);
 
        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
        public static readonly Uri ScopeMatchByNone = new Uri(ProtocolStrings.VersionInternal.ScopeMatchByNone);
 
        ContractTypeNameCollection contractTypeNames;
        NonNullItemCollection<XElement> extensions;
        Uri scopeMatchBy;
        ScopeCollection scopes;
        int maxResults;
        TimeSpan duration;
 
        public FindCriteria()
        {
            Initialize(null, DiscoveryDefaults.ScopeMatchBy);
        }
 
        public FindCriteria(Type contractType)
        {
            if (contractType == null)
            {
                throw FxTrace.Exception.ArgumentNull("contractType");
            }
 
            ContractTypeNameCollection contractTypeNamesArg = new ContractTypeNameCollection();
            contractTypeNamesArg.Add(GetContractTypeName(contractType));
 
            Initialize(contractTypeNamesArg, DiscoveryDefaults.ScopeMatchBy);
        }
 
        public Collection<XmlQualifiedName> ContractTypeNames
        {
            get
            {
                if (this.contractTypeNames == null)
                {
                    this.contractTypeNames = new ContractTypeNameCollection();
                }
 
                return this.contractTypeNames;
            }
        }
 
        public Collection<XElement> Extensions
        {
            get
            {
                if (this.extensions == null)
                {
                    this.extensions = new NonNullItemCollection<XElement>();
                }
 
                return this.extensions;
            }
        }
 
        public Uri ScopeMatchBy
        {
            get
            {
                return this.scopeMatchBy;
            }
 
            set
            {
                if (value == null)
                {
                    throw FxTrace.Exception.ArgumentNull("value");
                }
 
                this.scopeMatchBy = value;
            }
        }
 
        public Collection<Uri> Scopes
        {
            get
            {
                if (this.scopes == null)
                {
                    this.scopes = new ScopeCollection();
                }
 
                return this.scopes;
            }
        }
 
        public int MaxResults
        {
            get
            {
                return this.maxResults;
            }
            set
            {
                if (value <= 0)
                {
                    throw FxTrace.Exception.ArgumentOutOfRange("value", value, SR2.DiscoveryFindMaxResultsLessThanZero);
                }
                this.maxResults = value;
            }
        }
 
        public TimeSpan Duration
        {
            get
            {
                return this.duration;
            }
            set
            {
                if (value.CompareTo(TimeSpan.Zero) <= 0)
                {
                    throw FxTrace.Exception.ArgumentOutOfRange("duration", value, SR2.DiscoveryFindDurationLessThanZero);
                }
                this.duration = value;
            }
        }
 
        internal Collection<Uri> InternalScopes
        {
            get
            {
                return this.scopes;
            }
        }
 
        public static FindCriteria CreateMetadataExchangeEndpointCriteria()
        {
            FindCriteria criteria = new FindCriteria();
            criteria.ContractTypeNames.Add(EndpointDiscoveryMetadata.MetadataContractName);
 
            return criteria;
        }
 
        public static FindCriteria CreateMetadataExchangeEndpointCriteria(Type contractType)
        {
            FindCriteria criteria = CreateMetadataExchangeEndpointCriteria();
            criteria.Scopes.Add(GetContractTypeNameScope(GetContractTypeName(contractType)));
 
            return criteria;
        }
 
        public static FindCriteria CreateMetadataExchangeEndpointCriteria(IEnumerable<XmlQualifiedName> contractTypeNames)
        {
            if (contractTypeNames == null)
            {
                throw FxTrace.Exception.ArgumentNull("contractTypeNames");
            }
 
            FindCriteria criteria = CreateMetadataExchangeEndpointCriteria();
            foreach (XmlQualifiedName item in contractTypeNames)
            {
                if (item == null)
                {
                    throw FxTrace.Exception.ArgumentNull("item");
                }
 
                criteria.Scopes.Add(GetContractTypeNameScope(item));
            }
 
            return criteria;
        }
 
        public bool IsMatch(EndpointDiscoveryMetadata endpointDiscoveryMetadata)
        {
            if (endpointDiscoveryMetadata == null)
            {
                throw FxTrace.Exception.ArgumentNull("endpointDiscoveryMetadata");
            }
 
            return IsMatch(endpointDiscoveryMetadata, ScopeCompiler.CompileMatchCriteria(this.scopes, this.scopeMatchBy));
        }
 
        internal bool IsMatch(EndpointDiscoveryMetadata endpointDiscoveryMetadata, CompiledScopeCriteria[] compiledScopeMatchCriterias)
        {
            return (MatchTypes(endpointDiscoveryMetadata, this.contractTypeNames) &&
                MatchScopes(endpointDiscoveryMetadata, compiledScopeMatchCriterias, this.scopeMatchBy));
        }
 
        static bool MatchTypes(EndpointDiscoveryMetadata endpointDiscoveryMetadata, Collection<XmlQualifiedName> contractTypeNames)
        {
            if ((contractTypeNames == null) || (contractTypeNames.Count == 0))
            {
                return true;
            }
 
            if ((endpointDiscoveryMetadata.InternalContractTypeNames == null) ||
                (endpointDiscoveryMetadata.InternalContractTypeNames.Count == 0))
            {
                return false;
            }
 
            foreach (XmlQualifiedName contractTypeName in contractTypeNames)
            {
                if (!endpointDiscoveryMetadata.InternalContractTypeNames.Contains(contractTypeName))
                {
                    return false;
                }
            }
 
            return true;
        }
 
        static bool MatchScopes(EndpointDiscoveryMetadata endpointDiscoveryMetadata, CompiledScopeCriteria[] compiledScopeMatchCriterias, Uri scopeMatchBy)
        {
            if (compiledScopeMatchCriterias == null)
            {
                if (scopeMatchBy != FindCriteria.ScopeMatchByNone)
                {
                    return true;
                }
                else
                {
                    // the criteria matches any service with no scopes defined
                    return endpointDiscoveryMetadata.Scopes.Count == 0;
                }
            }
 
            if (scopeMatchBy == FindCriteria.ScopeMatchByNone)
            {
                // if scopeMatchBy is None, the Probe shouldn't have any Scopes defined
                return false;
            }
 
            string[] compiledScopes;
            if (endpointDiscoveryMetadata.IsOpen)
            {
                compiledScopes = endpointDiscoveryMetadata.CompiledScopes;
            }
            else
            {
                compiledScopes = ScopeCompiler.Compile(endpointDiscoveryMetadata.Scopes);
            }
 
            if (compiledScopes == null)
            {
                // non-zero scopes in the criteria, but zero scopes in the metadata
                return false;
            }
 
            for (int i = 0; i < compiledScopeMatchCriterias.Length; i++)
            {
                if (!ScopeCompiler.IsMatch(compiledScopeMatchCriterias[i], compiledScopes))
                {
                    return false;
                }
            }
 
            return true;
        }
 
        [Fx.Tag.Throws(typeof(XmlException), "throws on incorrect xml data")]
        internal void ReadFrom(DiscoveryVersion discoveryVersion, XmlReader reader)
        {            
            if (discoveryVersion == null)
            {
                throw FxTrace.Exception.ArgumentNull("discoveryVersion");
            }
            if (reader == null)
            {
                throw FxTrace.Exception.ArgumentNull("reader");
            }
 
            this.contractTypeNames = null;
            this.scopes = null;
            this.scopeMatchBy = DiscoveryDefaults.ScopeMatchBy;
            this.extensions = null;
            this.duration = TimeSpan.MaxValue;
            this.maxResults = int.MaxValue;
 
            reader.MoveToContent();
            if (reader.IsEmptyElement)
            {
                reader.Read();
                return;
            }
 
            int startDepth = reader.Depth;
            reader.ReadStartElement();
 
            if (reader.IsStartElement(ProtocolStrings.SchemaNames.TypesElement, discoveryVersion.Namespace))
            {
                this.contractTypeNames = new ContractTypeNameCollection();
                SerializationUtility.ReadContractTypeNames(this.contractTypeNames, reader);
            }
 
            if (reader.IsStartElement(ProtocolStrings.SchemaNames.ScopesElement, discoveryVersion.Namespace))
            {
                this.scopes = new ScopeCollection();
                Uri scopeMatchBy = SerializationUtility.ReadScopes(this.scopes, reader);
                if (scopeMatchBy != null)
                {
                    this.scopeMatchBy = discoveryVersion.Implementation.ToVersionIndependentScopeMatchBy(scopeMatchBy);
                }
            }
 
            while (true)
            {
                reader.MoveToContent();
 
                if ((reader.NodeType == XmlNodeType.EndElement) && (reader.Depth == startDepth))
                {
                    break;
                }
                else if (reader.IsStartElement(ProtocolStrings.SchemaNames.MaxResultsElement, ProtocolStrings.VersionInternal.Namespace))
                {
                    this.maxResults = SerializationUtility.ReadMaxResults(reader);
                }
                else if (reader.IsStartElement(ProtocolStrings.SchemaNames.DurationElement, ProtocolStrings.VersionInternal.Namespace))
                {
                    this.duration = SerializationUtility.ReadDuration(reader);
                }
                else if (reader.IsStartElement())
                {
                    XElement xElement = XElement.ReadFrom(reader) as XElement;
                    Extensions.Add(xElement);
                }
                else
                {
                    reader.Read();
                }
            }
 
            reader.ReadEndElement();         
        }
 
        internal void WriteTo(DiscoveryVersion discoveryVersion, XmlWriter writer)
        {
            if (discoveryVersion == null)
            {
                throw FxTrace.Exception.ArgumentNull("discoveryVersion");
            }
            if (writer == null)
            {
                throw FxTrace.Exception.ArgumentNull("writer");
            }
 
            SerializationUtility.WriteContractTypeNames(discoveryVersion, this.contractTypeNames, writer);                   
 
            SerializationUtility.WriteScopes(discoveryVersion, this.scopes, this.scopeMatchBy, writer);
 
            if (this.maxResults != int.MaxValue)
            {
                writer.WriteElementString(
                    ProtocolStrings.SchemaNames.MaxResultsElement, 
                    ProtocolStrings.VersionInternal.Namespace, 
                    this.maxResults.ToString(CultureInfo.InvariantCulture));
            }
            if (this.duration != TimeSpan.MaxValue)
            {
                writer.WriteElementString(
                    ProtocolStrings.SchemaNames.DurationElement, 
                    ProtocolStrings.VersionInternal.Namespace, 
                    XmlConvert.ToString(this.Duration));
            }
 
            if (this.extensions != null)
            {
                foreach (XElement xElement in Extensions)
                {
                    xElement.WriteTo(writer);
                }
            }
        }
 
        internal static XmlQualifiedName GetContractTypeName(Type contractType)
        {
            if (contractType == null)
            {
                throw FxTrace.Exception.ArgumentNull("contractType");
            }
 
            ContractDescription contract = ContractDescription.GetContract(contractType);
            return new XmlQualifiedName(contract.Name, contract.Namespace);
        }
 
        internal static Uri GetContractTypeNameScope(XmlQualifiedName contractTypeName)
        {
            Fx.Assert(contractTypeName != null, "The contractTypeName must be non null.");
 
            return new Uri(string.Format(CultureInfo.InvariantCulture, "urn:{0}", contractTypeName.ToString()));
        }
 
        internal FindCriteria Clone()
        {
            FindCriteria findCriteriaClone = new FindCriteria();
 
            foreach (Uri scope in this.Scopes)
            {
                findCriteriaClone.Scopes.Add(scope);
            }
 
            foreach (XmlQualifiedName contractTypeName in this.ContractTypeNames)
            {
                findCriteriaClone.ContractTypeNames.Add(new XmlQualifiedName(contractTypeName.Name, contractTypeName.Namespace));
            }
 
            foreach (XElement extension in this.Extensions)
            {
                findCriteriaClone.Extensions.Add(new XElement(extension));
            }
 
            findCriteriaClone.ScopeMatchBy = this.ScopeMatchBy;
            findCriteriaClone.Duration = this.Duration;
            findCriteriaClone.MaxResults = this.MaxResults;
 
            return findCriteriaClone;
        }
 
        void Initialize(ContractTypeNameCollection contractTypeNames, Uri scopeMatchBy)
        {
            this.contractTypeNames = contractTypeNames;
            this.scopeMatchBy = scopeMatchBy;
            this.maxResults = int.MaxValue;
            this.duration = DiscoveryDefaults.DiscoveryOperationDuration;
        }        
    }
}