Documentation Coverage Report
Current view: top level - Cqrs.WebApi/Formatters/FormMultipart/Converters - FormDataToObjectConverter.cs Hit Total Coverage
Version: 2.2 Artefacts: 3 3 100.0 %
Date: 2018-08-07 15:04:50

          Line data    Source code
       1             : #region Copyright
       2             : // // -----------------------------------------------------------------------
       3             : // // <copyright company="Chinchilla Software Limited">
       4             : // //   Copyright Chinchilla Software Limited. All rights reserved.
       5             : // // </copyright>
       6             : // // -----------------------------------------------------------------------
       7             : #endregion
       8             : 
       9             : using System;
      10             : using System.Collections.Generic;
      11             : using System.Linq;
      12             : using System.Net.Http.Formatting;
      13             : using System.Reflection;
      14             : using Cqrs.WebApi.Formatters.FormMultipart.Infrastructure;
      15             : using Cqrs.WebApi.Formatters.FormMultipart.Infrastructure.Extensions;
      16             : using Cqrs.WebApi.Formatters.FormMultipart.Infrastructure.Logger;
      17             : 
      18             : namespace Cqrs.WebApi.Formatters.FormMultipart.Converters
      19             : {
      20             :         /// <summary>
      21             :         /// Converts multi-part form-data to <see cref="object">objects</see>.
      22             :         /// </summary>
      23             :         public class FormDataToObjectConverter
      24           1 :         {
      25             :                 private readonly FormData _sourceData;
      26             : 
      27             :                 private readonly IFormDataConverterLogger _logger;
      28             : 
      29             :                 private readonly MultipartFormatterSettings _settings;
      30             : 
      31             :                 /// <summary>
      32             :                 /// Instantiate and initialise a new instance of <see cref="FormDataToObjectConverter"/>
      33             :                 /// </summary>
      34             :                 /// <param name="sourceData">The <see cref="FormData"/> to convert.</param>
      35             :                 /// <param name="logger">The <see cref="IFormatterLogger"/> to log events to.</param>
      36             :                 /// <param name="settings">The <see cref="MultipartFormatterSettings"/> to use.</param>
      37           1 :                 public FormDataToObjectConverter(FormData sourceData, IFormDataConverterLogger logger, MultipartFormatterSettings settings) 
      38             :                 {
      39             :                         if (sourceData == null)
      40             :                                 throw new ArgumentNullException("sourceData");
      41             :                         if (logger == null)
      42             :                                 throw new ArgumentNullException("logger");
      43             :                         if (settings == null)
      44             :                                 throw new ArgumentNullException("settings");
      45             : 
      46             :                         _settings = settings;
      47             :                         _sourceData = sourceData;
      48             :                         _logger = logger;
      49             :                 }
      50             : 
      51             :                 /// <summary>
      52             :                 /// Converts the multi-part form-data to the provided <paramref name="destinationType"/>
      53             :                 /// </summary>
      54             :                 /// <param name="destinationType">The <see cref="Type"/> to convert the multi-part form-data to.</param>
      55           1 :                 public object Convert(Type destinationType) 
      56             :                 {
      57             :                         if (destinationType == null)
      58             :                                 throw new ArgumentNullException("destinationType");
      59             : 
      60             :                         if (destinationType == typeof(FormData))
      61             :                                 return _sourceData;
      62             : 
      63             :                         var objResult = CreateObject(destinationType);
      64             :                         return objResult;
      65             :                 } 
      66             : 
      67             :                 private object CreateObject(Type destinationType, string propertyName = "")
      68             :                 {
      69             :                         object propValue = null;
      70             : 
      71             :                         if (propertyName == null)
      72             :                         {
      73             :                                 propertyName = "";
      74             :                         }
      75             : 
      76             :                         object buf;
      77             :                         if (TryGetAsNotIndexedListOrArray(destinationType, propertyName, out buf)
      78             :                                 || TryGetFromFormData(destinationType, propertyName, out buf)
      79             :                                 || TryGetAsGenericDictionary(destinationType, propertyName, out buf)
      80             :                                 || TryGetAsIndexedGenericListOrArray(destinationType, propertyName, out buf)
      81             :                                 || TryGetAsCustomType(destinationType, propertyName, out buf))
      82             :                         {
      83             :                                 propValue = buf;
      84             :                         }
      85             :                         else if (IsNotNullableValueType(destinationType)
      86             :                                 && IsNeedValidateMissedProperty(propertyName))
      87             :                         {
      88             :                                 _logger.LogError(propertyName, "The value is required.");
      89             :                         }
      90             :                         else if (!IsFileOrConvertableFromString(destinationType))
      91             :                         {
      92             :                                 _logger.LogError(propertyName, String.Format("Cannot parse type \"{0}\".", destinationType.FullName));
      93             :                         }
      94             : 
      95             :                         return propValue;
      96             :                 }
      97             : 
      98             :                 private bool TryGetAsNotIndexedListOrArray(Type destinationType, string propertyName, out object propValue)
      99             :                 {
     100             :                         propValue = null;
     101             : 
     102             :                         Type genericListItemType;
     103             :                         bool isGenericList = IsGenericListOrArray(destinationType, out genericListItemType);
     104             :                         if (isGenericList)
     105             :                         {
     106             :                                 var items = GetNotIndexedListItems(propertyName, genericListItemType);
     107             :                                 propValue = MakeList(genericListItemType, destinationType, items, propertyName);
     108             :                         }
     109             : 
     110             :                         return propValue != null;
     111             :                 }
     112             : 
     113             :                 private List<object> GetNotIndexedListItems(string propertyName, Type genericListItemType)
     114             :                 {
     115             :                         List<object> res;
     116             :                         if (!TryGetListFromFormData(genericListItemType, propertyName, out res))
     117             :                         {
     118             :                                 TryGetListFromFormData(genericListItemType, propertyName + "[]", out res);
     119             :                         }
     120             : 
     121             :                         return res ?? new List<object>();
     122             :                 }
     123             : 
     124             :                 private bool TryGetFromFormData(Type destinationType, string propertyName, out object propValue)
     125             :                 {
     126             :                         propValue = null;
     127             :                         List<object> values;
     128             :                         if (TryGetListFromFormData(destinationType, propertyName, out values))
     129             :                         {
     130             :                                 propValue = values.FirstOrDefault();
     131             :                                 return true;
     132             :                         }
     133             :                         return false;
     134             :                 }
     135             : 
     136             :                 private bool TryGetListFromFormData(Type destinationType, string propertyName, out List<object> propValue)
     137             :                 {
     138             :                         bool existsInFormData = false;
     139             :                         propValue = null;
     140             : 
     141             :                         if (destinationType == typeof(HttpFile) || destinationType == typeof(byte[]))
     142             :                         {
     143             :                                 var files = _sourceData.GetFiles(propertyName, _settings.CultureInfo);
     144             :                                 if (files.Any())
     145             :                                 {
     146             :                                         existsInFormData = true;
     147             :                                         propValue = new List<object>();
     148             : 
     149             :                                         foreach (var httpFile in files)
     150             :                                         {
     151             :                                                 var item = destinationType == typeof(byte[])
     152             :                                                         ? httpFile.Buffer
     153             :                                                         : (object)httpFile;
     154             : 
     155             :                                                 propValue.Add(item);
     156             :                                         }
     157             :                                 }
     158             :                         }
     159             :                         else
     160             :                         {
     161             :                                 var values = _sourceData.GetValues(propertyName, _settings.CultureInfo);
     162             :                                 if (values.Any())
     163             :                                 {
     164             :                                         existsInFormData = true;
     165             :                                         propValue = new List<object>();
     166             : 
     167             :                                         foreach (var value in values)
     168             :                                         {
     169             :                                                 object val;
     170             :                                                 if(TryConvertFromString(destinationType, propertyName, value, out val))
     171             :                                                 {
     172             :                                                         propValue.Add(val);
     173             :                                                 }
     174             :                                         }
     175             :                                 }
     176             :                         }
     177             : 
     178             :                         return existsInFormData;
     179             :                 }
     180             : 
     181             :                 private bool TryConvertFromString(Type destinationType, string propertyName, string val, out object propValue)
     182             :                 {
     183             :                         propValue = null;
     184             :                         var typeConverter = destinationType.GetFromStringConverter();
     185             :                         if (typeConverter == null)
     186             :                         {
     187             :                                 _logger.LogError(propertyName, "Cannot find type converter for field - " + propertyName);
     188             :                         }
     189             :                         else
     190             :                         {
     191             :                                 try
     192             :                                 {
     193             :                                         propValue = typeConverter.ConvertFromString(val, _settings.CultureInfo);
     194             :                                         return true;
     195             :                                 }
     196             :                                 catch (Exception ex)
     197             :                                 {
     198             :                                         _logger.LogError(propertyName, String.Format("Error parsing field \"{0}\": {1}", propertyName, ex.Message));
     199             :                                 }
     200             :                         }
     201             :                         return false;
     202             :                 }
     203             : 
     204             :                 private bool TryGetAsGenericDictionary(Type destinationType, string propertyName, out object propValue)
     205             :                 {
     206             :                         propValue = null;
     207             :                         Type keyType, valueType;
     208             :                         bool isGenericDictionary = IsGenericDictionary(destinationType, out keyType, out valueType);
     209             :                         if (isGenericDictionary)
     210             :                         {
     211             :                                 var dictType = typeof(Dictionary<,>).MakeGenericType(new[] { keyType, valueType });
     212             :                                 var add = dictType.GetMethod("Add");
     213             : 
     214             :                                 var pValue = Activator.CreateInstance(dictType);
     215             : 
     216             :                                 int index = 0;
     217             :                                 string origPropName = propertyName;
     218             :                                 bool isFilled = false;
     219             :                                 while (true)
     220             :                                 {
     221             :                                         string propertyKeyName = String.Format("{0}[{1}].Key", origPropName, index);
     222             :                                         var objKey = CreateObject(keyType, propertyKeyName);
     223             :                                         if (objKey != null)
     224             :                                         {
     225             :                                                 string propertyValueName = String.Format("{0}[{1}].Value", origPropName, index);
     226             :                                                 var objValue = CreateObject(valueType, propertyValueName);
     227             : 
     228             :                                                 if (objValue != null)
     229             :                                                 {
     230             :                                                         add.Invoke(pValue, new[] { objKey, objValue });
     231             :                                                         isFilled = true;
     232             :                                                 }
     233             :                                         }
     234             :                                         else
     235             :                                         {
     236             :                                                 break;
     237             :                                         }
     238             :                                         index++;
     239             :                                 }
     240             : 
     241             :                                 if (isFilled || IsRootProperty(propertyName))
     242             :                                 {
     243             :                                         propValue = pValue;
     244             :                                 }
     245             :                         }
     246             : 
     247             :                         return isGenericDictionary;
     248             :                 }
     249             : 
     250             :                 private bool TryGetAsIndexedGenericListOrArray(Type destinationType, string propertyName, out object propValue)
     251             :                 {
     252             :                         propValue = null;
     253             :                         Type genericListItemType;
     254             :                         bool isGenericList = IsGenericListOrArray(destinationType, out genericListItemType);
     255             :                         if (isGenericList)
     256             :                         {
     257             :                                 var items = GetIndexedListItems(propertyName, genericListItemType);
     258             :                                 propValue =  MakeList(genericListItemType, destinationType, items, propertyName);
     259             :                         }
     260             : 
     261             :                         return isGenericList;
     262             :                 }
     263             : 
     264             :                 private object MakeList(Type genericListItemType, Type destinationType, List<object> listItems, string propertyName)
     265             :                 {
     266             :                         object result = null;
     267             : 
     268             :                         if (listItems.Any() || IsRootProperty(propertyName))
     269             :                         {
     270             :                                 var listType = typeof(List<>).MakeGenericType(genericListItemType);
     271             : 
     272             :                                 var add = listType.GetMethod("Add");
     273             :                                 var pValue = Activator.CreateInstance(listType);
     274             : 
     275             :                                 foreach (var listItem in listItems)
     276             :                                 {
     277             :                                         add.Invoke(pValue, new[] { listItem });
     278             :                                 }
     279             : 
     280             :                                 if (destinationType.IsArray)
     281             :                                 {
     282             :                                         var toArrayMethod = listType.GetMethod("ToArray");
     283             :                                         result = toArrayMethod.Invoke(pValue, new object[0]);
     284             :                                 }
     285             :                                 else
     286             :                                 {
     287             :                                         result = pValue;
     288             :                                 }
     289             :                         }
     290             : 
     291             :                         return result;
     292             :                 }
     293             : 
     294             :                 private List<object> GetIndexedListItems(string origPropName, Type genericListItemType)
     295             :                 {
     296             :                         var res = new List<object>();
     297             :                         int index = 0;
     298             :                         while (true)
     299             :                         {
     300             :                                 var propertyName = String.Format("{0}[{1}]", origPropName, index);
     301             :                                 var objValue = CreateObject(genericListItemType, propertyName);
     302             :                                 if (objValue != null)
     303             :                                 {
     304             :                                         res.Add(objValue);
     305             :                                 }
     306             :                                 else
     307             :                                 {
     308             :                                         break;
     309             :                                 }
     310             : 
     311             :                                 index++;
     312             :                         }
     313             :                         return res;
     314             :                 }
     315             : 
     316             :                 private bool TryGetAsCustomType(Type destinationType, string propertyName, out object propValue)
     317             :                 {
     318             :                         propValue = null;
     319             :                         bool isCustomNonEnumerableType = destinationType.IsCustomNonEnumerableType();
     320             :                         if (isCustomNonEnumerableType && IsRootPropertyOrAnyChildPropertiesExistsInFormData(propertyName))
     321             :                         {
     322             :                                 propValue = Activator.CreateInstance(destinationType);
     323             :                                 foreach (PropertyInfo propertyInfo in destinationType.GetProperties().Where(m => m.SetMethod != null))
     324             :                                 {
     325             :                                         var propName = (!String.IsNullOrEmpty(propertyName) ? propertyName + "." : "") + propertyInfo.Name;
     326             : 
     327             :                                         var objValue = CreateObject(propertyInfo.PropertyType, propName);
     328             :                                         if (objValue != null)
     329             :                                         {
     330             :                                                 propertyInfo.SetValue(propValue, objValue);
     331             :                                         }
     332             :                                 }
     333             :                         }
     334             :                         return isCustomNonEnumerableType;
     335             :                 }
     336             : 
     337             :                 private bool IsGenericDictionary(Type type, out Type keyType, out Type valueType)
     338             :                 {
     339             :                         Type iDictType = type.GetInterface(typeof (IDictionary<,>).Name);
     340             :                         if (iDictType != null)
     341             :                         {
     342             :                                 var types = iDictType.GetGenericArguments();
     343             :                                 if (types.Length == 2)
     344             :                                 {
     345             :                                         keyType = types[0];
     346             :                                         valueType = types[1];
     347             :                                         return true;
     348             :                                 }
     349             :                         }
     350             : 
     351             :                         keyType = null;
     352             :                         valueType = null;
     353             :                         return false;
     354             :                 }
     355             : 
     356             :                 private bool IsGenericListOrArray(Type type, out Type itemType)
     357             :                 {
     358             :                         if (type.GetInterface(typeof(IDictionary<,>).Name) == null) //not a dictionary
     359             :                         {
     360             :                                 if (type.IsArray)
     361             :                                 {
     362             :                                         itemType = type.GetElementType();
     363             :                                         return true;
     364             :                                 }
     365             : 
     366             :                                 Type iListType = type.GetInterface(typeof(ICollection<>).Name);
     367             :                                 if (iListType != null) 
     368             :                                 {
     369             :                                         Type[] genericArguments = iListType.GetGenericArguments();
     370             :                                         if (genericArguments.Length == 1)
     371             :                                         {
     372             :                                                 itemType = genericArguments[0];
     373             :                                                 return true;
     374             :                                         }
     375             :                                 }
     376             :                         }
     377             :                   
     378             :                         itemType = null;
     379             :                         return false;
     380             :                 }
     381             : 
     382             :                 private bool IsFileOrConvertableFromString(Type type)
     383             :                 {
     384             :                         if (type == typeof (HttpFile))
     385             :                                 return true;
     386             : 
     387             :                         return type.GetFromStringConverter() != null;
     388             :                 }
     389             : 
     390             :                 private bool IsNotNullableValueType(Type type)
     391             :                 {
     392             :                         if (!type.IsValueType)
     393             :                                 return false;
     394             : 
     395             :                         return Nullable.GetUnderlyingType(type) == null;
     396             :                 }
     397             : 
     398             :                 private bool IsNeedValidateMissedProperty(string propertyName)
     399             :                 {
     400             :                         return _settings.ValidateNonNullableMissedProperty
     401             :                                         && !IsIndexedProperty(propertyName)
     402             :                                         && IsRootPropertyOrAnyParentsPropertyExistsInFormData(propertyName);
     403             :                 }
     404             : 
     405             :                 private bool IsRootPropertyOrAnyParentsPropertyExistsInFormData(string propertyName)
     406             :                 {
     407             :                         string parentName = "";
     408             :                         if (propertyName != null)
     409             :                         {
     410             :                                 int lastDotIndex = propertyName.LastIndexOf('.');
     411             :                                 if (lastDotIndex >= 0)
     412             :                                 {
     413             :                                         parentName = propertyName.Substring(0, lastDotIndex);
     414             :                                 }
     415             :                         }
     416             : 
     417             :                         bool result = IsRootPropertyOrAnyChildPropertiesExistsInFormData(parentName);
     418             :                         return result;
     419             :                 }
     420             : 
     421             :                 private bool IsRootPropertyOrAnyChildPropertiesExistsInFormData(string propertyName)
     422             :                 {
     423             :                         if (IsRootProperty(propertyName))
     424             :                                 return true;
     425             : 
     426             :                         string prefixWithDot = propertyName + ".";
     427             :                         bool result = _sourceData.GetAllKeys().Any(m => m.StartsWith(prefixWithDot, true, _settings.CultureInfo));
     428             :                         return result;
     429             :                 }
     430             : 
     431             :                 private bool IsRootProperty(string propertyName)
     432             :                 {
     433             :                         return propertyName == "";
     434             :                 }
     435             : 
     436             :                 private bool IsIndexedProperty(string propName)
     437             :                 {
     438             :                         return propName != null && propName.EndsWith("]");
     439             :                 }
     440             :         }
     441             : }

Generated by: LCOV version 1.12