File: System\ComponentModel\Composition\ConstraintServices.cs
Web Access
Project: src\src\libraries\System.ComponentModel.Composition\src\System.ComponentModel.Composition.csproj (System.ComponentModel.Composition)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Linq.Expressions;
using System.Reflection;
 
namespace System.ComponentModel.Composition
{
    internal static class ConstraintServices
    {
        // NOTE : these are here as Reflection member search is pretty expensive, and we want that to be done once.
        // Also, making these static would cause this class to fail loading if we rename members of ExportDefinition.
        private static readonly PropertyInfo _exportDefinitionContractNameProperty = typeof(ExportDefinition).GetProperty("ContractName")!;
        private static readonly PropertyInfo _exportDefinitionMetadataProperty = typeof(ExportDefinition).GetProperty("Metadata")!;
        private static readonly MethodInfo _metadataContainsKeyMethod = typeof(IDictionary<string, object>).GetMethod("ContainsKey")!;
        private static readonly MethodInfo _metadataItemMethod = typeof(IDictionary<string, object>).GetMethod("get_Item")!;
        private static readonly MethodInfo _metadataEqualsMethod = typeof(object).GetMethod("Equals", new Type[] { typeof(object) })!;
        private static readonly MethodInfo _typeIsInstanceOfTypeMethod = typeof(Type).GetMethod("IsInstanceOfType")!;
 
        public static Expression<Func<ExportDefinition, bool>> CreateConstraint(string contractName, string? requiredTypeIdentity, IEnumerable<KeyValuePair<string, Type>> requiredMetadata, CreationPolicy requiredCreationPolicy)
        {
            ParameterExpression parameter = Expression.Parameter(typeof(ExportDefinition), "exportDefinition");
 
            Expression constraintBody = ConstraintServices.CreateContractConstraintBody(contractName, parameter);
 
            if (!string.IsNullOrEmpty(requiredTypeIdentity))
            {
                Expression typeIdentityConstraintBody = ConstraintServices.CreateTypeIdentityConstraint(requiredTypeIdentity, parameter);
 
                constraintBody = Expression.AndAlso(constraintBody, typeIdentityConstraintBody);
            }
 
            if (requiredMetadata != null)
            {
                Expression? metadataConstraintBody = ConstraintServices.CreateMetadataConstraintBody(requiredMetadata, parameter);
                if (metadataConstraintBody != null)
                {
                    constraintBody = Expression.AndAlso(constraintBody, metadataConstraintBody);
                }
            }
 
            if (requiredCreationPolicy != CreationPolicy.Any)
            {
                Expression policyConstraintBody = ConstraintServices.CreateCreationPolicyConstraint(requiredCreationPolicy, parameter);
 
                constraintBody = Expression.AndAlso(constraintBody, policyConstraintBody);
            }
 
            Expression<Func<ExportDefinition, bool>> constraint = Expression.Lambda<Func<ExportDefinition, bool>>(constraintBody, parameter);
            return constraint;
        }
 
        private static BinaryExpression CreateContractConstraintBody(string contractName, ParameterExpression parameter)
        {
            ArgumentNullException.ThrowIfNull(parameter);
 
            // export.ContractName=<contract>;
            return Expression.Equal(
                    Expression.Property(parameter, ConstraintServices._exportDefinitionContractNameProperty),
                    Expression.Constant(contractName ?? string.Empty, typeof(string)));
        }
 
        private static Expression? CreateMetadataConstraintBody(IEnumerable<KeyValuePair<string, Type>> requiredMetadata, ParameterExpression parameter)
        {
            ArgumentNullException.ThrowIfNull(requiredMetadata);
            ArgumentNullException.ThrowIfNull(parameter);
 
            Expression? body = null;
            foreach (KeyValuePair<string, Type> requiredMetadataItem in requiredMetadata)
            {
                // export.Metadata.ContainsKey(<metadataItem>)
                Expression metadataItemExpression = CreateMetadataContainsKeyExpression(parameter, requiredMetadataItem.Key);
 
                body = (body != null) ? Expression.AndAlso(body, metadataItemExpression) : metadataItemExpression;
                body = Expression.AndAlso(body, CreateMetadataOfTypeExpression(parameter, requiredMetadataItem.Key, requiredMetadataItem.Value));
            }
 
            return body;
        }
 
        private static BinaryExpression CreateCreationPolicyConstraint(CreationPolicy policy, ParameterExpression parameter)
        {
            ArgumentNullException.ThrowIfNull(parameter);
 
            if (policy == CreationPolicy.Any)
            {
                throw new Exception(SR.Diagnostic_InternalExceptionMessage);
            }
 
            //    !definition.Metadata.ContainsKey(CompositionConstants.PartCreationPolicyMetadataName) ||
            //        CreationPolicy.Any.Equals(definition.Metadata[CompositionConstants.PartCreationPolicyMetadataName]) ||
            //        policy.Equals(definition.Metadata[CompositionConstants.PartCreationPolicyMetadataName]);
 
            return Expression.MakeBinary(ExpressionType.OrElse,
                        Expression.MakeBinary(ExpressionType.OrElse,
                            Expression.Not(CreateMetadataContainsKeyExpression(parameter, CompositionConstants.PartCreationPolicyMetadataName)),
                            CreateMetadataValueEqualsExpression(parameter, CreationPolicy.Any, CompositionConstants.PartCreationPolicyMetadataName)),
                        CreateMetadataValueEqualsExpression(parameter, policy, CompositionConstants.PartCreationPolicyMetadataName));
        }
 
