예제 #1
0
        /// <summary>
        /// Maps a property/field to a CSV field.
        /// </summary>
        /// <param name="classType">The type of the class this map is for. This may not be the same type
        /// as the member.DeclaringType or the current ClassType due to nested property mappings.</param>
        /// <param name="member">The property/field to map.</param>
        /// <param name="useExistingMap">If true, an existing map will be used if available.
        /// If false, a new map is created for the same property/field.</param>
        /// <returns>The property/field mapping.</returns>
        public CsvPropertyMap Map(Type classType, MemberInfo member, bool useExistingMap = true)
        {
            if (useExistingMap)
            {
                var existingMap = PropertyMaps.Find(member);
                if (existingMap != null)
                {
                    return(existingMap);
                }
            }

            var propertyMap = CsvPropertyMap.CreateGeneric(classType, member);

            propertyMap.Data.Index = GetMaxIndex() + 1;
            PropertyMaps.Add(propertyMap);

            return(propertyMap);
        }
예제 #2
0
        /// <summary>
        /// Auto maps the given map and checks for circular references as it goes.
        /// </summary>
        /// <param name="map">The map to auto map.</param>
        /// <param name="options">Options for auto mapping.</param>
        /// <param name="mapParents">The list of parents for the map.</param>
        /// <param name="indexStart">The index starting point.</param>
        internal static void AutoMapInternal(CsvClassMap map, AutoMapOptions options, LinkedList <Type> mapParents, int indexStart = 0)
        {
            var type = map.GetType().GetTypeInfo().BaseType.GetGenericArguments()[0];

            if (typeof(IEnumerable).IsAssignableFrom(type))
            {
                throw new CsvConfigurationException("Types that inherit IEnumerable cannot be auto mapped. " +
                                                    "Did you accidentally call GetRecord or WriteRecord which " +
                                                    "acts on a single record instead of calling GetRecords or " +
                                                    "WriteRecords which acts on a list of records?");
            }

            var flags = BindingFlags.Instance | BindingFlags.Public;

            if (options.IncludePrivateProperties)
            {
                flags = flags | BindingFlags.NonPublic;
            }

            var members = new List <MemberInfo>();

            if ((options.MemberTypes & MemberTypes.Properties) == MemberTypes.Properties)
            {
                // We need to go up the declaration tree and find the actual type the property
                // exists on and use that PropertyInfo instead. This is so we can get the private
                // set method for the property.
                var properties = new List <PropertyInfo>();
                foreach (var property in type.GetProperties(flags))
                {
                    properties.Add(ReflectionHelper.GetDeclaringProperty(type, property, flags));
                }

                members.AddRange(properties);
            }

            if ((options.MemberTypes & MemberTypes.Fields) == MemberTypes.Fields)
            {
                var fields = new List <MemberInfo>();
                foreach (var field in type.GetFields(flags))
                {
                    if (!field.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any())
                    {
                        fields.Add(field);
                    }
                }

                members.AddRange(fields);
            }

            foreach (var member in members)
            {
                var typeConverterType = TypeConverterFactory.Current.GetConverter(member.MemberType()).GetType();

                if (options.HasHeaderRecord && enumerableConverters.Contains(typeConverterType))
                {
                    // Enumerable converters can't write the header properly, so skip it.
                    continue;
                }

                var memberTypeInfo        = member.MemberType().GetTypeInfo();
                var isDefaultConverter    = typeConverterType == typeof(DefaultTypeConverter);
                var hasDefaultConstructor = member.MemberType().GetConstructor(new Type[0]) != null;
                var isUserDefinedStruct   = memberTypeInfo.IsValueType && !memberTypeInfo.IsPrimitive && !memberTypeInfo.IsEnum;
                if (isDefaultConverter && (hasDefaultConstructor || isUserDefinedStruct))
                {
                    if (options.IgnoreReferences)
                    {
                        continue;
                    }

                    // If the type is not one covered by our type converters
                    // and it has a parameterless constructor, create a
                    // reference map for it.
                    if (CheckForCircularReference(member.MemberType(), mapParents))
                    {
                        continue;
                    }

                    mapParents.AddLast(type);
                    var refMapType = typeof(DefaultCsvClassMap <>).MakeGenericType(member.MemberType());
                    var refMap     = (CsvClassMap)ReflectionHelper.CreateInstance(refMapType);
                    var refOptions = options.Copy();
                    refOptions.IgnoreReferences = false;
                    AutoMapInternal(refMap, options, mapParents, map.GetMaxIndex() + 1);
                    mapParents.Drop(mapParents.Find(type));

                    if (refMap.PropertyMaps.Count > 0 || refMap.ReferenceMaps.Count > 0)
                    {
                        var referenceMap = new CsvPropertyReferenceMap(member, refMap);
                        if (options.PrefixReferenceHeaders)
                        {
                            referenceMap.Prefix();
                        }

                        map.ReferenceMaps.Add(referenceMap);
                    }
                }
                else
                {
                    var propertyMap = CsvPropertyMap.CreateGeneric(map.ClassType, member);
                    // Use global values as the starting point.
                    propertyMap.Data.TypeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions(), options.TypeConverterOptionsFactory.GetOptions(member.MemberType()), propertyMap.Data.TypeConverterOptions);
                    propertyMap.Data.Index = map.GetMaxIndex() + 1;
                    if (!isDefaultConverter)
                    {
                        // Only add the property/field map if it can be converted later on.
                        // If the property/field will use the default converter, don't add it because
                        // we don't want the .ToString() value to be used when auto mapping.
                        map.PropertyMaps.Add(propertyMap);
                    }
                }
            }

            map.ReIndex(indexStart);
        }
