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

278 lines
8.4 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using JD.Kernel.Classes;
using JD.Reflection.Attributes;
using JD.Reflection.MefImplementations.Model;
using JD.Reflection.MefImplementations.Model.Internal;
namespace JD.Reflection.MefImplementations.Composition
{
internal class ImportEngine
{
#region Variables
private readonly object m_part;
private readonly ImportDefinition m_import;
private readonly ImportType m_importType;
#endregion
#region Constructor
public ImportEngine(object part, ImportDefinition import, ImportType importType)
{
m_part = part;
m_importType = importType;
m_import = import;
}
#endregion
#region Properties
private ValueMemberInfo ImportingMember
{
get { return m_importType.ImportingMember; }
}
#endregion
#region Methods
public void ApplyExportedValues(IEnumerable<ExportedValue> exportedValues)
{
switch (m_import.Cardinality)
{
case ImportCardinality.ZeroOrMore:
ApplyList(exportedValues.Select(e => e.Value));
break;
case ImportCardinality.ZeroOrOne:
case ImportCardinality.ExactlyOne:
if (exportedValues.Any())
{
SetValue(exportedValues.Single().Value);
}
break;
default:
throw new NotImplementedException();
}
}
private void SetValue(object value)
{
ThrowCompositionExceptionIf(!ImportingMember.CanRead, "Cannot set import property {0} on {1} because it is read-only", ImportingMember.Name, m_part.GetType().FullName);
ImportingMember.SetValue(m_part, value);
}
private void ApplyList(IEnumerable<object> exportedValues)
{
if (!ImportingMember.IsReadOnly && m_importType.IsAssignableCollectionType)
{
SetArray(exportedValues, m_importType.ElementType);
return;
}
ICollection<object> collection = GetCollectionWrapper();
ThrowCompositionExceptionIf(collection == null || collection.IsReadOnly, "Cannot populate the collection {0} in {1} because it does not implement ICollection<T> or is read-only. If the collection is not IEnumerable<T> or T[], it must implement ICollection<T> and be pre-initialized or be writable with a default constructor.", ImportingMember.Name, m_part.GetType().FullName);
PopulateCollection(collection, exportedValues);
}
private void SetArray(IEnumerable<object> exportedValues, Type elementType)
{
var values = exportedValues.ToList();
var array = Array.CreateInstance(elementType, values.Count);
for (int i = 0; i < values.Count; i++)
{
array.SetValue(values[i], i);
}
SetValue(array);
}
private ICollection<object> GetCollectionWrapper()
{
object collectionObject = null;
if (ImportingMember.CanRead)
{
collectionObject = ImportingMember.GetValue(m_part);
}
if (collectionObject == null)
{
ConstructorInfo constructor = ImportingMember.ValueType.GetConstructor(Type.EmptyTypes);
// If it contains a default public constructor, create a new instance.
if (constructor != null)
{
collectionObject = constructor.Invoke(new object[0]);
SetValue(collectionObject);
}
}
ThrowCompositionExceptionIf(collectionObject == null, "Cannot populate the value of enumerable member {0} on {1} because it is null. If the collection is not IEnumerable<T> or T[], it must implement ICollection<T> and be either pre-initialized or be writable with a default constructor.", ImportingMember.Name, m_part.GetType().FullName);
return GetCollectionWrapperImpl(m_importType.ElementType, collectionObject);
}
private static ICollection<object> GetCollectionWrapperImpl(Type itemType, object collectionObject)
{
if (itemType == typeof(object))
{
return (ICollection<object>)collectionObject;
}
// Most common .Net collections implement IList as well so for those
// cases we can optimize the wrapping instead of using reflection to create
// a generic type.
if (typeof(IList).IsAssignableFrom(collectionObject.GetType()))
{
return new CollectionOfObjectList((IList)collectionObject);
}
Type collectionType = typeof(CollectionOfObject<>).MakeGenericType(itemType);
return (ICollection<object>)Activator.CreateInstance(collectionType, collectionObject);
}
private static void PopulateCollection(ICollection<object> collection, IEnumerable values)
{
collection.Clear();
foreach (object value in values)
{
collection.Add(value);
}
}
private static void ThrowCompositionExceptionIf(bool condition, string msg, params object[] args)
{
if (condition)
{
throw new CompositionException(string.Format(msg, args));
}
}
#region Inner Types
private class CollectionOfObjectList : ICollection<object>
{
private readonly IList m_list;
public CollectionOfObjectList(IList list)
{
m_list = list;
}
public void Add(object item)
{
m_list.Add(item);
}
public void Clear()
{
m_list.Clear();
}
public bool Contains(object item)
{
throw new NotSupportedException();
}
public void CopyTo(object[] array, int arrayIndex)
{
throw new NotSupportedException();
}
public int Count
{
get { throw new NotSupportedException(); }
}
public bool IsReadOnly
{
get { return m_list.IsReadOnly; }
}
public bool Remove(object item)
{
throw new NotSupportedException();
}
public IEnumerator<object> GetEnumerator()
{
throw new NotSupportedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotSupportedException();
}
}
private class CollectionOfObject<T> : ICollection<object>
{
private readonly ICollection<T> m_collectionOfT;
public CollectionOfObject(object collectionOfT)
{
m_collectionOfT = (ICollection<T>)collectionOfT;
}
public void Add(object item)
{
m_collectionOfT.Add((T)item);
}
public void Clear()
{
m_collectionOfT.Clear();
}
public bool Contains(object item)
{
throw new NotSupportedException();
}
public void CopyTo(object[] array, int arrayIndex)
{
throw new NotSupportedException();
}
public int Count
{
get { throw new NotSupportedException(); }
}
public bool IsReadOnly
{
get { return m_collectionOfT.IsReadOnly; }
}
public bool Remove(object item)
{
throw new NotSupportedException();
}
public IEnumerator<object> GetEnumerator()
{
throw new NotSupportedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotSupportedException();
}
}
#endregion
#endregion
}
}