        private static BinaryExpression CreateTypeIdentityConstraint(string requiredTypeIdentity, ParameterExpression parameter)
        {
            ArgumentNullException.ThrowIfNull(requiredTypeIdentity);
            ArgumentNullException.ThrowIfNull(parameter);
 
            //    definition.Metadata.ContainsKey(CompositionServices.ExportTypeIdentity) &&
            //        requiredTypeIdentity.Equals(definition.Metadata[CompositionConstants.ExportTypeIdentityMetadataName]);
 
            return Expression.MakeBinary(ExpressionType.AndAlso,
                        CreateMetadataContainsKeyExpression(parameter, CompositionConstants.ExportTypeIdentityMetadataName),
                        CreateMetadataValueEqualsExpression(parameter, requiredTypeIdentity, CompositionConstants.ExportTypeIdentityMetadataName));
        }
 
        private static MethodCallExpression CreateMetadataContainsKeyExpression(ParameterExpression parameter, string constantKey)
        {
            if (parameter == null)
            {
                throw new ArgumentException(nameof(parameter));
            }
 
            if (constantKey == null)
            {
                throw new ArgumentException(nameof(constantKey));
            }
 
            // definition.Metadata.ContainsKey(constantKey)
            return Expression.Call(
                        Expression.Property(parameter, ConstraintServices._exportDefinitionMetadataProperty),
                        ConstraintServices._metadataContainsKeyMethod,
                        Expression.Constant(constantKey));
        }
 
        private static MethodCallExpression CreateMetadataOfTypeExpression(ParameterExpression parameter, string constantKey, Type constantType)
        {
            if (parameter == null)
            {
                throw new ArgumentException(nameof(parameter));
            }
 
            if (constantKey == null)
            {
                throw new ArgumentException(nameof(constantKey));
            }
 
            if (constantType == null)
            {
                throw new ArgumentException(nameof(constantType));
            }
 
            // constantType.IsInstanceOfType(definition.Metadata[constantKey])
            return Expression.Call(
                            Expression.Constant(constantType, typeof(Type)),
                            ConstraintServices._typeIsInstanceOfTypeMethod,
                            Expression.Call(
                                Expression.Property(parameter, ConstraintServices._exportDefinitionMetadataProperty),
                                ConstraintServices._metadataItemMethod,
                                Expression.Constant(constantKey))
                            );
        }
 
        private static MethodCallExpression CreateMetadataValueEqualsExpression(ParameterExpression parameter, object constantValue, string metadataName)
        {
            if (parameter == null)
            {
                throw new ArgumentException(nameof(parameter));
            }
 
            if (constantValue == null)
            {
                throw new ArgumentException(nameof(constantValue));
            }
 
            // constantValue.Equals(definition.Metadata[CompositionServices.PartCreationPolicyMetadataName])
            return Expression.Call(
                        Expression.Constant(constantValue),
                        ConstraintServices._metadataEqualsMethod,
                        Expression.Call(
                            Expression.Property(parameter, ConstraintServices._exportDefinitionMetadataProperty),
                            ConstraintServices._metadataItemMethod,
                            Expression.Constant(metadataName)));
        }
 
        public static Expression<Func<ExportDefinition, bool>> CreatePartCreatorConstraint(Expression<Func<ExportDefinition, bool>> baseConstraint, ImportDefinition productImportDefinition)
        {
            ParameterExpression exportDefinitionParameter = baseConstraint.Parameters[0];
 
            // exportDefinition.Metadata
            Expression metadataExpression = Expression.Property(exportDefinitionParameter, ConstraintServices._exportDefinitionMetadataProperty);
 
            // exportDefinition.Metadata.ContainsKey("ProductDefinition")
            Expression containsProductExpression = Expression.Call(
                metadataExpression,
                ConstraintServices._metadataContainsKeyMethod,
                Expression.Constant(CompositionConstants.ProductDefinitionMetadataName));
 
            // exportDefinition.Metadata["ProductDefinition"]
            Expression productExportDefinitionExpression = Expression.Call(
                    metadataExpression,
                    ConstraintServices._metadataItemMethod,
                    Expression.Constant(CompositionConstants.ProductDefinitionMetadataName));
 
            // ProductImportDefinition.Constraint((ExportDefinition)exportDefinition.Metadata["ProductDefinition"])
            Expression productMatchExpression =
                Expression.Invoke(productImportDefinition.Constraint,
                    Expression.Convert(productExportDefinitionExpression, typeof(ExportDefinition)));
 
            // baseConstraint(exportDefinition) &&
            // exportDefinition.Metadata.ContainsKey("ProductDefinition") &&
            // ProductImportDefinition.Constraint((ExportDefinition)exportDefinition.Metadata["ProductDefinition"])
            Expression<Func<ExportDefinition, bool>> constraint =
                 Expression.Lambda<Func<ExportDefinition, bool>>(
                    Expression.AndAlso(
                        baseConstraint.Body,
                        Expression.AndAlso(
                           containsProductExpression,
                           productMatchExpression)),
                    exportDefinitionParameter);
 
            return constraint;
        }
    }
}