using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using JD.Kernel.StaticHelpers; using JD.Reflection.Attributes; using JD.Reflection.Interfaces; using JD.Reflection.MefImplementations.Hosting; using JD.Reflection.MefImplementations.Metadata; using JD.Reflection.MefImplementations.Model; using JD.Reflection.MefImplementations.Model.Internal; namespace JD.Reflection.MefImplementations.Composition { internal class ExportEngine { #region Variables private static readonly MethodInfo ms_lazyCreator; private static readonly MethodInfo ms_lazyWithMetadataCreator; private static readonly MethodInfo ms_exportFactoryCreator; private static readonly MethodInfo ms_exportFactoryWithMetadataCreator; private static readonly MethodInfo ms_lazyTypeCreator; private static readonly MethodInfo ms_lazyTypeWithMetadataCreator; private readonly PartCatalog m_catalog; #endregion #region Constructor static ExportEngine() { ms_lazyCreator = typeof(ExportEngine).GetMethod(nameof(CreateLazyImpl), BindingFlags.NonPublic | BindingFlags.Static); ms_lazyWithMetadataCreator = typeof(ExportEngine).GetMethod(nameof(CreateLazyWithMetadataImpl), BindingFlags.NonPublic | BindingFlags.Static); ms_exportFactoryCreator = typeof(ExportEngine).GetMethod(nameof(CreateExportFactoryImpl), BindingFlags.NonPublic | BindingFlags.Static); ms_exportFactoryWithMetadataCreator = typeof(ExportEngine).GetMethod(nameof(CreateExportFactoryWithMetadataImpl), BindingFlags.NonPublic | BindingFlags.Static); ms_lazyTypeCreator = typeof(ExportEngine).GetMethod(nameof(CreateLazyTypeImpl), BindingFlags.NonPublic | BindingFlags.Static); ms_lazyTypeWithMetadataCreator = typeof(ExportEngine).GetMethod(nameof(CreateLazyTypeWithMetadataImpl), BindingFlags.NonPublic | BindingFlags.Static); } public ExportEngine(PartCatalog catalog) { Throw.IfNull(catalog, "catalog"); m_catalog = catalog; } #endregion #region Methods public IEnumerable GetExports(ImportDefinition import, ImportType importType) { var matchingExportDefinitions = m_catalog.GetExports(import); ValidateCardinality(import, matchingExportDefinitions); return matchingExportDefinitions.Select(e => new ExportedValue(e, ExportValue(importType, e))); } private static object ExportValue(ImportType importType, ExportDefinition exportDefinition) { switch (importType.Family) { case ImportType.ImportFamily.Normal: return exportDefinition.CreatePart(); case ImportType.ImportFamily.Lazy: return CreateLazy(importType.ContractType, importType.MetadataViewType, exportDefinition); case ImportType.ImportFamily.ExportFactory: return CreateExportFactory(importType.ContractType, importType.MetadataViewType, exportDefinition); case ImportType.ImportFamily.LazyType: return CreateLazyType(importType.ContractType, importType.MetadataViewType, exportDefinition); default: throw new NotImplementedException(); } } #region Validation methods private static void ValidateCardinality(ImportDefinition import, IEnumerable matchingExportDefinitions) { var cardinality = import.Cardinality; if (cardinality == ImportCardinality.ZeroOrMore) { return; } var actualCardinality = GetCardinality(matchingExportDefinitions); Debug.Assert(cardinality == ImportCardinality.ExactlyOne || cardinality == ImportCardinality.ZeroOrOne); if (actualCardinality == EnumerableCardinality.Many) { throw new ImportCardinalityMismatchException($"More than one export was found that matches the import {import}."); } if (actualCardinality == EnumerableCardinality.Zero && cardinality == ImportCardinality.ExactlyOne) { throw new ImportCardinalityMismatchException( $"No valid exports were found that match the import {import} (invalid exports may have been rejected)."); } } private static EnumerableCardinality GetCardinality(IEnumerable source) { using (var enumerator = source.GetEnumerator()) { if (!enumerator.MoveNext()) { return EnumerableCardinality.Zero; } if (!enumerator.MoveNext()) { return EnumerableCardinality.One; } return EnumerableCardinality.Many; } } #endregion #region Factory methods #region Lazy private static object CreateLazy(Type valueType, Type metadataViewType, ExportDefinition exportDefinition) { MethodInfo genericMethod = GetCreationMethod(valueType, metadataViewType, ms_lazyCreator, ms_lazyWithMetadataCreator); return genericMethod.Invoke(null, new object[] { exportDefinition }); } private static System.Lazy CreateLazyImpl(ExportDefinition exportDefinition) { return new System.Lazy(() => (T)exportDefinition.CreatePart(), false); } private static System.Lazy CreateLazyWithMetadataImpl(ExportDefinition exportDefinition) { var metadata = MetadataViewServices.GetMetadataView(exportDefinition.Metadata); return new System.Lazy(() => (T)exportDefinition.CreatePart(), metadata, false); } #endregion #region ExportFactory private static object CreateExportFactory(Type valueType, Type metadataViewType, ExportDefinition exportDefinition) { MethodInfo genericMethod = GetCreationMethod(valueType, metadataViewType, ms_exportFactoryCreator, ms_exportFactoryWithMetadataCreator); return genericMethod.Invoke(null, new object[] { exportDefinition }); } private static ExportFactory CreateExportFactoryImpl(ExportDefinition exportDefinition) { return new ExportFactory(() => (T)exportDefinition.CreatePart()); } private static ExportFactory CreateExportFactoryWithMetadataImpl(ExportDefinition exportDefinition) { var metadata = MetadataViewServices.GetMetadataView(exportDefinition.Metadata); return new ExportFactory(() => (T)exportDefinition.CreatePart(), metadata); } #endregion #region LazyType private static object CreateLazyType(Type valueType, Type metadataViewType, ExportDefinition exportDefinition) { MethodInfo genericMethod = GetCreationMethod(valueType, metadataViewType, ms_lazyTypeCreator, ms_lazyTypeWithMetadataCreator); return genericMethod.Invoke(null, new object[] { exportDefinition }); } private static LazyType CreateLazyTypeImpl(ExportDefinition exportDefinition) { return new LazyType(() => exportDefinition.Part.Type); } private static LazyType CreateLazyTypeWithMetadataImpl(ExportDefinition exportDefinition) { var metadata = MetadataViewServices.GetMetadataView(exportDefinition.Metadata); return new LazyType(() => exportDefinition.Part.Type, metadata); } #endregion #region Utilities private static MethodInfo GetCreationMethod(Type valueType, Type metadataViewType, MethodInfo creator, MethodInfo metadataCreator) { return metadataViewType != null ? metadataCreator.MakeGenericMethod(valueType, metadataViewType) : creator.MakeGenericMethod(valueType); } #endregion #endregion #endregion #region Inner Types private enum EnumerableCardinality { Zero, One, Many } #endregion } }