Configuration file parsing should be complete now (but creation and changing not so much)

git-svn-id: file:///srv/devel/repo-conversion/nusu@303 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2014-07-20 21:30:41 +00:00
parent 7e39bd684f
commit 0753aeb220
3 changed files with 129 additions and 15 deletions

View File

@ -116,7 +116,7 @@ namespace Nuclex.Support.Settings {
if(nameStartIndex >= lastCharacterIndex) { if(nameStartIndex >= lastCharacterIndex) {
return; // No space left for closing brace return; // No space left for closing brace
} }
int nameEndIndex = line.IndexOf(']', nameStartIndex); int nameEndIndex = line.IndexOf(']', nameStartIndex);
if(nameEndIndex == -1) { if(nameEndIndex == -1) {
return; // No closing brace in line return; // No closing brace in line
@ -163,15 +163,10 @@ namespace Nuclex.Support.Settings {
LineIndex = state.Store.lines.Count - 1, LineIndex = state.Store.lines.Count - 1,
OptionName = new StringSegment( OptionName = new StringSegment(
line, firstCharacterIndex, nameEndIndex - firstCharacterIndex + 1 line, firstCharacterIndex, nameEndIndex - firstCharacterIndex + 1
), )
}; };
// If there is a value in this assignment, parse it too parseOptionValue(option, line, assignmentIndex + 1);
int valueStartIndex = assignmentIndex + 1;
ParserHelper.SkipSpaces(line, ref valueStartIndex);
if(valueStartIndex < line.Length) {
parseOptionValue(option, line, valueStartIndex);
}
// We've got the option assignment, either with an empty or proper value // We've got the option assignment, either with an empty or proper value
state.Store.options.Add(option); state.Store.options.Add(option);
@ -181,9 +176,54 @@ namespace Nuclex.Support.Settings {
/// <summary>Parses the value assigned to an option</summary> /// <summary>Parses the value assigned to an option</summary>
/// <param name="option">Option to which a value is being assigned</param> /// <param name="option">Option to which a value is being assigned</param>
/// <param name="line">Line containing the option assignment</param> /// <param name="line">Line containing the option assignment</param>
/// <param name="valueStartIndex">Index of the value's first character</param> /// <param name="assignmentEndIndex">Index one after the assignment character</param>
private static void parseOptionValue(Option option, string line, int valueStartIndex) { private static void parseOptionValue(Option option, string line, int assignmentEndIndex) {
int firstCharacterIndex = assignmentEndIndex;
ParserHelper.SkipSpaces(line, ref firstCharacterIndex);
// Just for beauty, when the option value is empty but padded with spaces,
// leave one space between the equals sign and the value.
if(firstCharacterIndex > assignmentEndIndex) {
++assignmentEndIndex;
}
// If the line consists of only whitespace, create an empty value
if(firstCharacterIndex == line.Length) {
option.OptionValue = new StringSegment(line, assignmentEndIndex, 0);
return;
}
char firstCharacter = line[firstCharacterIndex];
// Values can be quoted to allow for comments characters appearing in them
int lastCharacterIndex;
if(firstCharacter == '"') {
lastCharacterIndex = line.LastIndexOf('"');
} else {
lastCharacterIndex = firstCharacterIndex;
}
int commentStartIndex = line.IndexOf(';', lastCharacterIndex);
if(commentStartIndex == -1) {
commentStartIndex = line.IndexOf('#', lastCharacterIndex);
}
if(commentStartIndex == -1) {
lastCharacterIndex = line.Length - 1;
} else {
lastCharacterIndex = commentStartIndex - 1;
}
while(lastCharacterIndex > firstCharacterIndex) {
if(char.IsWhiteSpace(line, lastCharacterIndex)) {
--lastCharacterIndex;
} else {
break;
}
}
option.OptionValue = new StringSegment(
line, firstCharacterIndex, lastCharacterIndex - firstCharacterIndex + 1
);
} }
/// <summary>Determines the best matching type for an option value</summary> /// <summary>Determines the best matching type for an option value</summary>

View File

@ -21,10 +21,10 @@ License along with this library
#if UNITTEST #if UNITTEST
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using NUnit.Framework; using NUnit.Framework;
using System.Collections.Generic;
namespace Nuclex.Support.Settings { namespace Nuclex.Support.Settings {
@ -32,6 +32,9 @@ namespace Nuclex.Support.Settings {
[TestFixture] [TestFixture]
internal class ConfigurationFileStoreTest { internal class ConfigurationFileStoreTest {
/// <summary>Loads a configuration file from a string</summary>
/// <param name="fileContents">Contents of the configuration file</param>
/// <returns>The configuration file loaded from the string</returns>
private static ConfigurationFileStore load(string fileContents) { private static ConfigurationFileStore load(string fileContents) {
using(var reader = new StringReader(fileContents)) { using(var reader = new StringReader(fileContents)) {
return ConfigurationFileStore.Parse(reader); return ConfigurationFileStore.Parse(reader);
@ -128,11 +131,84 @@ namespace Nuclex.Support.Settings {
for(int index = 0; index < options.Count; ++index) { for(int index = 0; index < options.Count; ++index) {
Assert.That( Assert.That(
configurationFile.Get<string>(null, options[index].Name), Is.Null configurationFile.Get<string>(null, options[index].Name), Is.Null.Or.Empty
); );
} }
} }
/// <summary>
/// Verifies that values assigned to options can contain space charcters
/// </summary>
[Test]
public void OptionValuesCanContainSpaces() {
string fileContents =
"test = hello world";
ConfigurationFileStore configurationFile = load(fileContents);
Assert.That(configurationFile.Get<string>(null, "test"), Is.EqualTo("hello world"));
}
/// <summary>
/// Verifies that values enclosed in quotes can embed comment characters
/// </summary>
[Test]
public void OptionValuesWithQuotesCanEmbedComments() {
string fileContents =
"test = \"This ; is # not a comment\" # but this is";
ConfigurationFileStore configurationFile = load(fileContents);
Assert.That(
configurationFile.Get<string>(null, "test"),
Is.EqualTo("\"This ; is # not a comment\"")
);
}
/// <summary>
/// Verifies that values can end on a quote without causing trouble
/// </summary>
[Test]
public void CommentsCanEndWithAQuote() {
string fileContents =
"test = \"This value ends with a quote\"";
ConfigurationFileStore configurationFile = load(fileContents);
Assert.That(
configurationFile.Get<string>(null, "test"),
Is.EqualTo("\"This value ends with a quote\"")
);
}
/// <summary>
/// Verifies that values can forget the closing quote without causing trouble
/// </summary>
[Test]
public void ClosingQuoteCanBeOmmitted() {
string fileContents =
"test = \"No closing quote";
ConfigurationFileStore configurationFile = load(fileContents);
Assert.That(
configurationFile.Get<string>(null, "test"),
Is.EqualTo("\"No closing quote")
);
}
/// <summary>
/// Verifies that text placed after the closing quote will also be part of
/// an option's value
/// </summary>
[Test]
public void TextAfterClosingQuoteBecomesPartOfValue() {
string fileContents =
"test = \"Begins here\" end ends here";
ConfigurationFileStore configurationFile = load(fileContents);
Assert.That(
configurationFile.Get<string>(null, "test"),
Is.EqualTo("\"Begins here\" end ends here")
);
}
} }
} // namespace Nuclex.Support.Settings } // namespace Nuclex.Support.Settings

View File

@ -21,8 +21,6 @@ License along with this library
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Nuclex.Support.Parsing;
using System.Text; using System.Text;
namespace Nuclex.Support.Settings { namespace Nuclex.Support.Settings {