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 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 exportedValues) { if (!ImportingMember.IsReadOnly && m_importType.IsAssignableCollectionType) { SetArray(exportedValues, m_importType.ElementType); return; } ICollection collection = GetCollectionWrapper(); ThrowCompositionExceptionIf(collection == null || collection.IsReadOnly, "Cannot populate the collection {0} in {1} because it does not implement ICollection or is read-only. If the collection is not IEnumerable or T[], it must implement ICollection and be pre-initialized or be writable with a default constructor.", ImportingMember.Name, m_part.GetType().FullName); PopulateCollection(collection, exportedValues); } private void SetArray(IEnumerable 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 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 or T[], it must implement ICollection 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 GetCollectionWrapperImpl(Type itemType, object collectionObject) { if (itemType == typeof(object)) { return (ICollection)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)Activator.CreateInstance(collectionType, collectionObject); } private static void PopulateCollection(ICollection 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 { 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 GetEnumerator() { throw new NotSupportedException(); } IEnumerator IEnumerable.GetEnumerator() { throw new NotSupportedException(); } } private class CollectionOfObject : ICollection { private readonly ICollection m_collectionOfT; public CollectionOfObject(object collectionOfT) { m_collectionOfT = (ICollection)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 GetEnumerator() { throw new NotSupportedException(); } IEnumerator IEnumerable.GetEnumerator() { throw new NotSupportedException(); } } #endregion #endregion } }