diff --git a/.gitignore b/.gitignore index 18cfa6839..d5e30466d 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,5 @@ FileHelpers.userprefs /FileHelpers.Analyzer/FileHelpers.Analyzer/FileHelpers.Analyzer.Vsix/bin /FileHelpers.Analyzer/FileHelpers.Analyzer/FileHelpers.Analyzer.Vsix/obj /FileHelpers.Examples/obj -/FileHelpers.Examples/Release \ No newline at end of file +/FileHelpers.Examples/Release +/_ReSharper.Caches \ No newline at end of file diff --git a/FileHelpers.Tests/Data/Good/NumberFormatFrench.txt b/FileHelpers.Tests/Data/Good/NumberFormatFrench.txt new file mode 100644 index 000000000..afa7b7c8e --- /dev/null +++ b/FileHelpers.Tests/Data/Good/NumberFormatFrench.txt @@ -0,0 +1,3 @@ +10248|32,38|32,38|32,38 +10249|1011,61|1011,61|1011,61 +10250|1 165,83|1 165,83|1 165,83 \ No newline at end of file diff --git a/FileHelpers.Tests/FileHelpers.Tests.csproj b/FileHelpers.Tests/FileHelpers.Tests.csproj index 2005f0d42..e5331f0b9 100644 --- a/FileHelpers.Tests/FileHelpers.Tests.csproj +++ b/FileHelpers.Tests/FileHelpers.Tests.csproj @@ -165,6 +165,7 @@ + Code @@ -509,6 +510,7 @@ + diff --git a/FileHelpers.Tests/Tests/Converters/DecimalNumbers.cs b/FileHelpers.Tests/Tests/Converters/DecimalNumbers.cs index e0e6b4b8b..a46228dbd 100644 --- a/FileHelpers.Tests/Tests/Converters/DecimalNumbers.cs +++ b/FileHelpers.Tests/Tests/Converters/DecimalNumbers.cs @@ -26,6 +26,10 @@ public void Decimals1() CheckDecimal((decimal) 81.91, res[9]); } + + + + private static void CheckDecimal(decimal dec, DecimalType res) { Assert.AreEqual((decimal) dec, res.DecimalField); @@ -33,6 +37,7 @@ private static void CheckDecimal(decimal dec, DecimalType res) Assert.AreEqual((float) dec, res.FloatField); } + [Test] public void NegativeNumbers() @@ -65,10 +70,13 @@ public class DecimalType public decimal DecimalField; } + + [Test] public void DecimalsWithExponents() { var engine = new FileHelperEngine(); + DecimalType[] res; res = TestCommon.ReadTest(engine, "Good", "NumberFormat2.txt"); diff --git a/FileHelpers.Tests/Tests/Converters/DefaultCultureInfo.cs b/FileHelpers.Tests/Tests/Converters/DefaultCultureInfo.cs new file mode 100644 index 000000000..2fcce02aa --- /dev/null +++ b/FileHelpers.Tests/Tests/Converters/DefaultCultureInfo.cs @@ -0,0 +1,158 @@ +using NUnit.Framework; + +namespace FileHelpers.Tests.Converters +{ + [TestFixture] + public class DefaultCultureInfo + { + [DelimitedRecord("|")] + public class RecordWithoutSpecifiedCulture + { + public decimal DecimalField; + } + + [DelimitedRecord("|", "fr-FR")] + public class RecordWithDefaultCulture + { + public decimal DecimalFieldWithoutCulture; + + + [FieldConverter(ConverterKind.Decimal, "en-US")] + public decimal DecimalFieldWithEnglishCulture; + } + + [DelimitedRecord("|")] + public class RecordWithFieldCulture + { + [FieldConverter(ConverterKind.Decimal, "fr-FR")] + public decimal DecimalFieldWithFrenchCulture; + + public decimal DecimalFieldWithoutCulture; + } + + + [Test] + public void RecordWithoutCultureHasDefaultSeparator() + { + var engine = new FileHelperEngine(); + Assert.AreEqual(1, engine.Options.Fields.Count); + var decimalConverter = engine.Options.Fields[0].Converter; + AssertCanConvertEnglishNumbers(decimalConverter); + } + + [Test] + public void RecordWithSpecifiedCultureInRecordAttributeUsesThatCultureByDefault() + { + var engine = new FileHelperEngine(); + Assert.AreEqual(2, engine.Options.Fields.Count); + var decimalConverterWithoutCulture = engine.Options.Fields[0].Converter; + AssertCanConvertFrenchNumbers(decimalConverterWithoutCulture); + + var decimalConverterWithEnglishCulture = engine.Options.Fields[1].Converter; + AssertCanConvertEnglishNumbers(decimalConverterWithEnglishCulture); + } + + [Test] + public void FieldWithSpecifiedCultureInFieldConverterAttributeUsesThatCulture() + { + var engine = new FileHelperEngine(); + Assert.AreEqual(2, engine.Options.Fields.Count); + var decimalConverterWithFrenchCulture = engine.Options.Fields[0].Converter; + AssertCanConvertFrenchNumbers(decimalConverterWithFrenchCulture); + + var decimalConverterWithoutCulture = engine.Options.Fields[1].Converter; + AssertCanConvertEnglishNumbers(decimalConverterWithoutCulture); + } + + #region Helpers + private static void AssertCanConvertEnglishNumbers(ConverterBase decimalConverter) + { + Assert.AreEqual(123.12, + decimalConverter.StringToField("123.12"), + "If no culture is specified, the decimal separator should be a dot"); + Assert.AreEqual(1234.12, + decimalConverter.StringToField("1,234.12"), + "If no culture is specified, the group separator should be a comma"); + } + + private static void AssertCanConvertFrenchNumbers(ConverterBase decimalConverterWithoutCulture) + { + Assert.AreEqual(1.23, decimalConverterWithoutCulture.StringToField("1,23"), "If a culture is specified, the decimal separator should be the specified culture decimal separator"); + Assert.AreEqual(1234.12, decimalConverterWithoutCulture.StringToField("1 234,12"), "If a culture is specified, the group separator should be the specified culture group separator"); + Assert.Catch(() => { decimalConverterWithoutCulture.StringToField("1.23"); }, "The dot is not a valid french separator"); + } + #endregion + + [DelimitedRecord("|", defaultCultureName: "fr-FR")] + public class DecimalTypeWithFrenchConversionAsAWhole + { + public int IntField; + public float FloatField; + public double DoubleField; + public decimal DecimalField; + } + + [DelimitedRecord("|")] + public class DecimalTypeWithFrenchConversion + { + [FieldConverter(ConverterKind.Int32, "fr-FR")] + public int IntField; + [FieldConverter(ConverterKind.Single, "fr-FR")] + public float FloatField; + [FieldConverter(ConverterKind.Double, "fr-FR")] + public double DoubleField; + [FieldConverter(ConverterKind.Decimal, "fr-FR")] + public decimal DecimalField; + } + + [Test] + public void DecimalsWithFrenchCulture() + { + var engine = new FileHelperEngine(); + var res = TestCommon.ReadTest(engine, "Good", "NumberFormatFrench.txt"); + + Assert.AreEqual(3, res.Length); + + Assert.AreEqual(10248, res[0].IntField); + CheckDecimal((decimal)32.38, res[0]); + + Assert.AreEqual(10249, res[1].IntField); + CheckDecimal((decimal)1011.61, res[1]); + + Assert.AreEqual(10250, res[2].IntField); + CheckDecimal((decimal)1165.83, res[2]); + } + + [Test] + public void DecimalsWithFrenchCulture2() + { + var engine = new FileHelperEngine(); + var res = TestCommon.ReadTest(engine, "Good", "NumberFormatFrench.txt"); + + Assert.AreEqual(3, res.Length); + + Assert.AreEqual(10248, res[0].IntField); + CheckDecimal((decimal)32.38, res[0]); + + Assert.AreEqual(10249, res[1].IntField); + CheckDecimal((decimal)1011.61, res[1]); + + Assert.AreEqual(10250, res[2].IntField); + CheckDecimal((decimal)1165.83, res[2]); + } + + private static void CheckDecimal(decimal dec, DecimalTypeWithFrenchConversion res) + { + Assert.AreEqual((decimal)dec, res.DecimalField); + Assert.AreEqual((double)dec, res.DoubleField); + Assert.AreEqual((float)dec, res.FloatField); + } + + private static void CheckDecimal(decimal dec, DecimalTypeWithFrenchConversionAsAWhole res) + { + Assert.AreEqual((decimal)dec, res.DecimalField); + Assert.AreEqual((double)dec, res.DoubleField); + Assert.AreEqual((float)dec, res.FloatField); + } + } +} \ No newline at end of file diff --git a/FileHelpers/Attributes/DelimitedRecordAttribute.cs b/FileHelpers/Attributes/DelimitedRecordAttribute.cs index 66721c974..f9fd54b99 100644 --- a/FileHelpers/Attributes/DelimitedRecordAttribute.cs +++ b/FileHelpers/Attributes/DelimitedRecordAttribute.cs @@ -13,7 +13,8 @@ public sealed class DelimitedRecordAttribute : TypedRecordAttribute /// Indicates that this class represents a delimited record. /// The separator string used to split the fields of the record. - public DelimitedRecordAttribute(string delimiter) + /// Default culture name used for each properties if no converter is specified otherwise. If null, the default decimal separator (".") will be used. + public DelimitedRecordAttribute(string delimiter, string defaultCultureName = null) : base(defaultCultureName: defaultCultureName) { if (Separator != String.Empty) this.Separator = delimiter; diff --git a/FileHelpers/Attributes/FixedLengthRecordAttribute.cs b/FileHelpers/Attributes/FixedLengthRecordAttribute.cs index b955f1c0c..f14784d5d 100644 --- a/FileHelpers/Attributes/FixedLengthRecordAttribute.cs +++ b/FileHelpers/Attributes/FixedLengthRecordAttribute.cs @@ -23,7 +23,8 @@ public FixedLengthRecordAttribute() /// specified variable length record behavior. /// /// The used for variable length records. By Default is FixedMode.ExactLength - public FixedLengthRecordAttribute(FixedMode fixedMode) + /// Default culture name used for each properties if no converter is specified otherwise. If null, the default decimal separator (".") will be used. + public FixedLengthRecordAttribute(FixedMode fixedMode, string defaultCultureName = null) : base(defaultCultureName: defaultCultureName) { FixedMode = fixedMode; } diff --git a/FileHelpers/Attributes/TypedRecordAttribute.cs b/FileHelpers/Attributes/TypedRecordAttribute.cs index f42660cf5..e43f6d638 100644 --- a/FileHelpers/Attributes/TypedRecordAttribute.cs +++ b/FileHelpers/Attributes/TypedRecordAttribute.cs @@ -10,10 +10,19 @@ namespace FileHelpers public abstract class TypedRecordAttribute : Attribute { + /// + /// Default culture name used for each properties if no converter is specified otherwise. If null, the default decimal separator (".") will be used. + /// + public string DefaultCultureName { get; private set; } + + #region " Constructors " /// Abstract class, see inheritors. - protected TypedRecordAttribute() {} + protected TypedRecordAttribute(string defaultCultureName) + { + this.DefaultCultureName = defaultCultureName; + } #endregion } diff --git a/FileHelpers/Converters/ConvertHelpers.cs b/FileHelpers/Converters/ConvertHelpers.cs index 0c2b4cc9c..e36b3b4ca 100644 --- a/FileHelpers/Converters/ConvertHelpers.cs +++ b/FileHelpers/Converters/ConvertHelpers.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using System.Linq; using System.Text; using FileHelpers.Helpers; @@ -16,27 +17,43 @@ internal static class ConvertHelpers { private const string DefaultDecimalSep = "."; + /// + /// Array of all allowed decimal separators + /// + private static readonly string[] mAllowedDecimalSeparators = { ".", "," }; + #region " CreateCulture " /// /// Return culture information for with comma decimal separator or comma decimal separator /// - /// Decimal separator string + /// Decimal separator string or culture name /// Cultural information based on separator - private static CultureInfo CreateCulture(string decimalSep) + private static CultureInfo CreateCulture(string decimalSepOrCultureName) { - var ci = new CultureInfo(CultureInfo.CurrentCulture.LCID); + CultureInfo ci; - if (decimalSep == ".") { - ci.NumberFormat.NumberDecimalSeparator = "."; - ci.NumberFormat.NumberGroupSeparator = ","; - } - else if (decimalSep == ",") { - ci.NumberFormat.NumberDecimalSeparator = ","; - ci.NumberFormat.NumberGroupSeparator = "."; + if (mAllowedDecimalSeparators.Contains(decimalSepOrCultureName)) + { + ci = new CultureInfo(CultureInfo.CurrentCulture.LCID); + + if (decimalSepOrCultureName == ".") + { + ci.NumberFormat.NumberDecimalSeparator = "."; + ci.NumberFormat.NumberGroupSeparator = ","; + } + else if (decimalSepOrCultureName == ",") + { + ci.NumberFormat.NumberDecimalSeparator = ","; + ci.NumberFormat.NumberGroupSeparator = "."; + } + else + throw new BadUsageException("You can only use '.' or ',' as decimal or group separators"); } else - throw new BadUsageException("You can only use '.' or ',' as decimal or group separators"); + { + ci = CultureInfo.GetCultureInfo(decimalSepOrCultureName); + } return ci; } @@ -50,20 +67,23 @@ private static CultureInfo CreateCulture(string decimalSep) /// /// Field name to check /// Type of the field to check + /// Default culture name used for each properties if no converter is specified otherwise. If null, the default decimal separator (".") will be used. /// Converter for this particular field - internal static ConverterBase GetDefaultConverter(string fieldName, Type fieldType) + internal static ConverterBase GetDefaultConverter(string fieldName, Type fieldType, string defaultCultureName=null) { if (fieldType.IsArray) { - - if (fieldType.GetArrayRank() != 1) { + + if (fieldType.GetArrayRank() != 1) + { throw new BadUsageException("The array field: '" + fieldName + "' has more than one dimension and is not supported by the library."); } fieldType = fieldType.GetElementType(); - if (fieldType.IsArray) { + if (fieldType.IsArray) + { throw new BadUsageException("The array field: '" + fieldName + "' is a jagged array and is not supported by the library."); } @@ -71,56 +91,56 @@ internal static ConverterBase GetDefaultConverter(string fieldName, Type fieldTy if (fieldType.IsValueType && fieldType.IsGenericType && - fieldType.GetGenericTypeDefinition() == typeof (Nullable<>)) + fieldType.GetGenericTypeDefinition() == typeof(Nullable<>)) fieldType = fieldType.GetGenericArguments()[0]; // Try to assign a default Converter - if (fieldType == typeof (string)) + if (fieldType == typeof(string)) return null; - if (fieldType == typeof (Int16)) - return new Int16Converter(); + if (fieldType == typeof(Int16)) + return new Int16Converter(defaultCultureName ?? DefaultDecimalSep); - if (fieldType == typeof (Int32)) - return new Int32Converter(); + if (fieldType == typeof(Int32)) + return new Int32Converter(defaultCultureName ?? DefaultDecimalSep); - if (fieldType == typeof (Int64)) - return new Int64Converter(); + if (fieldType == typeof(Int64)) + return new Int64Converter(defaultCultureName ?? DefaultDecimalSep); - if (fieldType == typeof (SByte)) - return new SByteConverter(); + if (fieldType == typeof(SByte)) + return new SByteConverter(defaultCultureName ?? DefaultDecimalSep); - if (fieldType == typeof (UInt16)) - return new UInt16Converter(); + if (fieldType == typeof(UInt16)) + return new UInt16Converter(defaultCultureName ?? DefaultDecimalSep); - if (fieldType == typeof (UInt32)) - return new UInt32Converter(); + if (fieldType == typeof(UInt32)) + return new UInt32Converter(defaultCultureName ?? DefaultDecimalSep); - if (fieldType == typeof (UInt64)) - return new UInt64Converter(); + if (fieldType == typeof(UInt64)) + return new UInt64Converter(defaultCultureName ?? DefaultDecimalSep); - if (fieldType == typeof (byte)) - return new ByteConverter(); + if (fieldType == typeof(byte)) + return new ByteConverter(defaultCultureName ?? DefaultDecimalSep); - if (fieldType == typeof (decimal)) - return new DecimalConverter(); + if (fieldType == typeof(decimal)) + return new DecimalConverter(defaultCultureName ?? DefaultDecimalSep); - if (fieldType == typeof (double)) - return new DoubleConverter(); + if (fieldType == typeof(double)) + return new DoubleConverter(defaultCultureName ?? DefaultDecimalSep); - if (fieldType == typeof (Single)) - return new SingleConverter(); + if (fieldType == typeof(Single)) + return new SingleConverter(defaultCultureName ?? DefaultDecimalSep); - if (fieldType == typeof (DateTime)) - return new DateTimeConverter(); + if (fieldType == typeof(DateTime)) + return new DateTimeConverter(ConverterBase.DefaultDateTimeFormat, defaultCultureName); - if (fieldType == typeof (bool)) + if (fieldType == typeof(bool)) return new BooleanConverter(); // Added by Alexander Obolonkov 2007.11.08 (the next three) - if (fieldType == typeof (char)) + if (fieldType == typeof(char)) return new CharConverter(); - if (fieldType == typeof (Guid)) + if (fieldType == typeof(Guid)) return new GuidConverter(); if (fieldType.IsEnum) return new EnumConverter(fieldType); @@ -151,10 +171,10 @@ internal abstract class CultureConverter /// Convert to a type given a decimal separator /// /// type we are converting - /// Separator - protected CultureConverter(Type T, string decimalSep) + /// Separator or culture name (eg. 'en-US', 'fr-FR'...) + protected CultureConverter(Type T, string decimalSepOrCultureName) { - mCulture = CreateCulture(decimalSep); + mCulture = CreateCulture(decimalSepOrCultureName); mType = T; } @@ -168,7 +188,7 @@ public override sealed string FieldToString(object from) if (from == null) return string.Empty; - return ((IConvertible) from).ToString(mCulture); + return ((IConvertible)from).ToString(mCulture); } /// @@ -198,14 +218,14 @@ internal sealed class ByteConverter : CultureConverter /// Convert a string to a byte value using the default decimal separator /// public ByteConverter() - : this(DefaultDecimalSep) {} + : this(DefaultDecimalSep) { } /// /// Convert a string to a byte /// - /// decimal separator to use '.' or ',' - public ByteConverter(string decimalSep) - : base(typeof (Byte), decimalSep) {} + /// decimal separator to use '.' or ',' + public ByteConverter(string decimalSepOrCultureName) + : base(typeof(Byte), decimalSepOrCultureName) { } /// /// Convert a string to a byte value @@ -230,14 +250,14 @@ internal sealed class UInt16Converter : CultureConverter /// Convert a number to a short integer /// public UInt16Converter() - : this(DefaultDecimalSep) {} + : this(DefaultDecimalSep) { } /// /// Convert a number to a short integer /// - /// Decimal separator - public UInt16Converter(string decimalSep) - : base(typeof (UInt16), decimalSep) {} + /// Decimal separator + public UInt16Converter(string decimalSepOrCultureName) + : base(typeof(UInt16), decimalSepOrCultureName) { } /// /// Parse a string to a short integer @@ -266,14 +286,14 @@ internal sealed class UInt32Converter : CultureConverter /// Unsigned integer converter /// public UInt32Converter() - : this(DefaultDecimalSep) {} + : this(DefaultDecimalSep) { } /// /// Unsigned integer converter with a decimal separator /// - /// dot or comma for to separate decimal - public UInt32Converter(string decimalSep) - : base(typeof (UInt32), decimalSep) {} + /// dot or comma for to separate decimal + public UInt32Converter(string decimalSepOrCultureName) + : base(typeof(UInt32), decimalSepOrCultureName) { } /// /// Convert a string to a unsigned integer value @@ -302,14 +322,14 @@ internal sealed class UInt64Converter : CultureConverter /// Unsigned long converter /// public UInt64Converter() - : this(DefaultDecimalSep) {} + : this(DefaultDecimalSep) { } /// /// Unsigned long with decimal separator /// - /// dot or comma for separator - public UInt64Converter(string decimalSep) - : base(typeof (UInt64), decimalSep) {} + /// dot or comma for separator + public UInt64Converter(string decimalSepOrCultureName) + : base(typeof(UInt64), decimalSepOrCultureName) { } /// /// Convert a string to an unsigned integer long @@ -342,14 +362,14 @@ internal sealed class SByteConverter : CultureConverter /// Signed byte converter (8 bit signed integer) /// public SByteConverter() - : this(DefaultDecimalSep) {} + : this(DefaultDecimalSep) { } /// /// Signed byte converter (8 bit signed integer) /// - /// dot or comma for separator - public SByteConverter(string decimalSep) - : base(typeof (SByte), decimalSep) {} + /// dot or comma for separator + public SByteConverter(string decimalSepOrCultureName) + : base(typeof(SByte), decimalSepOrCultureName) { } /// /// Convert a string to an signed byte @@ -374,14 +394,14 @@ internal sealed class Int16Converter : CultureConverter /// Convert a value to a short integer /// public Int16Converter() - : this(DefaultDecimalSep) {} + : this(DefaultDecimalSep) { } /// /// Convert a value to a short integer /// - /// dot or comma for separator - public Int16Converter(string decimalSep) - : base(typeof (short), decimalSep) {} + /// dot or comma for separator + public Int16Converter(string decimalSepOrCultureName) + : base(typeof(short), decimalSepOrCultureName) { } /// /// Convert a string to an short integer @@ -410,14 +430,14 @@ internal sealed class Int32Converter : CultureConverter /// Convert a value to a integer /// public Int32Converter() - : this(DefaultDecimalSep) {} + : this(DefaultDecimalSep) { } /// /// Convert a value to a integer /// - /// dot or comma for separator - public Int32Converter(string decimalSep) - : base(typeof (int), decimalSep) {} + /// dot or comma for separator + public Int32Converter(string decimalSepOrCultureName) + : base(typeof(int), decimalSepOrCultureName) { } /// /// Convert a string to an integer @@ -447,14 +467,14 @@ internal sealed class Int64Converter : CultureConverter /// Convert a value to a long integer /// public Int64Converter() - : this(DefaultDecimalSep) {} + : this(DefaultDecimalSep) { } /// /// Convert a value to a long integer /// - /// dot or comma for separator - public Int64Converter(string decimalSep) - : base(typeof (long), decimalSep) {} + /// dot or comma for separator + public Int64Converter(string decimalSepOrCultureName) + : base(typeof(long), decimalSepOrCultureName) { } /// /// Convert a string to an integer long @@ -491,14 +511,14 @@ internal sealed class DecimalConverter : CultureConverter /// Convert a value to a decimal value /// public DecimalConverter() - : this(DefaultDecimalSep) {} + : this(DefaultDecimalSep) { } /// /// Convert a value to a decimal value /// - /// dot or comma for separator - public DecimalConverter(string decimalSep) - : base(typeof (decimal), decimalSep) {} + /// dot or comma for separator + public DecimalConverter(string decimalSepOrCultureName) + : base(typeof(decimal), decimalSepOrCultureName) { } /// /// Convert a string to a decimal @@ -527,14 +547,14 @@ internal sealed class SingleConverter : CultureConverter /// Convert a value to a single floating point /// public SingleConverter() - : this(DefaultDecimalSep) {} + : this(DefaultDecimalSep) { } /// /// Convert a value to a single floating point /// - /// dot or comma for separator - public SingleConverter(string decimalSep) - : base(typeof (Single), decimalSep) {} + /// dot or comma for separator + public SingleConverter(string decimalSepOrCultureName) + : base(typeof(Single), decimalSepOrCultureName) { } /// /// Convert a string to an single precision floating point @@ -563,14 +583,14 @@ internal sealed class DoubleConverter : CultureConverter /// Convert a value to a floating point /// public DoubleConverter() - : this(DefaultDecimalSep) {} + : this(DefaultDecimalSep) { } /// /// Convert a value to a floating point /// - /// dot or comma for separator - public DoubleConverter(string decimalSep) - : base(typeof (Double), decimalSep) {} + /// dot or comma for separator + public DoubleConverter(string decimalSepOrCultureName) + : base(typeof(Double), decimalSepOrCultureName) { } /// /// Convert a string to an floating point @@ -602,14 +622,14 @@ internal sealed class PercentDoubleConverter : CultureConverter /// Convert a value to a floating point from a percentage /// public PercentDoubleConverter() - : this(DefaultDecimalSep) {} + : this(DefaultDecimalSep) { } /// /// Convert a value to a floating point from a percentage /// - /// dot or comma for separator - public PercentDoubleConverter(string decimalSep) - : base(typeof (Double), decimalSep) {} + /// dot or comma for separator + public PercentDoubleConverter(string decimalSepOrCultureName) + : base(typeof(Double), decimalSepOrCultureName) { } /// /// Convert a string to an floating point from percentage @@ -620,16 +640,18 @@ protected override object ParseString(string from) { double res; var blanksRemoved = StringHelper.RemoveBlanks(from); - if (blanksRemoved.EndsWith("%")) { + if (blanksRemoved.EndsWith("%")) + { if ( !Double.TryParse(blanksRemoved, NumberStyles.Number | NumberStyles.AllowExponent, mCulture, out res)) throw new ConvertException(from, mType); - return res/100.0; + return res / 100.0; } - else { + else + { if ( !Double.TryParse(blanksRemoved, NumberStyles.Number | NumberStyles.AllowExponent, @@ -661,14 +683,14 @@ internal sealed class DateTimeConverter : ConverterBase /// Convert a value to a date time value /// public DateTimeConverter() - : this(DefaultDateTimeFormat) {} + : this(DefaultDateTimeFormat) { } /// /// Convert a value to a date time value /// /// date format see .Net documentation public DateTimeConverter(string format) - :this(format, null) + : this(format, null) { } @@ -706,7 +728,8 @@ public override object StringToField(string from) from = string.Empty; DateTime val; - if (!DateTime.TryParseExact(from.Trim(), mFormat, mCulture, DateTimeStyles.None, out val)) { + if (!DateTime.TryParseExact(from.Trim(), mFormat, mCulture, DateTimeStyles.None, out val)) + { string extra; if (from.Length > mFormat.Length) @@ -717,7 +740,7 @@ public override object StringToField(string from) extra = " Using the format: '" + mFormat + "'"; - throw new ConvertException(from, typeof (DateTime), extra); + throw new ConvertException(from, typeof(DateTime), extra); } return val; } @@ -752,13 +775,13 @@ internal sealed class DateTimeMultiFormatConverter : ConverterBase /// Convert a value to a date time value using multiple formats /// public DateTimeMultiFormatConverter(string format1, string format2) - : this(new[] {format1, format2}) {} + : this(new[] { format1, format2 }) { } /// /// Convert a value to a date time value using multiple formats /// public DateTimeMultiFormatConverter(string format1, string format2, string format3) - : this(new[] {format1, format2, format3}) {} + : this(new[] { format1, format2, format3 }) { } /// /// Convert a date time value to a string @@ -766,15 +789,18 @@ public DateTimeMultiFormatConverter(string format1, string format2, string forma /// list of formats to try private DateTimeMultiFormatConverter(string[] formats) { - for (int i = 0; i < formats.Length; i++) { + for (int i = 0; i < formats.Length; i++) + { if (formats[i] == null || formats[i] == String.Empty) throw new BadUsageException("The format of the DateTime Converter can be null or empty."); - try { + try + { DateTime.Now.ToString(formats[i]); } - catch { + catch + { throw new BadUsageException("The format: '" + formats[i] + " is invalid for the DateTime Converter."); } @@ -794,9 +820,10 @@ public override object StringToField(string from) from = string.Empty; DateTime val; - if (!DateTime.TryParseExact(from.Trim(), mFormats, null, DateTimeStyles.None, out val)) { + if (!DateTime.TryParseExact(from.Trim(), mFormats, null, DateTimeStyles.None, out val)) + { string extra = " does not match any of the given formats: " + CreateFormats(); - throw new ConvertException(from, typeof (DateTime), extra); + throw new ConvertException(from, typeof(DateTime), extra); } return val; } @@ -809,7 +836,8 @@ private string CreateFormats() { var sb = new StringBuilder(); - for (int i = 0; i < mFormats.Length; i++) { + for (int i = 0; i < mFormats.Length; i++) + { if (i == 0) sb.Append("'" + mFormats[i] + "'"); else @@ -854,7 +882,7 @@ internal sealed class BooleanConverter : ConverterBase /// /// Simple boolean converter /// - public BooleanConverter() {} + public BooleanConverter() { } /// /// Boolean converter with true false values @@ -879,9 +907,11 @@ public override object StringToField(string from) object val; string testTo = from.ToLower(); - if (mTrueString == null) { + if (mTrueString == null) + { testTo = testTo.Trim(); - switch (testTo) { + switch (testTo) + { case "true": case "1": case "y": @@ -894,35 +924,38 @@ public override object StringToField(string from) case "n": case "f": - // I don't think that this case is possible without overriding the CustomNullHandling - // and it is possible that defaulting empty fields to be false is not correct + // I don't think that this case is possible without overriding the CustomNullHandling + // and it is possible that defaulting empty fields to be false is not correct case "": val = false; break; default: throw new ConvertException(from, - typeof (bool), + typeof(bool), "The string: " + from + " can't be recognized as boolean using default true/false values."); } } - else { + else + { // Most of the time the strings should match exactly. To improve performance // we skip the trim if the exact match is true if (testTo == mTrueStringLower) val = true; else if (testTo == mFalseStringLower) val = false; - else { + else + { testTo = testTo.Trim(); if (testTo == mTrueStringLower) val = true; else if (testTo == mFalseStringLower) val = false; - else { + else + { throw new ConvertException(from, - typeof (bool), + typeof(bool), "The string: " + from + " can't be recognized as boolean using the true/false values: " + mTrueString + "/" + mFalseString); @@ -941,7 +974,8 @@ public override object StringToField(string from) public override string FieldToString(object from) { bool b = Convert.ToBoolean(from); - if (b) { + if (b) + { if (mTrueString == null) return "True"; else @@ -999,7 +1033,7 @@ private enum CharFormat /// public CharConverter() : this("") // default, no upper or lower case conversion - {} + { } /// /// Single character converter that optionally makes it upper (X) or lower case (x) @@ -1007,7 +1041,8 @@ public CharConverter() /// empty string for no upper or lower, x for lower case, X for Upper case public CharConverter(string format) { - switch (format.Trim()) { + switch (format.Trim()) + { case "x": case "lower": mFormat = CharFormat.Lower; @@ -1038,8 +1073,10 @@ public override object StringToField(string from) if (string.IsNullOrEmpty(from)) return Char.MinValue; - try { - switch (mFormat) { + try + { + switch (mFormat) + { case CharFormat.NoChange: return from[0]; @@ -1051,12 +1088,13 @@ public override object StringToField(string from) default: throw new ConvertException(from, - typeof (Char), + typeof(Char), "Unknown char convert flag " + mFormat.ToString()); } } - catch { - throw new ConvertException(from, typeof (Char), "Upper or lower case of input string failed"); + catch + { + throw new ConvertException(from, typeof(Char), "Upper or lower case of input string failed"); } } @@ -1067,7 +1105,8 @@ public override object StringToField(string from) /// String containing the character public override string FieldToString(object from) { - switch (mFormat) { + switch (mFormat) + { case CharFormat.NoChange: return Convert.ToChar(from).ToString(); @@ -1078,7 +1117,7 @@ public override string FieldToString(object from) return char.ToUpper(Convert.ToChar(from)).ToString(); default: - throw new ConvertException("", typeof (Char), "Unknown char convert flag " + mFormat.ToString()); + throw new ConvertException("", typeof(Char), "Unknown char convert flag " + mFormat.ToString()); } } } @@ -1098,7 +1137,7 @@ internal sealed class GuidConverter : ConverterBase /// public GuidConverter() : this("D") // D or N or B or P (default is D: see Guid.ToString(string format)) - {} + { } /// /// Create a GUID converter with formats as defined for GUID @@ -1128,11 +1167,13 @@ public override object StringToField(string from) if (String.IsNullOrEmpty(from)) return Guid.Empty; - try { + try + { return new Guid(from); } - catch { - throw new ConvertException(from, typeof (Guid)); + catch + { + throw new ConvertException(from, typeof(Guid)); } } @@ -1145,7 +1186,7 @@ public override string FieldToString(object from) { if (from == null) return String.Empty; - return ((Guid) from).ToString(mFormat); + return ((Guid)from).ToString(mFormat); } } diff --git a/FileHelpers/Enums/ConverterKind.cs b/FileHelpers/Enums/ConverterKind.cs index fd7516a70..c3dc77e89 100644 --- a/FileHelpers/Enums/ConverterKind.cs +++ b/FileHelpers/Enums/ConverterKind.cs @@ -23,74 +23,74 @@ public enum ConverterKind /// /// Convert from or to Byte values. - /// Params: arg1 is the decimal separator, by default '.' + /// Params: arg1 is either a decimal separator, by default '.', or a culture name (eg. "en-US", "fr-FR") /// Byte, /// /// Convert from or to Int16 or short values. - /// Params: arg1 is the decimal separator, by default '.' + /// Params: arg1 is either a decimal separator, by default '.', or a culture name (eg. "en-US", "fr-FR") /// Int16, /// /// Convert from or to Int32 or int values. - /// Params: arg1 is the decimal separator, by default '.' + /// Params: arg1 is either a decimal separator, by default '.', or a culture name (eg. "en-US", "fr-FR") /// Int32, /// /// Convert from or to Int64 or long values. - /// Params: arg1 is the decimal separator, by default '.' + /// Params: arg1 is either a decimal separator, by default '.', or a culture name (eg. "en-US", "fr-FR") /// Int64, /// /// Convert from or to Decimal values. - /// Params: arg1 is the decimal separator, by default '.' + /// Params: arg1 is either a decimal separator, by default '.', or a culture name (eg. "en-US", "fr-FR") /// Decimal, /// /// Convert from or to Double values. - /// Params: arg1 is the decimal separator, by default '.' + /// Params: arg1 is either a decimal separator, by default '.', or a culture name (eg. "en-US", "fr-FR") /// Double, //Added by Shreyas Narasimhan (17 March 2010) /// /// Convert from or to Double values. Understands Percent '%' symbol /// and if present returns number /100 only while reading - /// Params: arg1 is the decimal separator, by default '.' + /// Params: arg1 is either a decimal separator, by default '.', or a culture name (eg. "en-US", "fr-FR") /// PercentDouble, /// /// Convert from or to Single values. - /// Params: arg1 is the decimal separator, by default '.' + /// Params: arg1 is either a decimal separator, by default '.', or a culture name (eg. "en-US", "fr-FR") /// Single, /// /// Convert from or to Byte values. - /// Params: arg1 is the decimal separator, by default '.' + /// Params: arg1 is either a decimal separator, by default '.', or a culture name (eg. "en-US", "fr-FR") /// SByte, /// /// Convert from or to UInt16 or unsigned short values. - /// Params: arg1 is the decimal separator, by default '.' + /// Params: arg1 is either a decimal separator, by default '.', or a culture name (eg. "en-US", "fr-FR") /// UInt16, /// /// Convert from or to UInt32 or unsigned int values. - /// Params: arg1 is the decimal separator, by default '.' + /// Params: arg1 is either a decimal separator, by default '.', or a culture name (eg. "en-US", "fr-FR") /// UInt32, /// /// Convert from or to UInt64 or unsigned long values. - /// Params: arg1 is the decimal separator, by default '.' + /// Params: arg1 is either a decimal separator, by default '.', or a culture name (eg. "en-US", "fr-FR") /// UInt64, diff --git a/FileHelpers/Fields/DelimitedField.cs b/FileHelpers/Fields/DelimitedField.cs index c94918fff..93142e5e5 100644 --- a/FileHelpers/Fields/DelimitedField.cs +++ b/FileHelpers/Fields/DelimitedField.cs @@ -25,8 +25,9 @@ private DelimitedField() {} /// /// field info structure /// field separator - internal DelimitedField(FieldInfo fi, string sep) - : base(fi) + /// Default culture name used for each properties if no converter is specified otherwise. If null, the default decimal separator (".") will be used. + internal DelimitedField(FieldInfo fi, string sep, string defaultCultureName=null) + : base(fi,defaultCultureName) { QuoteChar = '\0'; QuoteMultiline = MultilineMode.AllowForBoth; diff --git a/FileHelpers/Fields/FieldBase.cs b/FileHelpers/Fields/FieldBase.cs index ec357735d..6faf7ebd6 100644 --- a/FileHelpers/Fields/FieldBase.cs +++ b/FileHelpers/Fields/FieldBase.cs @@ -185,8 +185,8 @@ public static FieldBase CreateField(FieldInfo fi, TypedRecordAttribute recordAtt var memberName = "The field: '" + fi.Name; Type fieldType = fi.FieldType; string fieldFriendlyName = AutoPropertyName(fi); - if (string.IsNullOrEmpty(fieldFriendlyName)==false) - { + if (string.IsNullOrEmpty(fieldFriendlyName) == false) + { var prop = fi.DeclaringType.GetProperty(fieldFriendlyName); if (prop != null) { @@ -200,67 +200,82 @@ public static FieldBase CreateField(FieldInfo fi, TypedRecordAttribute recordAtt } // If ignored, return null #pragma warning disable 612,618 // disable obsolete warning - if (mi.IsDefined(typeof (FieldNotInFileAttribute), true) || - mi.IsDefined(typeof (FieldIgnoredAttribute), true) || - mi.IsDefined(typeof (FieldHiddenAttribute), true)) + if (mi.IsDefined(typeof(FieldNotInFileAttribute), true) || + mi.IsDefined(typeof(FieldIgnoredAttribute), true) || + mi.IsDefined(typeof(FieldHiddenAttribute), true)) #pragma warning restore 612,618 return null; - var attributes = (FieldAttribute[]) mi.GetCustomAttributes(typeof (FieldAttribute), true); + var attributes = (FieldAttribute[])mi.GetCustomAttributes(typeof(FieldAttribute), true); // CHECK USAGE ERRORS !!! // Fixed length record and no attributes at all if (recordAttribute is FixedLengthRecordAttribute && - attributes.Length == 0) { + attributes.Length == 0) + { throw new BadUsageException(memberName + "' must be marked the FieldFixedLength attribute because the record class is marked with FixedLengthRecord."); } - if (attributes.Length > 1) { + if (attributes.Length > 1) + { throw new BadUsageException(memberName + "' has a FieldFixedLength and a FieldDelimiter attribute."); } if (recordAttribute is DelimitedRecordAttribute && - mi.IsDefined(typeof (FieldAlignAttribute), false)) { + mi.IsDefined(typeof(FieldAlignAttribute), false)) + { throw new BadUsageException(memberName + "' can't be marked with FieldAlign attribute, it is only valid for fixed length records and are used only for write purpose."); } if (fieldType.IsArray == false && - mi.IsDefined(typeof (FieldArrayLengthAttribute), false)) { + mi.IsDefined(typeof(FieldArrayLengthAttribute), false)) + { throw new BadUsageException(memberName + "' can't be marked with FieldArrayLength attribute is only valid for array fields."); } // PROCESS IN NORMAL CONDITIONS - if (attributes.Length > 0) { + if (attributes.Length > 0) + { FieldAttribute fieldAttb = attributes[0]; - if (fieldAttb is FieldFixedLengthAttribute) { + if (fieldAttb is FieldFixedLengthAttribute) + { // Fixed Field - if (recordAttribute is DelimitedRecordAttribute) { + if (recordAttribute is DelimitedRecordAttribute) + { throw new BadUsageException(memberName + "' can't be marked with FieldFixedLength attribute, it is only for the FixedLengthRecords not for delimited ones."); } - var attbFixedLength = (FieldFixedLengthAttribute) fieldAttb; + var attbFixedLength = (FieldFixedLengthAttribute)fieldAttb; var attbAlign = Attributes.GetFirst(mi); - res = new FixedLengthField(fi, attbFixedLength.Length, attbAlign); - ((FixedLengthField) res).FixedMode = ((FixedLengthRecordAttribute) recordAttribute).FixedMode; + res = new FixedLengthField(fi, + attbFixedLength.Length, + attbAlign, + recordAttribute.DefaultCultureName); + ((FixedLengthField)res).FixedMode = ((FixedLengthRecordAttribute)recordAttribute).FixedMode; } - else if (fieldAttb is FieldDelimiterAttribute) { + else if (fieldAttb is FieldDelimiterAttribute) + { // Delimited Field - if (recordAttribute is FixedLengthRecordAttribute) { + if (recordAttribute is FixedLengthRecordAttribute) + { throw new BadUsageException(memberName + "' can't be marked with FieldDelimiter attribute, it is only for DelimitedRecords not for fixed ones."); } - res = new DelimitedField(fi, ((FieldDelimiterAttribute) fieldAttb).Delimiter); + res = new DelimitedField(fi, + ((FieldDelimiterAttribute)fieldAttb).Delimiter, + recordAttribute.DefaultCultureName); } - else { + else + { throw new BadUsageException( "Custom field attributes are not currently supported. Unknown attribute: " + fieldAttb.GetType().Name + " on field: " + fi.Name); @@ -272,35 +287,41 @@ public static FieldBase CreateField(FieldInfo fi, TypedRecordAttribute recordAtt if (delimitedRecordAttribute != null) { - res = new DelimitedField(fi, delimitedRecordAttribute.Separator); + res = new DelimitedField(fi, + delimitedRecordAttribute.Separator, + recordAttribute.DefaultCultureName); } } - if (res != null) { + if (res != null) + { // FieldDiscarded - res.Discarded = mi.IsDefined(typeof (FieldValueDiscardedAttribute), false); + res.Discarded = mi.IsDefined(typeof(FieldValueDiscardedAttribute), false); // FieldTrim Attributes.WorkWithFirst(mi, - (x) => { + (x) => + { res.TrimMode = x.TrimMode; res.TrimChars = x.TrimChars; }); // FieldQuoted Attributes.WorkWithFirst(mi, - (x) => { - if (res is FixedLengthField) { + (x) => + { + if (res is FixedLengthField) + { throw new BadUsageException( memberName + "' can't be marked with FieldQuoted attribute, it is only for the delimited records."); } - ((DelimitedField) res).QuoteChar = + ((DelimitedField)res).QuoteChar = x.QuoteChar; - ((DelimitedField) res).QuoteMode = + ((DelimitedField)res).QuoteMode = x.QuoteMode; - ((DelimitedField) res).QuoteMultiline = + ((DelimitedField)res).QuoteMultiline = x.QuoteMultiline; }); @@ -320,7 +341,8 @@ public static FieldBase CreateField(FieldInfo fi, TypedRecordAttribute recordAtt res.IsNotEmpty = mi.IsDefined(typeof(FieldNotEmptyAttribute), false); // FieldArrayLength - if (fieldType.IsArray) { + if (fieldType.IsArray) + { res.IsArray = true; res.ArrayType = fieldType.GetElementType(); @@ -329,13 +351,15 @@ public static FieldBase CreateField(FieldInfo fi, TypedRecordAttribute recordAtt res.ArrayMaxLength = int.MaxValue; Attributes.WorkWithFirst(mi, - (x) => { + (x) => + { res.ArrayMinLength = x.MinLength; res.ArrayMaxLength = x.MaxLength; if (res.ArrayMaxLength < res.ArrayMinLength || res.ArrayMinLength < 0 || - res.ArrayMaxLength <= 0) { + res.ArrayMaxLength <= 0) + { throw new BadUsageException(memberName + " has invalid length values in the [FieldArrayLength] attribute."); } @@ -360,8 +384,9 @@ internal static string AutoPropertyName(FieldInfo fi) fi.Name.StartsWith("<") && fi.Name.Contains(">")) return fi.Name.Substring(1, fi.Name.IndexOf(">") - 1); - + } + return ""; } @@ -390,10 +415,11 @@ internal FieldBase() /// Verify the settings against the actual field to ensure it will work. /// /// Field Info Object - internal FieldBase(FieldInfo fi) + /// Default culture name used for each properties if no converter is specified otherwise. If null, the default decimal separator (".") will be used. + internal FieldBase(FieldInfo fi, string defaultCultureName = null) : this() { - + FieldInfo = fi; FieldType = FieldInfo.FieldType; MemberInfo attibuteTarget = fi; @@ -416,28 +442,34 @@ internal FieldBase(FieldInfo fi) else FieldTypeInternal = FieldType; - IsStringField = FieldTypeInternal == typeof (string); + IsStringField = FieldTypeInternal == typeof(string); - object[] attribs = attibuteTarget.GetCustomAttributes(typeof (FieldConverterAttribute), true); + object[] attribs = attibuteTarget.GetCustomAttributes(typeof(FieldConverterAttribute), true); - if (attribs.Length > 0) { - var conv = (FieldConverterAttribute) attribs[0]; + if (attribs.Length > 0) + { + var conv = (FieldConverterAttribute)attribs[0]; Converter = conv.Converter; conv.ValidateTypes(FieldInfo); } else - Converter = ConvertHelpers.GetDefaultConverter(FieldFriendlyName ?? fi.Name, FieldType); + Converter = ConvertHelpers.GetDefaultConverter(FieldFriendlyName ?? fi.Name, + FieldType, + defaultCultureName: defaultCultureName); if (Converter != null) Converter.mDestinationType = FieldTypeInternal; - attribs = attibuteTarget.GetCustomAttributes(typeof (FieldNullValueAttribute), true); + attribs = attibuteTarget.GetCustomAttributes(typeof(FieldNullValueAttribute), true); - if (attribs.Length > 0) { - NullValue = ((FieldNullValueAttribute) attribs[0]).NullValue; + if (attribs.Length > 0) + { + NullValue = ((FieldNullValueAttribute)attribs[0]).NullValue; - if (NullValue != null) { - if (!FieldTypeInternal.IsAssignableFrom(NullValue.GetType())) { + if (NullValue != null) + { + if (!FieldTypeInternal.IsAssignableFrom(NullValue.GetType())) + { throw new BadUsageException("The NullValue is of type: " + NullValue.GetType().Name + " that is not asignable to the field " + FieldInfo.Name + " of type: " + @@ -448,7 +480,7 @@ internal FieldBase(FieldInfo fi) IsNullableType = FieldTypeInternal.IsValueType && FieldTypeInternal.IsGenericType && - FieldTypeInternal.GetGenericTypeDefinition() == typeof (Nullable<>); + FieldTypeInternal.GetGenericTypeDefinition() == typeof(Nullable<>); } #endregion @@ -478,7 +510,8 @@ internal FieldBase(FieldInfo fi) /// String representation of field internal string CreateFieldString(object fieldValue) { - if (Converter == null) { + if (Converter == null) + { if (fieldValue == null) return string.Empty; else @@ -501,9 +534,11 @@ internal object ExtractFieldValue(LineInfo line) { //-> extract only what I need - if (InNewLine) { + if (InNewLine) + { // Any trailing characters, terminate - if (line.EmptyFromPos() == false) { + if (line.EmptyFromPos() == false) + { throw new BadUsageException(line, "Text '" + line.CurrentString + "' found before the new line of the field: " + FieldInfo.Name + @@ -512,14 +547,16 @@ internal object ExtractFieldValue(LineInfo line) line.ReLoad(line.mReader.ReadNextLine()); - if (line.mLineStr == null) { + if (line.mLineStr == null) + { throw new BadUsageException(line, "End of stream found parsing the field " + FieldInfo.Name + ". Please check the class record."); } } - if (IsArray == false) { + if (IsArray == false) + { ExtractedInfo info = ExtractFieldString(line); if (info.mCustomExtractedString == null) line.mCurrentPos = info.ExtractedTo + 1; @@ -531,7 +568,8 @@ internal object ExtractFieldValue(LineInfo line) else return AssignFromString(info, line).Value; } - else { + else + { if (ArrayMinLength <= 0) ArrayMinLength = 0; @@ -540,14 +578,16 @@ internal object ExtractFieldValue(LineInfo line) var res = new ArrayList(Math.Max(ArrayMinLength, 10)); while (line.mCurrentPos - CharsToDiscard < line.mLineStr.Length && - i < ArrayMaxLength) { + i < ArrayMaxLength) + { ExtractedInfo info = ExtractFieldString(line); if (info.mCustomExtractedString == null) line.mCurrentPos = info.ExtractedTo + 1; line.mCurrentPos += CharsToDiscard; - try { + try + { var value = AssignFromString(info, line); if (value.NullValueUsed && @@ -557,16 +597,19 @@ internal object ExtractFieldValue(LineInfo line) res.Add(value.Value); } - catch (NullValueNotFoundException) { + catch (NullValueNotFoundException) + { if (i == 0) break; else throw; } + i++; } - if (res.Count < ArrayMinLength) { + if (res.Count < ArrayMinLength) + { throw new InvalidOperationException( string.Format( "Line: {0} Column: {1} Field: {2}. The array has only {3} values, less than the minimum length of {4}", @@ -576,7 +619,8 @@ internal object ExtractFieldValue(LineInfo line) res.Count, ArrayMinLength)); } - else if (IsLast && line.IsEOL() == false) { + else if (IsLast && line.IsEOL() == false) + { throw new InvalidOperationException( string.Format( "Line: {0} Column: {1} Field: {2}. The array has more values than the maximum length of {3}", @@ -613,18 +657,25 @@ private AssignResult AssignFromString(ExtractedInfo fieldString, LineInfo line) { var extractedString = fieldString.ExtractedString(); - try { + try + { object val; - if (IsNotEmpty && String.IsNullOrEmpty(extractedString)) { + if (IsNotEmpty && String.IsNullOrEmpty(extractedString)) + { throw new InvalidOperationException("The value is empty and must be populated."); - } else if (Converter == null) { + } + else if (Converter == null) + { if (IsStringField) val = TrimString(extractedString); - else { + else + { extractedString = extractedString.Trim(); - if (extractedString.Length == 0) { - return new AssignResult { + if (extractedString.Length == 0) + { + return new AssignResult + { Value = GetNullValue(line), NullValueUsed = true }; @@ -633,24 +684,30 @@ private AssignResult AssignFromString(ExtractedInfo fieldString, LineInfo line) val = Convert.ChangeType(extractedString, FieldTypeInternal, null); } } - else { + else + { var trimmedString = extractedString.Trim(); if (Converter.CustomNullHandling == false && - trimmedString.Length == 0) { - return new AssignResult { + trimmedString.Length == 0) + { + return new AssignResult + { Value = GetNullValue(line), NullValueUsed = true }; } - else { + else + { if (TrimMode == TrimMode.Both) val = Converter.StringToField(trimmedString); else val = Converter.StringToField(TrimString(extractedString)); - if (val == null) { - return new AssignResult { + if (val == null) + { + return new AssignResult + { Value = GetNullValue(line), NullValueUsed = true }; @@ -658,22 +715,27 @@ private AssignResult AssignFromString(ExtractedInfo fieldString, LineInfo line) } } - return new AssignResult { + return new AssignResult + { Value = val }; } - catch (ConvertException ex) { + catch (ConvertException ex) + { ex.FieldName = FieldInfo.Name; ex.LineNumber = line.mReader.LineNumber; ex.ColumnNumber = fieldString.ExtractedFrom + 1; throw; } - catch (BadUsageException) { + catch (BadUsageException) + { throw; } - catch (Exception ex) { + catch (Exception ex) + { if (Converter == null || - Converter.GetType().Assembly == typeof (FieldBase).Assembly) { + Converter.GetType().Assembly == typeof(FieldBase).Assembly) + { throw new ConvertException(extractedString, FieldTypeInternal, FieldInfo.Name, @@ -682,7 +744,8 @@ private AssignResult AssignFromString(ExtractedInfo fieldString, LineInfo line) ex.Message, ex); } - else { + else + { throw new ConvertException(extractedString, FieldTypeInternal, FieldInfo.Name, @@ -697,7 +760,8 @@ private AssignResult AssignFromString(ExtractedInfo fieldString, LineInfo line) private String TrimString(string extractedString) { - switch (TrimMode) { + switch (TrimMode) + { case TrimMode.None: return extractedString; @@ -723,8 +787,10 @@ private String TrimString(string extractedString) /// Null value for object private object GetNullValue(LineInfo line) { - if (NullValue == null) { - if (FieldTypeInternal.IsValueType) { + if (NullValue == null) + { + if (FieldTypeInternal.IsValueType) + { if (IsNullableType) return null; @@ -748,8 +814,10 @@ private object GetNullValue(LineInfo line) /// null value of discard? private object GetDiscardedNullValue() { - if (NullValue == null) { - if (FieldTypeInternal.IsValueType) { + if (NullValue == null) + { + if (FieldTypeInternal.IsValueType) + { if (IsNullableType) return null; @@ -780,10 +848,13 @@ public object CreateValueForField(object fieldValue) { object val; - if (fieldValue == null) { - if (NullValue == null) { + if (fieldValue == null) + { + if (NullValue == null) + { if (FieldTypeInternal.IsValueType && - Nullable.GetUnderlyingType(FieldTypeInternal) == null) { + Nullable.GetUnderlyingType(FieldTypeInternal) == null) + { throw new BadUsageException( "Null Value found. You must specify a FieldNullValueAttribute in the " + FieldInfo.Name + " field of type " + FieldTypeInternal.Name + ", because this is a ValueType."); @@ -796,18 +867,22 @@ public object CreateValueForField(object fieldValue) } else if (FieldTypeInternal == fieldValue.GetType()) val = fieldValue; - else { + else + { if (Converter == null) val = Convert.ChangeType(fieldValue, FieldTypeInternal, null); - else { - try { + else + { + try + { if (Nullable.GetUnderlyingType(FieldTypeInternal) != null && Nullable.GetUnderlyingType(FieldTypeInternal) == fieldValue.GetType()) val = fieldValue; else val = Convert.ChangeType(fieldValue, FieldTypeInternal, null); } - catch { + catch + { val = Converter.StringToField(fieldValue.ToString()); } } @@ -833,9 +908,12 @@ internal void AssignToString(StringBuilder sb, object fieldValue) if (InNewLine) sb.Append(StringHelper.NewLine); - if (IsArray) { - if (fieldValue == null) { - if (0 < ArrayMinLength) { + if (IsArray) + { + if (fieldValue == null) + { + if (0 < ArrayMinLength) + { throw new InvalidOperationException( string.Format("Field: {0}. The array is null, but the minimum length is {1}", FieldInfo.Name, @@ -845,9 +923,10 @@ internal void AssignToString(StringBuilder sb, object fieldValue) return; } - var array = (IList) fieldValue; + var array = (IList)fieldValue; - if (array.Count < ArrayMinLength) { + if (array.Count < ArrayMinLength) + { throw new InvalidOperationException( string.Format("Field: {0}. The array has {1} values, but the minimum length is {2}", FieldInfo.Name, @@ -855,7 +934,8 @@ internal void AssignToString(StringBuilder sb, object fieldValue) ArrayMinLength)); } - if (array.Count > ArrayMaxLength) { + if (array.Count > ArrayMaxLength) + { throw new InvalidOperationException( string.Format("Field: {0}. The array has {1} values, but the maximum length is {2}", FieldInfo.Name, @@ -863,7 +943,8 @@ internal void AssignToString(StringBuilder sb, object fieldValue) ArrayMaxLength)); } - for (int i = 0; i < array.Count; i++) { + for (int i = 0; i < array.Count; i++) + { object val = array[i]; CreateFieldString(sb, val, IsLast && i == array.Count - 1); } diff --git a/FileHelpers/Fields/FixedLengthField.cs b/FileHelpers/Fields/FixedLengthField.cs index 9312924fc..5b6b73470 100644 --- a/FileHelpers/Fields/FixedLengthField.cs +++ b/FileHelpers/Fields/FixedLengthField.cs @@ -42,8 +42,9 @@ private FixedLengthField() {} /// Field definitions /// Length of this field /// Alignment, left or right - internal FixedLengthField(FieldInfo fi, int length, FieldAlignAttribute align) - : base(fi) + /// Default culture name used for each properties if no converter is specified otherwise. If null, the default decimal separator (".") will be used. + internal FixedLengthField(FieldInfo fi, int length, FieldAlignAttribute align, string defaultCultureName=null) + : base(fi, defaultCultureName) { FixedMode = FixedMode.ExactLength; Align = new FieldAlignAttribute(AlignMode.Left, ' ');