JD2022-TU1/main/tools/framework/JD.Reflection/MefImplementations/Metadata/MetadataViewServices.cs

148 lines
6 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
namespace JD.Reflection.MefImplementations.Metadata
{
public static class MetadataViewServices
{
#region Constants
private static readonly Type MetadataType = typeof(IReadOnlyDictionary<string, object>);
#endregion
#region Methods
public static TMetadataView GetMetadataView<TMetadataView>(IReadOnlyDictionary<string, object> metadata)
{
Type metadataViewType = typeof(TMetadataView);
// If the Metadata dictionary is cast compatible with the passed in type
if (metadataViewType.IsAssignableFrom(typeof(IReadOnlyDictionary<string, object>)))
{
return (TMetadataView)metadata;
}
// otherwise is it a metadata view
Type proxyType;
if (metadataViewType.IsInterface)
{
try
{
proxyType = MetadataViewGenerator.GenerateView(metadataViewType);
}
catch (TypeLoadException ex)
{
throw new ArgumentException($"Unsupported metadata view interface: {ex.Message}", ex);
}
}
else
{
proxyType = metadataViewType;
}
try
{
return (TMetadataView)Activator.CreateInstance(proxyType, metadata);
}
catch (MissingMethodException)
{
throw new ArgumentException($"Type {proxyType.FullName} must have a constructor that takes a single IDictionary<string, object> argument");
}
catch (TargetInvocationException ex)
{
//Unwrap known failures that we want to present using better exceptions
if (metadataViewType.IsInterface)
{
if (ex.InnerException.GetType() == typeof(InvalidCastException))
{
throw new ArgumentException(
$"Unable to create an instance of the metadata view {metadataViewType.FullName} because the exporter exported the metadata for the item {ex.InnerException.Data[MetadataViewGenerator.MetadataItemKey]} with the value {ex.InnerException.Data[MetadataViewGenerator.MetadataItemValue]} as type {ex.InnerException.Data[MetadataViewGenerator.MetadataItemSourceType]} but the view imports it as type {ex.InnerException.Data[MetadataViewGenerator.MetadataItemTargetType]}.");
}
if (ex.InnerException.GetType() == typeof(NullReferenceException))
{
throw new ArgumentException(
$"Unable to create an instance of the metadata view {metadataViewType.FullName} because the exporter exported the metadata for the item {ex.InnerException.Data[MetadataViewGenerator.MetadataItemKey]} with a null value and null is not a valid value for type {ex.InnerException.Data[MetadataViewGenerator.MetadataItemTargetType]}.");
}
}
throw;
}
}
internal static IEnumerable<RequiredMetadataItem> GetRequiredMetadata(Type metadataViewType)
{
if ((metadataViewType == null) ||
metadataViewType.IsAssignableFrom(MetadataType) ||
IsDictionaryConstructorViewType(metadataViewType) ||
!metadataViewType.IsInterface)
{
return null;
}
// A metadata view is required to be an interface, and therefore only properties are allowed
var properties = GetAllProperties(metadataViewType).
Where(property => !property.HasAttribute<DefaultValueAttribute>(false)).
ToList();
return properties.Select(property => new RequiredMetadataItem(property.Name, property.PropertyType));
}
private static bool IsDictionaryConstructorViewType(Type metadataViewType)
{
// Does the view type have a constructor that is a IReadOnlyDictionary<string, object>
return metadataViewType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
Type.DefaultBinder,
new[] { MetadataType },
new ParameterModifier[0]) != null;
}
private static IEnumerable<PropertyInfo> GetAllProperties(Type type)
{
return type.GetInterfaces().Concat(new [] { type }).SelectMany(itf => itf.GetProperties());
}
private static bool IsValidMetadataType(Type type, bool arrayAllowed)
{
Debug.Assert(type != null);
// Definitions of valid attribute type taken from C# 3.0 Specification section 17.1.3.
// One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
if (type.IsPrimitive)
{
return true;
}
if (type == typeof(string))
{
return true;
}
// An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility
if (type.IsEnum && type.IsVisible)
{
return true;
}
if (typeof(Type).IsAssignableFrom(type))
{
return true;
}
// Single-dimensional arrays of the above types.
if (arrayAllowed && type.IsArray && type.GetArrayRank() == 1 && IsValidMetadataType(type.GetElementType(), false))
{
return true;
}
return false;
}
#endregion
}
}