diff --git a/Source/Ini/IniDocument.cs b/Source/Ini/IniDocument.cs index bbb2d42..cfab2e9 100644 --- a/Source/Ini/IniDocument.cs +++ b/Source/Ini/IniDocument.cs @@ -39,6 +39,7 @@ public class IniDocument IniSectionCollection sections = new IniSectionCollection (); ArrayList initialComment = new ArrayList (); IniFileType fileType = IniFileType.Standard; + bool extended = false; #endregion #region Public properties @@ -52,52 +53,108 @@ public IniFileType FileType #region Constructors /// - public IniDocument (string filePath) + public IniDocument(string filePath) { fileType = IniFileType.Standard; - Load (filePath); + Load(filePath); + } + + /// + public IniDocument(string filePath, bool supportExtended) + { + fileType = IniFileType.Standard; + extended = supportExtended; + Load(filePath); + } + + /// + public IniDocument(string filePath, IniFileType type) + { + fileType = type; + Load(filePath); } /// - public IniDocument (string filePath, IniFileType type) + public IniDocument(string filePath, IniFileType type, bool supportExtended) { fileType = type; - Load (filePath); + extended = supportExtended; + Load(filePath); } /// - public IniDocument (TextReader reader) + public IniDocument(TextReader reader) { fileType = IniFileType.Standard; - Load (reader); + Load(reader); + } + + /// + public IniDocument(TextReader reader, bool supportExtended) + { + fileType = IniFileType.Standard; + extended = supportExtended; + Load(reader); } /// - public IniDocument (TextReader reader, IniFileType type) + public IniDocument(TextReader reader, IniFileType type) { fileType = type; - Load (reader); + Load(reader); } - + + /// + public IniDocument(TextReader reader, IniFileType type, bool supportExtended) + { + fileType = type; + extended = supportExtended; + Load(reader); + } + /// - public IniDocument (Stream stream) + public IniDocument(Stream stream) { fileType = IniFileType.Standard; - Load (stream); + Load(stream); + } + + /// + public IniDocument(Stream stream, bool supportExtended) + { + fileType = IniFileType.Standard; + extended = supportExtended; + Load(stream); + } + + /// + public IniDocument(Stream stream, IniFileType type) + { + fileType = type; + Load(stream); } /// - public IniDocument (Stream stream, IniFileType type) + public IniDocument(Stream stream, IniFileType type, bool supportExtended) { fileType = type; - Load (stream); + extended = supportExtended; + Load(stream); + } + + /// + public IniDocument(IniReader reader) + { + fileType = IniFileType.Standard; + Load(reader); } /// - public IniDocument (IniReader reader) + public IniDocument(IniReader reader, bool supportExtended) { fileType = IniFileType.Standard; - Load (reader); + extended = supportExtended; + Load(reader); } /// @@ -232,6 +289,71 @@ private void LoadReader (IniReader reader) // Always close the file reader.Close (); } + + MergeSections(); + } + + /// + /// Supports section merging where a colon is in the key, e.g. ExtendedSection : BaseSection + /// + private void MergeSections() + { + if(!extended) + { + return; + } + + foreach (DictionaryEntry entry in sections) + { + IniSection section = (IniSection)entry.Value; + + if (!section.Name.Contains(":")) + { + continue; + } + + var parts = section.Name.Split(new char[] { ':' }); + var newName = parts[0].TrimEnd(); + var extendedSectionName = parts[1].TrimStart(); + + if (sections[newName] != null) + { + sections.Remove(newName); + } + + if (sections[extendedSectionName] == null) + { + throw new ArgumentException(String.Format("Section {0} is trying to extend section {1} but it does not exist.", newName, extendedSectionName)); + } + + IniSection baseSection = sections[extendedSectionName]; + var baseKeys = baseSection.GetKeys(); + + var newSection = new IniSection(newName, section.Comment); + + for (var i = 0; i < baseSection.ItemCount; i++) + { + var item = baseSection.GetItem(i); + + if (item.Value != null) + { + newSection.Set(item.Name, item.Value, item.Comment); + } + } + + for (var i = 0; i < section.ItemCount; i++) + { + var item = section.GetItem(i); + + if (item.Value != null) + { + newSection.Set(item.Name, item.Value, item.Comment); + } + } + + sections.Add(newSection); + sections.Remove(section.Name); + } } /// diff --git a/Source/Test/Config/ConfigBaseTests.cs b/Source/Test/Config/ConfigBaseTests.cs index 9dbb31a..62e501f 100644 --- a/Source/Test/Config/ConfigBaseTests.cs +++ b/Source/Test/Config/ConfigBaseTests.cs @@ -110,8 +110,11 @@ public void GetFloat () IniConfigSource source = new IniConfigSource (new StringReader (writer.ToString ())); IConfig config = source.Configs["Test"]; - - Assert.AreEqual (494.59, config.GetFloat ("value 1")); + + // Compensate for floating point storage. + var comparison = Math.Abs(494.59 - config.GetFloat("value 1")); + Assert.Less(comparison, 0.0001); + Assert.AreEqual ((float)5656.2853, config.GetFloat ("Not Here", (float)5656.2853)); } diff --git a/Source/Test/Ini/IniDocumentTests.cs b/Source/Test/Ini/IniDocumentTests.cs index 7b9415f..b2cb133 100644 --- a/Source/Test/Ini/IniDocumentTests.cs +++ b/Source/Test/Ini/IniDocumentTests.cs @@ -246,6 +246,41 @@ public void DuplicateSections () Assert.IsNotNull (doc.Sections["Test"].GetValue ("value 1")); } + [Test] + public void ExtendedSections () + { + StringWriter writer = new StringWriter(); + writer.WriteLine("[ExtendedSection : BaseSection]"); + writer.WriteLine("keyB = overwrittenB"); + writer.WriteLine("keyC = valueC"); + writer.WriteLine("[BaseSection]"); + writer.WriteLine("keyA = valueA"); + writer.WriteLine("keyB = valueB"); + + IniDocument doc = new IniDocument(new StringReader(writer.ToString()), true); + + Assert.IsNotNull(doc.Sections["BaseSection"], "BaseSection is null"); + Assert.IsNotNull(doc.Sections["ExtendedSection"], "ExtendedSection is null"); + Assert.IsNull(doc.Sections["ExtendedSection : BaseSection"], "ExtendedSection:BaseSection is not null"); + Assert.AreEqual(2, doc.Sections.Count); + + Assert.AreEqual("valueA", doc.Sections["BaseSection"].GetValue("keyA")); + Assert.AreEqual("valueB", doc.Sections["BaseSection"].GetValue("keyB")); + + Assert.AreEqual("valueC", doc.Sections["ExtendedSection"].GetValue("keyC")); + Assert.AreEqual("overwrittenB", doc.Sections["ExtendedSection"].GetValue("keyB")); + } + + [Test] + [ExpectedException(ExpectedException = typeof(ArgumentException), ExpectedMessage = "Section ExtendedSection is trying to extend section BaseSectionInvalid but it does not exist", MatchType = MessageMatch.Contains)] + public void ExtendedSectionsValidNames() + { + StringWriter writer = new StringWriter(); + writer.WriteLine("[ExtendedSection : BaseSectionInvalid]"); + + IniDocument doc = new IniDocument(new StringReader(writer.ToString()), true); + } + [Test] public void DuplicateKeys () {