using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace SabreTools.Library.IO
{
public class IniReader : IDisposable
{
///
/// Internal stream reader for inputting
///
private StreamReader sr;
///
/// Get if at end of stream
///
public bool EndOfStream
{
get
{
return sr?.EndOfStream ?? true;
}
}
///
/// Contents of the currently read line as a key value pair
///
public KeyValuePair? KeyValuePair { get; private set; } = null;
///
/// Contents of the current line, unprocessed
///
public string CurrentLine { get; private set; } = string.Empty;
///
/// Get the current line number
///
public long LineNumber { get; private set; } = 0;
///
/// Current row type
///
public IniRowType RowType { get; private set; } = IniRowType.None;
///
/// Current section being read
///
public string Section { get; private set; } = string.Empty;
///
/// Validate that rows are in key=value format
///
public bool ValidateRows { get; set; } = true;
///
/// Constructor for reading from a file
///
public IniReader(string filename)
{
sr = new StreamReader(filename);
}
///
/// Constructor for reading from a stream
///
public IniReader(Stream stream, Encoding encoding)
{
sr = new StreamReader(stream, encoding);
}
///
/// Read the next line in the INI file
///
public bool ReadNextLine()
{
if (!(sr.BaseStream?.CanRead ?? false) || sr.EndOfStream)
return false;
CurrentLine = sr.ReadLine().Trim();
LineNumber++;
ProcessLine();
return true;
}
///
/// Process the current line and extract out values
///
private void ProcessLine()
{
// Comment
if (CurrentLine.StartsWith(";"))
{
KeyValuePair = null;
RowType = IniRowType.Comment;
}
// Section
else if (CurrentLine.StartsWith("[") && CurrentLine.EndsWith("]"))
{
KeyValuePair = null;
RowType = IniRowType.SectionHeader;
Section = CurrentLine.TrimStart('[').TrimEnd(']');
}
// KeyValuePair
else if (CurrentLine.Contains("="))
{
// Split the line by '=' for key-value pairs
string[] data = CurrentLine.Split('=');
// If the value field contains an '=', we need to put them back in
string key = data[0].Trim();
string value = string.Join("=", data.Skip(1)).Trim();
KeyValuePair = new KeyValuePair(key, value);
RowType = IniRowType.KeyValue;
}
// Empty
else if (string.IsNullOrEmpty(CurrentLine))
{
KeyValuePair = null;
CurrentLine = string.Empty;
RowType = IniRowType.None;
}
// Invalid
else
{
KeyValuePair = null;
RowType = IniRowType.Invalid;
if (ValidateRows)
throw new InvalidDataException($"Invalid INI row found, cannot continue: {CurrentLine}");
}
}
///
/// Dispose of the underlying reader
///
public void Dispose()
{
sr.Dispose();
}
}
}