using System; using System.Collections.Generic; namespace Cthangover.Tools.Services { /// /// Reading and changing one key of a .properties text the way /// LocaleLoader reads it: key = value per line, # comments, /// no escapes and no multi-line values (a line break in a value becomes a /// space). Everything else in the text — comments, order, other keys, the /// line endings — stays byte for byte, so an editor save shows up in a diff /// as the one changed line. /// public static class PropertiesText { /// The value of , or null when the text does not declare it (the last line wins, as in the loader). public static string Get(string text, string key) { string found = null; foreach (var line in Lines(text)) { if (KeyOf(line) == key) found = line.Substring(line.IndexOf('=') + 1).Trim(); } return found; } /// All keys declared in the text. public static HashSet Keys(string text) { var keys = new HashSet(StringComparer.Ordinal); foreach (var line in Lines(text)) { var key = KeyOf(line); if (key != null) keys.Add(key); } return keys; } /// /// The text with set to : every /// line of the key is rewritten in place, or a key = value line is /// appended (after a line break the text may lack). Line endings follow the text. /// public static string Set(string text, string key, string value) { text ??= string.Empty; value = (value ?? string.Empty).Replace("\r", " ").Replace("\n", " ").Trim(); var newline = text.Contains("\r\n") ? "\r\n" : "\n"; var lines = text.Split('\n'); var replaced = false; for (var i = 0; i < lines.Length; i++) { var hasCr = lines[i].EndsWith("\r"); if (KeyOf(hasCr ? lines[i][..^1] : lines[i]) != key) continue; lines[i] = $"{key} = {value}" + (hasCr ? "\r" : string.Empty); replaced = true; } if (replaced) return string.Join("\n", lines); var head = text.Length == 0 || text.EndsWith("\n") ? text : text + newline; return head + $"{key} = {value}" + newline; } private static IEnumerable Lines(string text) => (text ?? string.Empty).Replace("\r\n", "\n").Split('\n'); private static string KeyOf(string rawLine) { var line = rawLine.Trim(); if (line.Length == 0 || line[0] == '#') return null; var eq = line.IndexOf('='); return eq <= 0 ? null : line.Substring(0, eq).Trim(); } } }