/// <summary> /// Applies attribute configurations to the map. /// </summary> /// <param name="referenceMap">The parameter reference map.</param> protected virtual void ApplyAttributes(ParameterReferenceMap referenceMap) { var parameter = referenceMap.Data.Parameter; var attributes = parameter.GetCustomAttributes().OfType <IParameterReferenceMapper>(); foreach (var attribute in attributes) { attribute.ApplyTo(referenceMap); } }
/// <summary> /// Auto maps the given map using constructor parameters. /// </summary> /// <param name="map">The map.</param> /// <param name="configuration">The configuration.</param> /// <param name="mapParents">The list of parents for the map.</param> /// <param name="indexStart">The index starting point.</param> protected virtual void AutoMapConstructorParameters(ClassMap map, CsvConfiguration configuration, LinkedList <Type> mapParents, int indexStart = 0) { var type = map.GetGenericType(); var constructor = configuration.GetConstructor(map.ClassType); var parameters = constructor.GetParameters(); foreach (var parameter in parameters) { var typeConverterType = configuration.TypeConverterCache.GetConverter(parameter.ParameterType).GetType(); var parameterMap = new ParameterMap(parameter); var memberTypeInfo = parameter.ParameterType.GetTypeInfo(); var isDefaultConverter = typeConverterType == typeof(DefaultTypeConverter); if (isDefaultConverter && (memberTypeInfo.HasParameterlessConstructor() || memberTypeInfo.IsUserDefinedStruct())) { // If the type is not one covered by our type converters // and it has a parameterless constructor, create a // reference map for it. if (configuration.IgnoreReferences) { throw new InvalidOperationException($"Configuration '{nameof(configuration.IgnoreReferences)}' can't be true " + "when using types without a default constructor. Constructor parameters " + "are used and all members including references must be used."); } if (CheckForCircularReference(parameter.ParameterType, mapParents)) { throw new InvalidOperationException($"A circular reference was detected in constructor paramter '{parameter.Name}'." + "Since all parameters must be supplied for a constructor, this parameter can't be skipped."); } mapParents.AddLast(type); var refMapType = typeof(DefaultClassMap <>).MakeGenericType(parameter.ParameterType); var refMap = (ClassMap)ReflectionHelper.CreateInstance(refMapType); AutoMapMembers(refMap, configuration, mapParents, Math.Max(map.GetMaxIndex() + 1, indexStart)); mapParents.Drop(mapParents.Find(type)); var referenceMap = new ParameterReferenceMap(parameter, refMap); if (configuration.ReferenceHeaderPrefix != null) { referenceMap.Data.Prefix = configuration.ReferenceHeaderPrefix(memberTypeInfo.MemberType(), memberTypeInfo.Name); } parameterMap.ReferenceMap = referenceMap; } else if (configuration.ShouldUseConstructorParameters(parameter.ParameterType)) { mapParents.AddLast(type); var constructorMapType = typeof(DefaultClassMap <>).MakeGenericType(parameter.ParameterType); var constructorMap = (ClassMap)ReflectionHelper.CreateInstance(constructorMapType); // Need to use Max here for nested types. AutoMapConstructorParameters(constructorMap, configuration, mapParents, Math.Max(map.GetMaxIndex() + 1, indexStart)); mapParents.Drop(mapParents.Find(type)); parameterMap.ConstructorTypeMap = constructorMap; } else { parameterMap.Data.TypeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions(), configuration.TypeConverterOptionsCache.GetOptions(parameter.ParameterType), parameterMap.Data.TypeConverterOptions); parameterMap.Data.Index = map.GetMaxIndex() + 1; } map.ParameterMaps.Add(parameterMap); } map.ReIndex(indexStart); }
/// <summary> /// Auto maps the given map using constructor parameters. /// </summary> /// <param name="map">The map.</param> /// <param name="context">The context.</param> /// <param name="mapParents">The list of parents for the map.</param> /// <param name="indexStart">The index starting point.</param> protected virtual void AutoMapConstructorParameters(ClassMap map, CsvContext context, LinkedList <Type> mapParents, int indexStart = 0) { var type = map.GetGenericType(); var args = new GetConstructorArgs(map.ClassType); var constructor = context.Configuration.GetConstructor(args); var parameters = constructor.GetParameters(); foreach (var parameter in parameters) { var parameterMap = new ParameterMap(parameter); if (parameter.GetCustomAttributes <IgnoreAttribute>(true).Any() || parameter.GetCustomAttributes <ConstantAttribute>(true).Any()) { // If there is an IgnoreAttribute or ConstantAttribute, we still need to add a map because a constructor requires // all parameters to be present. A default value will be used later on. ApplyAttributes(parameterMap); map.ParameterMaps.Add(parameterMap); continue; } var typeConverterType = context.TypeConverterCache.GetConverter(parameter.ParameterType).GetType(); var memberTypeInfo = parameter.ParameterType.GetTypeInfo(); var isDefaultConverter = typeConverterType == typeof(DefaultTypeConverter); if (isDefaultConverter && (memberTypeInfo.HasParameterlessConstructor() || memberTypeInfo.IsUserDefinedStruct())) { // If the type is not one covered by our type converters // and it has a parameterless constructor, create a // reference map for it. if (context.Configuration.IgnoreReferences) { throw new InvalidOperationException($"Configuration '{nameof(CsvConfiguration.IgnoreReferences)}' can't be true " + "when using types without a default constructor. Constructor parameters " + "are used and all members including references must be used."); } if (CheckForCircularReference(parameter.ParameterType, mapParents)) { throw new InvalidOperationException($"A circular reference was detected in constructor paramter '{parameter.Name}'." + "Since all parameters must be supplied for a constructor, this parameter can't be skipped."); } mapParents.AddLast(type); var refMapType = typeof(DefaultClassMap <>).MakeGenericType(parameter.ParameterType); var refMap = (ClassMap)ObjectResolver.Current.Resolve(refMapType); AutoMapMembers(refMap, context, mapParents, Math.Max(map.GetMaxIndex(isParameter: true) + 1, indexStart)); mapParents.Drop(mapParents.Find(type)); var referenceMap = new ParameterReferenceMap(parameter, refMap); if (context.Configuration.ReferenceHeaderPrefix != null) { var referenceHeaderPrefix = new ReferenceHeaderPrefixArgs(memberTypeInfo.MemberType(), memberTypeInfo.Name); referenceMap.Data.Prefix = context.Configuration.ReferenceHeaderPrefix(referenceHeaderPrefix); } ApplyAttributes(referenceMap); parameterMap.ReferenceMap = referenceMap; } else if (context.Configuration.ShouldUseConstructorParameters(new ShouldUseConstructorParametersArgs(parameter.ParameterType))) { mapParents.AddLast(type); var constructorMapType = typeof(DefaultClassMap <>).MakeGenericType(parameter.ParameterType); var constructorMap = (ClassMap)ObjectResolver.Current.Resolve(constructorMapType); // Need to use Max here for nested types. AutoMapConstructorParameters(constructorMap, context, mapParents, Math.Max(map.GetMaxIndex(isParameter: true) + 1, indexStart)); mapParents.Drop(mapParents.Find(type)); parameterMap.ConstructorTypeMap = constructorMap; } else { parameterMap.Data.TypeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions(), context.TypeConverterOptionsCache.GetOptions(parameter.ParameterType), parameterMap.Data.TypeConverterOptions); parameterMap.Data.Index = map.GetMaxIndex(isParameter: true) + 1; ApplyAttributes(parameterMap); } map.ParameterMaps.Add(parameterMap); } map.ReIndex(indexStart); }