From d01747f75a3373be80a957652cacd8d25a2bc4b0 Mon Sep 17 00:00:00 2001 From: Maier Stephan SI Date: Mon, 2 Jan 2023 04:33:49 +0100 Subject: [PATCH] Sicherung --- Config.Net/Config.Net.csproj | 76 + Config.Net/ConfigurationBuilder.cs | 72 + Config.Net/ConfigurationExtensions.cs | 146 + Config.Net/Core/Box/BoxFactory.cs | 204 + Config.Net/Core/Box/CollectionResultBox.cs | 109 + Config.Net/Core/Box/MethodResultBox.cs | 87 + Config.Net/Core/Box/PropertyResultBox.cs | 51 + Config.Net/Core/Box/ProxyResultBox.cs | 34 + Config.Net/Core/Box/ResultBox.cs | 73 + Config.Net/Core/DynamicReader.cs | 74 + Config.Net/Core/DynamicWriter.cs | 48 + Config.Net/Core/Extensions.cs | 16 + Config.Net/Core/FlatArrays.cs | 58 + Config.Net/Core/InterfaceInterceptor.cs | 103 + Config.Net/Core/IoHandler.cs | 70 + Config.Net/Core/LazyVar.cs | 96 + Config.Net/Core/OptionPath.cs | 88 + Config.Net/Core/ValueHandler.cs | 157 + Config.Net/IConfigStore.cs | 35 + Config.Net/ITypeParser.cs | 32 + Config.Net/OptionAttribute.cs | 19 + Config.Net/Properties/AssemblyInfo.cs | 3 + Config.Net/SettingsContainer.cs | 313 + Config.Net/Stores/AppConfigStore.cs | 48 + Config.Net/Stores/AssemblyConfigStore.cs | 61 + Config.Net/Stores/DictionaryConfigStore.cs | 64 + .../Stores/EnvironmentVariablesStore.cs | 76 + Config.Net/Stores/Formats/Ini/IniComment.cs | 21 + Config.Net/Stores/Formats/Ini/IniEntity.cs | 6 + Config.Net/Stores/Formats/Ini/IniKeyValue.cs | 72 + Config.Net/Stores/Formats/Ini/IniSection.cs | 121 + .../Stores/Formats/Ini/StructuredIniFile.cs | 130 + .../CommandLine/CommandLineConfigStore.cs | 102 + Config.Net/Stores/IniFileConfigStore.cs | 120 + Config.Net/Stores/JsonConfigStore.cs | 165 + Config.Net/TypeParsers/ByteParser.cs | 39 + Config.Net/TypeParsers/CoreParsers.cs | 95 + Config.Net/TypeParsers/DecimalParser.cs | 26 + Config.Net/TypeParsers/DefaultParser.cs | 69 + Config.Net/TypeParsers/DoubleParser.cs | 25 + Config.Net/TypeParsers/FloatParser.cs | 26 + Config.Net/TypeParsers/IntParser.cs | 24 + Config.Net/TypeParsers/LongParser.cs | 24 + .../TypeParsers/NetworkCredentialParser.cs | 24 + Config.Net/TypeParsers/SByteParser.cs | 25 + Config.Net/TypeParsers/ShortParser.cs | 25 + Config.Net/TypeParsers/StringArrayParser.cs | 131 + Config.Net/TypeParsers/StringParser.cs | 21 + Config.Net/TypeParsers/TimeSpanParser.cs | 23 + Config.Net/TypeParsers/TypeParserSettings.cs | 11 + Config.Net/TypeParsers/UIntParser.cs | 25 + Config.Net/TypeParsers/ULongParser.cs | 25 + Config.Net/TypeParsers/UShortParser.cs | 25 + Config.Net/Utils.cs | 87 + FSI.BT.Tools.sln | 12 + FSI.BT.Tools/App.xaml.cs | 14 +- FSI.BT.Tools/AppSettings.cs | 92 +- FSI.BT.Tools/Commands/ExitCommand.cs | 1 + FSI.BT.Tools/Commands/LoginCommand.cs | 9 + FSI.BT.Tools/Commands/OpenAppCommand.cs | 218 +- FSI.BT.Tools/Commands/OpenLinkCommand.cs | 21 +- .../Commands/TimeStampToClipboardCommand.cs | 7 +- FSI.BT.Tools/FSI.BT.Tools.csproj | 24 +- FSI.BT.Tools/FSI.BT.Tools.xml | 22 +- FSI.BT.Tools/FrmRadialMenu.xaml | 33 + FSI.BT.Tools/Global.cs | 33 +- FSI.BT.Tools/Gui/FrmProcesses.xaml | 32 +- FSI.BT.Tools/Icons/Folders.png | Bin 0 -> 11283 bytes FSI.BT.Tools/Settings/IAppSettings.cs | 43 + FSI.BT.Tools/Settings/IApps.cs | 39 + FSI.BT.Tools/Settings/IExe.cs | 18 + FSI.BT.Tools/Settings/IFolder.cs | 13 + FSI.BT.Tools/Settings/IStringValue.cs | 16 + FSI.BT.Tools/Settings/ITxtToClip.cs | 13 + FSI.BT.Tools/Settings/IUrls.cs | 25 + FSI.BT.Tools/ToolTip.xaml.cs | 2 +- FSI.BT.Tools/config.json | 215 + FSI.BT.Tools/nlog.config | 23 + FSI.Lib/FSI.Lib/FSI.Lib.csproj | 361 +- FSI.Lib/FSI.Lib/Guis/Folder.Mgt/FrmMain.xaml | 186 + .../FSI.Lib/Guis/Folder.Mgt/FrmMain.xaml.cs | 54 + FSI.Lib/FSI.Lib/Guis/Folder.Mgt/Model.cs | 38 + FSI.Lib/FSI.Lib/Guis/Folder.Mgt/ViewModel.cs | 180 + .../Guis/Folder.Mgt/_ViewModel/ViewModel.cs | 181 + .../Guis/IbaDirSync/ViewModel/ViewModelIba.cs | 6 +- FSI.Lib/FSI.Lib/Guis/Prj.Mgt/FrmMain.xaml.cs | 4 +- .../Guis/Prj.Mgt/ViewModel/ViewModelPrj.cs | 20 +- .../Guis/SetSizePosExWindow/Model/Window.cs | 3 +- .../ViewModel/ViewModelWindow.cs | 19 +- .../FSI.Lib/Guis/TxtToClip.Mgt/FrmMain.xaml | 183 + .../Guis/TxtToClip.Mgt/FrmMain.xaml.cs | 63 + .../Guis/TxtToClip.Mgt/Model/TxtToClip.cs | 34 + .../ViewModel/ViewModelTxtToClip.cs | 150 + FSI.Lib/FSI.Lib/Tools/RoboSharp/readme.md | 37 - FSI.Lib/FSI.Lib/TraceTool/Helper.cs | Bin 64602 -> 0 bytes FSI.Lib/FSI.Lib/TraceTool/Plugin.cs | 82 - FSI.Lib/FSI.Lib/TraceTool/TTrace.cs | 2367 - FSI.Lib/FSI.Lib/TraceTool/TraceListener.cs | 117 - FSI.Lib/FSI.Lib/TraceTool/TraceNode.cs | 645 - FSI.Lib/FSI.Lib/TraceTool/TraceNodeBase.cs | 67 - FSI.Lib/FSI.Lib/TraceTool/TraceNodeEx.cs | 2714 - FSI.Lib/FSI.Lib/TraceTool/TraceTable.cs | 108 - FSI.Lib/FSI.Lib/TraceTool/TraceToSend.cs | 970 - FSI.Lib/FSI.Lib/TraceTool/TraceTool.xml | 2474 - .../TraceTool/Viewer/DotNetWrapper.dll | Bin 145408 -> 0 bytes .../TraceTool/Viewer/FastMM_FullDebugMode.dll | Bin 144384 -> 0 bytes .../FSI.Lib/TraceTool/Viewer/TraceTool.drc | 1478 - .../FSI.Lib/TraceTool/Viewer/TraceTool.exe | Bin 4191232 -> 0 bytes .../Viewer/TraceTool.exe - debug.lnk | Bin 1260 -> 0 bytes .../FSI.Lib/TraceTool/Viewer/TraceTool.map | 45848 ---------------- .../TraceTool/Viewer/TraceTool_Icon.png | Bin 456 -> 0 bytes .../TraceTool/Viewer/TracetoolConfig.xml | 146 - .../TraceTool/Viewer/TracetoolConfigProd.xml | 146 - .../TraceTool/Viewer/WebSock/Fleck.dll | Bin 44032 -> 0 bytes .../WebSock/Microsoft.Win32.Primitives.dll | Bin 21216 -> 0 bytes .../Viewer/WebSock/System.AppContext.dll | Bin 21176 -> 0 bytes .../WebSock/System.Collections.Concurrent.dll | Bin 21224 -> 0 bytes .../WebSock/System.Collections.NonGeneric.dll | Bin 21224 -> 0 bytes .../System.Collections.Specialized.dll | Bin 21224 -> 0 bytes .../Viewer/WebSock/System.Collections.dll | Bin 21696 -> 0 bytes .../System.ComponentModel.EventBasedAsync.dll | Bin 21768 -> 0 bytes .../System.ComponentModel.Primitives.dll | Bin 21752 -> 0 bytes .../System.ComponentModel.TypeConverter.dll | Bin 22784 -> 0 bytes .../Viewer/WebSock/System.ComponentModel.dll | Bin 21192 -> 0 bytes .../Viewer/WebSock/System.Console.dll | Bin 21160 -> 0 bytes .../Viewer/WebSock/System.Data.Common.dll | Bin 154448 -> 0 bytes .../WebSock/System.Diagnostics.Contracts.dll | Bin 21736 -> 0 bytes .../WebSock/System.Diagnostics.Debug.dll | Bin 21208 -> 0 bytes .../System.Diagnostics.FileVersionInfo.dll | Bin 21248 -> 0 bytes .../WebSock/System.Diagnostics.Process.dll | Bin 21728 -> 0 bytes .../WebSock/System.Diagnostics.StackTrace.dll | Bin 23936 -> 0 bytes ...em.Diagnostics.TextWriterTraceListener.dll | Bin 21280 -> 0 bytes .../WebSock/System.Diagnostics.Tools.dll | Bin 21208 -> 0 bytes .../System.Diagnostics.TraceSource.dll | Bin 21744 -> 0 bytes .../WebSock/System.Diagnostics.Tracing.dll | Bin 31608 -> 0 bytes .../WebSock/System.Drawing.Primitives.dll | Bin 21208 -> 0 bytes .../Viewer/WebSock/System.Dynamic.Runtime.dll | Bin 22224 -> 0 bytes .../System.Globalization.Calendars.dll | Bin 21744 -> 0 bytes .../System.Globalization.Extensions.dll | Bin 25992 -> 0 bytes .../Viewer/WebSock/System.Globalization.dll | Bin 21192 -> 0 bytes .../WebSock/System.IO.Compression.ZipFile.dll | Bin 21224 -> 0 bytes .../Viewer/WebSock/System.IO.Compression.dll | Bin 110944 -> 0 bytes .../System.IO.FileSystem.DriveInfo.dll | Bin 21232 -> 0 bytes .../System.IO.FileSystem.Primitives.dll | Bin 21232 -> 0 bytes .../WebSock/System.IO.FileSystem.Watcher.dll | Bin 21224 -> 0 bytes .../Viewer/WebSock/System.IO.FileSystem.dll | Bin 21192 -> 0 bytes .../WebSock/System.IO.IsolatedStorage.dll | Bin 21208 -> 0 bytes .../WebSock/System.IO.MemoryMappedFiles.dll | Bin 21216 -> 0 bytes .../Viewer/WebSock/System.IO.Pipes.dll | Bin 21168 -> 0 bytes .../System.IO.UnmanagedMemoryStream.dll | Bin 21232 -> 0 bytes .../TraceTool/Viewer/WebSock/System.IO.dll | Bin 21144 -> 0 bytes .../WebSock/System.Linq.Expressions.dll | Bin 22224 -> 0 bytes .../Viewer/WebSock/System.Linq.Parallel.dll | Bin 21192 -> 0 bytes .../Viewer/WebSock/System.Linq.Queryable.dll | Bin 21192 -> 0 bytes .../TraceTool/Viewer/WebSock/System.Linq.dll | Bin 21152 -> 0 bytes .../Viewer/WebSock/System.Net.Http.dll | Bin 198472 -> 0 bytes .../WebSock/System.Net.NameResolution.dll | Bin 21208 -> 0 bytes .../WebSock/System.Net.NetworkInformation.dll | Bin 22248 -> 0 bytes .../Viewer/WebSock/System.Net.Ping.dll | Bin 21168 -> 0 bytes .../Viewer/WebSock/System.Net.Primitives.dll | Bin 22216 -> 0 bytes .../Viewer/WebSock/System.Net.Requests.dll | Bin 21184 -> 0 bytes .../Viewer/WebSock/System.Net.Security.dll | Bin 21696 -> 0 bytes .../Viewer/WebSock/System.Net.Sockets.dll | Bin 30544 -> 0 bytes .../System.Net.WebHeaderCollection.dll | Bin 21232 -> 0 bytes .../WebSock/System.Net.WebSockets.Client.dll | Bin 21224 -> 0 bytes .../Viewer/WebSock/System.Net.WebSockets.dll | Bin 21192 -> 0 bytes .../Viewer/WebSock/System.ObjectModel.dll | Bin 21696 -> 0 bytes .../WebSock/System.Reflection.Extensions.dll | Bin 21224 -> 0 bytes .../WebSock/System.Reflection.Primitives.dll | Bin 21736 -> 0 bytes .../Viewer/WebSock/System.Reflection.dll | Bin 22200 -> 0 bytes .../WebSock/System.Resources.Reader.dll | Bin 21200 -> 0 bytes .../System.Resources.ResourceManager.dll | Bin 21232 -> 0 bytes .../WebSock/System.Resources.Writer.dll | Bin 21200 -> 0 bytes ...ystem.Runtime.CompilerServices.VisualC.dll | Bin 21784 -> 0 bytes .../WebSock/System.Runtime.Extensions.dll | Bin 21720 -> 0 bytes .../Viewer/WebSock/System.Runtime.Handles.dll | Bin 21200 -> 0 bytes ...ime.InteropServices.RuntimeInformation.dll | Bin 28624 -> 0 bytes .../System.Runtime.InteropServices.dll | Bin 24296 -> 0 bytes .../WebSock/System.Runtime.Numerics.dll | Bin 21200 -> 0 bytes ...ystem.Runtime.Serialization.Formatters.dll | Bin 21264 -> 0 bytes .../System.Runtime.Serialization.Json.dll | Bin 21240 -> 0 bytes ...ystem.Runtime.Serialization.Primitives.dll | Bin 27048 -> 0 bytes .../System.Runtime.Serialization.Xml.dll | Bin 24816 -> 0 bytes .../Viewer/WebSock/System.Runtime.dll | Bin 29360 -> 0 bytes .../Viewer/WebSock/System.Security.Claims.dll | Bin 21200 -> 0 bytes ...ystem.Security.Cryptography.Algorithms.dll | Bin 47016 -> 0 bytes .../System.Security.Cryptography.Csp.dll | Bin 21240 -> 0 bytes .../System.Security.Cryptography.Encoding.dll | Bin 21256 -> 0 bytes ...ystem.Security.Cryptography.Primitives.dll | Bin 21784 -> 0 bytes ...Security.Cryptography.X509Certificates.dll | Bin 22832 -> 0 bytes .../WebSock/System.Security.Principal.dll | Bin 21208 -> 0 bytes .../WebSock/System.Security.SecureString.dll | Bin 22392 -> 0 bytes .../System.Text.Encoding.Extensions.dll | Bin 21232 -> 0 bytes .../Viewer/WebSock/System.Text.Encoding.dll | Bin 21696 -> 0 bytes .../System.Text.RegularExpressions.dll | Bin 21232 -> 0 bytes .../WebSock/System.Threading.Overlapped.dll | Bin 37752 -> 0 bytes .../System.Threading.Tasks.Parallel.dll | Bin 21232 -> 0 bytes .../Viewer/WebSock/System.Threading.Tasks.dll | Bin 22224 -> 0 bytes .../WebSock/System.Threading.Thread.dll | Bin 21200 -> 0 bytes .../WebSock/System.Threading.ThreadPool.dll | Bin 21216 -> 0 bytes .../Viewer/WebSock/System.Threading.Timer.dll | Bin 21200 -> 0 bytes .../Viewer/WebSock/System.Threading.dll | Bin 22192 -> 0 bytes .../Viewer/WebSock/System.ValueTuple.dll | Bin 79176 -> 0 bytes .../WebSock/System.Xml.ReaderWriter.dll | Bin 21712 -> 0 bytes .../Viewer/WebSock/System.Xml.XDocument.dll | Bin 21696 -> 0 bytes .../WebSock/System.Xml.XPath.XDocument.dll | Bin 22904 -> 0 bytes .../Viewer/WebSock/System.Xml.XPath.dll | Bin 21176 -> 0 bytes .../Viewer/WebSock/System.Xml.XmlDocument.dll | Bin 21712 -> 0 bytes .../WebSock/System.Xml.XmlSerializer.dll | Bin 21712 -> 0 bytes .../TraceTool/Viewer/WebSock/TraceTool.dll | Bin 80896 -> 0 bytes .../Viewer/WebSock/WebsockPlugin.dll | Bin 20480 -> 0 bytes .../TraceTool/Viewer/WebSock/netstandard.dll | Bin 98616 -> 0 bytes .../TraceTool/Viewer/clientaccesspolicy.xml | 15 - .../FSI.Lib/TraceTool/Viewer/crossdomain.xml | 6 - FSI.Lib/FSI.Lib/TraceTool/Viewer/debug.bat | 1 - .../TraceTool/Viewer/tracetool.jmin.js | 472 - FSI.Lib/FSI.Lib/TraceTool/Viewer/tracetool.js | 3914 -- FSI.Lib/FSI.Lib/TraceTool/WinTrace.cs | 640 - FSI.Lib/FSI.Lib/TraceTool/WinWatch.cs | 154 - FSI.Lib/FSI.Lib/TraceTool/member.cs | 313 - FSI.Lib/FSI.Lib/TraceTool/readme.md | 780 - FSI.Lib/FSI.Lib/TraceTool/reflection.cs | 910 - .../NHotkey.WindowsForms.csproj | 2 +- NHotkey/NHotkey.Wpf/NHotkey.Wpf.csproj | 2 +- NHotkey/NHotkey/NHotkey.csproj | 2 +- .../NotifyIconWpf/NotifyIconWpf.csproj | 2 +- RadialMenu/RadialMenu.csproj | 2 +- .../ApplicationConstants.cs | 2 +- .../RoboSharp => RoboSharp}/CopyOptions.cs | 83 +- .../Tools/RoboSharp => RoboSharp}/Debugger.cs | 2 +- .../RoboSharpConfig_DE.cs | 2 +- .../RoboSharpConfig_EN.cs | 2 +- .../EventArgObjects/CommandErrorEventArgs.cs | 2 +- .../EventArgObjects/CopyProgressEventArgs.cs | 2 +- .../EventArgObjects/ErrorEventArgs.cs | 2 +- .../EventArgObjects/FileProcessedEventArgs.cs | 2 +- .../IProgressEstimatorUpdateEventArgs.cs | 6 +- .../ProgressEstimatorCreatedEventArgs.cs | 4 +- .../ResultListUpdatedEventArgs.cs | 6 +- .../RoboCommandCompletedEventArgs.cs | 4 +- .../RoboQueueCommandStartedEventArgs.cs | 9 +- .../RoboQueueCompletedEventArgs.cs | 6 +- .../EventArgObjects/StatChangedEventArg.cs | 6 +- .../EventArgObjects/TimeSpanEventArgs.cs | 2 +- .../ExtensionMethods.cs | 2 +- .../ImpersonateUser.cs | 2 +- .../Interfaces/IProgressEstimator.cs | 4 +- .../Interfaces/IResults.cs | 4 +- .../Interfaces/IRoboCommand.cs | 9 +- RoboSharp/Interfaces/IRoboCommandFactory.cs | 76 + .../Interfaces/IRoboCopyCombinedExitStatus.cs | 4 +- .../Interfaces/IRoboCopyResultsList.cs | 4 +- .../Interfaces/IRoboQueue.cs | 6 +- .../Interfaces/IRoboQueueResults.cs | 2 +- .../Interfaces/ISpeedStatistic.cs | 4 +- .../Interfaces/IStatistic.cs | 4 +- .../Interfaces/ITimeSpan.cs | 2 +- .../Tools/RoboSharp => RoboSharp}/JobFile.cs | 24 +- .../RoboSharp => RoboSharp}/JobFileBuilder.cs | 2 +- .../RoboSharp => RoboSharp}/JobOptions.cs | 2 +- .../RoboSharp => RoboSharp}/LoggingOptions.cs | 2 +- .../RoboSharp => RoboSharp}/NativeMethods.cs | 2 +- .../RoboSharp => RoboSharp}/ObservableList.cs | 0 .../PriorityScheduler.cs | 3 +- .../ProcessedFileInfo.cs | 2 +- .../Results/ProgressEstimator.cs | 6 +- .../Results/ResultsBuilder.cs | 2 +- .../Results/RoboCopyExitCodes.cs | 2 +- .../Results/RoboCopyResults.cs | 4 +- .../Results/RoboCopyResultsList.cs | 8 +- .../Results/RoboCopyResultsStatus.cs | 4 +- .../Results/RoboQueueProgressEstimator.cs | 10 +- .../Results/RoboQueueResults.cs | 5 +- .../Results/SpeedStatistic.cs | 4 +- .../Results/Statistic.cs | 6 +- .../RoboSharp => RoboSharp}/RetryOptions.cs | 2 +- .../RoboSharp => RoboSharp}/RoboCommand.cs | 42 +- RoboSharp/RoboCommandFactory.cs | 67 + .../RoboSharp => RoboSharp}/RoboQueue.cs | 74 +- RoboSharp/RoboSharp.csproj | 44 + .../RoboSharp => RoboSharp}/RoboSharp.xml | 0 .../RoboSharpConfiguration.cs | 4 +- .../SelectionOptions.cs | 109 +- .../RoboSharp => RoboSharp}/VersionManager.cs | 2 +- 284 files changed, 6106 insertions(+), 65112 deletions(-) create mode 100644 Config.Net/Config.Net.csproj create mode 100644 Config.Net/ConfigurationBuilder.cs create mode 100644 Config.Net/ConfigurationExtensions.cs create mode 100644 Config.Net/Core/Box/BoxFactory.cs create mode 100644 Config.Net/Core/Box/CollectionResultBox.cs create mode 100644 Config.Net/Core/Box/MethodResultBox.cs create mode 100644 Config.Net/Core/Box/PropertyResultBox.cs create mode 100644 Config.Net/Core/Box/ProxyResultBox.cs create mode 100644 Config.Net/Core/Box/ResultBox.cs create mode 100644 Config.Net/Core/DynamicReader.cs create mode 100644 Config.Net/Core/DynamicWriter.cs create mode 100644 Config.Net/Core/Extensions.cs create mode 100644 Config.Net/Core/FlatArrays.cs create mode 100644 Config.Net/Core/InterfaceInterceptor.cs create mode 100644 Config.Net/Core/IoHandler.cs create mode 100644 Config.Net/Core/LazyVar.cs create mode 100644 Config.Net/Core/OptionPath.cs create mode 100644 Config.Net/Core/ValueHandler.cs create mode 100644 Config.Net/IConfigStore.cs create mode 100644 Config.Net/ITypeParser.cs create mode 100644 Config.Net/OptionAttribute.cs create mode 100644 Config.Net/Properties/AssemblyInfo.cs create mode 100644 Config.Net/SettingsContainer.cs create mode 100644 Config.Net/Stores/AppConfigStore.cs create mode 100644 Config.Net/Stores/AssemblyConfigStore.cs create mode 100644 Config.Net/Stores/DictionaryConfigStore.cs create mode 100644 Config.Net/Stores/EnvironmentVariablesStore.cs create mode 100644 Config.Net/Stores/Formats/Ini/IniComment.cs create mode 100644 Config.Net/Stores/Formats/Ini/IniEntity.cs create mode 100644 Config.Net/Stores/Formats/Ini/IniKeyValue.cs create mode 100644 Config.Net/Stores/Formats/Ini/IniSection.cs create mode 100644 Config.Net/Stores/Formats/Ini/StructuredIniFile.cs create mode 100644 Config.Net/Stores/Impl/CommandLine/CommandLineConfigStore.cs create mode 100644 Config.Net/Stores/IniFileConfigStore.cs create mode 100644 Config.Net/Stores/JsonConfigStore.cs create mode 100644 Config.Net/TypeParsers/ByteParser.cs create mode 100644 Config.Net/TypeParsers/CoreParsers.cs create mode 100644 Config.Net/TypeParsers/DecimalParser.cs create mode 100644 Config.Net/TypeParsers/DefaultParser.cs create mode 100644 Config.Net/TypeParsers/DoubleParser.cs create mode 100644 Config.Net/TypeParsers/FloatParser.cs create mode 100644 Config.Net/TypeParsers/IntParser.cs create mode 100644 Config.Net/TypeParsers/LongParser.cs create mode 100644 Config.Net/TypeParsers/NetworkCredentialParser.cs create mode 100644 Config.Net/TypeParsers/SByteParser.cs create mode 100644 Config.Net/TypeParsers/ShortParser.cs create mode 100644 Config.Net/TypeParsers/StringArrayParser.cs create mode 100644 Config.Net/TypeParsers/StringParser.cs create mode 100644 Config.Net/TypeParsers/TimeSpanParser.cs create mode 100644 Config.Net/TypeParsers/TypeParserSettings.cs create mode 100644 Config.Net/TypeParsers/UIntParser.cs create mode 100644 Config.Net/TypeParsers/ULongParser.cs create mode 100644 Config.Net/TypeParsers/UShortParser.cs create mode 100644 Config.Net/Utils.cs create mode 100644 FSI.BT.Tools/Icons/Folders.png create mode 100644 FSI.BT.Tools/Settings/IAppSettings.cs create mode 100644 FSI.BT.Tools/Settings/IApps.cs create mode 100644 FSI.BT.Tools/Settings/IExe.cs create mode 100644 FSI.BT.Tools/Settings/IFolder.cs create mode 100644 FSI.BT.Tools/Settings/IStringValue.cs create mode 100644 FSI.BT.Tools/Settings/ITxtToClip.cs create mode 100644 FSI.BT.Tools/Settings/IUrls.cs create mode 100644 FSI.BT.Tools/config.json create mode 100644 FSI.BT.Tools/nlog.config create mode 100644 FSI.Lib/FSI.Lib/Guis/Folder.Mgt/FrmMain.xaml create mode 100644 FSI.Lib/FSI.Lib/Guis/Folder.Mgt/FrmMain.xaml.cs create mode 100644 FSI.Lib/FSI.Lib/Guis/Folder.Mgt/Model.cs create mode 100644 FSI.Lib/FSI.Lib/Guis/Folder.Mgt/ViewModel.cs create mode 100644 FSI.Lib/FSI.Lib/Guis/Folder.Mgt/_ViewModel/ViewModel.cs create mode 100644 FSI.Lib/FSI.Lib/Guis/TxtToClip.Mgt/FrmMain.xaml create mode 100644 FSI.Lib/FSI.Lib/Guis/TxtToClip.Mgt/FrmMain.xaml.cs create mode 100644 FSI.Lib/FSI.Lib/Guis/TxtToClip.Mgt/Model/TxtToClip.cs create mode 100644 FSI.Lib/FSI.Lib/Guis/TxtToClip.Mgt/ViewModel/ViewModelTxtToClip.cs delete mode 100644 FSI.Lib/FSI.Lib/Tools/RoboSharp/readme.md delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Helper.cs delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Plugin.cs delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/TTrace.cs delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/TraceListener.cs delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/TraceNode.cs delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/TraceNodeBase.cs delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/TraceNodeEx.cs delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/TraceTable.cs delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/TraceToSend.cs delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/TraceTool.xml delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/DotNetWrapper.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/FastMM_FullDebugMode.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/TraceTool.drc delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/TraceTool.exe delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/TraceTool.exe - debug.lnk delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/TraceTool.map delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/TraceTool_Icon.png delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/TracetoolConfig.xml delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/TracetoolConfigProd.xml delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/Fleck.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/Microsoft.Win32.Primitives.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.AppContext.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Collections.Concurrent.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Collections.NonGeneric.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Collections.Specialized.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Collections.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.ComponentModel.EventBasedAsync.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.ComponentModel.Primitives.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.ComponentModel.TypeConverter.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.ComponentModel.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Console.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Data.Common.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Diagnostics.Contracts.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Diagnostics.Debug.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Diagnostics.FileVersionInfo.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Diagnostics.Process.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Diagnostics.StackTrace.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Diagnostics.TextWriterTraceListener.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Diagnostics.Tools.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Diagnostics.TraceSource.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Diagnostics.Tracing.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Drawing.Primitives.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Dynamic.Runtime.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Globalization.Calendars.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Globalization.Extensions.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Globalization.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.IO.Compression.ZipFile.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.IO.Compression.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.IO.FileSystem.DriveInfo.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.IO.FileSystem.Primitives.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.IO.FileSystem.Watcher.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.IO.FileSystem.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.IO.IsolatedStorage.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.IO.MemoryMappedFiles.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.IO.Pipes.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.IO.UnmanagedMemoryStream.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.IO.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Linq.Expressions.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Linq.Parallel.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Linq.Queryable.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Linq.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Net.Http.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Net.NameResolution.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Net.NetworkInformation.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Net.Ping.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Net.Primitives.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Net.Requests.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Net.Security.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Net.Sockets.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Net.WebHeaderCollection.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Net.WebSockets.Client.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Net.WebSockets.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.ObjectModel.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Reflection.Extensions.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Reflection.Primitives.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Reflection.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Resources.Reader.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Resources.ResourceManager.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Resources.Writer.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Runtime.CompilerServices.VisualC.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Runtime.Extensions.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Runtime.Handles.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Runtime.InteropServices.RuntimeInformation.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Runtime.InteropServices.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Runtime.Numerics.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Runtime.Serialization.Formatters.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Runtime.Serialization.Json.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Runtime.Serialization.Primitives.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Runtime.Serialization.Xml.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Runtime.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Security.Claims.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Security.Cryptography.Algorithms.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Security.Cryptography.Csp.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Security.Cryptography.Encoding.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Security.Cryptography.Primitives.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Security.Cryptography.X509Certificates.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Security.Principal.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Security.SecureString.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Text.Encoding.Extensions.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Text.Encoding.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Text.RegularExpressions.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Threading.Overlapped.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Threading.Tasks.Parallel.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Threading.Tasks.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Threading.Thread.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Threading.ThreadPool.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Threading.Timer.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Threading.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.ValueTuple.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Xml.ReaderWriter.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Xml.XDocument.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Xml.XPath.XDocument.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Xml.XPath.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Xml.XmlDocument.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/System.Xml.XmlSerializer.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/TraceTool.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/WebsockPlugin.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/WebSock/netstandard.dll delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/clientaccesspolicy.xml delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/crossdomain.xml delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/debug.bat delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/tracetool.jmin.js delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/Viewer/tracetool.js delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/WinTrace.cs delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/WinWatch.cs delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/member.cs delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/readme.md delete mode 100644 FSI.Lib/FSI.Lib/TraceTool/reflection.cs rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/ApplicationConstants.cs (94%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/CopyOptions.cs (88%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Debugger.cs (97%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/DefaultConfigurations/RoboSharpConfig_DE.cs (95%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/DefaultConfigurations/RoboSharpConfig_EN.cs (95%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/EventArgObjects/CommandErrorEventArgs.cs (97%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/EventArgObjects/CopyProgressEventArgs.cs (97%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/EventArgObjects/ErrorEventArgs.cs (98%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/EventArgObjects/FileProcessedEventArgs.cs (93%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/EventArgObjects/IProgressEstimatorUpdateEventArgs.cs (94%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/EventArgObjects/ProgressEstimatorCreatedEventArgs.cs (89%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/EventArgObjects/ResultListUpdatedEventArgs.cs (85%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/EventArgObjects/RoboCommandCompletedEventArgs.cs (90%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/EventArgObjects/RoboQueueCommandStartedEventArgs.cs (75%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/EventArgObjects/RoboQueueCompletedEventArgs.cs (87%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/EventArgObjects/StatChangedEventArg.cs (97%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/EventArgObjects/TimeSpanEventArgs.cs (96%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/ExtensionMethods.cs (99%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/ImpersonateUser.cs (98%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Interfaces/IProgressEstimator.cs (95%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Interfaces/IResults.cs (91%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Interfaces/IRoboCommand.cs (93%) create mode 100644 RoboSharp/Interfaces/IRoboCommandFactory.cs rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Interfaces/IRoboCopyCombinedExitStatus.cs (92%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Interfaces/IRoboCopyResultsList.cs (97%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Interfaces/IRoboQueue.cs (97%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Interfaces/IRoboQueueResults.cs (94%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Interfaces/ISpeedStatistic.cs (91%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Interfaces/IStatistic.cs (97%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Interfaces/ITimeSpan.cs (93%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/JobFile.cs (95%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/JobFileBuilder.cs (99%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/JobOptions.cs (99%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/LoggingOptions.cs (99%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/NativeMethods.cs (98%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/ObservableList.cs (100%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/PriorityScheduler.cs (98%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/ProcessedFileInfo.cs (96%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Results/ProgressEstimator.cs (99%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Results/ResultsBuilder.cs (99%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Results/RoboCopyExitCodes.cs (97%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Results/RoboCopyResults.cs (98%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Results/RoboCopyResultsList.cs (98%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Results/RoboCopyResultsStatus.cs (99%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Results/RoboQueueProgressEstimator.cs (97%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Results/RoboQueueResults.cs (97%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Results/SpeedStatistic.cs (99%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/Results/Statistic.cs (99%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/RetryOptions.cs (99%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/RoboCommand.cs (95%) create mode 100644 RoboSharp/RoboCommandFactory.cs rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/RoboQueue.cs (92%) create mode 100644 RoboSharp/RoboSharp.csproj rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/RoboSharp.xml (100%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/RoboSharpConfiguration.cs (99%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/SelectionOptions.cs (80%) rename {FSI.Lib/FSI.Lib/Tools/RoboSharp => RoboSharp}/VersionManager.cs (99%) diff --git a/Config.Net/Config.Net.csproj b/Config.Net/Config.Net.csproj new file mode 100644 index 0000000..04b7f21 --- /dev/null +++ b/Config.Net/Config.Net.csproj @@ -0,0 +1,76 @@ + + + Copyright (c) 2015-2022 by Ivan Gavryliuk + Config.Net + Ivan Gavryliuk (@aloneguid) + Config.Net + Config.Net + 4.0.0.0 + 4.7.3.0 + 4.7.3 + Super simple configuration framework for .NET focused on developer ergonomics and strong typing. Supports multiple configuration sources such as .ini, .json, .xml files, as well as external providers pluggable by other NuGet packages. + true + + icon.png + Apache-2.0 + + enable + latest + https://github.com/aloneguid/config + https://github.com/aloneguid/config + README.md + + + + + + netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0 + + + + + netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0 + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + True + \ + + + True + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + \ No newline at end of file diff --git a/Config.Net/ConfigurationBuilder.cs b/Config.Net/ConfigurationBuilder.cs new file mode 100644 index 0000000..a946b58 --- /dev/null +++ b/Config.Net/ConfigurationBuilder.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using Castle.DynamicProxy; +using Config.Net.Core; + +namespace Config.Net +{ + public class ConfigurationBuilder where T : class + { + private readonly ProxyGenerator _generator = new ProxyGenerator(); + private List _stores = new List(); + private TimeSpan _cacheInterval = TimeSpan.Zero; + private readonly List _customParsers = new List(); + + public ConfigurationBuilder() + { + TypeInfo ti = typeof(T).GetTypeInfo(); + + if (!ti.IsInterface) throw new ArgumentException($"{ti.FullName} must be an interface", ti.FullName); + } + + /// + /// Creates an instance of the configuration interface + /// + /// + public T Build() + { + var valueHandler = new ValueHandler(_customParsers); + var ioHandler = new IoHandler(_stores, valueHandler, _cacheInterval); + + T instance = _generator.CreateInterfaceProxyWithoutTarget(new InterfaceInterceptor(typeof(T), ioHandler)); + + return instance; + } + + /// + /// Set to anything different from to add caching for values. By default + /// Config.Net doesn't cache any values + /// + /// + /// + public ConfigurationBuilder CacheFor(TimeSpan time) + { + _cacheInterval = time; + + return this; + } + + public ConfigurationBuilder UseConfigStore(IConfigStore store) + { + _stores.Add(store); + return this; + } + + /// + /// Adds a custom type parser + /// + public ConfigurationBuilder UseTypeParser(ITypeParser parser) + { + if (parser == null) + { + throw new ArgumentNullException(nameof(parser)); + } + + _customParsers.Add(parser); + + return this; + } + } +} diff --git a/Config.Net/ConfigurationExtensions.cs b/Config.Net/ConfigurationExtensions.cs new file mode 100644 index 0000000..72ab7e0 --- /dev/null +++ b/Config.Net/ConfigurationExtensions.cs @@ -0,0 +1,146 @@ +using System.Reflection; +using Config.Net.Stores; +using System.Collections.Generic; +using Config.Net.Stores.Impl.CommandLine; + +namespace Config.Net +{ + /// + /// Configuration extensions + /// + public static class ConfigurationExtensions + { + /// + /// In-memory dictionary. Optionally you can pass pre-created dictionary, otherwise it will be created internally as empty. + /// + public static ConfigurationBuilder UseInMemoryDictionary( + this ConfigurationBuilder builder, + IDictionary? container = null) where TInterface : class + { + builder.UseConfigStore(new DictionaryConfigStore(container)); + return builder; + } + + /// + /// Standard app.config (web.config) builder store. Read-only. + /// + public static ConfigurationBuilder UseAppConfig(this ConfigurationBuilder builder) where TInterface : class + { + builder.UseConfigStore(new AppConfigStore()); + return builder; + } + + /// + /// Reads builder from the .dll.config or .exe.config file. + /// + /// + /// Reference to the assembly to look for + /// + public static ConfigurationBuilder UseAssemblyConfig(this ConfigurationBuilder builder, Assembly assembly) where TInterface : class + { + builder.UseConfigStore(new AssemblyConfigStore(assembly)); + return builder; + } + + /// + /// Uses system environment variables + /// + public static ConfigurationBuilder UseEnvironmentVariables(this ConfigurationBuilder builder) where TInterface : class + { + builder.UseConfigStore(new EnvironmentVariablesStore()); + return builder; + } + + + /// + /// Simple INI storage. + /// + /// + /// File does not have to exist, however it will be created as soon as you try to write to it. + /// When true, inline comments are parsed. It is set to false by default so inline comments are considered a part of the value. + /// + public static ConfigurationBuilder UseIniFile(this ConfigurationBuilder builder, + string iniFilePath, + bool parseInlineComments = false) where TInterface : class + { + builder.UseConfigStore(new IniFileConfigStore(iniFilePath, true, parseInlineComments)); + return builder; + } + + /// + /// Simple INI storage. + /// + /// + /// File contents + /// When true, inline comments are parsed. It is set to false by default so inline comments are considered a part of the value + /// + public static ConfigurationBuilder UseIniString(this ConfigurationBuilder builder, + string iniString, + bool parseInlineComments = false) where TInterface : class + { + builder.UseConfigStore(new IniFileConfigStore(iniString, false, parseInlineComments)); + return builder; + } + + /// + /// Accepts builder from the command line arguments. This is not intended to replace a command line parsing framework but rather + /// complement it in a builder like way. Uses current process' command line parameters automatically + /// + /// Configuration object + /// When true argument names are case sensitive, false by default + /// Changed builder + public static ConfigurationBuilder UseCommandLineArgs(this ConfigurationBuilder builder, + bool isCaseSensitive = false, + params KeyValuePair[] parameterNameToPosition) + where TInterface : class + { + builder.UseConfigStore(new CommandLineConfigStore(null, isCaseSensitive, parameterNameToPosition)); + return builder; + } + + public static ConfigurationBuilder UseCommandLineArgs(this ConfigurationBuilder builder, + bool isCaseSensitive = false, + string[]? args = null, + params KeyValuePair[] parameterNameToPosition) + where TInterface : class + { + builder.UseConfigStore(new CommandLineConfigStore(args, isCaseSensitive, parameterNameToPosition)); + return builder; + } + + public static ConfigurationBuilder UseCommandLineArgs(this ConfigurationBuilder builder, + params KeyValuePair[] parameterNameToPosition) + where TInterface : class + { + builder.UseConfigStore(new CommandLineConfigStore(null, false, parameterNameToPosition)); + return builder; + } + + /// + /// Uses JSON file as a builder storage. + /// + /// Configuration object. + /// Full path to json storage file. + /// Changed builder. + /// Storage file does not have to exist, however it will be created as soon as first write performed. + public static ConfigurationBuilder UseJsonFile(this ConfigurationBuilder builder, string jsonFilePath) where TInterface : class + { + builder.UseConfigStore(new JsonConfigStore(jsonFilePath, true)); + return builder; + } + + /// + /// Uses JSON file as a builder storage. + /// + /// Configuration object. + /// Json document. + /// Changed builder. + /// Storage file does not have to exist, however it will be created as soon as first write performed. + public static ConfigurationBuilder UseJsonString(this ConfigurationBuilder builder, string jsonString) where TInterface : class + { + builder.UseConfigStore(new JsonConfigStore(jsonString, false)); + return builder; + } + + } +} diff --git a/Config.Net/Core/Box/BoxFactory.cs b/Config.Net/Core/Box/BoxFactory.cs new file mode 100644 index 0000000..6e99479 --- /dev/null +++ b/Config.Net/Core/Box/BoxFactory.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; + +namespace Config.Net.Core.Box +{ + static class BoxFactory + { + public static Dictionary Discover(Type t, ValueHandler valueHandler, string? basePath) + { + var result = new Dictionary(); + + DiscoverProperties(t, valueHandler, result, basePath); + + DiscoverMethods(t, valueHandler, result); + + return result; + } + + private static void DiscoverProperties(Type t, ValueHandler valueHandler, Dictionary result, string? basePath) + { + IEnumerable properties = GetHierarchyPublicProperties(t); + + foreach (PropertyInfo pi in properties) + { + Type propertyType = pi.PropertyType; + ResultBox rbox; + bool isCollection = false; + + if(ResultBox.TryGetCollection(propertyType, out propertyType)) + { + if(pi.SetMethod != null) + { + throw new NotSupportedException($"Collection properties cannot have a setter. Detected at '{OptionPath.Combine(basePath, pi.Name)}'"); + } + + isCollection = true; + } + + if(propertyType.GetTypeInfo().IsInterface) + { + rbox = new ProxyResultBox(pi.Name, propertyType); + } + else + { + rbox = new PropertyResultBox(pi.Name, propertyType); + } + + ValidateSupportedType(rbox, valueHandler); + + AddAttributes(rbox, pi, valueHandler); + + //adjust to collection + if(isCollection) + { + rbox = new CollectionResultBox(pi.Name, rbox); + AddAttributes(rbox, pi, valueHandler); + } + + result[pi.Name] = rbox; + } + } + + private static void DiscoverMethods(Type t, ValueHandler valueHandler, Dictionary result) + { + TypeInfo ti = t.GetTypeInfo(); + + IEnumerable methods = ti.DeclaredMethods.Where(m => !m.IsSpecialName); + + foreach (MethodInfo method in methods) + { + var mbox = new MethodResultBox(method); + + AddAttributes(mbox, method, valueHandler); + + result[mbox.Name] = mbox; + } + } + + private static void ValidateSupportedType(ResultBox rb, ValueHandler valueHandler) + { + Type? t = null; + + if (rb is PropertyResultBox pbox) + t = rb.ResultBaseType; + + if (t != null && !valueHandler.IsSupported(t)) + { + throw new NotSupportedException($"type {t} on object '{rb.Name}' is not supported."); + } + } + + private static object? GetDefaultValue(Type t) + { + if (t.GetTypeInfo().IsValueType) return Activator.CreateInstance(t); + + return null; + } + + private static void AddAttributes(ResultBox box, PropertyInfo pi, ValueHandler valueHandler) + { + AddAttributes(box, valueHandler, + pi.GetCustomAttribute(), + pi.GetCustomAttribute()); + } + + private static void AddAttributes(ResultBox box, MethodInfo mi, ValueHandler valueHandler) + { + AddAttributes(box, valueHandler, mi.GetCustomAttribute(), mi.GetCustomAttribute()); + } + + + private static void AddAttributes(ResultBox box, ValueHandler valueHandler, params Attribute?[] attributes) + { + OptionAttribute? optionAttribute = attributes.OfType().FirstOrDefault(); + DefaultValueAttribute? defaultValueAttribute = attributes.OfType().FirstOrDefault(); + + if (optionAttribute?.Alias != null) + { + box.StoreByName = optionAttribute.Alias; + } + + box.DefaultResult = GetDefaultValue(optionAttribute?.DefaultValue, box, valueHandler) ?? + GetDefaultValue(defaultValueAttribute?.Value, box, valueHandler) ?? + GetDefaultValue(box.ResultType); + } + + private static object? GetDefaultValue(object? defaultValue, ResultBox box, ValueHandler valueHandler) + { + object? result = null; + if (defaultValue != null) + { + //validate that types for default value match + Type dvt = defaultValue.GetType(); + + if (dvt != box.ResultType && dvt != typeof(string)) + { + throw new InvalidCastException($"Default value for option {box.Name} is of type {dvt.FullName} whereas the property has type {box.ResultType.FullName}. To fix this, either set default value to type {box.ResultType.FullName} or a string parseable to the target type."); + } + + if (box.ResultType != typeof(string) && dvt == typeof(string)) + { + valueHandler.TryParse(box.ResultType, (string?)defaultValue, out result); + } + } + + if (result == null) + { + result = defaultValue; + } + + return result; + } + + private static PropertyInfo[] GetHierarchyPublicProperties(Type type) + { + var propertyInfos = new List(); + + var considered = new List(); + var queue = new Queue(); + considered.Add(type.GetTypeInfo()); + queue.Enqueue(type.GetTypeInfo()); + + while (queue.Count > 0) + { + TypeInfo typeInfo = queue.Dequeue(); + + //add base interfaces to the queue + foreach (Type subInterface in typeInfo.ImplementedInterfaces) + { + TypeInfo subInterfaceTypeInfo = subInterface.GetTypeInfo(); + + if (considered.Contains(subInterfaceTypeInfo)) continue; + + considered.Add(subInterfaceTypeInfo); + queue.Enqueue(subInterfaceTypeInfo); + } + + //add base classes to the queue + if (typeInfo.BaseType != null) + { + TypeInfo baseType = typeInfo.BaseType.GetTypeInfo(); + + if (!considered.Contains(baseType)) + { + considered.Add(baseType); + queue.Enqueue(baseType); + } + } + + + //get properties from the current type + IEnumerable newProperties = typeInfo.DeclaredProperties.Where(p => !propertyInfos.Contains(p)); + propertyInfos.InsertRange(0, newProperties); + } + + return propertyInfos.ToArray(); + } + + + } +} diff --git a/Config.Net/Core/Box/CollectionResultBox.cs b/Config.Net/Core/Box/CollectionResultBox.cs new file mode 100644 index 0000000..7f2f943 --- /dev/null +++ b/Config.Net/Core/Box/CollectionResultBox.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Config.Net.Core.Box +{ + class CollectionResultBox : ResultBox + { + private readonly ResultBox _elementResultBox; + private string? _basePath; + private DynamicReader? _reader; + + public CollectionResultBox(string name, ResultBox elementBox) : base(name, elementBox.ResultType, null) + { + _elementResultBox = elementBox; + } + + public ResultBox ElementResultBox => _elementResultBox; + + public bool IsInitialised { get; private set; } + + public IEnumerable? CollectionInstance { get; private set; } + + public void Initialise(string? basePath, int length, DynamicReader reader) + { + _basePath = basePath; + _reader = reader; + + CollectionInstance = CreateGenericEnumerable(length); + + IsInitialised = true; + } + + private IEnumerable? CreateGenericEnumerable(int count) + { + Type t = typeof(DynamicEnumerable<>); + t = t.MakeGenericType(ResultType); + + IEnumerable? instance = (IEnumerable?)Activator.CreateInstance(t, count, this); + + return instance; + } + + private object? ReadAt(int index) + { + return _reader?.Read(ElementResultBox, index); + } + + private class DynamicEnumerable : IEnumerable + { + private readonly int _count; + private readonly CollectionResultBox _parent; + + public DynamicEnumerable(int count, CollectionResultBox parent) + { + _count = count; + _parent = parent; + } + + public IEnumerator GetEnumerator() + { + return new DynamicEnumerator(_count, _parent); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new DynamicEnumerator(_count, _parent); + } + } + + private class DynamicEnumerator : IEnumerator + { + private int _index = -1; + private readonly int _count; + private readonly CollectionResultBox _parent; + private T? _current; + + public DynamicEnumerator(int count, CollectionResultBox parent) + { + _count = count; + _parent = parent; + } + +#pragma warning disable CS8603 // Possible null reference return. + public T Current => _current ?? default(T); + + object IEnumerator.Current => _current; +#pragma warning restore CS8603 // Possible null reference return. + + public void Dispose() + { + } + + public bool MoveNext() + { + _index += 1; + + _current = (T?)_parent.ReadAt(_index); + + return _index < _count; + } + + public void Reset() + { + _index = -1; + } + } + } +} diff --git a/Config.Net/Core/Box/MethodResultBox.cs b/Config.Net/Core/Box/MethodResultBox.cs new file mode 100644 index 0000000..adf29c3 --- /dev/null +++ b/Config.Net/Core/Box/MethodResultBox.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace Config.Net.Core.Box +{ + class MethodResultBox : ResultBox + { + public MethodResultBox(MethodInfo methodInfo) : base(GetName(methodInfo), GetReturnType(methodInfo), null) + { + StoreByName = GetStoreName(methodInfo); + IsGettter = IsGet(methodInfo); + } + + public bool IsGettter { get; private set; } + + /// + /// Composes a uniqueue method name using method name itself and parameter type names, separated by underscore + /// + public static string GetName(MethodInfo mi) + { + ParameterInfo[] parameters = mi.GetParameters(); + var sb = new StringBuilder(); + sb.Append(mi.Name); + foreach (ParameterInfo pi in parameters) + { + sb.Append("-"); + sb.Append(pi.ParameterType.ToString()); + } + return sb.ToString(); + } + + public string GetValuePath(object[] arguments) + { + var sb = new StringBuilder(); + sb.Append(StoreByName); + bool ignoreLast = !IsGettter; + + for (int i = 0; i < arguments.Length - (ignoreLast ? 1 : 0); i++) + { + object value = arguments[i]; + if (value == null) continue; + + if (sb.Length > 0) + { + sb.Append(OptionPath.Separator); + } + sb.Append(value.ToString()); + } + + return sb.ToString(); + } + + private static string GetStoreName(MethodInfo mi) + { + string name = mi.Name; + + if (name.StartsWith("get", StringComparison.OrdinalIgnoreCase) || + name.StartsWith("set", StringComparison.OrdinalIgnoreCase)) + { + name = name.Substring(3); + } + + return name; + } + + private static bool IsGet(MethodInfo mi) + { + return mi.ReturnType != typeof(void); + } + + private static Type GetReturnType(MethodInfo mi) + { + ParameterInfo[] parameters = mi.GetParameters(); + + if (parameters == null || parameters.Length == 0) + { + throw new InvalidOperationException($"method {mi.Name} must have at least one parameter"); + } + + Type returnType = IsGet(mi) ? mi.ReturnType : parameters[parameters.Length - 1].ParameterType; + + return returnType; + } + } +} diff --git a/Config.Net/Core/Box/PropertyResultBox.cs b/Config.Net/Core/Box/PropertyResultBox.cs new file mode 100644 index 0000000..0bef2c2 --- /dev/null +++ b/Config.Net/Core/Box/PropertyResultBox.cs @@ -0,0 +1,51 @@ +using System; +using System.Reflection; + +namespace Config.Net.Core.Box +{ + class PropertyResultBox : ResultBox + { + public PropertyResultBox(string name, Type resultType) : base(name, resultType, null) + { + } + + public static bool IsProperty(MethodInfo mi, out bool isGetter, out string? name) + { + if (mi.Name.StartsWith("get_")) + { + isGetter = true; + name = mi.Name.Substring(4); + return true; + } + + if (mi.Name.StartsWith("set_")) + { + isGetter = false; + name = mi.Name.Substring(4); + return true; + } + + isGetter = false; + name = null; + return false; + } + + public static bool IsProperty(MethodInfo mi, out string? name) + { + if (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")) + { + name = mi.Name.Substring(4); + return true; + } + + name = null; + return false; + } + + public static bool IsGetProperty(MethodInfo mi) + { + return mi.Name.StartsWith("get_"); + } + + } +} diff --git a/Config.Net/Core/Box/ProxyResultBox.cs b/Config.Net/Core/Box/ProxyResultBox.cs new file mode 100644 index 0000000..cff7c5b --- /dev/null +++ b/Config.Net/Core/Box/ProxyResultBox.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using Castle.DynamicProxy; + +namespace Config.Net.Core.Box +{ + class ProxyResultBox : ResultBox + { + private static readonly ProxyGenerator ProxyGenerator = new ProxyGenerator(); + private readonly Dictionary _indexToProxyInstance = new Dictionary(); + + public ProxyResultBox(string name, Type interfaceType) : base(name, interfaceType, null) + { + } + + public bool IsInitialisedAt(int index) + { + return _indexToProxyInstance.ContainsKey(index); + } + + public object GetInstanceAt(int index) + { + return _indexToProxyInstance[index]; + } + + public void InitialiseAt(int index, IoHandler ioHandler, string prefix) + { + object instance = ProxyGenerator.CreateInterfaceProxyWithoutTarget(ResultBaseType, + new InterfaceInterceptor(ResultBaseType, ioHandler, prefix)); + + _indexToProxyInstance[index] = instance; + } + } +} diff --git a/Config.Net/Core/Box/ResultBox.cs b/Config.Net/Core/Box/ResultBox.cs new file mode 100644 index 0000000..bfd96b4 --- /dev/null +++ b/Config.Net/Core/Box/ResultBox.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace Config.Net.Core.Box +{ + abstract class ResultBox + { + private string? _storeByName; + + protected ResultBox(string name, Type resultType, object? defaultResult) + { + Name = name; + ResultType = resultType; + ResultBaseType = GetBaseType(resultType); + DefaultResult = defaultResult; + } + + public string Name { get; } + + public string StoreByName + { + get => _storeByName ?? Name; + set => _storeByName = value; + } + + public Type ResultType { get; } + + public Type ResultBaseType { get; } + + public object? DefaultResult { get; set; } + + #region [ Utility Methods ] + + private static Type GetBaseType(Type t) + { + TypeInfo ti = t.GetTypeInfo(); + if (ti.IsClass) + { + return t; + } + else + { + if (ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + return ti.GenericTypeArguments[0]; + } + else + { + return t; + } + } + + } + + internal static bool TryGetCollection(Type t, out Type elementType) + { + TypeInfo ti = t.GetTypeInfo(); + + if(ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + elementType = ti.GenericTypeArguments[0]; + return true; + } + + elementType = t; + return false; + } + + #endregion + } +} diff --git a/Config.Net/Core/DynamicReader.cs b/Config.Net/Core/DynamicReader.cs new file mode 100644 index 0000000..81674c9 --- /dev/null +++ b/Config.Net/Core/DynamicReader.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Config.Net.Core.Box; + +namespace Config.Net.Core +{ + class DynamicReader + { + private readonly string? _basePath; + private readonly IoHandler _ioHandler; + + public DynamicReader(string? basePath, IoHandler ioHandler) + { + _basePath = basePath; + _ioHandler = ioHandler; + } + + public object? Read(ResultBox rbox, int index = -1, params object[] arguments) + { + if (rbox is PropertyResultBox pbox) return ReadProperty(pbox, index); + + if (rbox is ProxyResultBox xbox) return ReadProxy(xbox, index); + + if (rbox is CollectionResultBox cbox) return ReadCollection(cbox, index); + + if (rbox is MethodResultBox mbox) return ReadMethod(mbox, arguments); + + throw new NotImplementedException($"don't know how to read {rbox.GetType()}"); + } + + private object? ReadProperty(PropertyResultBox pbox, int index) + { + string path = OptionPath.Combine(index, _basePath, pbox.StoreByName); + + return _ioHandler.Read(pbox.ResultBaseType, path, pbox.DefaultResult); + } + + private object ReadProxy(ProxyResultBox xbox, int index) + { + if (!xbox.IsInitialisedAt(index)) + { + string prefix = OptionPath.Combine(index, _basePath, xbox.StoreByName); + + xbox.InitialiseAt(index, _ioHandler, prefix); + } + + return xbox.GetInstanceAt(index); + } + + private object? ReadCollection(CollectionResultBox cbox, int index) + { + string lengthPath = OptionPath.Combine(index, _basePath, cbox.StoreByName); + lengthPath = OptionPath.AddLength(lengthPath); + + if (!cbox.IsInitialised) + { + int length = (int?)_ioHandler.Read(typeof(int), lengthPath, 0) ?? 0; + + cbox.Initialise(_basePath, length, this); + } + + return cbox.CollectionInstance; + } + + private object? ReadMethod(MethodResultBox mbox, object[] arguments) + { + string path = mbox.GetValuePath(arguments); + path = OptionPath.Combine(_basePath, path); + + return _ioHandler.Read(mbox.ResultBaseType, path, mbox.DefaultResult); + } + } +} diff --git a/Config.Net/Core/DynamicWriter.cs b/Config.Net/Core/DynamicWriter.cs new file mode 100644 index 0000000..18039a8 --- /dev/null +++ b/Config.Net/Core/DynamicWriter.cs @@ -0,0 +1,48 @@ +using System; +using Config.Net.Core.Box; + +namespace Config.Net.Core +{ + class DynamicWriter + { + private readonly string? _basePath; + private readonly IoHandler _ioHandler; + + public DynamicWriter(string? basePath, IoHandler ioHandler) + { + _basePath = basePath; + _ioHandler = ioHandler; + } + + public void Write(ResultBox rbox, object[] arguments) + { + if (rbox is PropertyResultBox pbox) WriteProperty(pbox, arguments); + + else if (rbox is MethodResultBox mbox) WriteMethod(mbox, arguments); + + else if (rbox is ProxyResultBox xbox) WriteProxy(xbox, arguments); + + else throw new NotImplementedException($"don't know how to write {rbox.GetType()}"); + } + + private void WriteProperty(PropertyResultBox pbox, object[] arguments) + { + string path = OptionPath.Combine(_basePath, pbox.StoreByName); + + _ioHandler.Write(pbox.ResultBaseType, path, arguments[0]); + } + + private void WriteMethod(MethodResultBox mbox, object[] arguments) + { + object value = arguments[arguments.Length - 1]; + string path = mbox.GetValuePath(arguments); + + _ioHandler.Write(mbox.ResultBaseType, path, value); + } + + private void WriteProxy(ProxyResultBox xbox, object[] arguments) + { + throw new NotSupportedException("cannot assign values to interface properties"); + } + } +} diff --git a/Config.Net/Core/Extensions.cs b/Config.Net/Core/Extensions.cs new file mode 100644 index 0000000..646d92e --- /dev/null +++ b/Config.Net/Core/Extensions.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Config.Net.Core +{ + static class Extensions + { + public static TValue? GetValueOrDefaultInternal(this IDictionary dictionary, TKey key) + where TKey: notnull + where TValue: class + { + if (!dictionary.TryGetValue(key, out TValue? value)) return default(TValue); + + return value; + } + } +} diff --git a/Config.Net/Core/FlatArrays.cs b/Config.Net/Core/FlatArrays.cs new file mode 100644 index 0000000..e22bf56 --- /dev/null +++ b/Config.Net/Core/FlatArrays.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Config.Net.Core; +using Config.Net.TypeParsers; + +namespace Config.Net.Core +{ + /// + /// Helper class to implement flat arrays + /// + public static class FlatArrays + { + public static bool IsArrayLength(string? key, Func getValue, out int length) + { + if (!OptionPath.TryStripLength(key, out key)) + { + length = 0; + return false; + } + + string? value = key == null ? null : getValue(key); + if (value == null) + { + length = 0; + return false; + } + + if (!StringArrayParser.TryParse(value, out string[]? ar)) + { + length = 0; + return false; + } + + length = ar?.Length ?? 0; + return true; + } + + public static bool IsArrayElement(string? key, Func getValue, out string? value) + { + if(!OptionPath.TryStripIndex(key, out key, out int index)) + { + value = null; + return false; + } + + string? arrayString = key == null ? null : getValue(key); + if (!StringArrayParser.TryParse(arrayString, out string[]? array) || index >= array?.Length) + { + value = null; + return false; + } + + value = array?[index] ?? null; + return true; + } + } +} diff --git a/Config.Net/Core/InterfaceInterceptor.cs b/Config.Net/Core/InterfaceInterceptor.cs new file mode 100644 index 0000000..b02d3b3 --- /dev/null +++ b/Config.Net/Core/InterfaceInterceptor.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; +using System.Text; +using Castle.DynamicProxy; +using Config.Net.Core.Box; + +namespace Config.Net.Core +{ + class InterfaceInterceptor : IInterceptor + { + private readonly Dictionary _boxes; + private IoHandler _ioHandler; + private readonly string? _prefix; + private readonly DynamicReader _reader; + private readonly DynamicWriter _writer; + private readonly bool _isInpc; + private PropertyChangedEventHandler? _inpcHandler; + + public InterfaceInterceptor(Type interfaceType, IoHandler ioHandler, string? prefix = null) + { + _boxes = BoxFactory.Discover(interfaceType, ioHandler.ValueHandler, prefix); + _ioHandler = ioHandler; + _prefix = prefix; + _reader = new DynamicReader(prefix, ioHandler); + _writer = new DynamicWriter(prefix, ioHandler); + _isInpc = interfaceType.GetInterface(nameof(INotifyPropertyChanged)) != null; + } + + private ResultBox FindBox(IInvocation invocation) + { + if (PropertyResultBox.IsProperty(invocation.Method, out string? propertyName) && propertyName != null) + { + return _boxes[propertyName]; + } + else //method + { + string name = MethodResultBox.GetName(invocation.Method); + return _boxes[name]; + } + } + + public void Intercept(IInvocation invocation) + { + if (TryInterceptInpc(invocation)) return; + + ResultBox rbox = FindBox(invocation); + + bool isRead = + (rbox is PropertyResultBox && PropertyResultBox.IsGetProperty(invocation.Method)) || + (rbox is ProxyResultBox && PropertyResultBox.IsGetProperty(invocation.Method)) || + (rbox is MethodResultBox mbox && mbox.IsGettter) || + (rbox is CollectionResultBox); + + if(isRead) + { + invocation.ReturnValue = _reader.Read(rbox, -1, invocation.Arguments); + return; + } + else + { + _writer.Write(rbox, invocation.Arguments); + + TryNotifyInpc(invocation, rbox); + } + } + + private bool TryInterceptInpc(IInvocation invocation) + { + if (!_isInpc) return false; + + if (invocation.Method.Name == "add_PropertyChanged") + { + invocation.ReturnValue = + _inpcHandler = + (PropertyChangedEventHandler)Delegate.Combine(_inpcHandler, (Delegate)invocation.Arguments[0]); + return true; + } + else if(invocation.Method.Name == "remove_PropertyChanged") + { + invocation.ReturnValue = + _inpcHandler = + (PropertyChangedEventHandler?)Delegate.Remove(_inpcHandler, (Delegate)invocation.Arguments[0]); + return true; + } + + return false; + } + + private void TryNotifyInpc(IInvocation invocation, ResultBox rbox) + { + if (_inpcHandler == null || rbox is MethodResultBox) return; + + _inpcHandler.Invoke(invocation.InvocationTarget, new PropertyChangedEventArgs(rbox.Name)); + if(rbox.Name != rbox.StoreByName) + { + //notify on StoreByName as well + _inpcHandler.Invoke(invocation.InvocationTarget, new PropertyChangedEventArgs(rbox.StoreByName)); + } + } + } +} diff --git a/Config.Net/Core/IoHandler.cs b/Config.Net/Core/IoHandler.cs new file mode 100644 index 0000000..0d8e0e2 --- /dev/null +++ b/Config.Net/Core/IoHandler.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace Config.Net.Core +{ + class IoHandler + { + private readonly IEnumerable _stores; + private readonly ValueHandler _valueHandler; + private readonly TimeSpan _cacheInterval; + private readonly ConcurrentDictionary> _keyToValue = new ConcurrentDictionary>(); + + public IoHandler(IEnumerable stores, ValueHandler valueHandler, TimeSpan cacheInterval) + { + _stores = stores ?? throw new ArgumentNullException(nameof(stores)); + _valueHandler = valueHandler ?? throw new ArgumentNullException(nameof(valueHandler)); + _cacheInterval = cacheInterval; + } + + public ValueHandler ValueHandler => _valueHandler; + + public object? Read(Type baseType, string path, object? defaultValue) + { + if(!_keyToValue.TryGetValue(path, out _)) + { + var v = new LazyVar(_cacheInterval, () => ReadNonCached(baseType, path, defaultValue)); + _keyToValue[path] = v; + return v.GetValue(); + } + + return _keyToValue[path].GetValue(); + } + + public void Write(Type baseType, string path, object? value) + { + string? valueToWrite = _valueHandler.ConvertValue(baseType, value); + + foreach (IConfigStore store in _stores.Where(s => s.CanWrite)) + { + store.Write(path, valueToWrite); + } + } + + private object? ReadNonCached(Type baseType, string path, object? defaultValue) + { + string? rawValue = ReadFirstValue(path); + + return _valueHandler.ParseValue(baseType, rawValue, defaultValue); + } + + private string? ReadFirstValue(string key) + { + foreach (IConfigStore store in _stores) + { + if (store.CanRead) + { + string? value = store.Read(key); + + if (value != null) return value; + } + } + return null; + } + + + + } +} diff --git a/Config.Net/Core/LazyVar.cs b/Config.Net/Core/LazyVar.cs new file mode 100644 index 0000000..202f800 --- /dev/null +++ b/Config.Net/Core/LazyVar.cs @@ -0,0 +1,96 @@ +using System; +using System.Threading.Tasks; + +namespace Config.Net.Core +{ + /// + /// Implements a lazy value i.e. that can expire in future + /// + /// + class LazyVar where T : class + { + private readonly Func>? _renewFuncAsync; + private readonly Func? _renewFunc; + private DateTime _lastRenewed = DateTime.MinValue; + private readonly TimeSpan _timeToLive; + private T? _value; + + /// + /// Creates an instance of a lazy variable with time-to-live value + /// + /// Time to live. Setting to disables caching completely + /// + public LazyVar(TimeSpan timeToLive, Func> renewFunc) + { + _timeToLive = timeToLive; + _renewFuncAsync = renewFunc ?? throw new ArgumentNullException(nameof(renewFunc)); + _renewFunc = null; + } + + /// + /// Creates an instance of a lazy variable with time-to-live value + /// + /// Time to live. Setting to disables caching completely + /// + public LazyVar(TimeSpan timeToLive, Func renewFunc) + { + _timeToLive = timeToLive; + _renewFuncAsync = null; + _renewFunc = renewFunc ?? throw new ArgumentNullException(nameof(renewFunc)); + } + + /// + /// Gets the values, renewing it if necessary + /// + /// Value + public async Task GetValueAsync() + { + if (_renewFuncAsync == null) + { + throw new InvalidOperationException("cannot renew value, async delegate is not specified"); + } + + if (_timeToLive == TimeSpan.Zero) + { + return await _renewFuncAsync(); + } + + bool expired = (DateTime.UtcNow - _lastRenewed) > _timeToLive; + + if (expired) + { + _value = await _renewFuncAsync(); + _lastRenewed = DateTime.UtcNow; + } + + return _value; + } + + /// + /// Gets the values, renewing it if necessary + /// + /// Value + public T? GetValue() + { + if (_renewFunc == null) + { + throw new InvalidOperationException("cannot renew value, synchronous delegate is not specified"); + } + + if (_timeToLive == TimeSpan.Zero) + { + return _renewFunc(); + } + + bool expired = (DateTime.UtcNow - _lastRenewed) > _timeToLive; + + if (expired) + { + _value = _renewFunc(); + _lastRenewed = DateTime.UtcNow; + } + + return _value; + } + } +} diff --git a/Config.Net/Core/OptionPath.cs b/Config.Net/Core/OptionPath.cs new file mode 100644 index 0000000..ff1a02f --- /dev/null +++ b/Config.Net/Core/OptionPath.cs @@ -0,0 +1,88 @@ +namespace Config.Net.Core +{ + public static class OptionPath + { + public const string Separator = "."; + private const string IndexOpen = "["; + private const string IndexClose = "]"; + public const string LengthFunction = ".$l"; + + public static string Combine(params string?[] parts) + { + return Combine(-1, parts); + } + + public static string AddLength(string path) + { + return path + LengthFunction; + } + + public static bool TryStripLength(string? path, out string? noLengthPath) + { + if (path == null) + { + noLengthPath = path; + return false; + } + + if (!path.EndsWith(LengthFunction)) + { + noLengthPath = path; + return false; + } + + noLengthPath = path.Substring(0, path.Length - LengthFunction.Length); + return true; + } + + /// + /// For indexed paths like "creds[1]" strips index part so it becomes: + /// - noIndexPath: "creds" + /// - index: 1 + /// + /// If path is not indexed returns false and noIndexPath is equal to path itself + /// + public static bool TryStripIndex(string? path, out string? noIndexPath, out int index) + { + if (path == null) + { + index = 0; + noIndexPath = path; + return false; + } + + int openIdx = path.IndexOf(IndexOpen); + int closeIdx = path.IndexOf(IndexClose); + + if (openIdx == -1 || closeIdx == -1 || openIdx > closeIdx || closeIdx != path.Length - 1) + { + noIndexPath = path; + index = 0; + return false; + } + + noIndexPath = path.Substring(0, openIdx); + int.TryParse(path.Substring(openIdx + 1, closeIdx - openIdx - 1), out index); + return true; + } + + public static string Combine(int index, params string?[] parts) + { + string s = string.Empty; + + for (int i = 0; i < parts.Length; i++) + { + if (s.Length > 0) s += Separator; + + if (!string.IsNullOrEmpty(parts[i])) s += parts[i]; + } + + if (index != -1) + { + s = $"{s}{IndexOpen}{index}{IndexClose}"; + } + + return s; + } + } +} \ No newline at end of file diff --git a/Config.Net/Core/ValueHandler.cs b/Config.Net/Core/ValueHandler.cs new file mode 100644 index 0000000..56be158 --- /dev/null +++ b/Config.Net/Core/ValueHandler.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using Config.Net.TypeParsers; + +namespace Config.Net.Core +{ + class ValueHandler + { + private readonly DefaultParser _defaultParser = new DefaultParser(); + private readonly ConcurrentDictionary _allParsers = new ConcurrentDictionary(); + private readonly HashSet _supportedTypes = new HashSet(); + + private static readonly ValueHandler _default = new ValueHandler(); + + public ValueHandler(IEnumerable? customParsers = null) + { + foreach (ITypeParser pc in GetBuiltInParsers()) + { + AddParser(pc); + } + + if(customParsers != null) + { + foreach(ITypeParser pc in customParsers) + { + AddParser(pc); + } + } + } + + public void AddParser(ITypeParser parser) + { + if (parser == null) + { + throw new ArgumentNullException(nameof(parser)); + } + + foreach (Type t in parser.SupportedTypes) + { + _allParsers[t] = parser; + _supportedTypes.Add(t); + } + } + + public bool IsSupported(Type t) + { + return _supportedTypes.Contains(t) || _defaultParser.IsSupported(t); + } + + public object? ParseValue(Type baseType, string? rawValue, object? defaultValue) + { + object? result; + + if (rawValue == null) + { + result = defaultValue; + } + else + { + if(!TryParse(baseType, rawValue, out result)) + { + result = defaultValue; + } + } + + return result; + } + + public bool TryParse(Type propertyType, string? rawValue, out object? result) + { + if (_defaultParser.IsSupported(propertyType)) //type here must be a non-nullable one + { + if (!_defaultParser.TryParse(rawValue, propertyType, out result)) + { + return false; + } + } + else + { + ITypeParser? typeParser = GetParser(propertyType); + if (typeParser == null) + { + result = null; + return false; + } + if (!typeParser.TryParse(rawValue, propertyType, out result)) + { + return false; + } + } + + return true; + } + + public string? ConvertValue(Type baseType, object? value) + { + string? str; + + if (value == null) + { + str = null; + } + else + { + if (_defaultParser.IsSupported(baseType)) + { + str = _defaultParser.ToRawString(value); + } + else + { + ITypeParser? parser = GetParser(value.GetType()); + str = parser?.ToRawString(value); + } + } + + return str; + } + + private ITypeParser? GetParser(Type t) + { + ITypeParser? result; + _allParsers.TryGetValue(t, out result); + return result; + } + + /// + /// Scans assembly for types implementing and builds Type => instance dictionary. + /// Not sure if I should use reflection here, however the assembly is small and this shouldn't cause any + /// performance issues + /// + /// + private static IEnumerable GetBuiltInParsers() + { + return new ITypeParser[] + { + new FloatParser(), + new DoubleParser(), + new DecimalParser(), + new SByteParser(), + new ShortParser(), + new IntParser(), + new LongParser(), + new ByteParser(), + new UShortParser(), + new UIntParser(), + new ULongParser(), + new StringArrayParser(), + new StringParser(), + new TimeSpanParser(), + new CoreParsers(), + new NetworkCredentialParser() + }; + } + + } +} diff --git a/Config.Net/IConfigStore.cs b/Config.Net/IConfigStore.cs new file mode 100644 index 0000000..de1d949 --- /dev/null +++ b/Config.Net/IConfigStore.cs @@ -0,0 +1,35 @@ +using System; + +namespace Config.Net +{ + /// + /// Configuration store interface + /// + public interface IConfigStore : IDisposable + { + /// + /// Returns true if store supports read operation. + /// + bool CanRead { get; } + + /// + /// Returns true if store supports write operation. + /// + bool CanWrite { get; } + + /// + /// Reads a key from the store. + /// + /// Key name. + /// If key exists in the store returns the value, othwise returns null. + string? Read(string key); + + /// + /// Writes a key to the store. + /// + /// Key name + /// Key value. Value of NULL usually means the key will be deleted, at least + /// this is the recomendation for the custom store implementers. + void Write(string key, string? value); + } +} diff --git a/Config.Net/ITypeParser.cs b/Config.Net/ITypeParser.cs new file mode 100644 index 0000000..6793e0a --- /dev/null +++ b/Config.Net/ITypeParser.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; + +namespace Config.Net +{ + /// + /// Type parser interface + /// + public interface ITypeParser + { + /// + /// Returns the list of supported types this type parser handles + /// + IEnumerable SupportedTypes { get; } + + /// + /// Tries to parse a value from string + /// + /// + /// + /// + /// + bool TryParse(string? value, Type t, out object? result); + + /// + /// Converts value to a string to store in a backed store + /// + /// + /// + string? ToRawString(object? value); + } +} diff --git a/Config.Net/OptionAttribute.cs b/Config.Net/OptionAttribute.cs new file mode 100644 index 0000000..4fc11b5 --- /dev/null +++ b/Config.Net/OptionAttribute.cs @@ -0,0 +1,19 @@ +using System; + +namespace Config.Net +{ + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = false)] + public class OptionAttribute : Attribute + { + /// + /// Alias is used to override option name if it's stored by a different name in external stores + /// + public string? Alias { get; set; } + + /// + /// Set to override the default value if option is not found in any stores + /// + public object? DefaultValue { get; set; } + + } +} diff --git a/Config.Net/Properties/AssemblyInfo.cs b/Config.Net/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a746705 --- /dev/null +++ b/Config.Net/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Config.Net.Tests")] \ No newline at end of file diff --git a/Config.Net/SettingsContainer.cs b/Config.Net/SettingsContainer.cs new file mode 100644 index 0000000..2fdad37 --- /dev/null +++ b/Config.Net/SettingsContainer.cs @@ -0,0 +1,313 @@ +/*using System; +using System.Reflection; +using System.Collections.Concurrent; +using Config.Net.TypeParsers; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; + +namespace Config.Net +{ + /// + /// Generic container for test settings + /// + public abstract class SettingsContainer + { + private readonly IConfigConfiguration _config = new ContainerConfiguration(); + + private readonly ConcurrentDictionary _nameToOption = + new ConcurrentDictionary(); + + private readonly ConcurrentDictionary _nameToOptionValue = + new ConcurrentDictionary(); + + private static readonly DefaultParser DefaultParser = new DefaultParser(); + + private readonly string _namespace; + private bool _isConfigured; + + /// + /// Constructs the container in default namespace + /// + protected SettingsContainer() : this(null) + { + } + + /// + /// Constructs the container allowing to specify a custom namespace + /// + /// + protected SettingsContainer(string namespaceName) + { + _namespace = namespaceName; + + DiscoverProperties(); + } + + /// + /// Reads the option value + /// + /// Option type + /// Option reference + /// Option value + public T Read(Option option) + { + CheckConfigured(); + + CheckCanParse(option.NonNullableType); + + OptionValue optionValue; + _nameToOptionValue.TryGetValue(option.Name, out optionValue); + + if (!optionValue.IsExpired(_config.CacheTimeout)) + { + return (T) optionValue.RawValue; + } + + string value = ReadFirstValue(option.Name); + if (value == null) + { + optionValue.RawValue = option.DefaultValue; + } + else if (DefaultParser.IsSupported(option.NonNullableType)) + { + object resultObject; + if (DefaultParser.TryParse(value, option.NonNullableType, out resultObject)) + { + optionValue.Update((T) resultObject); + } + else + { + optionValue.Update(option.DefaultValue); + } + } + else + { + ITypeParser typeParser = _config.GetParser(option.NonNullableType); + object result; + typeParser.TryParse(value, option.NonNullableType, out result); + optionValue.Update((T) result); + } + + OnReadOption(option, optionValue.RawValue); + + return (T) optionValue.RawValue; + } + + /// + /// Writes a new value to the option + /// + /// Option type + /// Option reference + /// New value + public void Write(Option option, T value) + { + CheckConfigured(); + + CheckCanParse(option.NonNullableType); + + OptionValue optionValue; + _nameToOptionValue.TryGetValue(option.Name, out optionValue); + + foreach (IConfigStore store in _config.Stores) + { + if (store.CanWrite) + { + string rawValue = AreEqual(value, option.DefaultValue) ? null : GetRawStringValue(option, value); + store.Write(option.Name, rawValue); + break; + } + } + + optionValue.Update(value); + OnWriteOption(option, value); + } + + /// + /// This method is called internally before containers is ready for use. You can specify + /// configuration stores or any other options here. + /// + /// + protected abstract void OnConfigure(IConfigConfiguration configuration); + + /// + /// Called after any value is read + /// + /// Optiond that is read + /// Option value read from a store + protected virtual void OnReadOption(Option option, object value) + { + } + + /// + /// Called before any value is written + /// + /// Option that is written + /// Option value to write + protected virtual void OnWriteOption(Option option, object value) + { + } + + private void CheckConfigured() + { + if (_isConfigured) return; + + OnConfigure(_config); + + _isConfigured = true; + } + + [Ignore] + private void DiscoverProperties() + { + Type t = this.GetType(); + Type optionType = typeof(Option); + + IEnumerable properties = t.GetRuntimeProperties() + .Where(f => f.PropertyType.GetTypeInfo().IsSubclassOf(optionType) && f.GetCustomAttribute() == null).ToList(); + // Only include fields that have not already been added as properties + IEnumerable fields = t.GetRuntimeFields() + .Where(f => f.IsPublic && f.FieldType.GetTypeInfo().IsSubclassOf(optionType)).ToList(); + + foreach (PropertyInfo pi in properties) + { + AssignOption(pi.GetValue(this), pi, pi.PropertyType.GetTypeInfo(), pi.CanWrite, v => pi.SetValue(this, v)); + } + foreach (FieldInfo fi in fields) + { + if (properties.Any(p => p.Name == fi.Name)) + throw new ArgumentException( + $"Field '{fi.Name}' has already been defined as a property."); + + var methInfo = fi.FieldType.GetTypeInfo(); + if (!methInfo.IsSubclassOf(optionType)) continue; + AssignOption(fi.GetValue(this), fi, methInfo, true, v => fi.SetValue(this, v)); + } + } + + private void AssignOption(object objValue, MemberInfo pi, TypeInfo propInfo, bool writeable, + Action setter) + { + { + //check if it has the value + if (objValue == null) + { + // Throw an exception if it's impossible to assign a default value to a read-only property with no default object assigned + if (!writeable) + throw new ArgumentException( + $"Property/Field '{pi.Name}' must either be settable or be pre-initialised with an Option<> object as a property, or marked as readonly if a field"); + + //create default instance if it doesn't exist + var nt = typeof(Option<>); + Type[] ntArgs = propInfo.GetGenericArguments(); + Type ntGen = nt.MakeGenericType(ntArgs); + objValue = Activator.CreateInstance(ntGen); + + //set the instance value back to the container + setter(objValue); + } + + Option value = (Option) objValue; + if (string.IsNullOrEmpty(value.Name)) value.Name = pi.Name; + value.Name = GetFullKeyName(value.Name); + value._parent = this; + value.NonNullableType = Nullable.GetUnderlyingType(value.ValueType); + value.IsNullable = value.NonNullableType != null; + if (value.NonNullableType == null) value.NonNullableType = value.ValueType; + + _nameToOption[value.Name] = value; + _nameToOptionValue[value.Name] = new OptionValue(); + } + } + + private string GetFullKeyName(string name) + { + if (string.IsNullOrEmpty(_namespace)) return name; + + return _namespace + "." + name; + } + + private bool CanParse(Type t) + { + return _config.HasParser(t) || DefaultParser.IsSupported(t); + } + + private string ReadFirstValue(string key) + { + foreach (IConfigStore store in _config.Stores) + { + if (store.CanRead) + { + string value = store.Read(key); + + if (value != null) return value; + } + } + return null; + } + + private void CheckCanParse(Type t) + { + if (!CanParse(t)) + { + throw new ArgumentException("value parser for " + t.FullName + + " is not registered and not supported by default parser"); + } + } + + private bool AreEqual(object value1, object value2) + { + if (value1 == null && value2 == null) return true; + + if (value1 != null && value2 != null) + { + Type t1 = value1.GetType(); + Type t2 = value2.GetType(); + + if (t1.IsArray && t2.IsArray) + { + return AreEqual((Array) value1, (Array) value2); + } + } + + return value1 != null && value1.Equals(value2); + } + + private bool AreEqual(Array a, Array b) + { + if (a == null && b == null) return true; + + if (a == null || b == null) return false; + + if (a.Length != b.Length) return false; + + for (int i = 0; i < a.Length; i++) + { + object obj1 = a.GetValue(i); + object obj2 = b.GetValue(i); + + if (!AreEqual(obj1, obj2)) return false; + } + + return true; + } + + private string GetRawStringValue(Option option, T value) + { + string stringValue = null; + ITypeParser typeParser = _config.GetParser(option.NonNullableType); + if (typeParser != null) + { + stringValue = typeParser.ToRawString(value); + } + else + { + if (DefaultParser.IsSupported(typeof(T))) + { + stringValue = DefaultParser.ToRawString(value); + } + } + return stringValue; + } + } +}*/ \ No newline at end of file diff --git a/Config.Net/Stores/AppConfigStore.cs b/Config.Net/Stores/AppConfigStore.cs new file mode 100644 index 0000000..2394436 --- /dev/null +++ b/Config.Net/Stores/AppConfigStore.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Specialized; +using System.Configuration; + +namespace Config.Net.Stores +{ + /// + /// Standard app.config (web.config) configuration store. Read-only. + /// + class AppConfigStore : IConfigStore + { + public string Name => "App.config"; + + public bool CanRead => true; + + public bool CanWrite => false; + + public string? Read(string key) + { + if(key == null) return null; + + //first, look at appsettings and connection strings + string? value = ConfigurationManager.AppSettings[key] ?? ConfigurationManager.ConnectionStrings[key]?.ConnectionString; + + if(value == null) + { + int idx = key.IndexOf('.'); + if(idx != -1) + { + string sectionName = key.Substring(0, idx); + if(ConfigurationManager.GetSection(sectionName) is NameValueCollection nvsc) + { + string keyName = key.Substring(idx + 1); + value = nvsc[keyName]; + } + } + } + + return value; + } + + public void Write(string key, string? value) => throw new NotSupportedException(); + + public void Dispose() + { + } + } +} \ No newline at end of file diff --git a/Config.Net/Stores/AssemblyConfigStore.cs b/Config.Net/Stores/AssemblyConfigStore.cs new file mode 100644 index 0000000..ea8f5f8 --- /dev/null +++ b/Config.Net/Stores/AssemblyConfigStore.cs @@ -0,0 +1,61 @@ +using System; +using System.Configuration; +using System.IO; +using System.Reflection; + +namespace Config.Net.Stores +{ + /// + /// Reads configuration from the .dll.config or .exe.config file. + /// + class AssemblyConfigStore : IConfigStore + { + private readonly Configuration _configuration; + + /// + /// Creates a new instance of assembly configuration store (.dll.config files) + /// + /// reference to the assembly to look for + public AssemblyConfigStore(Assembly assembly) + { + _configuration = ConfigurationManager.OpenExeConfiguration(assembly.Location); + } + + /// + /// Store name + /// + public string Name => Path.GetFileName(_configuration.FilePath); + + /// + /// Store is readable + /// + public bool CanRead => true; + + /// + /// Store is not writeable + /// + public bool CanWrite => false; + + /// + /// Reads the value by key + /// + public string? Read(string key) + { + KeyValueConfigurationElement element = _configuration.AppSettings.Settings[key]; + + return element?.Value; + } + + /// + /// Writing is not supported + /// + public void Write(string key, string? value) => throw new NotSupportedException(); + + /// + /// Nothing to dispose + /// + public void Dispose() + { + } + } +} \ No newline at end of file diff --git a/Config.Net/Stores/DictionaryConfigStore.cs b/Config.Net/Stores/DictionaryConfigStore.cs new file mode 100644 index 0000000..7d1245e --- /dev/null +++ b/Config.Net/Stores/DictionaryConfigStore.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using Config.Net.Core; + +namespace Config.Net.Stores +{ + class DictionaryConfigStore : IConfigStore + { + private readonly IDictionary _container; + + public DictionaryConfigStore(IDictionary? container = null) + { + _container = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + if(container != null) + { + foreach(KeyValuePair item in container) + { + _container[item.Key] = item.Value; + } + } + } + + public bool CanRead => true; + + public bool CanWrite => true; + + public void Dispose() + { + + } + + public string? Read(string key) + { + if (key == null) return null; + + if (FlatArrays.IsArrayLength(key, k => _container.GetValueOrDefaultInternal(k), out int length)) + { + return length.ToString(); + } + + if (FlatArrays.IsArrayElement(key, k => _container.GetValueOrDefaultInternal(k), out string? element)) + { + return element; + } + + return _container.GetValueOrDefaultInternal(key); + } + + public void Write(string key, string? value) + { + if (key == null) return; + + if (value == null) + { + _container.Remove(key); + } + else + { + _container[key] = value; + } + } + } +} diff --git a/Config.Net/Stores/EnvironmentVariablesStore.cs b/Config.Net/Stores/EnvironmentVariablesStore.cs new file mode 100644 index 0000000..3a03475 --- /dev/null +++ b/Config.Net/Stores/EnvironmentVariablesStore.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using Config.Net.Core; + +namespace Config.Net.Stores +{ + /// + /// Uses system environment variables + /// + class EnvironmentVariablesStore : IConfigStore + { + /// + /// Readable + /// + public bool CanRead => true; + + /// + /// Writeable + /// + public bool CanWrite => true; + + /// + /// Store name + /// + public string Name => "System Environment"; + + /// + /// Reads value by key + /// + public string? Read(string key) + { + if (key == null) return null; + + foreach(string variant in GetAllKeyVariants(key)) + { + if (FlatArrays.IsArrayLength(variant, k => Environment.GetEnvironmentVariable(k), out int length)) + { + return length.ToString(); + } + + if (FlatArrays.IsArrayElement(variant, k => Environment.GetEnvironmentVariable(k), out string? element)) + { + return element; + } + + string? value = Environment.GetEnvironmentVariable(variant); + if (value != null) return value; + } + + return null; + } + + /// + /// Writes value by key + /// + public void Write(string key, string? value) + { + Environment.SetEnvironmentVariable(key, value); + } + + private IEnumerable GetAllKeyVariants(string key) + { + var result = new List(); + result.Add(key); + result.Add(key.ToUpper().Replace(".", "_")); + return result; + } + + /// + /// Nothing to dispose + /// + public void Dispose() + { + } + } +} \ No newline at end of file diff --git a/Config.Net/Stores/Formats/Ini/IniComment.cs b/Config.Net/Stores/Formats/Ini/IniComment.cs new file mode 100644 index 0000000..0e83091 --- /dev/null +++ b/Config.Net/Stores/Formats/Ini/IniComment.cs @@ -0,0 +1,21 @@ +namespace Config.Net.Stores.Formats.Ini +{ + class IniComment : IniEntity + { + public const string CommentSeparator = ";"; + + public IniComment(string value) + { + Value = value; + } + + public string Value { get; set; } + + public string EscapedValue + { + get { return Value.Replace("\r", @"\r").Replace("\n", @"\n"); } + } + + public override string ToString() => Value; + } +} diff --git a/Config.Net/Stores/Formats/Ini/IniEntity.cs b/Config.Net/Stores/Formats/Ini/IniEntity.cs new file mode 100644 index 0000000..420a44e --- /dev/null +++ b/Config.Net/Stores/Formats/Ini/IniEntity.cs @@ -0,0 +1,6 @@ +namespace Config.Net.Stores.Formats.Ini +{ + abstract class IniEntity + { + } +} diff --git a/Config.Net/Stores/Formats/Ini/IniKeyValue.cs b/Config.Net/Stores/Formats/Ini/IniKeyValue.cs new file mode 100644 index 0000000..c290d7c --- /dev/null +++ b/Config.Net/Stores/Formats/Ini/IniKeyValue.cs @@ -0,0 +1,72 @@ +using System; + +namespace Config.Net.Stores.Formats.Ini +{ + internal class IniKeyValue : IniEntity + { + public const string KeyValueSeparator = "="; + + public IniKeyValue(string key, string value, string? comment) + { + if(key == null) throw new ArgumentNullException(nameof(key)); + Key = key; + Value = value; + Comment = comment == null ? null : new IniComment(comment); + } + + public string Key { get; } + + public string Value { get; set; } + + public string EscapedKey + { + get { return Key.Replace("\r", @"\r").Replace("\n", @"\n"); } + } + + public string EscapedValue + { + get { return Value.Replace("\r", @"\r").Replace("\n", @"\n"); } + } + + public IniComment? Comment { get; } + + public static IniKeyValue? FromLine(string line, bool parseInlineComments, bool unescapeNewLines = false) + { + int idx = line.IndexOf(KeyValueSeparator, StringComparison.CurrentCulture); + if(idx == -1) return null; + + string key = line.Substring(0, idx).Trim(); + string value = line.Substring(idx + 1).Trim(); + string? comment = null; + + if (parseInlineComments) + { + idx = value.LastIndexOf(IniComment.CommentSeparator, StringComparison.CurrentCulture); + if (idx != -1) + { + comment = value.Substring(idx + 1).Trim(); + value = value.Substring(0, idx).Trim(); + } + } + + if(unescapeNewLines) + { + key = UnescapeString(key); + value = UnescapeString(value); + comment = (comment != null) ? UnescapeString(comment) : null; + } + + return new IniKeyValue(key, value, comment); + } + + private static string UnescapeString(string key) + { + return key.Replace(@"\r", "\r").Replace(@"\n", "\n"); + } + + public override string ToString() + { + return $"{Value}"; + } + } +} diff --git a/Config.Net/Stores/Formats/Ini/IniSection.cs b/Config.Net/Stores/Formats/Ini/IniSection.cs new file mode 100644 index 0000000..d5074ff --- /dev/null +++ b/Config.Net/Stores/Formats/Ini/IniSection.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Config.Net.Stores.Formats.Ini +{ + class IniSection + { + public const string SectionKeySeparator = "."; + + private readonly List _entities = new List(); + private readonly Dictionary _keyToValue = new Dictionary(); + + /// + /// Section name + /// + public string? Name { get; set; } + + /// + /// + /// + /// Pass null to work with global section + public IniSection(string? name) + { + if(name != null) + { + if (name.StartsWith("[")) name = name.Substring(1); + if (name.EndsWith("]")) name = name.Substring(0, name.Length - 1); + } + + Name = name; + } + + public void Add(IniEntity entity) + { + _entities.Add(entity); + + IniKeyValue? ikv = entity as IniKeyValue; + if(ikv != null) + { + _keyToValue[ikv.Key] = ikv; + } + } + + public IniKeyValue? Set(string key, string? value) + { + if(value == null) + { + IniKeyValue? ikv; + if(_keyToValue.TryGetValue(key, out ikv)) + { + _keyToValue.Remove(key); + return ikv; + } + return null; + } + else + { + IniKeyValue? ikv; + if(_keyToValue.TryGetValue(key, out ikv)) + { + ikv.Value = value; + } + else + { + ikv = new IniKeyValue(key, value, null); + Add(ikv); + } + return ikv; + } + } + + public static void SplitKey(string fullKey, out string? sectionName, out string keyName) + { + int idx = fullKey.IndexOf(SectionKeySeparator, StringComparison.CurrentCulture); + + if(idx == -1) + { + sectionName = null; + keyName = fullKey; + } + else + { + sectionName = fullKey.Substring(0, idx); + keyName = fullKey.Substring(idx + 1); + } + } + + public void WriteTo(StreamWriter writer) + { + foreach(IniEntity entity in _entities) + { + IniKeyValue? ikv = entity as IniKeyValue; + if(ikv != null) + { + writer.Write($"{ikv.EscapedKey}{IniKeyValue.KeyValueSeparator}{ikv.EscapedValue}"); + if(ikv.Comment != null) + { + writer.Write(" "); + writer.Write(IniComment.CommentSeparator); + writer.Write(ikv.Comment.EscapedValue); + } + writer.WriteLine(); + continue; + } + + IniComment? comment = entity as IniComment; + if(comment != null) + { + writer.Write(IniComment.CommentSeparator); + writer.WriteLine(comment.Value); + } + } + } + + public override string ToString() + { + return Name ?? string.Empty; + } + } +} diff --git a/Config.Net/Stores/Formats/Ini/StructuredIniFile.cs b/Config.Net/Stores/Formats/Ini/StructuredIniFile.cs new file mode 100644 index 0000000..3b72f61 --- /dev/null +++ b/Config.Net/Stores/Formats/Ini/StructuredIniFile.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Config.Net.Stores.Formats.Ini +{ + class StructuredIniFile + { + private const string _sectionBegin = "["; + private const string _sectionEnd = "]"; + private static readonly char[] _sectionTrims = {'[', ']'}; + + private readonly IniSection _globalSection; + private readonly List _sections = new List(); + private readonly Dictionary _fullKeyNameToValue = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + public StructuredIniFile() + { + _globalSection = new IniSection(null); + _sections.Add(_globalSection); + } + + public string? this[string key] + { + get + { + if(key == null) return null; + + IniKeyValue? value; + return !_fullKeyNameToValue.TryGetValue(key, out value) ? null : value.Value; + } + set + { + if(key == null) return; + + IniSection.SplitKey(key, out string? sectionName, out string keyName); + IniSection? section = sectionName == null + ? _globalSection + : _sections.FirstOrDefault(s => s.Name == sectionName); + if(section == null) + { + section = new IniSection(sectionName); + _sections.Add(section); + } + IniKeyValue? ikv = section.Set(keyName, value); + + //update the local cache + if(ikv != null) + { + if(value == null) + { + _fullKeyNameToValue.Remove(key); + } + else + { + _fullKeyNameToValue[key] = ikv; + } + } + } + } + + public static StructuredIniFile ReadFrom(Stream inputStream, bool parseInlineComments, bool unescapeNewLines = false) + { + if(inputStream == null) throw new ArgumentNullException(nameof(inputStream)); + + var file = new StructuredIniFile(); + + using(var reader = new StreamReader(inputStream)) + { + IniSection section = file._globalSection; + + string? line; + while((line = reader.ReadLine()) != null) + { + line = line.Trim(); + + if(line.StartsWith(_sectionBegin)) + { + //start new section + line = line.Trim(); + section = new IniSection(line); + file._sections.Add(section); + } + else if(line.StartsWith(IniComment.CommentSeparator)) + { + //whole line is a comment + string comment = line.Substring(1).Trim(); + section.Add(new IniComment(comment)); + } + else + { + IniKeyValue? ikv = IniKeyValue.FromLine(line, parseInlineComments, unescapeNewLines); + if(ikv == null) continue; + + section.Add(ikv); + string fullKey = section.Name == null + ? ikv.Key + : $"{section.Name}{IniSection.SectionKeySeparator}{ikv.Key}"; + file._fullKeyNameToValue[fullKey] = ikv; + + } + } + } + + return file; + } + + public void WriteTo(Stream outputStream) + { + if(outputStream == null) throw new ArgumentNullException(nameof(outputStream)); + + using(var writer = new StreamWriter(outputStream)) + { + foreach(IniSection section in _sections) + { + if(section.Name != null) + { + writer.WriteLine(); + writer.WriteLine($"{_sectionBegin}{section.Name}{_sectionEnd}"); + } + + section.WriteTo(writer); + } + } + } + + //private static + } +} diff --git a/Config.Net/Stores/Impl/CommandLine/CommandLineConfigStore.cs b/Config.Net/Stores/Impl/CommandLine/CommandLineConfigStore.cs new file mode 100644 index 0000000..6b25708 --- /dev/null +++ b/Config.Net/Stores/Impl/CommandLine/CommandLineConfigStore.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using Config.Net.Core; +using Config.Net.TypeParsers; + +namespace Config.Net.Stores.Impl.CommandLine +{ + class CommandLineConfigStore : IConfigStore + { + private readonly Dictionary _nameToValue; + private static readonly char[] ArgPrefixes = new[] { '-', '/' }; + private static readonly string[] ArgDelimiters = new[] { "=", ":" }; + private readonly bool _isCaseSensitive; + + public bool CanRead => true; + + public bool CanWrite => false; + + public CommandLineConfigStore(string[]? args = null, bool isCaseSensitive = false, IEnumerable>? nameToPosition = null) + { + _isCaseSensitive = isCaseSensitive; + + _nameToValue = new Dictionary(_isCaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase); + + Parse(args ?? Environment.GetCommandLineArgs(), nameToPosition); + } + + public void Dispose() + { + } + + public string? Read(string key) + { + if (key == null) return null; + + if(FlatArrays.IsArrayLength(key, k => _nameToValue.GetValueOrDefaultInternal(k), out int length)) + { + return length.ToString(); + } + + if(FlatArrays.IsArrayElement(key, k => _nameToValue.GetValueOrDefaultInternal(k), out string? element)) + { + return element; + } + + string? value; + _nameToValue.TryGetValue(key, out value); + return value; + } + + private string[]? GetAsArray(string key) + { + if (!_nameToValue.TryGetValue(key, out string? allString)) return null; + + if (!StringArrayParser.TryParse(allString, out string[]? ar)) return null; + return ar; + } + + public void Write(string key, string? value) + { + throw new NotSupportedException("command line cannot be written to"); + } + + private void Parse(string[] args, IEnumerable>? nameToPosition) + { + _nameToValue.Clear(); + + var posToName = new Dictionary(); + if (nameToPosition != null) + { + foreach(KeyValuePair p in nameToPosition) + { + if (p.Key != null) + { + posToName[p.Value] = p.Key; + } + } + } + + if (args == null) return; + + for (int i = 0; i < args.Length; i++) + { + string? name; + string? value; + + Tuple? nameValue = Utils.SplitByDelimiter(args[i], ArgDelimiters); + name = nameValue?.Item1.TrimStart(ArgPrefixes); + value = nameValue?.Item2; + + if (name != null && value != null) + { + _nameToValue[name] = value; + } + else if(name != null && posToName.TryGetValue(i, out string? ptnName)) + { + _nameToValue[ptnName] = args[i]; + } + } + } + } +} \ No newline at end of file diff --git a/Config.Net/Stores/IniFileConfigStore.cs b/Config.Net/Stores/IniFileConfigStore.cs new file mode 100644 index 0000000..8d16976 --- /dev/null +++ b/Config.Net/Stores/IniFileConfigStore.cs @@ -0,0 +1,120 @@ +using System; +using System.IO; +using System.Text; +using Config.Net.Core; +using Config.Net.Stores.Formats.Ini; + +namespace Config.Net.Stores +{ + /// + /// Simple INI storage. + /// + class IniFileConfigStore : IConfigStore + { + private readonly string? _fullName; + private readonly StructuredIniFile _iniFile; + + /// + /// + /// r + /// File does not have to exist, however it will be created as soon as you + /// try to write to it + public IniFileConfigStore(string name, bool isFilePath, bool parseInlineComments, bool unescapeNewLines = false) + { + if (name == null) throw new ArgumentNullException(nameof(name)); + + if (isFilePath) + { + _fullName = Path.GetFullPath(name); // Allow relative path to INI file + + string? parentDirPath = Path.GetDirectoryName(_fullName); + if (string.IsNullOrEmpty(parentDirPath)) throw new IOException("the provided directory path is not valid"); + if (!Directory.Exists(parentDirPath)) + { + Directory.CreateDirectory(parentDirPath); + } + + _iniFile = ReadIniFile(_fullName, parseInlineComments, unescapeNewLines); + + CanWrite = true; + } + else + { + _iniFile = ReadIniContent(name, parseInlineComments, unescapeNewLines); + + CanWrite = false; + } + + CanRead = true; + } + + public string Name => ".ini"; + + public bool CanRead { get; } + + public bool CanWrite { get; } + + public string? Read(string key) + { + if (FlatArrays.IsArrayLength(key, k => _iniFile[k], out int length)) + { + return length.ToString(); + } + + if (FlatArrays.IsArrayElement(key, k => _iniFile[k], out string? element)) + { + return element; + } + + return _iniFile[key]; + } + + public void Write(string key, string? value) + { + if (!CanWrite) return; + + _iniFile[key] = value; + + WriteIniFile(); + } + + private static StructuredIniFile ReadIniFile(string fullName, bool parseInlineComments, bool unescapeNewLines = false) + { + FileInfo iniFile = new FileInfo(fullName); + if(iniFile.Exists) + { + using(FileStream stream = iniFile.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + return StructuredIniFile.ReadFrom(stream, parseInlineComments, unescapeNewLines); + } + } + else + { + return new StructuredIniFile(); + } + } + + private static StructuredIniFile ReadIniContent(string content, bool parseInlineComments, bool unescapeNewLines = false) + { + using (Stream input = new MemoryStream(Encoding.UTF8.GetBytes(content))) + { + return StructuredIniFile.ReadFrom(input, parseInlineComments, unescapeNewLines); + } + } + + private void WriteIniFile() + { + if (_fullName == null) return; + + using(FileStream stream = File.Create(_fullName)) + { + _iniFile.WriteTo(stream); + } + } + + public void Dispose() + { + //nothing to dispose + } + } +} diff --git a/Config.Net/Stores/JsonConfigStore.cs b/Config.Net/Stores/JsonConfigStore.cs new file mode 100644 index 0000000..1859ec3 --- /dev/null +++ b/Config.Net/Stores/JsonConfigStore.cs @@ -0,0 +1,165 @@ +using System; +using System.IO; +using System.Runtime.InteropServices.ComTypes; +using System.Text.Json; +using System.Text.Json.Nodes; +using Config.Net.Core; + +namespace Config.Net.Stores +{ + /// + /// Simple JSON storage using System.Text.Json + /// + public class JsonConfigStore : IConfigStore + { + private readonly string? _pathName; + private JsonNode? _j; + + /// + /// Create JSON storage in the file specified in . + /// + /// Name of the file, either path to JSON storage file, or json file content. + /// Set to true if specifies file name, otherwise false. + /// is null. + /// Provided path is not valid. + /// Storage file does not have to exist, however it will be created as soon as first write performed. + public JsonConfigStore(string name, bool isFilePath) + { + if (name == null) throw new ArgumentNullException(nameof(name)); + + if (isFilePath) + { + _pathName = Path.GetFullPath(name); // Allow relative path to JSON file + _j = ReadJsonFile(_pathName); + } + else + { + _j = ReadJsonString(name); + } + } + + public void Dispose() + { + // nothing to dispose. + } + + public string Name => "json"; + + public bool CanRead => true; + + public bool CanWrite => _pathName != null; + + public string? Read(string rawKey) + { + if (string.IsNullOrEmpty(rawKey) || _j == null) return null; + + bool isLength = OptionPath.TryStripLength(rawKey, out string? key); + if (key == null) return null; + + string[] parts = key.Split('.'); + if (parts.Length == 0) return null; + + JsonNode? node = _j; + foreach (string rawPart in parts) + { + bool isIndex = OptionPath.TryStripIndex(rawPart, out string? part, out int partIndex); + if (part == null) return null; + + node = node![part]; + if (node == null) return null; + + if (isIndex) + { + if (!(node is JsonArray ja)) return null; + + if (partIndex < ja.Count) + { + node = ja[partIndex]; + } + else + return null; + } + } + + if (isLength) + return node is JsonArray ja ? ja.Count.ToString() : null; + + return node!.ToString(); + } + + public void Write(string key, string? value) + { + if (string.IsNullOrEmpty(_pathName)) + throw new InvalidOperationException("please specify file name for writeable config"); + + if (_j == null) _j = new JsonObject(); + + // navigate to target element, create if needed + string[] parts = key.Split('.'); + if (parts.Length == 0) return; + + JsonNode? node = _j; + string? lastPart = null; + foreach (string rawPart in parts) + { + bool isIndex = OptionPath.TryStripIndex(rawPart, out string? part, out int partIndex); + if (part == null) return; + lastPart = part; + + JsonNode? nextNode = node[part]; + + if (isIndex) + { + throw new NotImplementedException(); + } + else + { + if (nextNode == null) + { + //create missing node + nextNode = new JsonObject(); + node[part] = nextNode; + } + } + + node = nextNode; + + } + + JsonObject? parent = node.Parent as JsonObject; + parent!.Remove(lastPart!); + parent![lastPart!] = JsonValue.Create(value); + + string js = _j.ToJsonString(new JsonSerializerOptions { WriteIndented = true }); + + FileInfo file = new FileInfo(_pathName); + + if (file is not null) + { + if (file.Directory is not null) + { + file.Directory.Create(); + } + } + + File.WriteAllText(_pathName, js); + + } + + private static JsonNode? ReadJsonFile(string fileName) + { + if (File.Exists(fileName)) + { + string json = File.ReadAllText(fileName); + return ReadJsonString(json); + } + + return null; + } + + private static JsonNode? ReadJsonString(string jsonString) + { + return JsonNode.Parse(jsonString); + } + } +} \ No newline at end of file diff --git a/Config.Net/TypeParsers/ByteParser.cs b/Config.Net/TypeParsers/ByteParser.cs new file mode 100644 index 0000000..b6a228b --- /dev/null +++ b/Config.Net/TypeParsers/ByteParser.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Config.Net.TypeParsers +{ + class ByteParser : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(byte) }; + + public bool TryParse(string? value, Type t, out object? result) + { + if(value == null) + { + result = false; + return false; + } + + byte ir; + bool parsed; + if (value.StartsWith("0x")) + { + parsed = byte.TryParse(value.Substring(2), NumberStyles.HexNumber, TypeParserSettings.DefaultCulture, out ir); + } + else + { + parsed = byte.TryParse(value, NumberStyles.Integer, TypeParserSettings.DefaultCulture, out ir); + } + result = ir; + return parsed; + } + + public string? ToRawString(object? value) + { + return ((byte?)value)?.ToString(TypeParserSettings.DefaultNumericFormat, TypeParserSettings.DefaultCulture); + } + } +} diff --git a/Config.Net/TypeParsers/CoreParsers.cs b/Config.Net/TypeParsers/CoreParsers.cs new file mode 100644 index 0000000..3fa65ac --- /dev/null +++ b/Config.Net/TypeParsers/CoreParsers.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Config.Net.TypeParsers +{ + /// + /// Container for core types. Eventually all simple parsers will merge into this class. + /// + class CoreParsers : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(Uri), typeof(bool), typeof(Guid), typeof(DateTime) }; + + private static readonly Dictionary BooleanTrueValues = + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + {"true", true}, + {"yes", true}, + {"1", true}, + }; + + + public string? ToRawString(object? value) + { + if (value == null) return null; + + Type t = value.GetType(); + + if(t == typeof(Uri)) + return value.ToString(); + + if(t == typeof(bool)) + return value?.ToString()?.ToLowerInvariant(); + + if (t == typeof(Guid)) + return value.ToString(); + + if(t == typeof(DateTime)) + return ((DateTime)value).ToUniversalTime().ToString("u"); + + return null; + } + + public bool TryParse(string? value, Type t, out object? result) + { + if(value == null) + { + result = null; + return false; + } + + if(t == typeof(Uri)) + { + Uri uri = new Uri(value); + result = uri; + return true; + } + + if(t == typeof(bool)) + { + if(BooleanTrueValues.ContainsKey(value)) + { + result = true; + return true; + } + + result = false; + return true; + } + + if(t == typeof(Guid)) + { + if(Guid.TryParse(value, out Guid tg)) + { + result = tg; + return true; + } + + result = null; + return false; + } + + if(t == typeof(DateTime)) + { + DateTime dateResult; + bool parsed = DateTime.TryParse(value, TypeParserSettings.DefaultCulture, DateTimeStyles.None, out dateResult); + result = dateResult; + return parsed; + } + + result = null; + return false; + } + } +} diff --git a/Config.Net/TypeParsers/DecimalParser.cs b/Config.Net/TypeParsers/DecimalParser.cs new file mode 100644 index 0000000..ecf3e75 --- /dev/null +++ b/Config.Net/TypeParsers/DecimalParser.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Config.Net.TypeParsers +{ + class DecimalParser : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(decimal) }; + + public bool TryParse(string? value, Type t, out object? result) + { + decimal dr; + bool parsed = decimal.TryParse(value, NumberStyles.Float, TypeParserSettings.DefaultCulture, out dr); + result = dr; + return parsed; + + } + + public string? ToRawString(object? value) + { + return ((decimal?)value)?.ToString(TypeParserSettings.DefaultNumericFormat, TypeParserSettings.DefaultCulture); + } + } +} diff --git a/Config.Net/TypeParsers/DefaultParser.cs b/Config.Net/TypeParsers/DefaultParser.cs new file mode 100644 index 0000000..15e9929 --- /dev/null +++ b/Config.Net/TypeParsers/DefaultParser.cs @@ -0,0 +1,69 @@ +using System; +using System.Reflection; + +namespace Config.Net.TypeParsers +{ + class DefaultParser + { + public bool TryParse(string? value, Type t, out object? result) + { + if(IsEnum(t)) + { + if(value == null) + { + result = null; + return false; + } + + try + { + result = Enum.Parse(t, value, true); + return true; + } + catch(ArgumentException) + { + + } + catch(OverflowException) + { + + } + + result = null; + return false; + } + + throw new NotSupportedException(); + } + + public bool IsSupported(Type t) + { + return IsEnum(t); + } + + public string? ToRawString(object? value) + { + if(value == null) return null; + + Type t = value.GetType(); + + if(IsEnum(t)) + { + return value.ToString(); + } + + throw new NotSupportedException(); + } + + static bool IsEnum(Type t) + { + if(t == null) return false; + + //try to get the underlying type if this is a nullable type + Type? nullable = Nullable.GetUnderlyingType(t); + if(nullable != null) t = nullable; + + return t.GetTypeInfo().IsEnum; + } + } +} diff --git a/Config.Net/TypeParsers/DoubleParser.cs b/Config.Net/TypeParsers/DoubleParser.cs new file mode 100644 index 0000000..2e4178d --- /dev/null +++ b/Config.Net/TypeParsers/DoubleParser.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Config.Net.TypeParsers +{ + class DoubleParser : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(double) }; + + public bool TryParse(string? value, Type t, out object? result) + { + double dr; + bool parsed = double.TryParse(value, NumberStyles.Float, TypeParserSettings.DefaultCulture, out dr); + result = dr; + return parsed; + + } + + public string? ToRawString(object? value) + { + return ((double?)value)?.ToString(TypeParserSettings.DefaultNumericFormat, TypeParserSettings.DefaultCulture); + } + } +} diff --git a/Config.Net/TypeParsers/FloatParser.cs b/Config.Net/TypeParsers/FloatParser.cs new file mode 100644 index 0000000..3f48724 --- /dev/null +++ b/Config.Net/TypeParsers/FloatParser.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Config.Net.TypeParsers +{ + class FloatParser : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(float) }; + + public bool TryParse(string? value, Type t, out object? result) + { + float dr; + bool parsed = float.TryParse(value, NumberStyles.Float, TypeParserSettings.DefaultCulture, out dr); + result = dr; + return parsed; + + } + + public string? ToRawString(object? value) + { + return ((float?)value)?.ToString(TypeParserSettings.DefaultNumericFormat, TypeParserSettings.DefaultCulture); + } + } +} diff --git a/Config.Net/TypeParsers/IntParser.cs b/Config.Net/TypeParsers/IntParser.cs new file mode 100644 index 0000000..a89a4dc --- /dev/null +++ b/Config.Net/TypeParsers/IntParser.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Config.Net.TypeParsers +{ + class IntParser : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(int) }; + + public bool TryParse(string? value, Type t, out object? result) + { + int ir; + bool parsed = int.TryParse(value, NumberStyles.Integer, TypeParserSettings.DefaultCulture, out ir); + result = ir; + return parsed; + } + + public string? ToRawString(object? value) + { + return ((int?)value)?.ToString(TypeParserSettings.DefaultNumericFormat, TypeParserSettings.DefaultCulture); + } + } +} diff --git a/Config.Net/TypeParsers/LongParser.cs b/Config.Net/TypeParsers/LongParser.cs new file mode 100644 index 0000000..f576a83 --- /dev/null +++ b/Config.Net/TypeParsers/LongParser.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Config.Net.TypeParsers +{ + class LongParser : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(long) }; + + public bool TryParse(string? value, Type t, out object? result) + { + long lr; + bool parsed = long.TryParse(value, NumberStyles.Integer, TypeParserSettings.DefaultCulture, out lr); + result = lr; + return parsed; + } + + public string? ToRawString(object? value) + { + return ((long?)value)?.ToString(TypeParserSettings.DefaultNumericFormat, TypeParserSettings.DefaultCulture); + } + } +} diff --git a/Config.Net/TypeParsers/NetworkCredentialParser.cs b/Config.Net/TypeParsers/NetworkCredentialParser.cs new file mode 100644 index 0000000..f3c30be --- /dev/null +++ b/Config.Net/TypeParsers/NetworkCredentialParser.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Net; + +namespace Config.Net.TypeParsers +{ + class NetworkCredentialParser : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(NetworkCredential) }; + + public string? ToRawString(object? value) + { + NetworkCredential? nc = value as NetworkCredential; + return Utils.ToFriendlyString(nc); + } + + public bool TryParse(string? value, Type t, out object? result) + { + NetworkCredential? nc = value.ToNetworkCredential(); + result = nc; + return true; + } + } +} diff --git a/Config.Net/TypeParsers/SByteParser.cs b/Config.Net/TypeParsers/SByteParser.cs new file mode 100644 index 0000000..4a38631 --- /dev/null +++ b/Config.Net/TypeParsers/SByteParser.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Config.Net.TypeParsers +{ + class SByteParser : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(sbyte) }; + + public bool TryParse(string? value, Type t, out object? result) + { + sbyte ir; + bool parsed = sbyte.TryParse(value, NumberStyles.Integer, TypeParserSettings.DefaultCulture, out ir); + result = ir; + return parsed; + } + + public string? ToRawString(object? value) + { + return ((sbyte?)value)?.ToString(TypeParserSettings.DefaultNumericFormat, TypeParserSettings.DefaultCulture); + } + } +} diff --git a/Config.Net/TypeParsers/ShortParser.cs b/Config.Net/TypeParsers/ShortParser.cs new file mode 100644 index 0000000..8048fdf --- /dev/null +++ b/Config.Net/TypeParsers/ShortParser.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Config.Net.TypeParsers +{ + class ShortParser : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(short) }; + + public bool TryParse(string? value, Type t, out object? result) + { + short ir; + bool parsed = short.TryParse(value, NumberStyles.Integer, TypeParserSettings.DefaultCulture, out ir); + result = ir; + return parsed; + } + + public string? ToRawString(object? value) + { + return ((short?)value)?.ToString(TypeParserSettings.DefaultNumericFormat, TypeParserSettings.DefaultCulture); + } + } +} diff --git a/Config.Net/TypeParsers/StringArrayParser.cs b/Config.Net/TypeParsers/StringArrayParser.cs new file mode 100644 index 0000000..e7fdb57 --- /dev/null +++ b/Config.Net/TypeParsers/StringArrayParser.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Config.Net.TypeParsers +{ + class StringArrayParser : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(string[]) }; + + public bool TryParse(string? value, Type t, out object? result) + { + if (value == null) + { + result = null; + return false; + } + + result = ParseAsArray(value); + return true; + } + + public static bool TryParse(string? value, out string[]? result) + { + if(value == null) + { + result = null; + return false; + } + + result = ParseAsArray(value); + return true; + } + + public string? ToRawString(object? value) + { + string[]? arv = (string[]?)value; + + if (arv == null || arv.Length == 0) return null; + + return string.Join(" ", arv.Select(Escape)); + } + + private static string Escape(string s) + { + string s1 = s.Replace("\"", "\"\""); + + return (s == s1 && !s.Contains(" ")) + ? s + : $"\"{s1}\""; + } + + private static string[] ParseAsArray(string s) + { + var a = new List(); + string v = string.Empty; + + int state = 0; + for(int i = 0; i < s.Length;) + { + char ch = s[i]; + + switch(state) + { + case 0: //default + if (ch == '\"') + { + state = 2; + } + else if(ch == ' ') + { + //skip spaces in default mode + } + else + { + v += ch; + state = 1; + } + i++; + break; + case 1: //reading unquoted value + if (ch == ' ') + { + a.Add(v); + v = string.Empty; + state = 0; + } + else + { + v += ch; + } + i++; + break; + case 2: //reading quoted value + if(ch == '\"') + { + state = 3; + } + else + { + v += ch; + } + i++; + break; + + case 3: //after quote in quoted mode + if (ch == '\"') + { + v += ch; + state = 2; + } + else + { + a.Add(v); + v = string.Empty; + state = 0; + } + i++; + break; + } + } + + if(!string.IsNullOrEmpty(v)) + { + a.Add(v); + } + + return a.ToArray(); + } + } +} diff --git a/Config.Net/TypeParsers/StringParser.cs b/Config.Net/TypeParsers/StringParser.cs new file mode 100644 index 0000000..10320f5 --- /dev/null +++ b/Config.Net/TypeParsers/StringParser.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; + +namespace Config.Net.TypeParsers +{ + class StringParser : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(string) }; + + public bool TryParse(string? value, Type t, out object? result) + { + result = value; + return value != null; + } + + public string? ToRawString(object? value) + { + return value as string; + } + } +} diff --git a/Config.Net/TypeParsers/TimeSpanParser.cs b/Config.Net/TypeParsers/TimeSpanParser.cs new file mode 100644 index 0000000..bd2dccc --- /dev/null +++ b/Config.Net/TypeParsers/TimeSpanParser.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; + +namespace Config.Net.TypeParsers +{ + class TimeSpanParser : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(TimeSpan) }; + + public bool TryParse(string? value, Type t, out object? result) + { + TimeSpan ts; + bool parsed = TimeSpan.TryParse(value, TypeParserSettings.DefaultCulture, out ts); + result = ts; + return parsed; + } + + public string? ToRawString(object? value) + { + return ((TimeSpan?)value)?.ToString(null, TypeParserSettings.DefaultCulture); + } + } +} diff --git a/Config.Net/TypeParsers/TypeParserSettings.cs b/Config.Net/TypeParsers/TypeParserSettings.cs new file mode 100644 index 0000000..7e22b9b --- /dev/null +++ b/Config.Net/TypeParsers/TypeParserSettings.cs @@ -0,0 +1,11 @@ +using System.Globalization; + +namespace Config.Net.TypeParsers +{ + internal static class TypeParserSettings + { + public const string DefaultNumericFormat = "G"; + + public static readonly CultureInfo DefaultCulture = CultureInfo.InvariantCulture; + } +} diff --git a/Config.Net/TypeParsers/UIntParser.cs b/Config.Net/TypeParsers/UIntParser.cs new file mode 100644 index 0000000..0b0fb0d --- /dev/null +++ b/Config.Net/TypeParsers/UIntParser.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Config.Net.TypeParsers +{ + class UIntParser : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(uint) }; + + public bool TryParse(string? value, Type t, out object? result) + { + uint ir; + bool parsed = uint.TryParse(value, NumberStyles.Integer, TypeParserSettings.DefaultCulture, out ir); + result = ir; + return parsed; + } + + public string? ToRawString(object? value) + { + return ((uint?)value)?.ToString(TypeParserSettings.DefaultNumericFormat, TypeParserSettings.DefaultCulture); + } + } +} diff --git a/Config.Net/TypeParsers/ULongParser.cs b/Config.Net/TypeParsers/ULongParser.cs new file mode 100644 index 0000000..4bfb40e --- /dev/null +++ b/Config.Net/TypeParsers/ULongParser.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Config.Net.TypeParsers +{ + class ULongParser : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(ulong) }; + + public bool TryParse(string? value, Type t, out object? result) + { + ulong ir; + bool parsed = ulong.TryParse(value, NumberStyles.Integer, TypeParserSettings.DefaultCulture, out ir); + result = ir; + return parsed; + } + + public string? ToRawString(object? value) + { + return ((ulong?)value)?.ToString(TypeParserSettings.DefaultNumericFormat, TypeParserSettings.DefaultCulture); + } + } +} diff --git a/Config.Net/TypeParsers/UShortParser.cs b/Config.Net/TypeParsers/UShortParser.cs new file mode 100644 index 0000000..92ebeea --- /dev/null +++ b/Config.Net/TypeParsers/UShortParser.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Config.Net.TypeParsers +{ + class UShortParser : ITypeParser + { + public IEnumerable SupportedTypes => new[] { typeof(ushort) }; + + public bool TryParse(string? value, Type t, out object? result) + { + ushort ir; + bool parsed = ushort.TryParse(value, NumberStyles.Integer, TypeParserSettings.DefaultCulture, out ir); + result = ir; + return parsed; + } + + public string? ToRawString(object? value) + { + return ((ushort?)value)?.ToString(TypeParserSettings.DefaultNumericFormat, TypeParserSettings.DefaultCulture); + } + } +} diff --git a/Config.Net/Utils.cs b/Config.Net/Utils.cs new file mode 100644 index 0000000..f171539 --- /dev/null +++ b/Config.Net/Utils.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; + +namespace Config.Net +{ + static class Utils + { + public static NetworkCredential? ToNetworkCredential(this string? s) + { + if (s == null) return null; + + Tuple? credsAndDomain = SplitByDelimiter(s, "@"); + string? creds = credsAndDomain?.Item1; + string? domain = credsAndDomain?.Item2; + + Tuple? usernameAndPassword = SplitByDelimiter(creds, ":"); + string? username = usernameAndPassword?.Item1; + string? password = usernameAndPassword?.Item2; + + return new NetworkCredential(username, password, domain); + } + + public static string? ToFriendlyString(NetworkCredential? credential) + { + if (credential == null) return null; + + string usernameAndPassword; + + if (string.IsNullOrEmpty(credential.UserName) && string.IsNullOrEmpty(credential.Password)) + { + usernameAndPassword = string.Empty; + } + else if (string.IsNullOrEmpty(credential.UserName)) + { + usernameAndPassword = $":{credential.Password}"; + } + else if (string.IsNullOrEmpty(credential.Password)) + { + usernameAndPassword = credential.UserName; + } + else + { + usernameAndPassword = $"{credential.UserName}:{credential.Password}"; + } + + return string.IsNullOrEmpty(credential.Domain) + ? usernameAndPassword + : $"{usernameAndPassword}@{credential.Domain}"; + } + + public static Tuple? SplitByDelimiter(string? s, params string[] delimiter) + { + if (s == null) return null; + + string key; + string? value; + + if (delimiter == null || delimiter.Length == 0) + { + key = s.Trim(); + value = null; + } + else + { + + List indexes = delimiter.Where(d => d != null).Select(d => s.IndexOf(d)).Where(d => d != -1).ToList(); + + if (indexes.Count == 0) + { + key = s.Trim(); + value = null; + } + else + { + int idx = indexes.OrderBy(i => i).First(); + key = s.Substring(0, idx); + value = s.Substring(idx + 1); + } + } + + return new Tuple(key, value); + } + } +} diff --git a/FSI.BT.Tools.sln b/FSI.BT.Tools.sln index 159134b..8a3d777 100644 --- a/FSI.BT.Tools.sln +++ b/FSI.BT.Tools.sln @@ -13,6 +13,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NHotkey.Wpf", "NHotkey\NHot EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RadialMenu", "RadialMenu\RadialMenu.csproj", "{9DF116EE-45B1-4297-BE75-0F6B78B33689}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RoboSharp", "RoboSharp\RoboSharp.csproj", "{4280952B-6FEA-495F-B72B-14B2B9BD22E9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Config.Net", "Config.Net\Config.Net.csproj", "{D5C7AFF9-2226-4CC4-87F6-6303DB60FEA0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +43,14 @@ Global {9DF116EE-45B1-4297-BE75-0F6B78B33689}.Debug|Any CPU.Build.0 = Debug|Any CPU {9DF116EE-45B1-4297-BE75-0F6B78B33689}.Release|Any CPU.ActiveCfg = Release|Any CPU {9DF116EE-45B1-4297-BE75-0F6B78B33689}.Release|Any CPU.Build.0 = Release|Any CPU + {4280952B-6FEA-495F-B72B-14B2B9BD22E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4280952B-6FEA-495F-B72B-14B2B9BD22E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4280952B-6FEA-495F-B72B-14B2B9BD22E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4280952B-6FEA-495F-B72B-14B2B9BD22E9}.Release|Any CPU.Build.0 = Release|Any CPU + {D5C7AFF9-2226-4CC4-87F6-6303DB60FEA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5C7AFF9-2226-4CC4-87F6-6303DB60FEA0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5C7AFF9-2226-4CC4-87F6-6303DB60FEA0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5C7AFF9-2226-4CC4-87F6-6303DB60FEA0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FSI.BT.Tools/App.xaml.cs b/FSI.BT.Tools/App.xaml.cs index 828afdd..513cecd 100644 --- a/FSI.BT.Tools/App.xaml.cs +++ b/FSI.BT.Tools/App.xaml.cs @@ -1,12 +1,12 @@ using Hardcodet.Wpf.TaskbarNotification; using NHotkey; using NHotkey.Wpf; -using System.Collections; using System.Windows; using System.Windows.Input; -using FSI.Lib.Helpers; using FSI.Lib.CompareNetObjects; -using FSI.Lib.Guis.SetSizePosExWindow; +using Config.Net.Stores; +using System.IO; +using Config.Net; namespace FSI.BT.Tools { @@ -17,13 +17,21 @@ namespace FSI.BT.Tools { private static readonly KeyGesture RadialMenu = new KeyGesture(Key.OemBackslash, ModifierKeys.Control); private static readonly KeyGesture TimeStamp = new KeyGesture(Key.C, ModifierKeys.Control | ModifierKeys.Alt); + public void Application_Startup(object sender, StartupEventArgs e) { + Global.Log.Info("Anwendung wurde gestartet!"); + Global.Settings = new AppSettings(GetType().Namespace.ToString() + ".xml"); Global.Settings.Load(); + JsonConfigStore _store = new JsonConfigStore(System.IO.Path.Combine(Directory.GetCurrentDirectory(), "config.json"), true); + Global.AppSettings = new ConfigurationBuilder() + .UseConfigStore(_store) + .Build(); + Global.TaskbarIcon = (TaskbarIcon)FindResource("FSINotifyIcon"); Global.AdminRights = Admin.CheckAdminRight(); diff --git a/FSI.BT.Tools/AppSettings.cs b/FSI.BT.Tools/AppSettings.cs index 569e6b0..7eebf7a 100644 --- a/FSI.BT.Tools/AppSettings.cs +++ b/FSI.BT.Tools/AppSettings.cs @@ -8,8 +8,7 @@ namespace FSI.BT.Tools public AppSettings(string fileName) : base(fileName) { - TimeStampFormat = "_yyyyMMdd_HHmmss"; - EplArguments = "/Variant:\"Electric P8\""; + SuperAdmin = "maier_s"; } @@ -19,45 +18,45 @@ namespace FSI.BT.Tools public string[] Admins { get; set; } [EncryptedSetting] public string SuperAdmin { get; set; } - public string TimeStampFormat { get; set; } - public string[] SieSimaticManagerExe { get; set; } - public string[] SieTiaV13Exe { get; set; } - public string[] SieTiaV14Exe { get; set; } - public string[] SieTiaV15Exe { get; set; } - public string[] SieTiaV16Exe { get; set; } - public string[] SieTiaV17Exe { get; set; } - public string[] SieTiaVStarterExe { get; set; } + //public string TimeStampFormat { get; set; } + //public string[] SieSimaticManagerExe { get; set; } + //public string[] SieTiaV13Exe { get; set; } + //public string[] SieTiaV14Exe { get; set; } + //public string[] SieTiaV15Exe { get; set; } + //public string[] SieTiaV16Exe { get; set; } + //public string[] SieTiaV17Exe { get; set; } + //public string[] SieTiaVStarterExe { get; set; } public string[] EplExe { get; set; } - public string EplArguments { get; set; } - public string[] NppExe { get; set; } - public string[] TotalCmdExe { get; set; } - public string[] TeXstudioExe { get; set; } - public string[] TeXstudioPath { get; set; } - public string[] VsExe { get; set; } - public string[] VsCodeExe { get; set; } - public string[] RdpExe { get; set; } - public string[] OutlookExe { get; set; } - public string[] TeamsExe { get; set; } - public string TeamsArg { get; set; } - public string[] ExcelExe { get; set; } - public string[] WordExe { get; set; } - public string[] PaintNetExe { get; set; } - public string[] GimpExe { get; set; } - public string[] VncExe { get; set; } - public string[] VncAdrBookExe { get; set; } - public string[] IbaAnalyzerExe { get; set; } - public string ZentralWebUrl { get; set; } - public string SchichtbuchUrl { get; set; } - public string SPSUrl { get; set; } - public string Pl1PlsUrl { get; set; } - public string Pl2PlsUrl { get; set; } - public string Pl2Als { get; set; } - public string Pl3PlsUrl { get; set; } - public string GiteaUrl { get; set; } - public string WikiUrl { get; set; } - public string ErpUrl { get; set; } - public string EplPdfPath { get; set; } - public string EplPrjPath { get; set; } + //public string EplArguments { get; set; } + //public string[] NppExe { get; set; } + //public string[] TotalCmdExe { get; set; } + //public string[] TeXstudioExe { get; set; } + //public string[] TeXstudioPath { get; set; } + //public string[] VsExe { get; set; } + //public string[] VsCodeExe { get; set; } + //public string[] RdpExe { get; set; } + //public string[] OutlookExe { get; set; } + //public string[] TeamsExe { get; set; } + //public string TeamsArg { get; set; } + //public string[] ExcelExe { get; set; } + //public string[] WordExe { get; set; } + //public string[] PaintNetExe { get; set; } + //public string[] GimpExe { get; set; } + //public string[] VncExe { get; set; } + //public string[] VncAdrBookExe { get; set; } + //public string[] IbaAnalyzerExe { get; set; } + //public string ZentralWebUrl { get; set; } + //public string SchichtbuchUrl { get; set; } + //public string SPSUrl { get; set; } + //public string Pl1PlsUrl { get; set; } + //public string Pl2PlsUrl { get; set; } + //public string Pl2Als { get; set; } + //public string Pl3PlsUrl { get; set; } + //public string GiteaUrl { get; set; } + //public string WikiUrl { get; set; } + //public string ErpUrl { get; set; } + //public string EplPdfPath { get; set; } + //public string EplPrjPath { get; set; } public bool SieTiaWinCCMsgMgtAutostart { get; set; } public int SieTiaWinCCMsgMgtUpdateIntervall { get; set; } public string SieTiaWinCCMsgMgtWindowsName { get; set; } @@ -66,13 +65,14 @@ namespace FSI.BT.Tools public bool IbaAutoSync { get; set; } public string IbaRecordSourcePath { get; set; } public string IbaRecordDestinationath { get; set; } - public string[] WindowMgtName { get; set; } - public string[] WindowMgtClassName { get; set; } public int WindowMgtUpdateInterval { get; set; } public bool WindowMgtAutostart { get; set; } - public int[] WindowMgtX { get; set; } - public int[] WindowMgtY { get; set; } - public int[] WindowMgtHeight { get; set; } - public int[] WindowMgtWight { get; set; } + public string[] WindowMgtBezeichnung { get; set; } + public string[] WindowMgtName { get; set; } + public string[] WindowMgtClassName { get; set; } + public string[] WindowMgtX { get; set; } + public string[] WindowMgtY { get; set; } + public string[] WindowMgtHeight { get; set; } + public string[] WindowMgtWight { get; set; } } } diff --git a/FSI.BT.Tools/Commands/ExitCommand.cs b/FSI.BT.Tools/Commands/ExitCommand.cs index faa9524..cf3bdd0 100644 --- a/FSI.BT.Tools/Commands/ExitCommand.cs +++ b/FSI.BT.Tools/Commands/ExitCommand.cs @@ -9,6 +9,7 @@ namespace FSI.BT.Tools.Commands { public override void Execute(object parameter) { + Global.Log.Info("Anwendung wurde beendet!"); Application.Current.Shutdown(); } diff --git a/FSI.BT.Tools/Commands/LoginCommand.cs b/FSI.BT.Tools/Commands/LoginCommand.cs index 376fc36..c76a15c 100644 --- a/FSI.BT.Tools/Commands/LoginCommand.cs +++ b/FSI.BT.Tools/Commands/LoginCommand.cs @@ -18,6 +18,15 @@ namespace FSI.BT.Tools.Commands Global.UserRights = Global.AdminRights = frmMain.PwOk; + + if (frmMain.PwOk) + { + Global.Log.Info("Admin-Passowrt wurde korrekt eingegben."); + } + else + { + Global.Log.Info("Anmeldung wurde vom Benutzer abgebrochen."); + } } public override bool CanExecute(object parameter) diff --git a/FSI.BT.Tools/Commands/OpenAppCommand.cs b/FSI.BT.Tools/Commands/OpenAppCommand.cs index bcce347..c123e23 100644 --- a/FSI.BT.Tools/Commands/OpenAppCommand.cs +++ b/FSI.BT.Tools/Commands/OpenAppCommand.cs @@ -1,7 +1,10 @@ -using FSI.Lib; +using FSI.BT.Tools.Settings; +using FSI.Lib; using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Windows; namespace FSI.BT.Tools.Commands @@ -13,53 +16,52 @@ namespace FSI.BT.Tools.Commands { public override void Execute(object parameter) { - string[] files = new string[] { }; - string[] pathes = new string[] { }; - string arguments = string.Empty; + IEnumerable files = new List(); + (string ExePath, string Path, string Arguments) selectedFile = (string.Empty, string.Empty, string.Empty); switch ((string)parameter) { case "SimaticManager": - files = Global.Settings.SieSimaticManagerExe; + files = Global.AppSettings.Apps.SieSimaticManager; break; case "TIAv13": - files = Global.Settings.SieTiaV13Exe; + files = Global.AppSettings.Apps.SieTiaV13; break; case "TIAv14": - files = Global.Settings.SieTiaV14Exe; + files = Global.AppSettings.Apps.SieTiaV14; break; case "TIAv15": - files = Global.Settings.SieTiaV15Exe; + files = Global.AppSettings.Apps.SieTiaV15; break; case "TIAv16": - files = Global.Settings.SieTiaV16Exe; + files = Global.AppSettings.Apps.SieTiaV16; break; case "TIAv17": - files = Global.Settings.SieTiaV17Exe; + files = Global.AppSettings.Apps.SieTiaV17; break; case "Starter": - files = Global.Settings.SieTiaVStarterExe; + files = Global.AppSettings.Apps.SieTiaVStarter; break; case "Epl": - files = Global.Settings.EplExe; - arguments = Global.Settings.EplArguments; + files = Global.AppSettings.Apps.Epl; break; case "EplPrj": + selectedFile = GetApp(Global.AppSettings.Apps.Epl); Lib.Guis.Prj.Mgt.FrmMain frmMainEplPrj = new Lib.Guis.Prj.Mgt.FrmMain() { ShowPdf = false, CloseAtLostFocus = true, WindowStartupLocation = WindowStartupLocation.CenterScreen, - Path = Global.Settings.EplPrjPath, - EplExes = Global.Settings.EplExe, + Path = FSI.BT.Tools.Settings.AppSettings.GetFolderByName(Global.AppSettings.Folders, "EplPrj").path, + EplExe = selectedFile.ExePath, }; frmMainEplPrj.Show(); return; @@ -70,7 +72,7 @@ namespace FSI.BT.Tools.Commands ShowPdf = true, CloseAtLostFocus = true, WindowStartupLocation = WindowStartupLocation.CenterScreen, - Path = Global.Settings.EplPdfPath, + Path = FSI.BT.Tools.Settings.AppSettings.GetFolderByName(Global.AppSettings.Folders, "EplPdf").path }; frmMainEplPdf.Show(); return; @@ -84,29 +86,28 @@ namespace FSI.BT.Tools.Commands return; case "Npp": - files = Global.Settings.NppExe; + files = Global.AppSettings.Apps.Npp; break; case "TotalCmd": - files = Global.Settings.TotalCmdExe; + files = Global.AppSettings.Apps.TotalCmd; break; case "TeXstudio": - files = Global.Settings.TeXstudioExe; - pathes = Global.Settings.TeXstudioPath; + files = Global.AppSettings.Apps.TeXstudio; break; case "VS": - files = Global.Settings.VsExe; + files = Global.AppSettings.Apps.Vs; break; case "VS.Code": - files = Global.Settings.VsCodeExe; + files = Global.AppSettings.Apps.VsCode; break; case "Rdp": - files = Global.Settings.RdpExe; + files = Global.AppSettings.Apps.Rdp; break; case "DeEncrypt": @@ -139,84 +140,80 @@ namespace FSI.BT.Tools.Commands } return; + case "Folder": + Lib.Guis.Folder.Mgt.FrmMain frmFolderMgtMain = new Lib.Guis.Folder.Mgt.FrmMain() + { + CloseAtLostFocus = true, + Data = Global.AppSettings.Folders + }; + frmFolderMgtMain.Show(); + return; + + //case "TxtToClip": + // Lib.Guis.TxtToClip.Mgt.FrmMain frmTxtToClipMain = new Lib.Guis.TxtToClip.Mgt.FrmMain() + // { + // CloseAtLostFocus = false, + // Data = Global.AppSettings.TxtToClip + // }; + // frmTxtToClipMain.Show(); + // return; + case "Outlook": - files = Global.Settings.OutlookExe; + files = Global.AppSettings.Apps.Outlook; break; case "Teams": - files = Global.Settings.TeamsExe; - arguments = Global.Settings.TeamsArg; + files = Global.AppSettings.Apps.Teams; break; case "Excel": - files = Global.Settings.ExcelExe; + files = Global.AppSettings.Apps.Excel; break; case "Word": - files = Global.Settings.WordExe; + files = Global.AppSettings.Apps.Word; break; case "PaintNet": - files = Global.Settings.PaintNetExe; + files = Global.AppSettings.Apps.PaintNet; break; case "Gimp": - files = Global.Settings.GimpExe; + files = Global.AppSettings.Apps.Gimp; break; case "Vnc": - files = Global.Settings.VncExe; + files = Global.AppSettings.Apps.Vnc; break; case "VncAdrBook": - files = Global.Settings.VncAdrBookExe; + files = Global.AppSettings.Apps.VncAdrBook; break; case "IbaAnalyzer": - files = Global.Settings.VncAdrBookExe; + files = Global.AppSettings.Apps.IbaAnalyzer; break; - } - string fileName = string.Empty; - string path = string.Empty; + selectedFile = GetApp(files); - for (int i = 0; i <= files.Length - 1; i++) + if (ProgramIsRunning(selectedFile.ExePath)) { - var test = Environment.ExpandEnvironmentVariables(files[i].Trim()); - if (File.Exists(Environment.ExpandEnvironmentVariables(files[i].Trim()))) - { - fileName = Environment.ExpandEnvironmentVariables(files[i].Trim()); - } - else - { - continue; - } - - if (pathes.Length == 0) - { - path = Path.GetDirectoryName(fileName); - } - else - { - path = Environment.ExpandEnvironmentVariables(pathes[i].Trim()); - } - } - - if (ProgramIsRunning(fileName)) - { - ProgramToFront(fileName); + ProgramToFront(selectedFile.ExePath); + Global.Log.Info("Anwendung \"{0}\" wurde in den Vordergrund gebracht", selectedFile.ExePath); } else { Process process = new Process(); - process.StartInfo.FileName = fileName; - process.StartInfo.WorkingDirectory = path; - process.StartInfo.Arguments = arguments; + process.StartInfo.FileName = selectedFile.ExePath; + process.StartInfo.WorkingDirectory = selectedFile.Path; + process.StartInfo.Arguments = selectedFile.Arguments; try { + process.Start(); + Global.Log.Info("Anwendung \"{0}\" wurde gestartet", selectedFile.ExePath); } catch (System.ComponentModel.Win32Exception ex) when (ex.NativeErrorCode == 740) { @@ -225,47 +222,51 @@ namespace FSI.BT.Tools.Commands process.StartInfo.UseShellExecute = true; process.StartInfo.Verb = "runas"; process.Start(); + Global.Log.Info("Anwendung \"{0}\" wurde als Admin gestartet", selectedFile.ExePath); + } + catch (Exception ex2) + { + Global.Log.Info("Anwendung konnte durch folgenden Fehler \"{0}\" nicht gestartet werden.", ex2.Message); } - catch { } } } } public override bool CanExecute(object parameter) { - string[] files = new string[] { }; + IEnumerable files = new List(); switch ((string)parameter) { case "SimaticManager": - files = Global.Settings.SieSimaticManagerExe; + files = Global.AppSettings.Apps.SieSimaticManager; break; case "TIAv13": - files = Global.Settings.SieTiaV13Exe; + files = Global.AppSettings.Apps.SieTiaV13; break; case "TIAv14": - files = Global.Settings.SieTiaV14Exe; + files = Global.AppSettings.Apps.SieTiaV14; break; case "TIAv15": - files = Global.Settings.SieTiaV13Exe; + files = Global.AppSettings.Apps.SieTiaV15; break; case "TIAv16": - files = Global.Settings.SieTiaV16Exe; + files = Global.AppSettings.Apps.SieTiaV16; break; case "TIAv17": - files = Global.Settings.SieTiaV17Exe; + files = Global.AppSettings.Apps.SieTiaV17; break; case "Starter": - files = Global.Settings.SieTiaVStarterExe; + files = Global.AppSettings.Apps.SieTiaVStarter; break; case "Epl": - files = Global.Settings.EplExe; + files = Global.AppSettings.Apps.Epl; break; case "EplPrj": @@ -278,28 +279,27 @@ namespace FSI.BT.Tools.Commands return Global.AdminRights; case "Npp": - files = Global.Settings.NppExe; + files = Global.AppSettings.Apps.Npp; break; case "TotalCmd": - files = Global.Settings.TotalCmdExe; + files = Global.AppSettings.Apps.TotalCmd; break; case "TeXstudio": - files = Global.Settings.TeXstudioExe; + files = Global.AppSettings.Apps.TeXstudio; break; case "VS": - files = Global.Settings.VsExe; + files = Global.AppSettings.Apps.Vs; break; case "VS.Code": - files = Global.Settings.VsCodeExe; + files = Global.AppSettings.Apps.VsCode; break; - case "Rdp": - files = Global.Settings.RdpExe; + files = Global.AppSettings.Apps.Rdp; break; case "DeEncrypt": @@ -311,49 +311,55 @@ namespace FSI.BT.Tools.Commands case "Admin": return Global.SuperAdminRights; + case "Folder": + return Global.AppSettings.Folders != null; + + //case "TxtToClip": + // return Global.AppSettings.TxtToClip != null; + case "Outlook": - files = Global.Settings.OutlookExe; + files = Global.AppSettings.Apps.Outlook; break; case "Teams": - files = Global.Settings.TeamsExe; + files = Global.AppSettings.Apps.Teams; break; case "Excel": - files = Global.Settings.ExcelExe; + files = Global.AppSettings.Apps.Excel; break; case "Word": - files = Global.Settings.WordExe; + files = Global.AppSettings.Apps.Word; break; case "PaintNet": - files = Global.Settings.PaintNetExe; + files = Global.AppSettings.Apps.PaintNet; break; case "Gimp": - files = Global.Settings.GimpExe; + files = Global.AppSettings.Apps.Gimp; break; case "Vnc": - files = Global.Settings.VncExe; + files = Global.AppSettings.Apps.Vnc; break; case "VncAdrBook": - files = Global.Settings.VncAdrBookExe; + files = Global.AppSettings.Apps.VncAdrBook; break; case "IbaAnalyzer": - files = Global.Settings.VncAdrBookExe; + files = Global.AppSettings.Apps.IbaAnalyzer; break; default: return false; } - foreach (string file in files) + foreach (var file in files) { - if (File.Exists(Environment.ExpandEnvironmentVariables(file.Trim()))) + if (File.Exists(Environment.ExpandEnvironmentVariables(file.ExePath.Trim()))) { return true; } @@ -382,6 +388,36 @@ namespace FSI.BT.Tools.Commands return isRunning; } + private (string ExePath, string Path, string Arguments) GetApp(IEnumerable files) + { + (string ExePath, string Path, string Arguments) selectedFile = (string.Empty, string.Empty, string.Empty); + + for (int i = 0; i < files.ToList().Count; i++) + { + if (File.Exists(Environment.ExpandEnvironmentVariables(files.ToList()[i].ExePath.Trim()))) + { + selectedFile.ExePath = Environment.ExpandEnvironmentVariables(files.ToList()[i].ExePath.Trim()); + selectedFile.Arguments = files.ToList()[i].Arguments; + } + else + { + continue; + } + + if (selectedFile.Path == String.Empty) + { + selectedFile.Path = Path.GetDirectoryName(selectedFile.ExePath); + } + else + { + selectedFile.Path = Path.GetDirectoryName(files.ToList()[i].ExePath.Trim()); + //selectedFile.Path = Environment.ExpandEnvironmentVariables(files.ToList()[i].ExePath.Trim()); + } + } + + return selectedFile; + } + [System.Runtime.InteropServices.DllImport("User32.dll")] private static extern bool SetForegroundWindow(IntPtr handle); [System.Runtime.InteropServices.DllImport("User32.dll")] diff --git a/FSI.BT.Tools/Commands/OpenLinkCommand.cs b/FSI.BT.Tools/Commands/OpenLinkCommand.cs index cdfc4ba..d85fa57 100644 --- a/FSI.BT.Tools/Commands/OpenLinkCommand.cs +++ b/FSI.BT.Tools/Commands/OpenLinkCommand.cs @@ -16,43 +16,43 @@ namespace FSI.BT.Tools.Commands switch ((string)parameter) { case "ZentralWeb": - url = Global.Settings.ZentralWebUrl; + url = Global.AppSettings.Urls.ZentralWeb; break; case "Schichtbuch": - url = Global.Settings.SchichtbuchUrl; + url = Global.AppSettings.Urls.Schichtbuch; break; case "SPS": - url = Global.Settings.SPSUrl; + url = Global.AppSettings.Urls.SPS; break; case "PL1.Pls": - url = Global.Settings.Pl1PlsUrl; + url = Global.AppSettings.Urls.Pl1Pls; break; case "PL2.Pls": - url = Global.Settings.Pl2PlsUrl; + url = Global.AppSettings.Urls.Pl2Pls; break; case "PL2.Als": - url = Global.Settings.Pl2Als; + url = Global.AppSettings.Urls.Pl2Als; break; case "PL3.Pls": - url = Global.Settings.Pl3PlsUrl; + url = Global.AppSettings.Urls.Pl3Pls; break; case "FSI.Gitea": - url = Global.Settings.GiteaUrl; + url = Global.AppSettings.Urls.Gitea; break; case "FSI.Wiki": - url = Global.Settings.WikiUrl; + url = Global.AppSettings.Urls.Wiki; break; case "Erp": - url = Global.Settings.ErpUrl; + url = Global.AppSettings.Urls.Erp; break; } @@ -61,6 +61,7 @@ namespace FSI.BT.Tools.Commands url = url.Replace("&", "^&"); Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); + Global.Log.Info("Link \"{0}\" wurde geföffnet.", url); } public override bool CanExecute(object parameter) diff --git a/FSI.BT.Tools/Commands/TimeStampToClipboardCommand.cs b/FSI.BT.Tools/Commands/TimeStampToClipboardCommand.cs index 855ce65..1e4abe5 100644 --- a/FSI.BT.Tools/Commands/TimeStampToClipboardCommand.cs +++ b/FSI.BT.Tools/Commands/TimeStampToClipboardCommand.cs @@ -10,13 +10,16 @@ namespace FSI.BT.Tools.Commands { public override void Execute(object parameter) { - System.Windows.Forms.Clipboard.SetDataObject(DateTime.Now.ToString(Global.Settings.TimeStampFormat)); + var timeStampFormat = Global.AppSettings.TimeStampFormat.Value; + System.Windows.Forms.Clipboard.SetDataObject(DateTime.Now.ToString(timeStampFormat)); + Global.Log.Debug("Zeitstempel \"{0}\" wurde in die Zwischenablage kopiert.", DateTime.Now.ToString(timeStampFormat)); + var balloon = new ToolTip() { BalloonText = "Zeitstempel", BalloonDesc = "Der aktuelle Zeitstempel wurde in die Zwischenablage kopiert." }; - Global.TaskbarIcon.ShowCustomBalloon(balloon, PopupAnimation.Slide, 2000); + Global.TaskbarIcon.ShowCustomBalloon(balloon, PopupAnimation.Slide, 2000); } public override bool CanExecute(object parameter) diff --git a/FSI.BT.Tools/FSI.BT.Tools.csproj b/FSI.BT.Tools/FSI.BT.Tools.csproj index 67b80ce..2b3ac7e 100644 --- a/FSI.BT.Tools/FSI.BT.Tools.csproj +++ b/FSI.BT.Tools/FSI.BT.Tools.csproj @@ -1,8 +1,7 @@  - - WinExe - net472;net6.0-windows + + net6.0-windows WinExe true true @@ -13,9 +12,9 @@ - + @@ -59,10 +58,16 @@ + + + + + + @@ -75,6 +80,7 @@ + @@ -107,9 +113,17 @@ + + Always + - PreserveNewest + Always + + + Always + + diff --git a/FSI.BT.Tools/FSI.BT.Tools.xml b/FSI.BT.Tools/FSI.BT.Tools.xml index 053807b..712c5a3 100644 --- a/FSI.BT.Tools/FSI.BT.Tools.xml +++ b/FSI.BT.Tools/FSI.BT.Tools.xml @@ -2,9 +2,9 @@ +I945AMzKKYBAAAAB21haWVyX3M= e+Dt7FRUDDoBAAAAB21haWVyX3M= - + _yyyyMMdd_HHmmss - + C:\Program Files (x86)\Siemens\Step7\S7BIN\S7tgtopx.exe C:\Program Files (x86)\Siemens\Automation\Portal V13\Bin\Siemens.Automation.Portal.exe C:\Program Files\Siemens\Automation\Portal V14\Bin\Siemens.Automation.Portal.exe @@ -31,7 +31,7 @@ C:\Program Files\RealVNC\VNC Viewer\vncviewer.exe,c:\Users\maier_s\OneDrive - Fondium Group GmbH\Documents\Apps\VNC-Viewer-6.20.113-Windows-64bit.exe C:\Program Files\RealVNC\VNC Viewer\vncaddrbook.exe C:\Program Files\iba\ibaAnalyzer\ibaAnalyzer.exe - + http://desiaugetwf/web/?AspxAutoDetectCookieSupport=1 http://10.10.1.42/SKSchichtbuchWeb/de-DE/Plugin/ShiftBook/ShiftBook/IR http://10.10.1.42/SKChangeTrackerWeb/de-DE/Plugin/ChangeTracker @@ -42,17 +42,27 @@ http://desiaugetc7-088:3000/ http://desiaugetc7-088:3001/en/home https://mingle-portal.eu1.inforcloudsuite.com/FONDIUM_prd - + \\10.10.1.40\Betriebstechnik\Eplan \\fondium.org\DESI$\AUG_Abteilung\Betriebstechnik\EPL\P8\Data\Projekte\FSI\ - + true 10 #32770 Zur Kenntnis genommen - + true d:\tmp c:\tmp + + false + 10 + Starter Trace + Signalauswahl Trace + #32770 + 10 + 10 + 800 + 1000 \ No newline at end of file diff --git a/FSI.BT.Tools/FrmRadialMenu.xaml b/FSI.BT.Tools/FrmRadialMenu.xaml index 0f1cb00..0106939 100644 --- a/FSI.BT.Tools/FrmRadialMenu.xaml +++ b/FSI.BT.Tools/FrmRadialMenu.xaml @@ -363,6 +363,39 @@ + + + + + + + + + Verzeichnisse + + + + + + + + + + + + + Verzeichnisse + + + + + diff --git a/FSI.BT.Tools/Global.cs b/FSI.BT.Tools/Global.cs index a50041c..4eb0f3b 100644 --- a/FSI.BT.Tools/Global.cs +++ b/FSI.BT.Tools/Global.cs @@ -1,50 +1,59 @@ -using FSI.Lib.Guis.IbaDirSync.ViewModel; +using Config.Net.Stores; +using FSI.Lib.Guis.IbaDirSync.ViewModel; using FSI.Lib.Guis.SetSizePosExWindow.ViewModel; using FSI.Lib.Guis.SieTiaWinCCMsgMgt.ViewModel; using Hardcodet.Wpf.TaskbarNotification; +using Microsoft.Extensions.Logging; +using NLog; namespace FSI.BT.Tools { internal static class Global { + public static Logger Log = LogManager.GetCurrentClassLogger(); public static FrmRadialMenu FrmRadialMenu { get; set; } public static TaskbarIcon TaskbarIcon { get; set; } public static ViewModelWinCC WinCC { get; set; } public static AppSettings Settings { get; set; } + + public static Settings.AppSettings.IAppSettings AppSettings { get; set; } + public static ViewModelIba Iba { get; set; } public static ViewModelWindow WindowMgt { get; set; } public static bool UserRights { get; set; } public static bool AdminRights { get; set; } public static bool SuperAdminRights { get; set; } - + public static class Window { public static void Load() { - for (int i = 0; i < Global.Settings.WindowMgtName.Length; i++) + for (int i = 0; i < Global.Settings.WindowMgtBezeichnung.Length; i++) { WindowMgt.Windows.Add(new Lib.Guis.SetSizePosExWindow.Model.Window { + Bezeichnung = Global.Settings.WindowMgtBezeichnung[i], Name = Global.Settings.WindowMgtName[i], ClassName = Global.Settings.WindowMgtClassName[i], - Height = Global.Settings.WindowMgtHeight[i], - Width = Global.Settings.WindowMgtWight[i], - X = Global.Settings.WindowMgtX[i], - Y = Global.Settings.WindowMgtY[i], + Height = int.Parse(Global.Settings.WindowMgtHeight[i]), + Width = int.Parse(Global.Settings.WindowMgtWight[i]), + X = int.Parse(Global.Settings.WindowMgtX[i]), + Y = int.Parse(Global.Settings.WindowMgtY[i]), }); - } + } } public static void Save() { for (int i = 0; i < Global.WindowMgt.Windows.Count; i++) { + Global.Settings.WindowMgtBezeichnung[i] = Global.WindowMgt.Windows[i].Bezeichnung; Global.Settings.WindowMgtName[i] = Global.WindowMgt.Windows[i].Name; Global.Settings.WindowMgtClassName[i] = Global.WindowMgt.Windows[i].ClassName; - Global.Settings.WindowMgtHeight[i] = Global.WindowMgt.Windows[i].Height; - Global.Settings.WindowMgtWight[i] = Global.WindowMgt.Windows[i].Width; - Global.Settings.WindowMgtX[i] = Global.WindowMgt.Windows[i].X; - Global.Settings.WindowMgtY[i] = Global.WindowMgt.Windows[i].Y; + Global.Settings.WindowMgtHeight[i] = Global.WindowMgt.Windows[i].Height.ToString(); + Global.Settings.WindowMgtWight[i] = Global.WindowMgt.Windows[i].Width.ToString(); + Global.Settings.WindowMgtX[i] = Global.WindowMgt.Windows[i].X.ToString(); + Global.Settings.WindowMgtY[i] = Global.WindowMgt.Windows[i].Y.ToString(); } } } diff --git a/FSI.BT.Tools/Gui/FrmProcesses.xaml b/FSI.BT.Tools/Gui/FrmProcesses.xaml index 1fd2053..079335b 100644 --- a/FSI.BT.Tools/Gui/FrmProcesses.xaml +++ b/FSI.BT.Tools/Gui/FrmProcesses.xaml @@ -174,8 +174,8 @@ - + + + + + + + +