using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace NewXp.IniApi { /// /// Represents a property in an INI file. /// public class IniProperty { /// /// Property name (key). /// public string Name { get; set; } /// /// Property value. /// public string Value { get; set; } /// /// Set the comment to display above this property. /// public string Comment { get; set; } } /// /// Represents a section in an INI file. /// public class IniSection { private readonly IDictionary _properties; /// /// Section name. /// public string Name { get; set; } /// /// Set the comment to display above this section. /// public string Comment { get; set; } /// /// Get the properties in this section. /// public IniProperty[] Properties => _properties.Values.ToArray(); /// /// Create a new IniSection. /// /// public IniSection(string name) { Name = name; _properties = new Dictionary(); } /// /// Get a property value. /// /// Name of the property. /// Value of the property or null if it doesn't exist. public string Get(string name) { if (_properties.ContainsKey(name)) return _properties[name].Value; return null; } /// /// Get a property value, coercing the type of the value /// into the type given by the generic parameter. /// /// Name of the property. /// The type to coerce the value into. /// public T Get(string name) { if (_properties.ContainsKey(name)) return (T)Convert.ChangeType(_properties[name].Value, typeof(T)); return default(T); } /// /// Set a property value. /// /// Name of the property. /// Value of the property. /// A comment to display above the property. public void Set(string name, string value, string comment = null) { if (string.IsNullOrWhiteSpace(value)) { RemoveProperty(name); return; } if (!_properties.ContainsKey(name)) _properties.Add(name, new IniProperty { Name = name, Value = value, Comment = comment }); else { _properties[name].Value = value; if (comment != null) _properties[name].Comment = comment; } } /// /// Remove a property from this section. /// /// The property name to remove. public void RemoveProperty(string propertyName) { if (_properties.ContainsKey(propertyName)) _properties.Remove(propertyName); } } /// /// Represenst an INI file that can be read from or written to. /// public class IniFile { private readonly IDictionary _sections; /// /// If True, writes extra spacing between the property name and the property value. /// (foo=bar) vs (foo = bar) /// public bool WriteSpacingBetweenNameAndValue { get; set; } /// /// The character a comment line will begin with. Default '#'. /// public char CommentChar { get; set; } /// /// Get the sections in this IniFile. /// public IniSection[] Sections => _sections.Values.ToArray(); /// /// Create a new IniFile instance. /// public IniFile() { _sections = new Dictionary(); CommentChar = '#'; } /// /// Load an INI file from the file system. /// /// Path to the INI file. public IniFile(string path) : this() { Load(path); } /// /// Load an INI file. /// /// A TextReader instance. public IniFile(TextReader reader) : this() { Load(reader); } private void Load(string path) { using (var file = new StreamReader(path)) Load(file); } private void Load(TextReader reader) { IniSection section = null; string line; while ((line = reader.ReadLine()) != null) { line = line.Trim(); // skip empty lines if (line == string.Empty) continue; // skip comments if (line.StartsWith(";") || line.StartsWith("#")) continue; if (line.StartsWith("[") && line.EndsWith("]")) { var sectionName = line.Substring(1, line.Length - 2); if (!_sections.ContainsKey(sectionName)) { section = new IniSection(sectionName); _sections.Add(sectionName, section); } continue; } if (section != null) { var keyValue = line.Split(new[] { "=" }, 2, StringSplitOptions.RemoveEmptyEntries); if (keyValue.Length != 2) continue; section.Set(keyValue[0].Trim(), keyValue[1].Trim()); } } } /// /// Get a section by name. If the section doesn't exist, it is created. /// /// The name of the section. /// A section. If the section doesn't exist, it is created. public IniSection Section(string sectionName) { IniSection section; if (!_sections.TryGetValue(sectionName, out section)) { section = new IniSection(sectionName); _sections.Add(sectionName, section); } return section; } /// /// Remove a section. /// /// Name of the section to remove. public void RemoveSection(string sectionName) { if (_sections.ContainsKey(sectionName)) _sections.Remove(sectionName); } /// /// Create a new INI file. /// /// Path to the INI file to create. public void Save(string path) { using (var file = new StreamWriter(path)) Save(file); } /// /// Create a new INI file. /// /// A TextWriter instance. public void Save(TextWriter writer) { foreach (var section in _sections.Values) { if (section.Properties.Length == 0) continue; if (section.Comment != null) writer.WriteLine($"{CommentChar} {section.Comment}"); writer.WriteLine($"[{section.Name}]"); foreach (var property in section.Properties) { if (property.Comment != null) writer.WriteLine($"{CommentChar} {property.Comment}"); var format = WriteSpacingBetweenNameAndValue ? "{0} = {1}" : "{0}={1}"; writer.WriteLine(format, property.Name, property.Value); } writer.WriteLine(); } } /// /// Returns the content of this INI file as a string. /// /// The text content of this INI file. public override string ToString() { using (var sw = new StringWriter()) { Save(sw); return sw.ToString(); } } } }