148 lines
6 KiB
C#
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
|
|
}
|
|
}
|