// Copyright 2009-2022 Josh Close // This file is a part of CsvHelper and is dual licensed under MS-PL and Apache 2.0. // See LICENSE.txt for details or visit http://www.opensource.org/licenses/ms-pl.html for MS-PL and http://opensource.org/licenses/Apache-2.0 for Apache 2.0. // https://github.com/JoshClose/CsvHelper using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Text; using CsvHelper.Configuration.Attributes; using CsvHelper.Delegates; using CsvHelper.TypeConversion; namespace CsvHelper.Configuration { /// /// Configuration used for reading and writing CSV data. /// public record CsvConfiguration : IReaderConfiguration, IWriterConfiguration { private string newLine = "\r\n"; /// public virtual bool AllowComments { get; set; } /// public virtual BadDataFound BadDataFound { get; set; } = ConfigurationFunctions.BadDataFound; /// public virtual int BufferSize { get; set; } = 0x1000; /// public virtual bool CacheFields { get; set; } /// public virtual char Comment { get; set; } = '#'; /// public virtual bool CountBytes { get; set; } /// public virtual CultureInfo CultureInfo { get; protected set; } /// public virtual string Delimiter { get; set; } /// public virtual bool DetectDelimiter { get; set; } /// public virtual GetDelimiter GetDelimiter { get; set; } = ConfigurationFunctions.GetDelimiter; /// public virtual string[] DetectDelimiterValues { get; set; } = new[] { ",", ";", "|", "\t" }; /// public virtual bool DetectColumnCountChanges { get; set; } /// public virtual IComparer? DynamicPropertySort { get; set; } /// public virtual Encoding Encoding { get; set; } = Encoding.UTF8; /// public virtual char Escape { get; set; } = '"'; /// public virtual bool ExceptionMessagesContainRawData { get; set; } = true; /// public virtual GetConstructor GetConstructor { get; set; } = ConfigurationFunctions.GetConstructor; /// public virtual GetDynamicPropertyName GetDynamicPropertyName { get; set; } = ConfigurationFunctions.GetDynamicPropertyName; /// public virtual bool HasHeaderRecord { get; set; } = true; /// public virtual HeaderValidated HeaderValidated { get; set; } = ConfigurationFunctions.HeaderValidated; /// public virtual bool IgnoreBlankLines { get; set; } = true; /// public virtual bool IgnoreReferences { get; set; } /// public virtual bool IncludePrivateMembers { get; set; } /// public virtual char[] InjectionCharacters { get; set; } = new[] { '=', '@', '+', '-', '\t', '\r' }; /// public virtual char InjectionEscapeCharacter { get; set; } = '\''; /// public virtual InjectionOptions InjectionOptions { get; set; } /// public bool IsNewLineSet { get; private set; } /// public virtual bool LineBreakInQuotedFieldIsBadData { get; set; } /// public double MaxFieldSize { get; set; } /// public virtual MemberTypes MemberTypes { get; set; } = MemberTypes.Properties; /// public virtual MissingFieldFound MissingFieldFound { get; set; } = ConfigurationFunctions.MissingFieldFound; /// public virtual CsvMode Mode { get; set; } /// public virtual string NewLine { get => newLine; set { IsNewLineSet = true; newLine = value; } } /// public virtual PrepareHeaderForMatch PrepareHeaderForMatch { get; set; } = ConfigurationFunctions.PrepareHeaderForMatch; /// public virtual int ProcessFieldBufferSize { get; set; } = 1024; /// public virtual char Quote { get; set; } = '"'; /// public virtual ReadingExceptionOccurred ReadingExceptionOccurred { get; set; } = ConfigurationFunctions.ReadingExceptionOccurred; /// public virtual ReferenceHeaderPrefix? ReferenceHeaderPrefix { get; set; } /// public ShouldQuote ShouldQuote { get; set; } = ConfigurationFunctions.ShouldQuote; /// public virtual ShouldSkipRecord? ShouldSkipRecord { get; set; } /// public virtual ShouldUseConstructorParameters ShouldUseConstructorParameters { get; set; } = ConfigurationFunctions.ShouldUseConstructorParameters; /// public virtual TrimOptions TrimOptions { get; set; } /// public virtual bool UseNewObjectForNullReferenceMembers { get; set; } = true; /// public virtual char[] WhiteSpaceChars { get; set; } = new char[] { ' ' }; /// /// Initializes a new instance of the class /// using the given . Since /// uses for it's default, the given /// will be used instead. /// /// The culture information. /// The type that contains the configuration attributes. /// This will call automatically. public CsvConfiguration(CultureInfo cultureInfo, Type? attributesType = null) { CultureInfo = cultureInfo; Delimiter = cultureInfo.TextInfo.ListSeparator; if (attributesType != null) { ApplyAttributes(attributesType); } } /// /// Validates the configuration. /// public void Validate() { var escape = Escape.ToString(); var quote = Quote.ToString(); var lineEndings = new[] { "\r", "\n", "\r\n" }; var whiteSpaceChars = WhiteSpaceChars.Select(c => c.ToString()).ToArray(); // Escape if (escape == Delimiter) throw new ConfigurationException($"The escape character '{Escape}' and delimiter '{Delimiter}' cannot be the same."); if (escape == NewLine && IsNewLineSet) throw new ConfigurationException($"The escape character '{Escape}' and new line '{NewLine}' cannot be the same."); if (lineEndings.Contains(Escape.ToString()) && !IsNewLineSet) throw new ConfigurationException($"The escape character '{Escape}' cannot be a line ending. ('\\r', '\\n', '\\r\\n')"); if (whiteSpaceChars.Contains(escape)) throw new ConfigurationException($"The escape character '{Escape}' cannot be a WhiteSpaceChar."); // Quote if (quote == Delimiter) throw new ConfigurationException($"The quote character '{Quote}' and the delimiter '{Delimiter}' cannot be the same."); if (quote == NewLine && IsNewLineSet) throw new ConfigurationException($"The quote character '{Quote}' and new line '{NewLine}' cannot be the same."); if (lineEndings.Contains(quote)) throw new ConfigurationException($"The quote character '{Quote}' cannot be a line ending. ('\\r', '\\n', '\\r\\n')"); if (whiteSpaceChars.Contains(quote)) throw new ConfigurationException($"The quote character '{Quote}' cannot be a WhiteSpaceChar."); // Delimiter if (Delimiter == NewLine && IsNewLineSet) throw new ConfigurationException($"The delimiter '{Delimiter}' and new line '{NewLine}' cannot be the same."); if (lineEndings.Contains(Delimiter)) throw new ConfigurationException($"The delimiter '{Delimiter}' cannot be a line ending. ('\\r', '\\n', '\\r\\n')"); if (whiteSpaceChars.Contains(Delimiter)) throw new ConfigurationException($"The delimiter '{Delimiter}' cannot be a WhiteSpaceChar."); // Detect Delimiter if (DetectDelimiter && DetectDelimiterValues.Length == 0) throw new ConfigurationException($"At least one value is required for {nameof(DetectDelimiterValues)} when {nameof(DetectDelimiter)} is enabled."); } /// /// Applies class level attribute to configuration. /// /// Type with attributes. public CsvConfiguration ApplyAttributes() { return ApplyAttributes(typeof(T)); } /// /// Applies class level attribute to configuration. /// /// Type with attributes. public CsvConfiguration ApplyAttributes(Type type) { var attributes = type.GetCustomAttributes().OfType(); foreach (var attribute in attributes) { attribute.ApplyTo(this); } return this; } } }