Ver código fonte

Date:26.06.2025

 - Fix support to use quoted arguments in ordinal type
 - update dependency to qdr.fnd.core.0.0.10.0-alpha
Dalibor Votruba 1 ano atrás
pai
commit
bf9cffdfc7

+ 134 - 0
@Scripts/Cmd/duplicatefile.cmd

@@ -0,0 +1,134 @@
+@echo off
+setlocal enabledelayedexpansion
+
+:: Initialize variables
+set "source_file="
+set "dry_run=false"
+set "use_list=false"
+set "list_file="
+set "output_files="
+
+:: Check if no arguments provided
+if "%~1"=="" (
+    echo Usage: duplicatefile ^<source_file^> [-dry] [-list] ^<out_file1^> .. ^<out_filen^>
+    echo.
+    echo Options:
+    echo   -dry    : Dry run - show what would be copied without actually copying
+    echo   -list   : Use first output file as a list file containing target filenames
+    echo.
+    echo Examples:
+    echo   duplicatefile source.txt file1.txt file2.txt file3.txt
+    echo   duplicatefile source.txt -dry file1.txt file2.txt
+    echo   duplicatefile source.txt -list filelist.txt
+    exit /b 1
+)
+
+:: Parse command line arguments
+set "arg_index=0"
+for %%a in (%*) do (
+    set /a arg_index+=1
+    
+    if !arg_index! equ 1 (
+        set "source_file=%%~a"
+    ) else if "%%~a"=="-dry" (
+        set "dry_run=true"
+    ) else if "%%~a"=="-list" (
+        set "use_list=true"
+    ) else (
+        if "!use_list!"=="true" and "!list_file!"=="" (
+            set "list_file=%%~a"
+        ) else (
+            set "output_files=!output_files! %%~a"
+        )
+    )
+)
+
+:: Validate source file
+if "!source_file!"=="" (
+    echo Error: Source file not specified
+    exit /b 1
+)
+
+if not exist "!source_file!" (
+    echo Error: Source file "!source_file!" does not exist
+    exit /b 1
+)
+
+:: Display operation mode
+if "!dry_run!"=="true" (
+    echo [DRY RUN MODE] - No files will be copied
+    echo.
+)
+
+echo Source file: !source_file!
+echo.
+
+:: Handle list mode
+if "!use_list!"=="true" (
+    if "!list_file!"=="" (
+        echo Error: List file not specified when using -list option
+        exit /b 1
+    )
+    
+    if not exist "!list_file!" (
+        echo Error: List file "!list_file!" does not exist
+        exit /b 1
+    )
+    
+    echo Reading target files from list: !list_file!
+    echo.
+    
+    for /f "usebackq delims=" %%f in ("!list_file!") do (
+        set "target_file=%%f"
+        
+        :: Skip empty lines
+        if not "!target_file!"=="" (
+            call :copy_file "!source_file!" "!target_file!" "!dry_run!"
+        )
+    )
+) else (
+    :: Handle direct file list mode
+    if "!output_files!"=="" (
+        echo Error: No output files specified
+        exit /b 1
+    )
+    
+    echo Target files: !output_files!
+    echo.
+    
+    for %%f in (!output_files!) do (
+        call :copy_file "!source_file!" "%%f" "!dry_run!"
+    )
+)
+
+echo.
+echo Operation completed.
+exit /b 0
+
+:: Subroutine to copy a file
+:copy_file
+set "src=%~1"
+set "dst=%~2"
+set "is_dry=%~3"
+
+if "%is_dry%"=="true" (
+    echo [DRY RUN] Would copy: "%src%" -^> "%dst%"
+) else (
+    echo Copying: "%src%" -^> "%dst%"
+    
+    :: Create directory if it doesn't exist
+    for %%d in ("%dst%") do (
+        if not exist "%%~dpd" (
+            mkdir "%%~dpd" 2>nul
+        )
+    )
+    
+    :: Copy the file
+    copy "%src%" "%dst%" >nul 2>&1
+    if !errorlevel! equ 0 (
+        echo   Success
+    ) else (
+        echo   Error: Failed to copy file
+    )
+)
+goto :eof

+ 186 - 0
@Scripts/Cmd/duplicatefile.md

