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:
parent
7e39bd684f
commit
0753aeb220
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user