/// <summary> /// Checks if the specified <paramref name="maybeCollectionTarget"/> parameter /// is an object which is not null and which supports any collection element adding /// facility. The specified <paramref name="value"/> will then be assigned to the /// collection. /// </summary> /// <remarks> /// <para> /// This method will be needed in two cases: /// <list type="number"> /// <item>A property should be assigned, which has a collection-like type. These properties /// won't be assigned directly but they are expected to have a not-null collection value. /// Adding children to them will cause calling some "Add" method on the existing instance.</item> /// <item>Child elements should be assigned to an object which doesn't support /// the <see cref="IContentEnabled"/> interface. So if there is no content property, /// the parser tries to add children directly to the object, if it is supported.</item> /// </list> /// In both cases, there is already an existing (collection-like) instance, to that the /// specified <paramref name="value"/> should be assigned, so this method has no /// need to create the collection itself. /// </para> /// <para> /// If this method cannot handle the specified target object /// <paramref name="maybeCollectionTarget"/>, or if this parameter is <c>null</c>, /// it will return <c>false</c> to signal that it couldn't do the assignment. /// </para> /// </remarks> /// <param name="maybeCollectionTarget">Parameter holding an instance which may be /// a collection type to add the <paramref name="value"/> to. May also be <c>null</c>, /// which will result in a return value of <c>false</c>.</param> /// <param name="value">The value to assign to the target object. If the value has /// a collection type compatible with the <paramref name="maybeCollectionTarget"/> target, /// the contents will be transferred to the target object, else this parameter value /// will be added to the target itself.</param> /// <returns><c>true</c>, if the method could handle the assignment, else <c>false</c>.</returns> public static bool CheckHandleCollectionAssignment(object maybeCollectionTarget, object value) { if (maybeCollectionTarget == null || value == null) { return(false); } Type targetType = maybeCollectionTarget.GetType(); // Check for List Type resultType; Type entryType; MethodInfo method; FindImplementedListType(targetType, out resultType, out entryType); if (resultType != null) { method = entryType == null?targetType.GetMethod("Add") : targetType.GetMethod("Add", new Type[] { entryType }); // Have to cast to ICollection, because the type converter cannot cope with the situation corretcly if we cast to IEnumerable ICollection col = (ICollection)TypeConverter.Convert(value, typeof(ICollection)); if (col == null) { // The type converter converts null to null rather than to an empty collection, so we have to handle this case explicitly method.Invoke(maybeCollectionTarget, new object[] { null }); } else { foreach (object child in col) { method.Invoke(maybeCollectionTarget, new object[] { child }); } } return(true); } // Check for Dictionary Type keyType; Type valueType; FindImplementedDictionaryType(targetType, out resultType, out keyType, out valueType); Type sourceDictType; Type sourceKeyType; Type sourceValueType; FindImplementedDictionaryType(value.GetType(), out sourceDictType, out sourceKeyType, out sourceValueType); if (resultType != null && sourceDictType != null) { PropertyInfo targetItemProperty = keyType == null?targetType.GetProperty("Item") : targetType.GetProperty("Item", new Type[] { keyType }); MethodInfo targetItemSetter = targetItemProperty.GetSetMethod(); foreach (KeyValuePair <object, object> kvp in (IEnumerable)value) { targetItemSetter.Invoke(maybeCollectionTarget, new object[] { kvp.Key, kvp.Value }); } return(true); } // Check for IAddChild if (IsIAddChild(maybeCollectionTarget.GetType(), out method, out entryType)) { foreach (object child in (ICollection)TypeConverter.Convert(value, typeof(ICollection))) { method.Invoke(maybeCollectionTarget, new object[] { TypeConverter.Convert(child, entryType) }); } return(true); } return(false); }