public static bool AutoMapClass <T>(FallbackStrategy emptyValueStrategy, out ExcelClassMap <T> classMap) { Type type = typeof(T); if (type.GetTypeInfo().IsInterface) { classMap = null; return(false); } var map = new ExcelClassMap <T>(); IEnumerable <MemberInfo> properties = type.GetRuntimeProperties().Where(p => p.CanWrite); IEnumerable <MemberInfo> fields = type.GetRuntimeFields().Where(f => f.IsPublic); foreach (MemberInfo member in properties.Concat(fields)) { Type memberType = member.MemberType(); MethodInfo method = MappingMethod.MakeGenericMethod(memberType); var parameters = new object[] { member, emptyValueStrategy, null }; bool result = (bool)method.Invoke(null, parameters); if (!result) { classMap = null; return(false); } map.Mappings.Add((ExcelPropertyMap)parameters[2]); } classMap = map; return(true); }
public void MapObject_ClassMapFactory_ReturnsExpected() { var map = new TestClassMap(FallbackStrategy.ThrowIfPrimitive); ExcelClassMap <string> mapping = map.MapObject(t => t.Value); Assert.Empty(mapping.Properties); }
public void Ctor_Type() { var map = new ExcelClassMap(typeof(string)); Assert.Equal(typeof(string), map.Type); Assert.Empty(map.Properties); Assert.Same(map.Properties, map.Properties); }
public void Map_MultipleMemberAccessTypeAlreadyMapped_ThrowsInvalidOperationException() { var iconvertibleType = new IConvertibleType(); var classMap = new ExcelClassMap <IConvertibleValue>(); classMap.Map(p => p.IConvertibleType); Assert.Throws <InvalidOperationException>(() => classMap.Map(p => p.IConvertibleType.Value)); }
public void TryGetClassMap_NullClassType_ThrowsArgumentNullException() { using (var importer = Helpers.GetImporter("Primitives.xlsx")) { ExcelClassMap classMap = null; Assert.Throws <ArgumentNullException>("classType", () => importer.Configuration.TryGetClassMap(null, out classMap)); Assert.Null(classMap); } }
public void RegisterClassMapsInNamespace_NoAssemblyAndValidNamespaceString_ReturnsExpected() { using (var importer = Helpers.GetImporter("Strings.xlsx")) { IEnumerable <ExcelClassMap> classMaps = ExcelImporterUtils.RegisterClassMapsInNamespace(importer, "ExcelMapper.Utilities.Tests"); ExcelClassMap classMap = Assert.Single(classMaps); Assert.IsType <TestClassMap>(classMap); } }
/// <summary> /// Adds the properties from the mapping. This will recursively /// traverse the mapping tree and add all properties for /// reference maps. /// </summary> /// <param name="properties">The properties to be added to.</param> /// <param name="mapping">The mapping where the properties are added from.</param> protected void AddProperties( ExcelPropertyMapCollection properties, ExcelClassMap mapping) { properties.AddRange(mapping.PropertyMaps); foreach (var refMap in mapping.ReferenceMaps) { AddProperties(properties, refMap.Mapping); } }
public void WithClassMap_ClassMap_ReturnsExpected() { var classMap = new ExcelClassMap <string>(); MemberInfo propertyInfo = typeof(TestClass).GetProperty(nameof(TestClass.Value)); var propertyMap = new ObjectExcelPropertyMap <string>(propertyInfo, new ExcelClassMap <string>()); Assert.Same(propertyMap, propertyMap.WithClassMap(classMap)); Assert.Same(classMap, propertyMap.ClassMap); }
public void WithClassMap_NullClassMap_ThrowsArgumentNullException() { var classMap = new ExcelClassMap <string>(); MemberInfo propertyInfo = typeof(TestClass).GetProperty(nameof(TestClass.Value)); var propertyMap = new ObjectExcelPropertyMap <string>(propertyInfo, new ExcelClassMap <string>()) { ClassMap = classMap }; Assert.Throws <ArgumentNullException>("classMap", () => propertyMap.WithClassMap((ExcelClassMap <string>)null)); }
/// <summary> /// Creates a class map for the given type using the given strategy. /// </summary> /// <param name="emptyValueStrategy">The default strategy to use when the value of a cell is empty.</param> /// <param name="classMap">The class map for the given type.</param> /// <returns>True if the class map could be created, else false.</returns> public static bool TryCreateClassMap <T>(FallbackStrategy emptyValueStrategy, out ExcelClassMap <T> classMap) { if (!Enum.IsDefined(typeof(FallbackStrategy), emptyValueStrategy)) { throw new ArgumentException($"Invalid value \"{emptyValueStrategy}\".", nameof(emptyValueStrategy)); } Type type = typeof(T); if (type.GetTypeInfo().IsInterface) { classMap = null; return(false); } var map = new ExcelClassMap <T>(emptyValueStrategy); IEnumerable <MemberInfo> properties = type.GetRuntimeProperties().Where(p => p.CanWrite && p.SetMethod.IsPublic && !p.SetMethod.IsStatic); IEnumerable <MemberInfo> fields = type.GetRuntimeFields().Where(f => f.IsPublic && !f.IsStatic); foreach (MemberInfo member in properties.Concat(fields)) { // Ignore this property/field. if (Attribute.IsDefined(member, typeof(ExcelIgnoreAttribute))) { continue; } // Infer the mapping for each member (property/field) belonging to the type. Type memberType = member.MemberType(); MethodInfo method = TryCreateMemberMapMethod.MakeGenericMethod(memberType); if (memberType == type) { throw new ExcelMappingException($"Cannot map recursive property \"{member.Name}\" of type {memberType}. Consider applying the ExcelIgnore attribute."); } var parameters = new object[] { member, emptyValueStrategy, null }; bool result = (bool)method.Invoke(null, parameters); if (!result) { classMap = null; return(false); } // Get the out parameter representing the property map for the member. map.Properties.Add(new ExcelPropertyMap(member, (IMap)parameters[2])); } classMap = map; return(true); }
/// <summary> /// Creates a property expression for the given property on the record. /// This will recursively traverse the mapping to find the property /// and create a safe property accessor for each level as it goes. /// </summary> /// <param name="recordExpression">The current property expression.</param> /// <param name="mapping">The mapping to look for the property to map on.</param> /// <param name="propertyMap">The property map to look for on the mapping.</param> /// <returns>An Expression to access the given property.</returns> protected Expression CreatePropertyExpression( Expression recordExpression, ExcelClassMap mapping, ExcelPropertyMap propertyMap) { // Handle the simple case where the property is on this level. if (mapping.PropertyMaps.Any(pm => pm == propertyMap)) { return(Expression.Property(recordExpression, propertyMap.Data.Property)); } // The property isn't on this level of the mapping. We need to search down through the reference maps. foreach (var refMap in mapping.ReferenceMaps) { // Recursively find the property access expression for this property var wrapped = Expression.Property(recordExpression, refMap.Property); var propertyExpression = CreatePropertyExpression(wrapped, refMap.Mapping, propertyMap); if (propertyExpression == null) { // Not in this reference map, try the next one continue; } // Build an expression that looks like this for value types: // // (record.RefMap == null) ? return new type() : record.RefMap.Property // // and like this for nullable types: // // (record.RefMap == null) ? return null : record.RefMap.Property // // So that the properties of the reference mapped object will be written as null or a default value // if the reference map is not present in the record being written var nullCheckExpression = Expression.Equal(wrapped, Expression.Constant(null)); var isValueType = propertyMap.Data.Property.PropertyType.IsValueType; var defaultValueExpression = isValueType ? (Expression)Expression.New(propertyMap.Data.Property.PropertyType) : Expression.Constant(null, propertyMap.Data.Property.PropertyType); var conditionExpression = Expression.Condition(nullCheckExpression, defaultValueExpression, propertyExpression); return(conditionExpression); } // We get here if the property did not match anything in this mapping return(null); }
/// <summary> /// Creates the property bindings for the given <see cref="ExcelClassMap"/>. /// </summary> /// <param name="mapping">The mapping to create the bindings for.</param> /// <param name="bindings">The bindings that will be added to from the mapping.</param> private void CreatePropertyBindingsForMapping( ExcelClassMap mapping, List <MemberBinding> bindings) { // First bind all the regular properties for this record AddPropertyBindings(mapping.PropertyMaps, bindings); // Now process each reference map to map embedded classes foreach (var referenceMap in mapping.ReferenceMaps) { // Ignore any maps we cannot read if (!CanRead(referenceMap)) { continue; } // Now map all the properties in this reference map var referenceBindings = new List <MemberBinding>(); CreatePropertyBindingsForMapping(referenceMap.Mapping, referenceBindings); var referenceBody = Expression.MemberInit(Expression.New(referenceMap.Property.PropertyType), referenceBindings); bindings.Add(Expression.Bind(referenceMap.Property, referenceBody)); } }
internal static bool TryCreateObjectMap <T>(FallbackStrategy emptyValueStrategy, out ExcelClassMap <T> mapping) { if (!TryCreateClassMap(emptyValueStrategy, out ExcelClassMap <T> excelClassMap)) { mapping = null; return(false); } mapping = excelClassMap; return(true); }
public void WithClassMap_InvokeClassMap_Success() { var map = new ExcelClassMap <Helpers.TestClass>(); Assert.Same(this, WithClassMap(map)); }