@@ -0,0 +1,186 @@
+# duplicatefile.cmd
+
+A Windows batch script for efficiently duplicating a source file to multiple destination files with various operational modes.
+
+## Overview
+
+`duplicatefile.cmd` is a command-line utility that allows you to copy a single source file to multiple destination files in one operation. It supports both direct file specification and list-based operations, with an optional dry-run mode for testing.
+
+## Syntax
+
+```cmd
+duplicatefile <source_file> [-dry] [-list] <out_file1> .. <out_filen>
+```
+
+### Parameters
+
+- `<source_file>` - The file to be duplicated (required)
+- `[-dry]` - Optional flag for dry-run mode (shows operations without executing)
+- `[-list]` - Optional flag to use list file mode
+- `<out_file1>` .. `<out_filen>` - Target destination files
+
+## Usage Modes
+
+### 1. Direct File Mode
+
+Copy source file to multiple specified destinations:
+
+```cmd
+duplicatefile source.txt file1.txt file2.txt file3.txt
+```
+
+This will copy `source.txt` to `file1.txt`, `file2.txt`, and `file3.txt`.
+
+### 2. List File Mode
+
+Use a text file containing the list of target destinations:
+
+```cmd
+duplicatefile source.txt -list targets.txt
+```
+
+The `targets.txt` file should contain one destination file per line:
+```
+backup1.txt
+subfolder/backup2.txt
+../backup3.txt
+logs/archive.txt
+```
+
+### 3. Dry Run Mode
+
+Preview operations without actually copying files:
+
+```cmd
+duplicatefile source.txt -dry file1.txt file2.txt file3.txt
+```
+
+```cmd
+duplicatefile source.txt -dry -list targets.txt
+```
+
+## Features
+
+### Automatic Directory Creation
+The script automatically creates target directories if they don't exist:
+```cmd
+duplicatefile config.ini backup/2025/config.ini logs/config.ini
+```
+
+### Error Handling
+- Validates source file existence
+- Checks for list file availability when using `-list` mode
+- Reports copy operation success/failure for each file
+- Provides clear error messages
+
+### Flexible Path Support
+Supports various path formats:
+- Relative paths: `backup/file.txt`
+- Parent directories: `../archive/file.txt`
+- Absolute paths: `C:\backups\file.txt`
+
+## Examples
+
+### Database Backup Scenario
+```cmd
+duplicatefile database.db -list backup_locations.txt
+```
+
+With `backup_locations.txt`:
+```
+daily/database_2025-06-12.db
+weekly/database_week24.db
+monthly/database_june.db
+archive/database_backup.db
+```
+
+### Configuration File Distribution
+```cmd
+duplicatefile app.config -dry ^
+  server1/app.config ^
+  server2/app.config ^
+  test/app.config
+```
+
+### Log File Archiving
+```cmd
+duplicatefile current.log ^
+  archive/2025-06-12.log ^
+  backup/current_backup.log ^
+  ../shared/current.log
+```
+
+## Output Format
+
+### Normal Mode
+```
+Source file: source.txt
+
+Target files:  file1.txt file2.txt file3.txt
+
+Copying: "source.txt" -> "file1.txt"
+  Success
+Copying: "source.txt" -> "file2.txt"
+  Success
+Copying: "source.txt" -> "file3.txt"
+  Success
+
+Operation completed.
+```
+
+### Dry Run Mode
+```
+[DRY RUN MODE] - No files will be copied
+
+Source file: source.txt
+
+Target files:  file1.txt file2.txt file3.txt
+
+[DRY RUN] Would copy: "source.txt" -> "file1.txt"
+[DRY RUN] Would copy: "source.txt" -> "file2.txt"
+[DRY RUN] Would copy: "source.txt" -> "file3.txt"
+
+Operation completed.
+```
+
+## Error Handling
+
+The script handles various error conditions:
+
+- **Missing source file**: `Error: Source file "missing.txt" does not exist`
+- **No arguments**: Displays usage help
+- **Missing list file**: `Error: List file "targets.txt" does not exist`
+- **Copy failures**: Reports individual file copy errors
+
+## Tips and Best Practices
+
+### For Database Backups
+Use list files to maintain consistent backup locations:
+```cmd
+duplicatefile production.db -list db_backup_targets.txt
+```
+
+### For Development Workflows
+Use dry-run mode to verify operations before execution:
+```cmd
+duplicatefile config.ini -dry -list deployment_targets.txt
+```
+
+### Path Considerations
+- Use quotes around file paths containing spaces
+- Forward slashes (/) and backslashes (\) both work on Windows
+- Relative paths are resolved from the current working directory
+
+## Requirements
+
+- Windows operating system
+- Command Prompt or PowerShell
+- Appropriate file system permissions for source and target locations
+
+## Installation
+
+1. Save the script as `duplicatefile.cmd`
+2. Place it in a directory included in your system PATH, or
+3. Run it from its directory using the full path
+
+The script is self-contained and requires no additional dependencies.

+ 0 - 64
qdr.fnd.core.data/qdr.fnd.core.data.csproj.Backup.tmp

@@ -1,64 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <TargetFramework>net9.0</TargetFramework>
-    <SignAssembly>true</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\bo_strong_key_pair.snk</AssemblyOriginatorKeyFile>
-    <DelaySign>false</DelaySign>
-    <RootNamespace>Quadarax.Foundation.Core.Data</RootNamespace>
-    <ApplicationIcon>..\quadarax_dll.ico</ApplicationIcon>
-    <Title>Quadarax.Foundation.Core.Data</Title>
-    <Authors>Dalibor Votruba</Authors>
-    <Company>Quadarax</Company>
-    <Product>Quadarax.Foundation</Product>
-    <Description>Quadarax.Foundation.Core.Data - data layer foundation library</Description>
-    <Copyright>Copyright © 2023. 2024, 2025 Quadarax</Copyright>
-    <PackageIcon>quadarax_dll.png</PackageIcon>
-    <PackageTags>QDR;FND;CORE;DATA;DAO</PackageTags>
-    <AssemblyVersion>0.0.3.0</AssemblyVersion>
-    <FileVersion>0.0.3.0</FileVersion>
-    <Version>0.0.3.0-alpha</Version>
-    <Nullable>enable</Nullable>
-    <PackageReadmeFile>releasenotes.md</PackageReadmeFile>
-    <PackageReleaseNotes>
-      Date:7.1.2025
-		- update dependency to qdr.fnd.core v.0.0.6.0
-		- update to .NET 9.0
-    </PackageReleaseNotes>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <Content Include="releasenotes.md">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-    </Content>
-  </ItemGroup>
-
-  <ItemGroup>
-    <Content Include="..\quadarax_dll.png">
-      <Pack>True</Pack>
-      <PackagePath>\</PackagePath>
-    </Content>
-  </ItemGroup>
-
-  <ItemGroup>
-    <None Include="..\qdr.fnd.core.business\releasenotes.md">
-      <Pack>True</Pack>
-      <PackagePath>\</PackagePath>
-    </None>
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
-    <PackageReference Include="qdr.fnd.core" Version="0.0.6-alpha" />
-    <PackageReference Include="qdr.fnd.core.data.itfc" Version="0.0.4-alpha" />
-    <PackageReference Include="System.Data.SqlClient" Version="4.9.0" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <None Update="releasenotes.md">
-      <Pack>True</Pack>
-      <PackagePath>\</PackagePath>
-    </None>
-  </ItemGroup>
-
-</Project>

+ 0 - 55
qdr.fnd.core.nlog/qdr.fnd.core.nlog.csproj.Backup.tmp