예제 #3
0
        /// <summary>
        /// Auto maps the given map and checks for circular references as it goes.
        /// </summary>
        /// <param name="map">The map to auto map.</param>
        /// <param name="options">Options for auto mapping.</param>
        /// <param name="mapParents">The list of parents for the map.</param>
        /// <param name="indexStart">The index starting point.</param>
        protected virtual void AutoMapProperties(CsvClassMap map, AutoMapOptions options, LinkedList <Type> mapParents, int indexStart = 0)
        {
            var type = map.GetGenericType();

            var flags = BindingFlags.Instance | BindingFlags.Public;

            if (options.IncludePrivateProperties)
            {
                flags = flags | BindingFlags.NonPublic;
            }

            var members = new List <MemberInfo>();

            if (options.MemberTypes.HasFlag(MemberTypes.Properties))
            {
                // We need to go up the declaration tree and find the actual type the property
                // exists on and use that PropertyInfo instead. This is so we can get the private
                // set method for the property.
                var properties = new List <PropertyInfo>();
                foreach (var property in type.GetProperties(flags))
                {
                    properties.Add(ReflectionHelper.GetDeclaringProperty(type, property, flags));
                }

                members.AddRange(properties);
            }

            if (options.MemberTypes.HasFlag(MemberTypes.Fields))
            {
                var fields = new List <MemberInfo>();
                foreach (var field in type.GetFields(flags))
                {
                    if (!field.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any())
                    {
                        fields.Add(field);
                    }
                }

                members.AddRange(fields);
            }

            foreach (var member in members)
            {
                var typeConverterType = TypeConverterFactory.Current.GetConverter(member.MemberType()).GetType();

                if (options.HasHeaderRecord && enumerableConverters.Contains(typeConverterType))
                {
                    // Enumerable converters can't write the header properly, so skip it.
                    continue;
                }

                var memberTypeInfo     = member.MemberType().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 (options.IgnoreReferences)
                    {
                        continue;
                    }

                    if (CheckForCircularReference(member.MemberType(), mapParents))
                    {
                        continue;
                    }

                    mapParents.AddLast(type);
                    var refMapType = typeof(DefaultCsvClassMap <>).MakeGenericType(member.MemberType());
                    var refMap     = (CsvClassMap)ReflectionHelper.CreateInstance(refMapType);
                    var refOptions = options.Copy();
                    refOptions.IgnoreReferences = false;
                    // Need to use Max here for nested types.
                    AutoMapProperties(refMap, options, mapParents, Math.Max(map.GetMaxIndex() + 1, indexStart));
                    mapParents.Drop(mapParents.Find(type));

                    if (refMap.PropertyMaps.Count > 0 || refMap.ReferenceMaps.Count > 0)
                    {
                        var referenceMap = new CsvPropertyReferenceMap(member, refMap);
                        if (options.PrefixReferenceHeaders)
                        {
                            referenceMap.Prefix();
                        }

                        map.ReferenceMaps.Add(referenceMap);
                    }
                }
                else
                {
                    var propertyMap = CsvPropertyMap.CreateGeneric(map.ClassType, member);
                    // Use global values as the starting point.
                    propertyMap.Data.TypeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions(), options.TypeConverterOptionsFactory.GetOptions(member.MemberType()), propertyMap.Data.TypeConverterOptions);
                    propertyMap.Data.Index = map.GetMaxIndex() + 1;
                    if (!isDefaultConverter)
                    {
                        // Only add the property/field map if it can be converted later on.
                        // If the property/field will use the default converter, don't add it because
                        // we don't want the .ToString() value to be used when auto mapping.
                        map.PropertyMaps.Add(propertyMap);
                    }
                }
            }

            map.ReIndex(indexStart);
        }