diff --git a/Source/Settings/ConfigurationFileStore.Test.cs b/Source/Settings/ConfigurationFileStore.Test.cs
index 9a0c39d..e5333d5 100644
--- a/Source/Settings/ConfigurationFileStore.Test.cs
+++ b/Source/Settings/ConfigurationFileStore.Test.cs
@@ -244,6 +244,144 @@ namespace Nuclex.Support.Settings {
Assert.That(save(configurationFile), Contains.Substring("[general]"));
}
+ ///
+ /// Verifies that accessing an option that doesn't exist throws an exception
+ ///
+ [Test]
+ public void AccessingNonExistingOptionThrowsException() {
+ var configurationFile = new ConfigurationFileStore();
+
+ Assert.That(
+ () => configurationFile.Get(null, "doesn't exist"),
+ Throws.Exception.AssignableTo()
+ );
+ }
+
+ ///
+ /// Verifies that accessing a category that doesn't exist throws an exception
+ ///
+ [Test]
+ public void AccessingNonExistingCategoryThrowsException() {
+ var configurationFile = new ConfigurationFileStore();
+ configurationFile.Set(null, "test", "123");
+
+ Assert.That(
+ () => configurationFile.Get("doesn't exist", "test"),
+ Throws.Exception.AssignableTo()
+ );
+ }
+
+ ///
+ /// Verifies that it's possible to enumerate a category that doesn't exist
+ ///
+ [Test]
+ public void NonExistingCategoryCanBeEnumerated() {
+ var configurationFile = new ConfigurationFileStore();
+
+ Assert.That(configurationFile.EnumerateOptions("doesn't exist"), Is.Empty);
+ }
+
+ ///
+ /// Verifies that it's possible to create an option without a value
+ ///
+ [Test]
+ public void ValuelessOptionsCanBeCreated() {
+ var configurationFile = new ConfigurationFileStore();
+
+ configurationFile.Set(null, "test", null);
+ Assert.That(configurationFile.Get(null, "test"), Is.Null.Or.Empty);
+ }
+
+ ///
+ /// Verifies that it's possible to assign an empty value to an option
+ ///
+ [Test]
+ public void OptionValueCanBeCleared() {
+ string fileContents = "test = 123 ; comment";
+ ConfigurationFileStore configurationFile = load(fileContents);
+
+ configurationFile.Set(null, "test", null);
+ Assert.That(configurationFile.Get(null, "test"), Is.Null.Or.Empty);
+ }
+
+ ///
+ /// Verifies that it's possible to assign an empty value to an option
+ ///
+ [Test]
+ public void OptionsCanBeRemoved() {
+ var configurationFile = new ConfigurationFileStore();
+ configurationFile.Set(null, "test", null);
+
+ Assert.That(configurationFile.Remove(null, "test"), Is.True);
+ }
+
+ ///
+ /// Verifies that it's possible to assign an empty value to an option
+ ///
+ [Test]
+ public void RemovingOptionShiftsFollowingOptionsUp() {
+ string fileContents =
+ "first = 1\r\n" +
+ "second = 2";
+ ConfigurationFileStore configurationFile = load(fileContents);
+
+ Assert.That(configurationFile.Remove(null, "first"), Is.True);
+ configurationFile.Set(null, "second", "yay! first!");
+
+ Assert.That(save(configurationFile), Has.No.ContainsSubstring("1"));
+ Assert.That(save(configurationFile), Contains.Substring("second"));
+ Assert.That(save(configurationFile), Contains.Substring("yay! first!"));
+ }
+
+ ///
+ /// Verifies that it's not an error to remove an option from a non-existing category
+ ///
+ [Test]
+ public void CanRemoveOptionFromNonExistingCategory() {
+ var configurationFile = new ConfigurationFileStore();
+ Assert.That(configurationFile.Remove("nothing", "first"), Is.False);
+ }
+
+ ///
+ /// Verifies that it's not an error to remove a non-existing option
+ ///
+ [Test]
+ public void CanRemoveNonExistingOption() {
+ var configurationFile = new ConfigurationFileStore();
+ Assert.That(configurationFile.Remove(null, "first"), Is.False);
+ }
+
+ ///
+ /// Verifies that it's not an error to remove a non-existing option
+ ///
+ [
+ Test,
+ TestCase("text = world", typeof(string)),
+ TestCase("short=9", typeof(int)),
+ TestCase("integer = 123", typeof(int)),
+ TestCase("integer = 123 ", typeof(int)),
+ TestCase("float = 123.45", typeof(float)),
+ TestCase("float = 123.45 ", typeof(float)),
+ TestCase("boolean = true", typeof(bool)),
+ TestCase("boolean = false", typeof(bool)),
+ TestCase("boolean = yes", typeof(bool)),
+ TestCase("boolean = no", typeof(bool))
+ ]
+ public void OptionTypeCanBeIdentified(string assignment, Type expectedType) {
+ ConfigurationFileStore configurationFile = load(assignment);
+
+ OptionInfo info;
+ using(
+ IEnumerator enumerator = configurationFile.EnumerateOptions().GetEnumerator()
+ ) {
+ Assert.That(enumerator.MoveNext(), Is.True);
+ info = enumerator.Current;
+ Assert.That(enumerator.MoveNext(), Is.False);
+ }
+
+ Assert.That(info.OptionType, Is.EqualTo(expectedType));
+ }
+
/// Loads a configuration file from a string
/// Contents of the configuration file
/// The configuration file loaded from the string
diff --git a/Source/Settings/ConfigurationFileStore.cs b/Source/Settings/ConfigurationFileStore.cs
index d78c121..cd96236 100644
--- a/Source/Settings/ConfigurationFileStore.cs
+++ b/Source/Settings/ConfigurationFileStore.cs
@@ -103,6 +103,9 @@ namespace Nuclex.Support.Settings {
/// An enumerable list of all options in the category
public IEnumerable EnumerateOptions(string category = null) {
Category enumeratedCategory = getCategoryByName(category);
+ if(enumeratedCategory == null) {
+ yield break;
+ }
foreach(Option option in this.RootCategory.OptionLookup.Values) {
OptionInfo optionInfo = new OptionInfo() {
@@ -147,15 +150,16 @@ namespace Nuclex.Support.Settings {
///
public bool TryGet(string category, string optionName, out TValue value) {
Category containingCategory = getCategoryByName(category);
-
- Option option;
- if(containingCategory.OptionLookup.TryGetValue(optionName, out option)) {
- value = (TValue)Convert.ChangeType(option.OptionValue.ToString(), typeof(TValue));
- return true;
- } else {
- value = default(TValue);
- return false;
+ if(containingCategory != null) {
+ Option option;
+ if(containingCategory.OptionLookup.TryGetValue(optionName, out option)) {
+ value = (TValue)Convert.ChangeType(option.OptionValue.ToString(), typeof(TValue));
+ return true;
+ }
}
+
+ value = default(TValue);
+ return false;
}
/// Saves an option in the settings store
@@ -164,30 +168,22 @@ namespace Nuclex.Support.Settings {
/// Name of the option that will be saved
/// The value under which the option will be saved
public void Set(string category, string optionName, TValue value) {
- Category targetCategory;
- bool optionMightExist;
-
- if(string.IsNullOrEmpty(category)) {
- targetCategory = this.RootCategory;
- optionMightExist = true;
- } else if(this.categoryLookup.TryGetValue(category, out targetCategory)) {
- optionMightExist = true;
- } else {
- targetCategory = createCategory(category);
- optionMightExist = false;
- }
-
string valueAsString = (string)Convert.ChangeType(
value, typeof(string), CultureInfo.InvariantCulture
);
+ Category targetCategory;
+ if(string.IsNullOrEmpty(category)) {
+ targetCategory = this.RootCategory;
+ } else if(!this.categoryLookup.TryGetValue(category, out targetCategory)) {
+ targetCategory = createCategory(category);
+ createOption(targetCategory, optionName, valueAsString);
+ return;
+ }
+
Option targetOption;
- if(optionMightExist) {
- if(targetCategory.OptionLookup.TryGetValue(optionName, out targetOption)) {
- changeOption(targetCategory, targetOption, valueAsString);
- } else {
- createOption(targetCategory, optionName, valueAsString);
- }
+ if(targetCategory.OptionLookup.TryGetValue(optionName, out targetOption)) {
+ changeOption(targetCategory, targetOption, valueAsString);
} else {
createOption(targetCategory, optionName, valueAsString);
}
@@ -198,7 +194,26 @@ namespace Nuclex.Support.Settings {
/// Name of the option that will be removed
/// True if the option was found and removed
public bool Remove(string category, string optionName) {
- throw new NotImplementedException();
+ Category sourceCategory = getCategoryByName(category);
+ if(sourceCategory == null) {
+ return false;
+ }
+
+ Option option;
+ if(!sourceCategory.OptionLookup.TryGetValue(optionName, out option)) {
+ return false;
+ }
+
+ sourceCategory.Lines.RemoveAt(option.LineIndex);
+ sourceCategory.OptionLookup.Remove(optionName);
+
+ foreach(Option shiftedOption in sourceCategory.OptionLookup.Values) {
+ if(shiftedOption.LineIndex > option.LineIndex) {
+ --shiftedOption.LineIndex;
+ }
+ }
+
+ return true;
}
/// Looks a category up by its name
@@ -212,9 +227,7 @@ namespace Nuclex.Support.Settings {
if(string.IsNullOrEmpty(categoryName)) {
category = this.RootCategory;
} else if(!this.categoryLookup.TryGetValue(categoryName, out category)) {
- throw new KeyNotFoundException(
- "There is no category named '" + categoryName + "' in the configuration file"
- );
+ return null;
}
return category;
@@ -281,7 +294,7 @@ namespace Nuclex.Support.Settings {
string line = option.OptionValue.Text;
{
StringBuilder builder = new StringBuilder(
- line.Length - option.OptionValue.Count + newValue.Length
+ line.Length - option.OptionValue.Count + newValueLength
);
// Stuff before the value