@@ -1,55 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <TargetFramework>net9.0</TargetFramework>
-    <SignAssembly>true</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\bo_strong_key_pair.snk</AssemblyOriginatorKeyFile>
-    <DelaySign>false</DelaySign>
-    <RootNamespace>Quadarax.Foundation.Core.NLog</RootNamespace>
-    <ApplicationIcon>..\quadarax_dll.ico</ApplicationIcon>
-    <Title>Quadarax.Foundation.Core.NLog</Title>
-    <Authors>Dalibor Votruba</Authors>
-    <Company>Quadarax</Company>
-    <Product>Quadarax.Foundation</Product>
-    <Description>Quadarax.Foundation.Core.NLog - logging provider for NLog under logging infrastructure in foundation library</Description>
-    <Copyright>Copyright © 2023,2024 Quadarax</Copyright>
-    <PackageIcon>quadarax_dll.png</PackageIcon>
-    <PackageTags>QDR;FND;CORE;NLOG</PackageTags>
-    <AssemblyVersion>0.0.3.0</AssemblyVersion>
-    <FileVersion>0.0.3.0</FileVersion>
-    <Version>0.0.3.0-alpha</Version>
-    <Nullable>enable</Nullable>
-    <PackageReadmeFile>releasenotes.md</PackageReadmeFile>
-    <PackageReleaseNotes>
-      Date:24.3.2024
-      - update dependencies packages (qdr.fnd.core)
-    </PackageReleaseNotes>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <Content Include="releasenotes.md">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-    </Content>
-  </ItemGroup>
-
-  <ItemGroup>
-    <None Include="..\quadarax_dll.png">
-      <Pack>True</Pack>
-      <PackagePath>\</PackagePath>
-    </None>
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="NLog" Version="5.3.4" />
-    <PackageReference Include="NLog.Extensions.Logging" Version="5.3.15" />
-    <PackageReference Include="qdr.fnd.core" Version="0.0.6-alpha" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <None Update="releasenotes.md">
-      <Pack>True</Pack>
-      <PackagePath>\</PackagePath>
-    </None>
-  </ItemGroup>
-
-</Project>

+ 51 - 0
qdr.fnd.core.qconsole.test/Commands/DummyCmd.cs

@@ -0,0 +1,51 @@
+using Quadarax.Foundation.Core.QConsole;
+using Quadarax.Foundation.Core.QConsole.Argument;
+using Quadarax.Foundation.Core.QConsole.Attributes;
+using Quadarax.Foundation.Core.QConsole.Command.Base;
+using Quadarax.Foundation.Core.Value;
+
+namespace qdr.fnd.core.qconsole.test.Commands
+{
+    [CommandDefinition]
+
+    internal class DummyCmd : AbstractCommand
+    {
+        public const string CommandName = "dummy";
+        public const string ArgResultName = "result";
+
+        protected bool ResultSuccess {get;set;}
+
+        public DummyCmd(Engine engine) : base(engine)
+        {
+        }
+
+        public override string Name => CommandName;
+
+        public override string Description => CommandName;
+
+       
+
+        protected override Result OnExecute()
+        {
+            if(!ResultSuccess)
+            {
+                Console.WriteLine("Dummy command execution failed.");
+                return new Result(new Exception("Dummy command execution failed by user request."));
+            }
+            Console.WriteLine("Dummy command executed successfully.");
+            return new Result();
+        }
+
+        protected override void OnValidateArguments()
+        {
+            ResultSuccess = GetArgumentValueOrDefault<bool>(ArgResultName);
+        }
+
+        protected override IEnumerable<AbstractArgument> OnSetupArguments()
+        {
+            var args = base.OnSetupArguments().ToList();
+            args.Add(new NamedArgument(ArgResultName,ArgResultName,ArgResultName, Quadarax.Foundation.Core.QConsole.Value.TypeValuesEnum.Bool, "True", false));
+            return args;
+        }
+    }
+}

+ 48 - 0
qdr.fnd.core.qconsole.test/Commands/DummyOrdinalCmd.cs

@@ -0,0 +1,48 @@
+using Quadarax.Foundation.Core.QConsole;
+using Quadarax.Foundation.Core.QConsole.Argument;
+using Quadarax.Foundation.Core.QConsole.Attributes;
+using Quadarax.Foundation.Core.QConsole.Command.Base;
+using Quadarax.Foundation.Core.Value;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace qdr.fnd.core.qconsole.test.Commands
+{
+    [CommandDefinition]
+    internal class DummyOrdinalCmd : AbstractCommand
+    {
+        public const string CommandName = "dummyord";
+        public const string ArgOrdName = "ord";
+
+        public override string Name => CommandName;
+
+        public override string Description => CommandName;
+
+        protected string? Argument{get;set; } = string.Empty;
+
+        public DummyOrdinalCmd(Engine engine) : base(engine)
+        {
+        }
+
+        protected override Result OnExecute()
+        {
+            WriteInfo($"Argument={Argument}");
+            return new Result();
+        }
+
+        protected override void OnValidateArguments()
+        {
+            Argument = GetArgumentValueOrDefault<string>(ArgOrdName);
+        }
+
+        protected override IEnumerable<AbstractArgument> OnSetupArguments()
+        {
+            var args = base.OnSetupArguments().ToList();
+            args.Add(new OrdinalArgument(0,false, ArgOrdName,ArgOrdName,ArgOrdName, Quadarax.Foundation.Core.QConsole.Value.TypeValuesEnum.String, string.Empty, true));
+            return args;
+        }
+    }
+}

+ 26 - 0
qdr.fnd.core.qconsole.test/Contexts/BasicCommandContext.cs

@@ -0,0 +1,26 @@
+using Quadarax.Foundation.Core.Object;
+using Quadarax.Foundation.Core.QConsole.Context;
+using System.IO.Abstractions;
+
+namespace qdr.fnd.core.qconsole.test.Contexts
+{
+    internal class BasicCommandContext : DisposableObject, ICommandContext
+    {
+
+        private IFileSystem? _fileSystem;
+
+        public IFileSystem FileSystem => _fileSystem ?? new FileSystem();
+
+        public string Status => "READY";
+
+        public BasicCommandContext(IFileSystem fileSystem)
+        {
+            _fileSystem = fileSystem;
+        }
+
+        protected override void OnDisposing()
+        {
+            _fileSystem = null;
+        }
+    }
+}

