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();
}
}
}
}