/// <summary> /// Can be called multiple times. Other methods calls must be after this initialization. /// csvDb is used for queries resolving. /// </summary> /// <param name="map"></param> /// <param name="tags"></param> /// <param name="csvDb"></param> public void Initialize(CaseInsensitiveDictionary <List <string?> > map, CaseInsensitiveDictionary <List <string?> > tags, CsvDb?csvDb = null) { Map = map; Tags = tags; _csvDb = csvDb; var values = Map.TryGetValue("%(GenericTag)"); if (values is not null && values.Count > 1 && !String.IsNullOrEmpty(values[1])) { GenericTag = values[1] ?? @""; } values = Map.TryGetValue("%(TagTypeSeparator)"); if (values is not null && values.Count > 1 && !String.IsNullOrEmpty(values[1])) { TagTypeSeparator = values[1] ?? @""; } values = Map.TryGetValue("%(TagAndPropertySeparator)"); if (values is not null && values.Count > 1 && !String.IsNullOrEmpty(values[1])) { TagAndPropertySeparator = values[1] ?? @""; } }
/// <summary> /// result != null /// </summary> public static CaseInsensitiveDictionary <string> Parse(string nameValueCollectionString) { var result = new CaseInsensitiveDictionary <string>(); if (String.IsNullOrEmpty(nameValueCollectionString)) { return(result); } int l = nameValueCollectionString.Length; int i = 0; while (i < l) { // find next & while noting first = on the way (and if there are more) int si = i; int ti = -1; while (i < l) { char ch = nameValueCollectionString[i]; if (ch == '=') { if (ti < 0) { ti = i; } } else if (ch == '&') { break; } i++; } // extract the name / value pair if (ti >= 0) // has '=' char { string name = nameValueCollectionString.Substring(si, ti - si); string value = nameValueCollectionString.Substring(ti + 1, i - ti - 1); // add name / value pair to the collection result[UrlDecode(name)] = UrlDecode(value); } // trailing '&' if (i == l - 1 && nameValueCollectionString[i] == '&') { result[String.Empty] = null; } i++; } return(result); }
/// <summary> /// Saves data to file. /// </summary> /// <param name="fileFullName"></param> /// <param name="fileData"></param> public static void SaveCsvFile(string fileFullName, CaseInsensitiveDictionary <List <string?> > fileData) { using (var writer = new StreamWriter(File.Create(fileFullName), new UTF8Encoding(true))) { foreach (var fileLine in fileData.OrderBy(kvp => kvp.Key)) { writer.WriteLine(FormatForCsv(",", fileLine.Value)); } } }
/// <summary> /// </summary> /// <param name="obj"></param> /// <param name="nameValueCollection"></param> public static void SetNameValueCollection(object obj, CaseInsensitiveDictionary <string> nameValueCollection) { if (obj is null || nameValueCollection is null) { return; } foreach (var kvp in nameValueCollection) { obj.SetPropertyValue(kvp.Key, kvp.Value); } }
/// <summary> /// </summary> /// <param name="obj"></param> /// <param name="nameValueCollection"></param> public static void SetNameValueCollection(ref object obj, CaseInsensitiveDictionary <string?>?nameValueCollection) { if (nameValueCollection is null) { return; } foreach (var kvp in nameValueCollection) { ObjectHelper.SetPropertyValue(obj, kvp.Key, kvp.Value); } }
/// <summary> /// </summary> /// <param name="obj"></param> /// <returns></returns> public static CaseInsensitiveDictionary <string> GetNameValueCollection(object obj) { if (obj is null) { return(null); } var result = new CaseInsensitiveDictionary <string>(); PropertyInfo[] props = obj.GetType().GetProperties(); foreach (PropertyInfo prop in props) { if (!prop.CanRead || !prop.CanWrite) { continue; } var designerSerializationVisibilityAttribute = prop.GetCustomAttributes(typeof(DesignerSerializationVisibilityAttribute), true) .OfType <DesignerSerializationVisibilityAttribute>().FirstOrDefault(); if (designerSerializationVisibilityAttribute != null && designerSerializationVisibilityAttribute.Visibility == DesignerSerializationVisibility.Hidden) { continue; } object propValue = prop.GetValue(obj, null); var defaultValueAttribute = prop.GetCustomAttributes(typeof(DefaultValueAttribute), true) .OfType <DefaultValueAttribute>().FirstOrDefault(); if (defaultValueAttribute != null) { if (Equals(defaultValueAttribute.Value, propValue)) { continue; } } if (propValue != null) { result[prop.Name] = Any.ConvertTo <string>(propValue, false); } else { result[prop.Name] = null; } } return(result); }
/// <summary> /// result != null /// </summary> /// <param name="nameValueCollection"></param> /// <returns></returns> public static string GetNameValueCollectionString(CaseInsensitiveDictionary <string> nameValueCollection) { if (nameValueCollection is null || nameValueCollection.Count == 0) { return(""); } var items = new List <string>(); foreach (var kvp in nameValueCollection.OrderBy(i => i.Key)) { items.Add(UrlEncode(kvp.Key) + @"=" + UrlEncode(kvp.Value)); } return(String.Join("&", items)); }
public static string?GetValue(CaseInsensitiveDictionary <List <string?> > data, string key, int column) { if (column < 0) { return(null); } if (!data.TryGetValue(key, out List <string?>?fileLine)) { return(null); } if (column >= fileLine.Count) { return(null); } return(fileLine[column]); }
/// <summary> /// First column in file is Key and must be unique. /// Can contain include directives, defines and comments. /// If file does not exist returns empty result. /// All fields values are trimmed. /// result != null, lists != null /// </summary> /// <param name="fileFullName"></param> /// <param name="includeFiles"></param> /// <param name="defines"></param> /// <returns></returns> public static CaseInsensitiveDictionary <List <string> > ParseCsvFile(string fileFullName, bool includeFiles, Dictionary <Regex, string> defines = null) { var fileData = new CaseInsensitiveDictionary <List <string> >(); if (File.Exists(fileFullName)) { if (defines is null) { defines = new Dictionary <Regex, string>(); } string filePath = Path.GetDirectoryName(fileFullName); using (var reader = new StreamReader(fileFullName, true)) { string line = ""; string l; while ((l = reader.ReadLine()) != null) { l = l.Trim(); if (l.Length > 0 && l[l.Length - 1] == '\\') { line += l.Substring(0, l.Length - 1); continue; } else { line += l; } if (line == "") { continue; } if (includeFiles && StringHelper.StartsWithIgnoreCase(line, @"#include") && line.Length > 8) { var q1 = line.IndexOf('"', 8); if (q1 != -1 && q1 + 1 < line.Length) { var q2 = line.IndexOf('"', q1 + 1); if (q2 != -1 && q2 > q1 + 1) { var includeFileName = line.Substring(q1 + 1, q2 - q1 - 1); foreach (var kvp in ParseCsvFile(filePath + @"\" + includeFileName, false, defines)) { fileData[kvp.Key] = kvp.Value; } } } } else if (StringHelper.StartsWithIgnoreCase(line, @"#define") && line.Length > 7) { int q1 = 7; for (; q1 < line.Length; q1++) { char ch = line[q1]; if (Char.IsWhiteSpace(ch)) { continue; } else { break; } } if (q1 < line.Length) { int q2 = q1 + 1; for (; q2 < line.Length; q2++) { char ch = line[q2]; if (Char.IsWhiteSpace(ch)) { break; } else { continue; } } string define = line.Substring(q1, q2 - q1); string subst = @""; if (q2 < line.Length - 1) { subst = ReplaceDefines(line.Substring(q2 + 1).Trim(), defines); } defines[new Regex(@"\b" + define + @"\b", RegexOptions.IgnoreCase)] = subst; } } else if (line[0] == '#') { // Comment, skip } else { List <string> fields = CsvHelper.ParseCsvLine(@",", ReplaceDefines(line, defines)).ToList(); if (fields.Count > 0) { if (String.IsNullOrEmpty(fields[0])) { if (fields.Count > 1) { fileData[@""] = fields; } } else { fileData[fields[0]] = fields; } } } line = ""; } } } return(fileData); }
/// <summary> /// First column in file is Key and must be unique. /// Can contain include directives, defines and comments. /// If file does not exist, returns empty result. /// userFriendlyLogger: Messages are localized. Priority is Information, Error, Warning. /// includeFileNames: File names in Upper-Case. /// Result: List.Count >= 1, List[0] is not null /// </summary> /// <param name="fileFullName"></param> /// <param name="includeFiles"></param> /// <param name="defines"></param> /// <param name="userFriendlyLogger"></param> /// <param name="includeFileNames"></param> /// <returns></returns> public static CaseInsensitiveDictionary <List <string?> > LoadCsvFile(string fileFullName, bool includeFiles, Dictionary <Regex, string>?defines = null, ILogger?userFriendlyLogger = null, List <string>?includeFileNames = null) { var fileData = new CaseInsensitiveDictionary <List <string?> >(); using (userFriendlyLogger?.BeginScope(Path.GetFileName(fileFullName))) { if (!File.Exists(fileFullName)) { userFriendlyLogger?.LogError(Properties.Resources.CsvHelper_CsvFileDoesNotExist); return(fileData); } try { if (defines is null) { defines = new Dictionary <Regex, string>(); } string?filePath = Path.GetDirectoryName(fileFullName); using (var reader = new StreamReader(fileFullName, true)) { string line = ""; string? l; List <string?>?beginFields = null; bool inQuotes = false; while ((l = reader.ReadLine()) is not null) { l = l.Trim(); if (l.Length > 0 && l[l.Length - 1] == '\\') { if (line != @"") { if (StringHelper.StartsWithIgnoreCase(line, @"#define")) { line += l.Substring(0, l.Length - 1); continue; } } else { if (StringHelper.StartsWithIgnoreCase(l, @"#define")) { line = l.Substring(0, l.Length - 1); continue; } } } line += l; if (line == "") { continue; } List <string?> fields; if (beginFields is null) { if (includeFiles && StringHelper.StartsWithIgnoreCase(line, @"#include") && line.Length > 8) { var q1 = line.IndexOf('"', 8); if (q1 != -1 && q1 + 1 < line.Length) { var q2 = line.IndexOf('"', q1 + 1); if (q2 != -1 && q2 > q1 + 1) { var includeFileName = line.Substring(q1 + 1, q2 - q1 - 1); if (includeFileNames is not null) { includeFileNames.Add(includeFileName.ToUpperInvariant()); } foreach (var kvp in LoadCsvFile(filePath + @"\" + includeFileName, false, defines, userFriendlyLogger)) { if (fileData.ContainsKey(kvp.Key)) { userFriendlyLogger?.LogError(Properties.Resources.CsvHelper_CsvFileDuplicateKey + ", Key='" + kvp.Key + "'"); } fileData[kvp.Key] = kvp.Value; } } } line = ""; continue; } if (StringHelper.StartsWithIgnoreCase(line, @"#define") && line.Length > 7) { int q1 = 7; for (; q1 < line.Length; q1++) { char ch = line[q1]; if (Char.IsWhiteSpace(ch)) { continue; } else { break; } } if (q1 < line.Length) { int q2 = q1 + 1; for (; q2 < line.Length; q2++) { char ch = line[q2]; if (Char.IsWhiteSpace(ch)) { break; } else { continue; } } string define = line.Substring(q1, q2 - q1); string subst = @""; if (q2 < line.Length - 1) { subst = ReplaceDefines(line.Substring(q2 + 1).Trim(), defines); } defines[new Regex(@"\b" + define + @"\b", RegexOptions.IgnoreCase)] = subst; } line = ""; continue; } if (line[0] == '#') { // Comment, skip line = ""; continue; } fields = ParseCsvLineInternal(@",", ReplaceDefines(line, defines), ref inQuotes); if (inQuotes) { beginFields = fields; line = ""; continue; } } else { fields = ParseCsvLineInternal(@",", ReplaceDefines(line, defines), ref inQuotes); beginFields[beginFields.Count - 1] = beginFields[beginFields.Count - 1] + '\n' + fields[0]; beginFields.AddRange(fields.Skip(1)); if (inQuotes) { line = ""; continue; } fields = beginFields; beginFields = null; } string?field0 = fields[0]; if (field0 is null) { fields[0] = @""; field0 = @""; } if (field0 == @"") { if (!fields.All(f => String.IsNullOrEmpty(f))) { if (fileData.ContainsKey(@"")) { userFriendlyLogger?.LogError(Properties.Resources.CsvHelper_CsvFileDuplicateKey + ", Key=''"); } fileData[@""] = fields; } } else { if (fileData.ContainsKey(field0)) { userFriendlyLogger?.LogError(Properties.Resources.CsvHelper_CsvFileDuplicateKey + ", Key='" + field0 + "'"); } fileData[field0] = fields; } line = ""; } } } catch (Exception ex) { userFriendlyLogger?.LogError(ex, Properties.Resources.CsvHelper_CsvFileReadingError); } } return(fileData); }
/// <summary> /// /// </summary> /// <param name="nameValueCollectionString"></param> /// <returns></returns> public static CaseInsensitiveDictionary <string?> Parse(string?nameValueCollectionString) { var result = new CaseInsensitiveDictionary <string?>(); if (nameValueCollectionString is null || nameValueCollectionString == @"") { return(result); } int length = nameValueCollectionString.Length; int i = 0; while (i <= length) { // find next & while noting first = on the way (and if there are more) int nameValueBeginIndex = i; int equalsCharIndex = -1; while (i <= length) { char ch; if (i < length) { ch = nameValueCollectionString[i]; } else { ch = default(char); } if (ch == '=') { if (equalsCharIndex < 0) { equalsCharIndex = i; } } else if (ch == '&' || ch == default(char)) { break; } i++; } // extract the name / value pair if (equalsCharIndex >= 0) // has '=' char { string name = nameValueCollectionString.Substring(nameValueBeginIndex, equalsCharIndex - nameValueBeginIndex); string value = nameValueCollectionString.Substring(equalsCharIndex + 1, i - equalsCharIndex - 1); // add name / value pair to the collection result[UrlDecode(name)] = UrlDecode(value); } else { string name = nameValueCollectionString.Substring(nameValueBeginIndex, i - nameValueBeginIndex); result[UrlDecode(name)] = null; } i++; } return(result); }