From e5f8b99519ed38fac189129456a539a3ca4da26d Mon Sep 17 00:00:00 2001 From: Markus Ewald Date: Tue, 22 Jul 2014 10:43:23 +0000 Subject: [PATCH] Enumerating categories and options with the registry-based settings store is now working; updated mono-3.5 project git-svn-id: file:///srv/devel/repo-conversion/nusu@316 d2e56fa2-650e-0410-a79f-9358c0239efd --- Nuclex.Support (mono-3.5).csproj | 7 -- Source/Parsing/ParserHelper.cs | 69 +++++++++++ .../ConfigurationFileStore.Parsing.cs | 56 +-------- Source/Settings/WindowsRegistryStore.Test.cs | 3 + Source/Settings/WindowsRegistryStore.cs | 116 +++++++++++++++++- Source/WeakReference.cs | 12 +- 6 files changed, 192 insertions(+), 71 deletions(-) diff --git a/Nuclex.Support (mono-3.5).csproj b/Nuclex.Support (mono-3.5).csproj index c5a0fb9..66adcc3 100644 --- a/Nuclex.Support (mono-3.5).csproj +++ b/Nuclex.Support (mono-3.5).csproj @@ -267,10 +267,6 @@ TypeHelper.cs - - - Semaphore.cs - FloatHelper.cs @@ -300,9 +296,6 @@ StringSegment.cs - - WeakReference.cs - WeakReference.cs diff --git a/Source/Parsing/ParserHelper.cs b/Source/Parsing/ParserHelper.cs index b6dbf10..69032ae 100644 --- a/Source/Parsing/ParserHelper.cs +++ b/Source/Parsing/ParserHelper.cs @@ -176,6 +176,75 @@ namespace Nuclex.Support.Parsing { return false; } + /// Tried to parse a boolean literal + /// Value that will be parsed as a boolean literal + /// + /// True or false if the value was a boolean literal, null if it wasn't + /// + public static bool? ParseBooleanLiteral(string value) { + if(value == null) { + return null; + } + + var stringSegment = new StringSegment(value, 0, value.Length); + return ParseBooleanLiteral(ref stringSegment); + } + + /// Tried to parse a boolean literal + /// Value that will be parsed as a boolean literal + /// + /// True or false if the value was a boolean literal, null if it wasn't + /// + public static bool? ParseBooleanLiteral(ref StringSegment value) { + switch(value.Count) { + + // If the string spells 'no', it is considered a boolean + case 2: { + bool isSpellingNo = + ((value.Text[value.Offset + 0] == 'n') || (value.Text[value.Offset + 0] == 'N')) && + ((value.Text[value.Offset + 1] == 'o') || (value.Text[value.Offset + 1] == 'O')); + return isSpellingNo ? new Nullable(false) : null; + } + + // If the string spells 'yes', it is considered a boolean + case 3: { + bool isSpellingYes = + ((value.Text[value.Offset + 0] == 'y') || (value.Text[value.Offset + 0] == 'Y')) && + ((value.Text[value.Offset + 1] == 'e') || (value.Text[value.Offset + 1] == 'E')) && + ((value.Text[value.Offset + 2] == 's') || (value.Text[value.Offset + 2] == 'S')); + return isSpellingYes ? new Nullable(true) : null; + } + + // If the string spells 'true', it is considered a boolean + case 4: { + bool isSpellingTrue = + ((value.Text[value.Offset + 0] == 't') || (value.Text[value.Offset + 0] == 'T')) && + ((value.Text[value.Offset + 1] == 'r') || (value.Text[value.Offset + 1] == 'R')) && + ((value.Text[value.Offset + 2] == 'u') || (value.Text[value.Offset + 2] == 'U')) && + ((value.Text[value.Offset + 3] == 'e') || (value.Text[value.Offset + 3] == 'E')); + return isSpellingTrue ? new Nullable(true) : null; + } + + // If the string spells 'false', it is considered a boolean + case 5: { + bool isSpellingFalse = + ((value.Text[value.Offset + 0] == 'f') || (value.Text[value.Offset + 0] == 'F')) && + ((value.Text[value.Offset + 1] == 'a') || (value.Text[value.Offset + 1] == 'A')) && + ((value.Text[value.Offset + 2] == 'l') || (value.Text[value.Offset + 2] == 'L')) && + ((value.Text[value.Offset + 3] == 's') || (value.Text[value.Offset + 3] == 'S')) && + ((value.Text[value.Offset + 4] == 'e') || (value.Text[value.Offset + 4] == 'E')); + return isSpellingFalse ? new Nullable(false) : null; + } + + // Anything else is not considered a boolean + default: { + return null; + } + + } + } + + } } // namespace Nuclex.Support.Parsing diff --git a/Source/Settings/ConfigurationFileStore.Parsing.cs b/Source/Settings/ConfigurationFileStore.Parsing.cs index ed8b6cc..d5a2e0b 100644 --- a/Source/Settings/ConfigurationFileStore.Parsing.cs +++ b/Source/Settings/ConfigurationFileStore.Parsing.cs @@ -253,67 +253,13 @@ namespace Nuclex.Support.Settings { } // If it parses as a boolean literal, then it must be a boolean - if(parseBooleanLiteral(ref value) != null) { + if(ParserHelper.ParseBooleanLiteral(ref value) != null) { return typeof(bool); } return typeof(string); } - /// Tried to parse a boolean literal - /// Value that will be parsed as a boolean literal - /// - /// True or false if the value was a boolean literal, null if it wasn't - /// - private static bool? parseBooleanLiteral(ref StringSegment value) { - switch(value.Count) { - - // If the string spells 'no', it is considered a boolean - case 2: { - bool isSpellingNo = - ((value.Text[value.Offset + 0] == 'n') || (value.Text[value.Offset + 0] == 'N')) && - ((value.Text[value.Offset + 1] == 'o') || (value.Text[value.Offset + 1] == 'O')); - return isSpellingNo ? new Nullable(false) : null; - } - - // If the string spells 'yes', it is considered a boolean - case 3: { - bool isSpellingYes = - ((value.Text[value.Offset + 0] == 'y') || (value.Text[value.Offset + 0] == 'Y')) && - ((value.Text[value.Offset + 1] == 'e') || (value.Text[value.Offset + 1] == 'E')) && - ((value.Text[value.Offset + 2] == 's') || (value.Text[value.Offset + 2] == 'S')); - return isSpellingYes ? new Nullable(true) : null; - } - - // If the string spells 'true', it is considered a boolean - case 4: { - bool isSpellingTrue = - ((value.Text[value.Offset + 0] == 't') || (value.Text[value.Offset + 0] == 'T')) && - ((value.Text[value.Offset + 1] == 'r') || (value.Text[value.Offset + 1] == 'R')) && - ((value.Text[value.Offset + 2] == 'u') || (value.Text[value.Offset + 2] == 'U')) && - ((value.Text[value.Offset + 3] == 'e') || (value.Text[value.Offset + 3] == 'E')); - return isSpellingTrue ? new Nullable(true) : null; - } - - // If the string spells 'false', it is considered a boolean - case 5: { - bool isSpellingFalse = - ((value.Text[value.Offset + 0] == 'f') || (value.Text[value.Offset + 0] == 'F')) && - ((value.Text[value.Offset + 1] == 'a') || (value.Text[value.Offset + 1] == 'A')) && - ((value.Text[value.Offset + 2] == 'l') || (value.Text[value.Offset + 2] == 'L')) && - ((value.Text[value.Offset + 3] == 's') || (value.Text[value.Offset + 3] == 'S')) && - ((value.Text[value.Offset + 4] == 'e') || (value.Text[value.Offset + 4] == 'E')); - return isSpellingFalse ? new Nullable(false) : null; - } - - // Anything else is not considered a boolean - default: { - return null; - } - - } - } - } } // namespace Nuclex.Support.Configuration diff --git a/Source/Settings/WindowsRegistryStore.Test.cs b/Source/Settings/WindowsRegistryStore.Test.cs index a99399b..79107d4 100644 --- a/Source/Settings/WindowsRegistryStore.Test.cs +++ b/Source/Settings/WindowsRegistryStore.Test.cs @@ -29,6 +29,9 @@ namespace Nuclex.Support.Settings { /// Unit tests for the windows registry settings store [TestFixture] internal class WindowsRegistryStoreTest { + + + } } // namespace Nuclex.Support.Settings diff --git a/Source/Settings/WindowsRegistryStore.cs b/Source/Settings/WindowsRegistryStore.cs index 33def6b..04e0567 100644 --- a/Source/Settings/WindowsRegistryStore.cs +++ b/Source/Settings/WindowsRegistryStore.cs @@ -23,28 +23,74 @@ License along with this library using System; using System.Collections.Generic; +using Microsoft.Win32; +using Nuclex.Support.Parsing; + namespace Nuclex.Support.Settings { /// Stores settings in the registry on Windows operating systems public class WindowsRegistryStore : ISettingsStore, IDisposable { - + /// Initializes a new settings store on the specified registry path + /// Hive in which to look + /// Base path of the settings in the specified hive + /// Whether to open the registry in writable mode + public WindowsRegistryStore(RegistryHive hive, string directory, bool writable = true) { + using(RegistryKey hiveKey = RegistryKey.OpenBaseKey(hive, RegistryView.Default)) { + this.rootKey = hiveKey.OpenSubKey(directory, writable); + } + this.writable = writable; + } + + /// Initializes a new settings store on the specified registry key + /// Registry key the settings are stored under + /// Whether the registry was opened in writable mode + /// + /// This constructor takes ownership of the registry key. It will be disposed when + /// the settings store is disposed. + /// + public WindowsRegistryStore(RegistryKey rootKey, bool writable = true) { + this.rootKey = rootKey; + this.writable = writable; + } /// Immediately releases all resources owned by the instance public void Dispose() { + if(this.rootKey != null) { + this.rootKey.Dispose(); + this.rootKey = null; + } } /// Enumerates the categories defined in the configuration /// An enumerable list of all used categories public IEnumerable EnumerateCategories() { - throw new NotImplementedException(); + return this.rootKey.GetSubKeyNames(); } /// Enumerates the options stored under the specified category /// Category whose options will be enumerated /// An enumerable list of all options in the category public IEnumerable EnumerateOptions(string category = null) { - throw new NotImplementedException(); + if(string.IsNullOrEmpty(category)) { + string[] valueNames = this.rootKey.GetValueNames(); + for(int index = 0; index < valueNames.Length; ++index) { + yield return new OptionInfo() { + Name = valueNames[index], + OptionType = getBestMatchingType(this.rootKey, valueNames[index]) + }; + } + } else { + using(RegistryKey categoryKey = this.rootKey.OpenSubKey(category, this.writable)) { + string[] valueNames = categoryKey.GetValueNames(); + for(int index = 0; index < valueNames.Length; ++index) { + yield return new OptionInfo() { + Name = valueNames[index], + OptionType = getBestMatchingType(categoryKey, valueNames[index]) + }; + } + } + } } /// Retrieves the value of the specified option @@ -81,6 +127,15 @@ namespace Nuclex.Support.Settings { /// public bool TryGet(string category, string optionName, out TValue value) { throw new NotImplementedException(); + if(string.IsNullOrEmpty(category)) { + object valueAsObject = this.rootKey.GetValue(optionName); + value = (TValue)Convert.ChangeType(valueAsObject, typeof(TValue)); + } else { + using(RegistryKey categoryKey = this.rootKey.OpenSubKey(category, this.writable)) { + object valueAsObject = this.rootKey.GetValue(optionName); + value = (TValue)Convert.ChangeType(valueAsObject, typeof(TValue)); + } + } } /// Saves an option in the settings store @@ -100,6 +155,61 @@ namespace Nuclex.Support.Settings { throw new NotImplementedException(); } + /// Figures out which .NET type best matches the registry value + /// Registry key the key is stored in + /// Name of the option that will be retrieved + /// The best matching .NET type for the registry key's value + private static Type getBestMatchingType(RegistryKey categoryKey, string optionName) { + RegistryValueKind valueKind = categoryKey.GetValueKind(optionName); + switch(valueKind) { + case RegistryValueKind.Binary: { return typeof(byte[]); } + case RegistryValueKind.DWord: { return typeof(int); } + case RegistryValueKind.QWord: { return typeof(long); } + case RegistryValueKind.MultiString: { return typeof(string[]); } + case RegistryValueKind.ExpandString: + case RegistryValueKind.String: { + string value = (string)categoryKey.GetValue(optionName); + if(value.Length == 0) { + return typeof(string); + } + + // If there are at least two characters, it may be an integer with + // a sign in front of it + if(value.Length >= 2) { + int index = 0; + if(ParserHelper.SkipInteger(value, ref index)) { + if(index >= value.Length) { + return typeof(int); + } + if(value[index] == '.') { + return typeof(float); + } + } + } else { // If it's just a single character, it may be a number + if(char.IsNumber(value, 0)) { + return typeof(int); + } + } + + // If it parses as a boolean literal, then it must be a boolean + if(ParserHelper.ParseBooleanLiteral(value) != null) { + return typeof(bool); + } + + return typeof(string); + } + + case RegistryValueKind.Unknown: + case RegistryValueKind.None: + default: { return typeof(string); } + } + } + + /// Key on which the registry store is operating + private RegistryKey rootKey; + /// Whether the user can write to the registry key + private bool writable; + } } // namespace Nuclex.Support.Settings diff --git a/Source/WeakReference.cs b/Source/WeakReference.cs index f5b7e34..7c62959 100644 --- a/Source/WeakReference.cs +++ b/Source/WeakReference.cs @@ -31,15 +31,15 @@ namespace Nuclex.Support { #if !NO_SERIALIZATION [Serializable] #endif - public class WeakReference : WeakReference - where ReferencedType : class { + public class WeakReference : WeakReference + where TReferenced : class { /// /// Initializes a new instance of the WeakReference class, referencing /// the specified object. /// /// The object to track or null. - public WeakReference(ReferencedType target) : + public WeakReference(TReferenced target) : base(target) { } /// @@ -51,7 +51,7 @@ namespace Nuclex.Support { /// Indicates when to stop tracking the object. If true, the object is tracked /// after finalization; if false, the object is only tracked until finalization. /// - public WeakReference(ReferencedType target, bool trackResurrection) : + public WeakReference(TReferenced target, bool trackResurrection) : base(target, trackResurrection) { } #if !NO_SERIALIZATION @@ -89,8 +89,8 @@ namespace Nuclex.Support { /// The reference to the target object is invalid. This can occur if the current /// System.WeakReference object has been finalized /// - public new ReferencedType Target { - get { return (base.Target as ReferencedType); } + public new TReferenced Target { + get { return (base.Target as TReferenced); } set { base.Target = value; } }