|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Composition.Primitives;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using Microsoft.Internal;
using Microsoft.Internal.Collections;
namespace System.ComponentModel.Composition.ReflectionModel
{
internal sealed class ImportingMember : ImportingItem
{
private readonly ReflectionWritableMember _member;
public ImportingMember(ContractBasedImportDefinition definition, ReflectionWritableMember member, ImportType importType)
: base(definition, importType)
{
ArgumentNullException.ThrowIfNull(member);
_member = member;
}
public void SetExportedValue(object? instance, object value)
{
if (RequiresCollectionNormalization())
{
SetCollectionMemberValue(instance, (IEnumerable)value);
}
else
{
SetSingleMemberValue(instance, value);
}
}
private bool RequiresCollectionNormalization()
{
if (Definition.Cardinality != ImportCardinality.ZeroOrMore)
{ // If we're not looking at a collection import, then don't
// 'normalize' the collection.
return false;
}
if (_member.CanWrite && ImportType.IsAssignableCollectionType)
{ // If we can simply replace the entire value of the property/field, then
// we don't need to 'normalize' the collection.
return false;
}
return true;
}
private void SetSingleMemberValue(object? instance, object? value)
{
EnsureWritable();
try
{
_member.SetValue(instance, value);
}
catch (TargetInvocationException exception)
{ // Member threw an exception. Avoid letting this
// leak out as a 'raw' unhandled exception, instead,
// we'll add some context and rethrow.
throw new ComposablePartException(
SR.Format(
SR.ReflectionModel_ImportThrewException,
_member.GetDisplayName()),
Definition.ToElement(),
exception.InnerException);
}
catch (TargetParameterCountException exception)
{
// Exception was a TargetParameterCountException this occurs when we try to set an Indexer that has an Import
// this is not supported in MEF currently. Ideally we would validate against it, however, we already shipped
// so we will turn it into a ComposablePartException instead, that they should already be prepared for
throw new ComposablePartException(
SR.Format(
SR.ImportNotValidOnIndexers,
_member.GetDisplayName()),
Definition.ToElement(),
exception.InnerException);
}
}
private void EnsureWritable()
{
if (!_member.CanWrite)
{ // Property does not have a setter, or
// field is marked as read-only.
throw new ComposablePartException(
SR.Format(
SR.ReflectionModel_ImportNotWritable,
_member.GetDisplayName()),
Definition.ToElement());
}
}
private void SetCollectionMemberValue(object? instance, IEnumerable values)
{
ArgumentNullException.ThrowIfNull(values);
ICollection<object>? collection = null;
Type? itemType = CollectionServices.GetCollectionElementType(ImportType.ActualType);
if (itemType != null)
{
collection = GetNormalizedCollection(itemType, instance);
}
EnsureCollectionIsWritable(collection);
PopulateCollection(collection!, values);
}
private ICollection<object> GetNormalizedCollection(Type itemType, object? instance)
{
ArgumentNullException.ThrowIfNull(itemType);
object? collectionObject = null;
if (_member.CanRead)
{
try
{
collectionObject = _member.GetValue(instance);
}
catch (TargetInvocationException exception)
{
throw new ComposablePartException(
SR.Format(
SR.ReflectionModel_ImportCollectionGetThrewException,
_member.GetDisplayName()),
Definition.ToElement(),
exception.InnerException);
}
}
if (collectionObject == null)
{
ConstructorInfo? constructor = ImportType.ActualType.GetConstructor(Type.EmptyTypes);
// If it contains a default public constructor create a new instance.
if (constructor != null)
{
try
{
collectionObject = constructor.SafeInvoke();
}
catch (TargetInvocationException exception)
{
throw new ComposablePartException(
SR.Format(
SR.ReflectionModel_ImportCollectionConstructionThrewException,
_member.GetDisplayName(),
ImportType.ActualType.FullName),
Definition.ToElement(),
exception.InnerException);
}
SetSingleMemberValue(instance, collectionObject);
}
}
if (collectionObject == null)
{
throw new ComposablePartException(
SR.Format(
SR.ReflectionModel_ImportCollectionNull,
_member.GetDisplayName()),
Definition.ToElement());
}
return CollectionServices.GetCollectionWrapper(itemType, collectionObject);
}
private void EnsureCollectionIsWritable(ICollection<object>? collection)
{
bool isReadOnly = true;
try
{
if (collection != null)
{
isReadOnly = collection.IsReadOnly;
}
}
catch (Exception exception)
{
throw new ComposablePartException(
SR.Format(
SR.ReflectionModel_ImportCollectionIsReadOnlyThrewException,
_member.GetDisplayName(),
collection!.GetType().FullName),
Definition.ToElement(),
exception);
}
if (isReadOnly)
{
throw new ComposablePartException(
SR.Format(
SR.ReflectionModel_ImportCollectionNotWritable,
_member.GetDisplayName()),
Definition.ToElement());
}
}
private void PopulateCollection(ICollection<object?> collection, IEnumerable values)
{
ArgumentNullException.ThrowIfNull(collection);
ArgumentNullException.ThrowIfNull(values);
try
{
collection.Clear();
}
catch (Exception exception)
{
throw new ComposablePartException(
SR.Format(
SR.ReflectionModel_ImportCollectionClearThrewException,
_member.GetDisplayName(),
collection.GetType().FullName),
Definition.ToElement(),
exception);
}
foreach (object? value in values)
{
try
{
collection.Add(value);
}
catch (Exception exception)
{
throw new ComposablePartException(
SR.Format(
SR.ReflectionModel_ImportCollectionAddThrewException,
_member.GetDisplayName(),
collection.GetType().FullName),
Definition.ToElement(),
exception);
}
}
}
}
}
|