215 lines
8.4 KiB
C#
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
|
|
}
|
|
}
|