+ 31 - 0
qdr.fnd.core.qconsole.test/Contexts/BasicEngineContext.cs

@@ -0,0 +1,31 @@
+using Quadarax.Foundation.Core.Object;
+using Quadarax.Foundation.Core.QConsole.Context;
+using System.IO.Abstractions;
+
+namespace qdr.fnd.core.qconsole.test.Contexts
+{
+    internal class BasicEngineContext : DisposableObject, IEngineContext
+    {
+
+        private IFileSystem? _fileSystem;
+
+        public string Status => throw new NotImplementedException();
+
+
+        public BasicEngineContext(IFileSystem fileSystem)
+        {
+            _fileSystem = fileSystem;
+        }
+
+
+        public ICommandContext CreateCommandContext()
+        {
+            return new BasicCommandContext(_fileSystem ?? new FileSystem());
+        }
+
+        protected override void OnDisposing()
+        {
+            _fileSystem = null;
+        }
+    }
+}

+ 68 - 0
qdr.fnd.core.qconsole.test/EngineTests.cs

@@ -0,0 +1,68 @@
+using qdr.fnd.core.qconsole.test.Commands;
+using qdr.fnd.core.qconsole.test.Contexts;
+using Quadarax.Foundation.Core.IO.FileSystem.Memory;
+using Quadarax.Foundation.Core.QConsole;
+using Quadarax.Foundation.Core.QConsole.Configuration;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace qdr.fnd.core.qconsole.test
+{
+    public class Tests
+    {
+        private StartupConfiguration? _configuration;
+
+        [SetUp]
+        public void Setup()
+        {
+            _configuration = new StartupConfiguration("Quadarax QConsole Test",Version.Parse("1.0.0.0"))
+            {
+                ConsoleCopyright = "Me",
+                AllowInteractive = false,
+                WaitOnKeyInNonInteractiveMode = false,
+                CommandDefinitionAssembly = new Assembly[] { Assembly.GetExecutingAssembly()! }
+            };
+            _configuration.DisableDefaultCommands();
+            _configuration.ShowStatusOnEveryCommand = false;
+            _configuration.IsConsoleDebug = false;
+            _configuration.ThrowExceptionWhenNotSuccess = true;
+        }
+
+        [Test]
+        public void DummyCommandOk()
+        {
+            var arguments = new string[] { DummyCmd.CommandName };
+            var engine = CreateEngine(arguments);
+            engine.Start();
+        }
+
+        [Test]
+        public void DummyCommandFailed()
+        {
+            var arguments = new string[] { DummyCmd.CommandName, "-result:false" };
+            var engine = CreateEngine(arguments);
+            Assert.Throws<AggregateException>(() => engine.Start(),"Dummy command execution failed by user request.");
+
+        }
+
+        [Test]
+        public void DummyOrdinalCommandQuotedOk()
+        {
+            var filename = "D:\\temp\\file-some.dat";   
+            var arguments = new string[] { DummyOrdinalCmd.CommandName, "\"" + filename + "\"" };
+            var engine = CreateEngine(arguments);
+            engine.Start();
+            Assert.That(engine.GetCommand(DummyOrdinalCmd.CommandName)?.Arguments.First(s=>s.Code == DummyOrdinalCmd.ArgOrdName).Value.AsString(), Is.EqualTo(filename));
+        }
+
+        private Engine CreateEngine(string[] args)
+        {
+            if (_configuration == null)
+            {
+                throw new InvalidOperationException("Configuration is not initialized.");
+            }
+            return new Engine(_configuration, new BasicEngineContext(new MemoryFileSystem()), args);
+        }
+    }
+}

+ 28 - 0
qdr.fnd.core.qconsole.test/qdr.fnd.core.qconsole.test.csproj

@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net9.0</TargetFramework>
+    <LangVersion>latest</LangVersion>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+    <IsPackable>false</IsPackable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="coverlet.collector" Version="6.0.2" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
+    <PackageReference Include="NUnit" Version="4.2.2" />
+    <PackageReference Include="NUnit.Analyzers" Version="4.4.0" />
+    <PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\qdr.fnd.core.qconsole\qdr.fnd.core.qconsole.csproj" />
+    <ProjectReference Include="..\qdr.fnd.core\qdr.fnd.core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Using Include="NUnit.Framework" />
+  </ItemGroup>
+
+</Project>

+ 2 - 1
qdr.fnd.core.qconsole/Command/Base/AbstractCommand.cs

@@ -7,6 +7,7 @@ using Quadarax.Foundation.Core.QConsole.Argument;
 using Quadarax.Foundation.Core.QConsole.Context;
 using Quadarax.Foundation.Core.QConsole.Extensions;
 using Quadarax.Foundation.Core.Value;
+using Quadarax.Foundation.Core.Value.Extensions;
 
 namespace Quadarax.Foundation.Core.QConsole.Command.Base
 {
@@ -156,7 +157,7 @@ namespace Quadarax.Foundation.Core.QConsole.Command.Base
                         throw new Exception(
                             $"Invalid ordinal argument '{arg}' at position {index} for this command.");
 
-                    argument.Value.Set(arg);
+                    argument.Value.Set(arg.RemoveDecorator(Engine.Configuration.CharacterTextValueBraceSeparator));
                     argument.Hit();
                     WriteDebugInfo($"Ordinal argument '{argument.Code}' is set to value '{argument.Value.AsString()}'.");
                 }

+ 242 - 238
qdr.fnd.core.qconsole/Configuration/StartupConfiguration.cs

