// ----------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ----------------------------------------------------------------------- using System; using System.Collections.Generic; using System.ComponentModel.Composition.Hosting; using System.ComponentModel.Composition.Primitives; using System.Linq; using System.Linq.Expressions; using System.Reflection; using Microsoft.Internal; 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).GetMethod("ContainsKey"); private static readonly MethodInfo _metadataItemMethod = typeof(IDictionary).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> CreateConstraint(IEnumerable> requiredMetadata) { ParameterExpression parameter = Expression.Parameter(typeof(ExportDefinition), "exportDefinition"); Expression metadataConstraintBody = null; if (requiredMetadata != null) { metadataConstraintBody = ConstraintServices.CreateMetadataConstraintBody(requiredMetadata, parameter); } if (metadataConstraintBody != null) { return Expression.Lambda>(metadataConstraintBody, parameter); } return null; } public static Expression> CreateConstraint(string contractName, string requiredTypeIdentity, IEnumerable> requiredMetadata, CreationPolicy requiredCreationPolicy) { ParameterExpression parameter = Expression.Parameter(typeof(ExportDefinition), "exportDefinition"); Expression constraintBody = ConstraintServices.CreateContractConstraintBody(contractName, parameter); if (!string.IsNullOrEmpty(requiredTypeIdentity)) { Expression typeIdentityConstraintBody = ConstraintServices.CreateTypeIdentityContraint(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.CreateCreationPolicyContraint(requiredCreationPolicy, parameter); constraintBody = Expression.AndAlso(constraintBody, policyConstraintBody); } Expression> constraint = Expression.Lambda>(constraintBody, parameter); return constraint; } private static Expression CreateContractConstraintBody(string contractName, ParameterExpression parameter) { Assumes.NotNull(parameter); // export.ContractName=; return Expression.Equal( Expression.Property(parameter, ConstraintServices._exportDefinitionContractNameProperty), Expression.Constant(contractName ?? string.Empty, typeof(string))); } private static Expression CreateMetadataConstraintBody(IEnumerable> requiredMetadata, ParameterExpression parameter) { Assumes.NotNull(requiredMetadata); Assumes.NotNull(parameter); Expression body = null; foreach (KeyValuePair requiredMetadataItem in requiredMetadata) { // export.Metadata.ContainsKey() 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 Expression CreateCreationPolicyContraint(CreationPolicy policy, ParameterExpression parameter) { Assumes.IsTrue(policy != CreationPolicy.Any); Assumes.NotNull(parameter); // !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 Expression CreateTypeIdentityContraint(string requiredTypeIdentity, ParameterExpression parameter) { Assumes.NotNull(requiredTypeIdentity); Assumes.NotNull(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 Expression CreateMetadataContainsKeyExpression(ParameterExpression parameter, string constantKey) { Assumes.NotNull(parameter, constantKey); // definition.Metadata.ContainsKey(constantKey) return Expression.Call( Expression.Property(parameter, ConstraintServices._exportDefinitionMetadataProperty), ConstraintServices._metadataContainsKeyMethod, Expression.Constant(constantKey)); } private static Expression CreateMetadataOfTypeExpression(ParameterExpression parameter, string constantKey, Type constantType) { Assumes.NotNull(parameter, constantKey); Assumes.NotNull(parameter, 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 Expression CreateMetadataValueEqualsExpression(ParameterExpression parameter, object constantValue, string metadataName) { Assumes.NotNull(parameter, 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> CreatePartCreatorConstraint(Expression> 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.Contraint((ExportDefinition)exportDefinition.Metadata["ProductDefinition"]) Expression productMatchExpression = Expression.Invoke(productImportDefinition.Constraint, Expression.Convert(productExportDefinitionExpression, typeof(ExportDefinition))); // baseContraint(exportDefinition) && // exportDefinition.Metadata.ContainsKey("ProductDefinition") && // ProductImportDefinition.Contraint((ExportDefinition)exportDefinition.Metadata["ProductDefinition"]) Expression> constraint = Expression.Lambda>( Expression.AndAlso( baseConstraint.Body, Expression.AndAlso( containsProductExpression, productMatchExpression)), exportDefinitionParameter); return constraint; } } }