JD2022-TU1/main/tools/framework/JD.Reflection/MefImplementations/Composition/ExportEngine.cs

215 lines
8.4 KiB
C#

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<ExportedValue> 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<ExportDefinition> 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<T>(IEnumerable<T> 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<T> CreateLazyImpl<T>(ExportDefinition exportDefinition)
{
return new System.Lazy<T>(() => (T)exportDefinition.CreatePart(), false);
}
private static System.Lazy<T, TMetadata> CreateLazyWithMetadataImpl<T, TMetadata>(ExportDefinition exportDefinition)
{
var metadata = MetadataViewServices.GetMetadataView<TMetadata>(exportDefinition.Metadata);
return new System.Lazy<T, TMetadata>(() => (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<T> CreateExportFactoryImpl<T>(ExportDefinition exportDefinition)
{
return new ExportFactory<T>(() => (T)exportDefinition.CreatePart());
}
private static ExportFactory<T, TMetadata> CreateExportFactoryWithMetadataImpl<T, TMetadata>(ExportDefinition exportDefinition)
{
var metadata = MetadataViewServices.GetMetadataView<TMetadata>(exportDefinition.Metadata);
return new ExportFactory<T, TMetadata>(() => (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<T> CreateLazyTypeImpl<T>(ExportDefinition exportDefinition)
{
return new LazyType<T>(() => exportDefinition.Part.Type);
}
private static LazyType<T, TMetadata> CreateLazyTypeWithMetadataImpl<T, TMetadata>(ExportDefinition exportDefinition)
{
var metadata = MetadataViewServices.GetMetadataView<TMetadata>(exportDefinition.Metadata);
return new LazyType<T, TMetadata>(() => 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
}
}