@@ -1,238 +1,242 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Xml.XPath;
-using Quadarax.Foundation.Core.Console;
-using Quadarax.Foundation.Core.Logging;
-using Quadarax.Foundation.Core.QConsole.Handlers;
-
-namespace Quadarax.Foundation.Core.QConsole.Configuration
-{
-    public class StartupConfiguration
-    {
-        #region *** Private Fields ***
-        private bool _isConsoleDebug;
-        private string[] _defaultCommands = {"CmdClear", "CmdExit", "CmdPrint", "CmdSelection"};
-
-        private readonly IList<string> _disabledCommands = new List<string>();
-        #endregion
-
-        #region *** Properties ***
-
-        /// <summary>
-        /// Name of console application, If not defined uses default name from entryAssembly.
-        /// </summary>
-        public string ConsoleName { get; } = string.Empty;
-        /// <summary>
-        /// Console application version. If not defined uses version of entryAssembly.
-        /// </summary>
-        public Version ConsoleVersion { get; } = Version.Parse("0.0.0.0");
-        /// <summary>
-        /// Console application copyright message. If not set skip this info.
-        /// </summary>
-        public string ConsoleCopyright { get; set; } = string.Empty;
-        /// <summary>
-        /// Defines if console is initialized from XML definition.
-        /// </summary>
-        public bool IsInitFromXml { get; }
-
-        /// <summary>
-        /// Left introducing of interactive mode character.
-        /// </summary>
-        public string CharacterLineIntroduce { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Separator between two ordinal arguments
-        /// </summary>
-        public string CharacterOrdinalArgumentSeparator { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Named argument prefix character.
-        /// </summary>
-        public string CharacterNamedArgumentSeparator { get; set; } = string.Empty;
-        /// <summary>
-        /// Named argument postfix character {defines end of argument code and value)
-        /// </summary>
-        public string CharacterNamedArgumentValueSeparator { get; set; } = string.Empty;
-        /// <summary>
-        /// Start end end string value character
-        /// </summary>
-        public string CharacterTextValueBraceSeparator { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Separator between two selection inputs
-        /// </summary>
-        public string CharacterSelectionInputValueSeparator { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Date input/output value format
-        /// </summary>
-        public string DateFormat { get; set; } = string.Empty;
-        /// <summary>
-        /// Time input/output value format
-        /// </summary>
-        public string TimeFormat { get; set; } = string.Empty;
-        /// <summary>
-        /// Defines if interactive mode is enabled
-        /// </summary>
-        public bool AllowInteractive { get; set; }
-        /// <summary>
-        /// Defines if initial variables is allowed for interactive mode.
-        /// </summary>
-        public bool AllowPreset { get; set; }
-
-        /// <summary>
-        /// Defines assembly list where custom commands are defined (in case explicit command definition).
-        /// </summary>
-        public Assembly[] CommandDefinitionAssembly { get; set; } = Array.Empty<Assembly>();
-
-        /// <summary>
-        /// Returns name of command classes that will be disabled (hidden)
-        /// </summary>
-        public string[] DisabledCommands => _disabledCommands.ToArray();
-
-        /// <summary>
-        /// Defines if waits on key at the end of process in non-interactive mode (inline mode)
-        /// </summary>
-        public bool WaitOnKeyInNonInteractiveMode { get; set; }
-
-        /// <summary>
-        /// Defines if show status of engine and command context before every command executed.
-        /// </summary>
-        public bool ShowStatusOnEveryCommand { get; set; }
-
-
-        public bool UseDefaultHandlers { get; set; }
-
-        /// <summary>
-        /// Handler when no arguments input specified. If not set then do nothing.
-        /// </summary>
-        public NothingToDo? HandlerNothingToDo { get; set; }
-
-        public bool IsConsoleDebug {
-            get
-            {
-                return _isConsoleDebug;
-            }
-            set 
-            { 
-                _isConsoleDebug = value;
-                DebugConsoleWriter.IsWriteDebugEnabled = value;
-            }
-        }
-
-        public ILogger? DefaultLoggerFactory { get; set; }
-        #endregion
-
-        #region *** Constructor ***
-
-        public StartupConfiguration(string consoleName, Version consoleVersion)
-        {
-            ConsoleName = consoleName;
-            ConsoleVersion = consoleVersion;
-            IsInitFromXml = false;
-            InitDefaults();
-        }
-
-        public StartupConfiguration(string consoleXmlFileName)
-        {
-            IsInitFromXml = true;
-            InitDefaults();
-        }
-
-        public StartupConfiguration(XPathNavigator configurationNavigator)
-        {
-            IsInitFromXml = true;
-            InitDefaults();
-        }
-        #endregion
-
-        #region *** Public Operations ***
-        public StartupConfiguration EnableDebugMode()
-        {
-            IsConsoleDebug = true;
-            return this;
-        }
-        public StartupConfiguration DisableDebugMode()
-        {
-            IsConsoleDebug = false;
-            return this;
-        }
-
-        public StartupConfiguration DisableDefaultCommands()
-        {
-            return DisableCommands(_defaultCommands);
-        }
-
-        public StartupConfiguration EnableDefaultCommands()
-        {
-            return EnableCommands(_defaultCommands);
-        }
-
-        public StartupConfiguration EnableCommands(params string[]? commandClassNames)
-        {
-            if (commandClassNames == null)
-                return this;
-
-            foreach (var className in commandClassNames)
-            {
-                if (_disabledCommands.Contains(className))
-                    _disabledCommands.Remove(className);
-            }
-            return this;
-        }
-
-        public StartupConfiguration DisableCommands(params string[]? commandClassNames)
-        {
-            if (commandClassNames == null)
-                return this;
-            foreach (var className in commandClassNames)
-            {
-                if (!_disabledCommands.Contains(className))
-                    _disabledCommands.Add(className);
-            }
-
-            return this;
-        }
-
-        #endregion
-
-        #region *** Private Operations ***
-        private void InitDefaults()
-        {
-            CharacterLineIntroduce = Defaults.RoleCharacters.ConsoleLineIntroduce;
-            CharacterOrdinalArgumentSeparator = Defaults.RoleCharacters.OrdinalArgumentSeparator;
-            CharacterNamedArgumentSeparator = Defaults.RoleCharacters.NamedArgumentSeparator;
-            CharacterNamedArgumentValueSeparator = Defaults.RoleCharacters.NamedArgumentValueSeparator;
-            CharacterTextValueBraceSeparator = Defaults.RoleCharacters.TextValueBraceSeparator;
-            CharacterSelectionInputValueSeparator = Defaults.RoleCharacters.InputSelectionValueSeparator;
-
-            DateFormat = Defaults.Formats.DateFormat;
-            TimeFormat = Defaults.Formats.TimeFormat;
-
-            AllowInteractive = Defaults.Console.InteractiveAllowed;
-            AllowPreset = Defaults.Console.PresetAllowed;
-            IsConsoleDebug = Defaults.Console.ConsoleDebugMode;
-            WaitOnKeyInNonInteractiveMode = Defaults.Console.WaitOnKeyInNonInteractiveMode;
-            ShowStatusOnEveryCommand = Defaults.Console.ShowStatusOnEveryCommand;
-            UseDefaultHandlers = Defaults.Console.UseDefaultHadlers;
-
-            if (UseDefaultHandlers)
-            {
-                HandlerNothingToDo += engine =>
-                {
-                    using (var writer = new ConsoleWriter(ConsoleColor.Yellow))
-                    {
-                        writer.WriteLine("Nothing to do.");
-                    }
-                    using (var writer = new ConsoleWriter(ConsoleColor.DarkGreen))
-                    {
-                        writer.WriteLine("Put some arguments to do something. For example -help to list commands and arguments.");
-                    }
-                };
-            }
-        }
-        #endregion
-    }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Xml.XPath;
+using Quadarax.Foundation.Core.Console;
+using Quadarax.Foundation.Core.Logging;
+using Quadarax.Foundation.Core.QConsole.Handlers;
+
+namespace Quadarax.Foundation.Core.QConsole.Configuration
+{
+    public class StartupConfiguration
+    {
+        #region *** Private Fields ***
+        private bool _isConsoleDebug;
+        private string[] _defaultCommands = {"CmdClear", "CmdExit", "CmdPrint", "CmdSelection"};
+
+        private readonly IList<string> _disabledCommands = new List<string>();
+        #endregion
+
+        #region *** Properties ***
+
+        /// <summary>
+        /// Name of console application, If not defined uses default name from entryAssembly.
+        /// </summary>
+        public string ConsoleName { get; } = string.Empty;
+        /// <summary>
+        /// Console application version. If not defined uses version of entryAssembly.
+        /// </summary>
+        public Version ConsoleVersion { get; } = Version.Parse("0.0.0.0");
+        /// <summary>
+        /// Console application copyright message. If not set skip this info.
+        /// </summary>
+        public string ConsoleCopyright { get; set; } = string.Empty;
+        /// <summary>
+        /// Defines if console is initialized from XML definition.
+        /// </summary>
+        public bool IsInitFromXml { get; }
+
+        /// <summary>
+        /// Left introducing of interactive mode character.
+        /// </summary>
+        public string CharacterLineIntroduce { get; set; } = string.Empty;
+
+        /// <summary>
+        /// Separator between two ordinal arguments
+        /// </summary>
+        public string CharacterOrdinalArgumentSeparator { get; set; } = string.Empty;
+
+        /// <summary>
+        /// Named argument prefix character.
+        /// </summary>
+        public string CharacterNamedArgumentSeparator { get; set; } = string.Empty;
+        /// <summary>
+        /// Named argument postfix character {defines end of argument code and value)
+        /// </summary>
+        public string CharacterNamedArgumentValueSeparator { get; set; } = string.Empty;
+        /// <summary>
+        /// Start end end string value character
+        /// </summary>
+        public string CharacterTextValueBraceSeparator { get; set; } = string.Empty;
+
+        /// <summary>
+        /// Separator between two selection inputs
+        /// </summary>
+        public string CharacterSelectionInputValueSeparator { get; set; } = string.Empty;
+
+        /// <summary>
+        /// Date input/output value format
+        /// </summary>
+        public string DateFormat { get; set; } = string.Empty;
+        /// <summary>
+        /// Time input/output value format
+        /// </summary>
+        public string TimeFormat { get; set; } = string.Empty;
+        /// <summary>
+        /// Defines if interactive mode is enabled
+        /// </summary>
+        public bool AllowInteractive { get; set; }
+        /// <summary>
+        /// Defines if initial variables is allowed for interactive mode.
+        /// </summary>
+        public bool AllowPreset { get; set; }
+
+        /// <summary>
+        /// Defines assembly list where custom commands are defined (in case explicit command definition).
+        /// </summary>
+        public Assembly[] CommandDefinitionAssembly { get; set; } = Array.Empty<Assembly>();
+
+        /// <summary>
+        /// Returns name of command classes that will be disabled (hidden)
+        /// </summary>
+        public string[] DisabledCommands => _disabledCommands.ToArray();
+
+        /// <summary>
+        /// Defines if waits on key at the end of process in non-interactive mode (inline mode)
+        /// </summary>
+        public bool WaitOnKeyInNonInteractiveMode { get; set; }
+
+        /// <summary>
+        /// Defines if show status of engine and command context before every command executed.
+        /// </summary>
+        public bool ShowStatusOnEveryCommand { get; set; }
+
+        /// <summary>
+        /// Defines if throws exception when command execution is not successful.
+        /// </summary>
+        public bool ThrowExceptionWhenNotSuccess { get; set; } = false;
+
+        public bool UseDefaultHandlers { get; set; }
+
+        /// <summary>
+        /// Handler when no arguments input specified. If not set then do nothing.
+        /// </summary>
+        public NothingToDo? HandlerNothingToDo { get; set; }
+
+        public bool IsConsoleDebug {
+            get
+            {
+                return _isConsoleDebug;
+            }
+            set 
+            { 
+                _isConsoleDebug = value;
+                DebugConsoleWriter.IsWriteDebugEnabled = value;
+            }
+        }
+
+        public ILogger? DefaultLoggerFactory { get; set; }
+        #endregion
+
+        #region *** Constructor ***
+
+        public StartupConfiguration(string consoleName, Version consoleVersion)
+        {
+            ConsoleName = consoleName;
+            ConsoleVersion = consoleVersion;
+            IsInitFromXml = false;
+            InitDefaults();
+        }
+
+        public StartupConfiguration(string consoleXmlFileName)
+        {
+            IsInitFromXml = true;
+            InitDefaults();
+        }
+
+        public StartupConfiguration(XPathNavigator configurationNavigator)
+        {
+            IsInitFromXml = true;
+            InitDefaults();
+        }
+        #endregion
+
+        #region *** Public Operations ***
+        public StartupConfiguration EnableDebugMode()
+        {
+            IsConsoleDebug = true;
+            return this;
+        }
+        public StartupConfiguration DisableDebugMode()
+        {
+            IsConsoleDebug = false;
+            return this;
+        }
+
+        public StartupConfiguration DisableDefaultCommands()
+        {
+            return DisableCommands(_defaultCommands);
+        }
+
+        public StartupConfiguration EnableDefaultCommands()
+        {
+            return EnableCommands(_defaultCommands);
+        }
+
+        public StartupConfiguration EnableCommands(params string[]? commandClassNames)
+        {
+            if (commandClassNames == null)
+                return this;
+
+            foreach (var className in commandClassNames)
+            {
+                if (_disabledCommands.Contains(className))
+                    _disabledCommands.Remove(className);
+            }
+            return this;
+        }
+
+        public StartupConfiguration DisableCommands(params string[]? commandClassNames)
+        {
+            if (commandClassNames == null)
+                return this;
+            foreach (var className in commandClassNames)
+            {
+                if (!_disabledCommands.Contains(className))
+                    _disabledCommands.Add(className);
+            }
+
+            return this;
+        }
+
+        #endregion
+
+        #region *** Private Operations ***
+        private void InitDefaults()
+        {
+            CharacterLineIntroduce = Defaults.RoleCharacters.ConsoleLineIntroduce;
+            CharacterOrdinalArgumentSeparator = Defaults.RoleCharacters.OrdinalArgumentSeparator;
+            CharacterNamedArgumentSeparator = Defaults.RoleCharacters.NamedArgumentSeparator;
+            CharacterNamedArgumentValueSeparator = Defaults.RoleCharacters.NamedArgumentValueSeparator;
+            CharacterTextValueBraceSeparator = Defaults.RoleCharacters.TextValueBraceSeparator;
+            CharacterSelectionInputValueSeparator = Defaults.RoleCharacters.InputSelectionValueSeparator;
+
+            DateFormat = Defaults.Formats.DateFormat;
+            TimeFormat = Defaults.Formats.TimeFormat;
+
+            AllowInteractive = Defaults.Console.InteractiveAllowed;
+            AllowPreset = Defaults.Console.PresetAllowed;
+            IsConsoleDebug = Defaults.Console.ConsoleDebugMode;
+            WaitOnKeyInNonInteractiveMode = Defaults.Console.WaitOnKeyInNonInteractiveMode;
+            ShowStatusOnEveryCommand = Defaults.Console.ShowStatusOnEveryCommand;
+            UseDefaultHandlers = Defaults.Console.UseDefaultHadlers;
+
+            if (UseDefaultHandlers)
+            {
+                HandlerNothingToDo += engine =>
+                {
+                    using (var writer = new ConsoleWriter(ConsoleColor.Yellow))
+                    {
+                        writer.WriteLine("Nothing to do.");
+                    }
+                    using (var writer = new ConsoleWriter(ConsoleColor.DarkGreen))
+                    {
+                        writer.WriteLine("Put some arguments to do something. For example -help to list commands and arguments.");
+                    }
+                };
+            }
+        }
+        #endregion
+    }
+}

+ 21 - 16
qdr.fnd.core.qconsole/Engine.cs

@@ -3,10 +3,12 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Reflection;
 using Quadarax.Foundation.Core.Console;
+using Quadarax.Foundation.Core.Exceptions;
 using Quadarax.Foundation.Core.QConsole.Attributes;
 using Quadarax.Foundation.Core.QConsole.Command.Base;
 using Quadarax.Foundation.Core.QConsole.Configuration;
 using Quadarax.Foundation.Core.QConsole.Context;
+using Quadarax.Foundation.Core.Value.Extensions;
 
 namespace Quadarax.Foundation.Core.QConsole
 {
@@ -39,21 +41,20 @@ namespace Quadarax.Foundation.Core.QConsole
                 args = new string[0];
 
             // correction of arguments (fix " values)
-            for (var i = 0; i < args.Length; i++)
-            {
-                if (args[i].Contains(" ") && args[i].Contains(configuration.CharacterNamedArgumentValueSeparator) ||
-                    args[i].Contains(configuration.CharacterNamedArgumentSeparator) && args[i].Contains(configuration.CharacterNamedArgumentValueSeparator))
-                {
-                    var parts = args[i].Split(new[] {configuration.CharacterNamedArgumentValueSeparator},
-                        StringSplitOptions.RemoveEmptyEntries);
-                    if (parts.Length == 2)
-                        args[i] = parts[0] + configuration.CharacterNamedArgumentValueSeparator + configuration.CharacterTextValueBraceSeparator + parts[1] +
-                                  configuration.CharacterTextValueBraceSeparator;
-                    else
-                        args[i] = parts[0] + configuration.CharacterNamedArgumentValueSeparator + configuration.CharacterTextValueBraceSeparator + string.Join(":",parts.Skip(1).Take(parts.Length - 1)) +
-                                  configuration.CharacterTextValueBraceSeparator;
-                }
-            }
+            //for (var i = 0; i < args.Length; i++)
+            //{
+            //    if (args[i].Contains(" ") && args[i].Contains(configuration.CharacterNamedArgumentValueSeparator) ||
+            //        args[i].Contains(configuration.CharacterNamedArgumentSeparator) && args[i].Contains(configuration.CharacterNamedArgumentValueSeparator))
+            //    {
+            //        var parts = args[i].SplitQuoted(configuration.CharacterNamedArgumentValueSeparator,configuration.CharacterTextValueBraceSeparator, removeEmptyItems:true, keepBracket:true);
+            //        if (parts.Length == 2)
+            //            args[i] = parts[0] + configuration.CharacterNamedArgumentValueSeparator + configuration.CharacterTextValueBraceSeparator + parts[1] +
+            //                      configuration.CharacterTextValueBraceSeparator;
+            //        else
+            //            args[i] = parts[0] + configuration.CharacterNamedArgumentValueSeparator + configuration.CharacterTextValueBraceSeparator + string.Join(":",parts.Skip(1).Take(parts.Length - 1)) +
+            //                      configuration.CharacterTextValueBraceSeparator;
+            //    }
+            //}
 
             // Ensure if debug mode -> Enable debug mode and remove explicit argument
             if (args.Any(x => x.Trim().ToLower() == Constants.Console.DebugModeExplicitArg))
@@ -114,7 +115,7 @@ namespace Quadarax.Foundation.Core.QConsole
                         _isExitSignal = true;
                     }
 
-                    var args = (input ?? String.Empty).Split(new[] {" "}, StringSplitOptions.RemoveEmptyEntries);
+                    var args = (input ?? String.Empty).Split([" "], StringSplitOptions.RemoveEmptyEntries);
                     if (args.Length == 0)
                     {
                         if (_isExitSignal == true && !Configuration.AllowInteractive &&
@@ -131,6 +132,8 @@ namespace Quadarax.Foundation.Core.QConsole
                         using (var writerError = new ConsoleWriter(ConsoleColor.Red))
                         {
                             writerError.WriteLine($"Unknown command '{args[0]}'.");
+                            if (Configuration.ThrowExceptionWhenNotSuccess)
+                                throw new Exception($"Command '{args[0]}' is not found in the console commands list.");
                         }
                         continue;
                     }
@@ -149,6 +152,8 @@ namespace Quadarax.Foundation.Core.QConsole
                         {
                             writerError.WriteLine("Error:");
                             var e = result.ThrownException;
+                            if (Configuration.ThrowExceptionWhenNotSuccess)
+                                throw e.ToAggregateException();
 
                             if (e is AggregateException aggr)
                             {

+ 7 - 6
qdr.fnd.core.qconsole/qdr.fnd.core.qconsole.csproj

@@ -15,14 +15,15 @@
     <Copyright>Copyright © 2023,2024,2025 Quadarax</Copyright>
     <PackageIcon>quadarax_dll.png</PackageIcon>
     <PackageTags>QDR;FND;CORE;CONSOLE</PackageTags>
-    <AssemblyVersion>0.0.7.0</AssemblyVersion>
-    <FileVersion>0.0.7.0</FileVersion>
-    <Version>0.0.7.0-alpha</Version>
+    <AssemblyVersion>0.0.8.0</AssemblyVersion>
+    <FileVersion>0.0.8.0</FileVersion>
+    <Version>0.0.8.0-alpha</Version>
     <Nullable>enable</Nullable>
     <PackageReadmeFile>releasenotes.md</PackageReadmeFile>
     <PackageReleaseNotes>
-		Date:22.05.2025
-		- update dependency to qdr.fnd.core.0.0.8.0-alpha
+		Date:26.06.2025
+		- Fix support to use quoted arguments in ordinal type
+		- update dependency to qdr.fnd.core.0.0.10.0-alpha
     </PackageReleaseNotes>
   </PropertyGroup>
 
@@ -41,7 +42,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="qdr.fnd.core" Version="0.0.8-alpha" />
+    <PackageReference Include="qdr.fnd.core" Version="0.0.10-alpha" />
   </ItemGroup>
 
   <ItemGroup>

+ 5 - 0
qdr.fnd.core.qconsole/releasenotes.md

@@ -1,6 +1,11 @@
 # Quadarax.Foundation.Core.QConsole * Release notes
 Ordered by version descending
 
+## 0.0.8.0
+Date:__26.06.2025__
+- Fix support to use quoted arguments in ordinal type
+- update dependency to qdr.fnd.core.0.0.10.0-alpha
+---
 ## 0.0.7.0
 Date:__22.05.2025__
 - update dependency to qdr.fnd.core.0.0.8.0-alpha

+ 7 - 0
qdr.fnd.sln

@@ -25,6 +25,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qdr.fnd.core.avalonia", "qd
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qdr.fnd.core.windows", "qdr.fnd.core.windows\qdr.fnd.core.windows.csproj", "{78BFE917-FF74-4B0D-8775-444A8A5527A7}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qdr.fnd.core.qconsole.test", "qdr.fnd.core.qconsole.test\qdr.fnd.core.qconsole.test.csproj", "{55A17A98-3A42-45FD-BB62-5F6BA52B7146}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -71,12 +73,17 @@ Global
 		{78BFE917-FF74-4B0D-8775-444A8A5527A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{78BFE917-FF74-4B0D-8775-444A8A5527A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{78BFE917-FF74-4B0D-8775-444A8A5527A7}.Release|Any CPU.Build.0 = Release|Any CPU
+		{55A17A98-3A42-45FD-BB62-5F6BA52B7146}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{55A17A98-3A42-45FD-BB62-5F6BA52B7146}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{55A17A98-3A42-45FD-BB62-5F6BA52B7146}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{55A17A98-3A42-45FD-BB62-5F6BA52B7146}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
 	GlobalSection(NestedProjects) = preSolution
 		{B172BF70-17B7-4B8D-8649-EF79254182AB} = {62EEC894-61DC-4D39-83F6-28FF82F6E758}
+		{55A17A98-3A42-45FD-BB62-5F6BA52B7146} = {62EEC894-61DC-4D39-83F6-28FF82F6E758}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {89C7D7E9-6E4B-4A30-A6C8-FDE75DFDEB1B}