using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Developpez.Dotnet.Properties;
namespace Developpez.Dotnet.Configuration
{
///
/// Représente un fichier de configuration de type INI
///
public class IniFile
{
#region Membres privés statiques
private static readonly Regex _regexSection = new Regex(@"\s*\[\s*([\.\w\s]+)\]", RegexOptions.Compiled);
private static readonly Regex _regexKeyValuePair = new Regex(@"^\s*(\w[\s\w]+)=(.*?)(\s*;.*)?$", RegexOptions.Compiled);
#endregion
#region Membres privés
private readonly bool _caseSensitive;
private readonly IDictionary> _sections;
private Encoding _encoding;
#endregion
#region Constructeurs
///
/// Crée une instance vide de IniFile, pour créer dynamiquement
/// le contenu du fichier avant de l'enregistrer.
///
public IniFile()
: this(null, false)
{
}
///
/// Crée une instance vide de IniFile, pour créer dynamiquement
/// le contenu du fichier avant de l'enregistrer.
///
/// Indique si le IniFile doit tenir compte de la casse
public IniFile(bool caseSensitive)
: this(null, caseSensitive)
{
}
///
/// Crée une instance de IniFile en chargeant le fichier spécifié
///
/// Nom du fichier INI à ouvrir
public IniFile(string fileName)
: this(fileName, false)
{
}
///
/// Crée une instance de IniFile en chargeant le fichier spécifié
///
/// Nom du fichier INI à ouvrir
/// Indique si le IniFile doit tenir compte de la casse
public IniFile(string fileName, bool caseSensitive) : this(fileName, caseSensitive, null)
{
}
///
/// Crée une instance de IniFile en chargeant le fichier spécifié
///
/// Nom du fichier INI à ouvrir
/// Encodage à utiliser pour lire le fichier
public IniFile(string fileName, Encoding encoding) : this(fileName, false, encoding)
{
}
///
/// Crée une instance de IniFile en chargeant le fichier spécifié
///
/// Nom du fichier INI à ouvrir
/// Indique si le IniFile doit tenir compte de la casse
/// Encodage à utiliser pour lire le fichier
public IniFile(string fileName, bool caseSensitive, Encoding encoding)
{
_caseSensitive = caseSensitive;
_encoding = encoding;
var comparer = _caseSensitive ? StringComparer.CurrentCulture : StringComparer.CurrentCultureIgnoreCase;
_sections = new Dictionary>(comparer);
if (fileName != null)
{
FileName = fileName;
Reload();
}
}
#endregion
#region Propriétés publiques
///
/// Le nom du fichier
///
public string FileName { get; set; }
///
/// Sections du fichier INI, accessibles sous forme d'un dictionnaire de sections
/// Chaque section est elle-même un dictionnaire de propriétés
///
public IDictionary> Sections
{
get { return _sections; }
}
#endregion
#region Indexeur
///
/// Renvoie la valeur de la propriété spécifiée dans la section spécifiée
///
/// Nom de la section, ou une chaine vide
/// Nom de la propriété
/// La valeur de la propriété demandée, ou null si la
/// propriété n'existe pas dans le fichier
public string this[string section, string key]
{
get { return GetValue(section, key); }
set { SetValue(section, key, value); }
}
#endregion
#region Méthodes publiques
///
/// Charge ou recharge le fichier indiqué par la propriété FileName
///
public void Reload()
{
if (FileName.IsNullOrEmpty())
throw new InvalidOperationException(ExceptionMessages.IniFileNameNotSet);
_sections.Clear();
using (StreamReader rd = new StreamReader(FileName, _encoding ?? Encoding.UTF8, true))
{
string curSection = "";
string line;
Match m;
while ((line = rd.ReadLine()) != null)
{
// Skip comments
if (line.Trim().StartsWith(";"))
continue;
if ((m = _regexSection.Match(line)).Success)
{
curSection = m.Groups[1].Value;
}
else if ((m = _regexKeyValuePair.Match(line)).Success)
{
string key = m.Groups[1].Value;
string value = m.Groups[2].Value;
IDictionary dSection;
if (!Sections.TryGetValue(curSection, out dSection))
dSection = AddSection(curSection);
dSection.Add(key, value);
}
}
_encoding = rd.CurrentEncoding;
}
}
///
/// Ajoute une section au fichier INI
///
/// Nom de la section à ajouter
/// La section créée, sous form d'un dictionnaire
public IDictionary AddSection(string name)
{
Dictionary section;
if (_caseSensitive)
section = new Dictionary();
else
section = new Dictionary(StringComparer.OrdinalIgnoreCase);
Sections.Add(name, section);
return section;
}
///
/// Obtient la valeur de la propriété spécifiée dans la section spécifiée
/// Si la propriété n'est pas dans une section, passer une chaine vide
///
/// Nom de la section, ou une chaine vide
/// Nom de la propriété
/// Valeur par défaut si la propriété n'existe pas
/// a valeur de la propriété demandée, ou defaultValue si la
/// propriété n'existe pas dans le fichier
public string GetValue(string section, string key, string defaultValue)
{
if (section == null) section = "";
IDictionary dSection;
if (Sections.TryGetValue(section, out dSection))
{
string value;
if (dSection.TryGetValue(key, out value))
return value;
return defaultValue;
}
return defaultValue;
}
///
/// Obtient la valeur de la propriété spécifiée dans la section spécifiée
/// Si la propriété n'est pas dans une section, passer une chaine vide
///
/// Nom de la section, ou une chaine vide
/// Nom de la propriété
/// a valeur de la propriété demandée, ou null si la
/// propriété n'existe pas dans le fichier
public string GetValue(string section, string key)
{
return GetValue(section, key, null);
}
///
/// Définit la valeur d'une propriété dans une section donnée.
/// Pour n'indiquer aucune section, passer une chaine vide.
/// Si la section n'existe pas, elle est créée
///
/// Nom de la section, ou une chaine vide
/// Nom de la propriété
/// Valeur de la propriété
public void SetValue(string section, string key, string value)
{
if (section == null)
section = "";
IDictionary dSection;
if (!Sections.TryGetValue(section, out dSection))
dSection = AddSection(section);
dSection[key] = value;
}
///
/// Enregistre le fichier INI sous le chemin indiqué par la propriété FileName.
/// Attention, les commentaires éventuellement présents dans le fichier avant
/// le chargement ne sont pas conservés.
///
public void Save()
{
Save(FileName);
}
///
/// Enregistre le fichier INI sous le chemin indiqué.
/// Attention, les commentaires éventuellement présents dans le fichier avant
/// le chargement ne sont pas conservés.
///
/// Chemin du fichier
public void Save(string fileName)
{
Save(fileName, _encoding);
}
///
/// Enregistre le fichier INI sous le chemin indiqué.
/// Attention, les commentaires éventuellement présents dans le fichier avant
/// le chargement ne sont pas conservés.
///
/// Chemin du fichier
/// Encodage à utiliser pour lire le fichier
public void Save(string fileName, Encoding encoding)
{
if (fileName.IsNullOrEmpty())
throw new InvalidOperationException(ExceptionMessages.IniFileNameNotSet);
_encoding = encoding ?? Encoding.UTF8;
using (StreamWriter wr = new StreamWriter(fileName, false, _encoding))
{
IDictionary noSection;
if (Sections.TryGetValue("", out noSection))
{
foreach (string key in noSection.Keys)
{
wr.WriteLine("{0}={1}", key, Sections[""][key]);
}
wr.WriteLine();
}
foreach (string secName in Sections.Keys)
{
if (secName.Length == 0)
continue;
wr.WriteLine("[{0}]", secName);
foreach (string key in Sections[secName].Keys)
{
wr.WriteLine("{0}={1}", key, Sections[secName][key]);
}
wr.WriteLine();
}
}
}
#endregion
}
}