|
@@ -1,326 +1,330 @@
|
|
|
-using System;
|
|
|
|
|
-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
|
|
|
|
|
-{
|
|
|
|
|
- public class Engine : IDisposable
|
|
|
|
|
- {
|
|
|
|
|
- #region *** Private Fields ***
|
|
|
|
|
- private bool _isDispoising;
|
|
|
|
|
- private bool _isExitSignal;
|
|
|
|
|
- private IList<AbstractCommand> _commands = new List<AbstractCommand>();
|
|
|
|
|
- private StartupConfiguration _configuration;
|
|
|
|
|
- private IList<ISelectionEntry> _selections = new List<ISelectionEntry>();
|
|
|
|
|
- #endregion
|
|
|
|
|
-
|
|
|
|
|
- #region *** Properties ***
|
|
|
|
|
- public string[] RawArguments { get; }
|
|
|
|
|
- public StartupConfiguration Configuration => _configuration;
|
|
|
|
|
- public AbstractCommand[] Commands => _commands.ToArray();
|
|
|
|
|
- public ISelectionEntry[] Selections => _selections.ToArray();
|
|
|
|
|
-
|
|
|
|
|
- public IEngineContext Context { get; }
|
|
|
|
|
- #endregion
|
|
|
|
|
-
|
|
|
|
|
- #region *** Constructor ***
|
|
|
|
|
- public Engine(StartupConfiguration configuration, IEngineContext context, string[] args)
|
|
|
|
|
- {
|
|
|
|
|
- _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
|
|
|
|
- Context = context ?? throw new ArgumentNullException(nameof(context));
|
|
|
|
|
-
|
|
|
|
|
- if (args == null)
|
|
|
|
|
- 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].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))
|
|
|
|
|
- {
|
|
|
|
|
- configuration.EnableDebugMode();
|
|
|
|
|
- var newArgs = new List<string>(args);
|
|
|
|
|
- newArgs.Remove(Constants.Console.DebugModeExplicitArg);
|
|
|
|
|
- args = newArgs.ToArray();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- WriteDebugInfo($"Console '{_configuration.ConsoleName}' initialization started.");
|
|
|
|
|
- WriteDebugInfo($"Engine assembly version '{Assembly.GetExecutingAssembly().GetName().Version}'.");
|
|
|
|
|
- WriteDebugInfo($"IsInitFromXml is set to '{_configuration.IsInitFromXml}'.");
|
|
|
|
|
-
|
|
|
|
|
- ShowHeader();
|
|
|
|
|
-
|
|
|
|
|
- RawArguments = args;
|
|
|
|
|
- WriteDebugInfo($"Input {args.Length} arguments appended.");
|
|
|
|
|
- WriteDebugInfo($"Context set: {Context}");
|
|
|
|
|
-
|
|
|
|
|
- var commandClasses = GetTypesWithHelpAttribute(new CommandDefinitionAttribute());
|
|
|
|
|
- WriteDebugInfo($"Console found {commandClasses.Count()} command definitions.");
|
|
|
|
|
-
|
|
|
|
|
- var paramConstrTypes = new Type[] { typeof(Engine) };
|
|
|
|
|
- var paramConstrValues = new object[] { this };
|
|
|
|
|
-
|
|
|
|
|
- foreach (var commandClass in commandClasses)
|
|
|
|
|
- {
|
|
|
|
|
- var command = Construct<AbstractCommand>(commandClass, paramConstrTypes, paramConstrValues);
|
|
|
|
|
- _commands.Add(command);
|
|
|
|
|
- WriteDebugInfo($"Command definition '{command}' added - class '{command.GetType().FullName}', assembly '{command.GetType().Assembly.GetName().Name}'.");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- WriteDebugInfo("Console initialized.");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public void Start()
|
|
|
|
|
- {
|
|
|
|
|
- WriteDebugInfo("Console starting.");
|
|
|
|
|
- var commandLineArguments = string.Join(" ", RawArguments);
|
|
|
|
|
-
|
|
|
|
|
- using (var writer = new ConsoleWriter(ConsoleColor.White))
|
|
|
|
|
- {
|
|
|
|
|
- while (!_isExitSignal)
|
|
|
|
|
- {
|
|
|
|
|
- var input = string.Empty;
|
|
|
|
|
- if (commandLineArguments.Length == 0 && Configuration.AllowInteractive)
|
|
|
|
|
- {
|
|
|
|
|
- // interactive command entry
|
|
|
|
|
- writer.Write(_configuration.CharacterLineIntroduce);
|
|
|
|
|
- input = System.Console.ReadLine();
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- // inline command entry
|
|
|
|
|
- input = commandLineArguments;
|
|
|
|
|
- _isExitSignal = true;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- var args = (input ?? String.Empty).Split([" "], StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
- if (args.Length == 0)
|
|
|
|
|
- {
|
|
|
|
|
- if (_isExitSignal == true && !Configuration.AllowInteractive &&
|
|
|
|
|
- Configuration.HandlerNothingToDo != null)
|
|
|
|
|
- {
|
|
|
|
|
- Configuration.HandlerNothingToDo.Invoke(this);
|
|
|
|
|
- }
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- var command = GetCommand(args[0]);
|
|
|
|
|
- if (command == null)
|
|
|
|
|
- {
|
|
|
|
|
- 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;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- var result = command.Execute((input ?? String.Empty));
|
|
|
|
|
- if (result.IsSuccess)
|
|
|
|
|
- {
|
|
|
|
|
- using (var writerSucc = new ConsoleWriter(ConsoleColor.Green))
|
|
|
|
|
- {
|
|
|
|
|
- writerSucc.WriteLine("Done.");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- using (var writerError = new ConsoleWriter(ConsoleColor.Red))
|
|
|
|
|
- {
|
|
|
|
|
- writerError.WriteLine("Error:");
|
|
|
|
|
- var e = result.ThrownException;
|
|
|
|
|
- if (Configuration.ThrowExceptionWhenNotSuccess)
|
|
|
|
|
- throw e.ToAggregateException();
|
|
|
|
|
-
|
|
|
|
|
- if (e is AggregateException aggr)
|
|
|
|
|
- {
|
|
|
|
|
- foreach (var ex in aggr.InnerExceptions)
|
|
|
|
|
- DumpException(writerError, ex);
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- while (e != null)
|
|
|
|
|
- {
|
|
|
|
|
- writerError.WriteLine(e.Message);
|
|
|
|
|
- e = e.InnerException;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (!Configuration.AllowInteractive && Configuration.WaitOnKeyInNonInteractiveMode)
|
|
|
|
|
- {
|
|
|
|
|
- WriteDebugInfo("Waiting for the key by configuration settings...");
|
|
|
|
|
- using (var writerGreen = new ConsoleWriter(ConsoleColor.Magenta))
|
|
|
|
|
- {
|
|
|
|
|
- writerGreen.WriteLine("Press any key to close...");
|
|
|
|
|
- System.Console.ReadKey();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public void Stop()
|
|
|
|
|
- {
|
|
|
|
|
- _isExitSignal = true;
|
|
|
|
|
- WriteDebugInfo("Console stopped.");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public AbstractCommand? GetCommand(string commandName)
|
|
|
|
|
- {
|
|
|
|
|
- return _commands.FirstOrDefault(x => x.Name.ToUpper() == commandName.ToUpper());
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public void AppendSelection(params ISelectionEntry[] selectionEntries)
|
|
|
|
|
- {
|
|
|
|
|
- if (selectionEntries == null)
|
|
|
|
|
- throw new ArgumentNullException(nameof(selectionEntries));
|
|
|
|
|
- foreach (var entry in selectionEntries)
|
|
|
|
|
- {
|
|
|
|
|
- _selections.Add(entry);
|
|
|
|
|
- }
|
|
|
|
|
- WriteDebugInfo($"{selectionEntries.Length} was added to global selections. Total count {_selections.Count} entries.");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public void ClearSelection()
|
|
|
|
|
- {
|
|
|
|
|
- _selections.Clear();
|
|
|
|
|
- WriteDebugInfo("Global selections was cleared.");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public ISelectionEntry GetSelection(int ordinal)
|
|
|
|
|
- {
|
|
|
|
|
- if (ordinal==0)
|
|
|
|
|
- throw new ArgumentOutOfRangeException(nameof(ordinal), "Ordinal value must be greater than 0.");
|
|
|
|
|
- if (_selections.Count < ordinal)
|
|
|
|
|
- throw new ArgumentOutOfRangeException(nameof(ordinal), $"Ordinal value must be less than {_selections.Count}.");
|
|
|
|
|
-
|
|
|
|
|
- return _selections[ordinal - 1];
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- #endregion
|
|
|
|
|
-
|
|
|
|
|
- #region *** Public Operations ***
|
|
|
|
|
- public void Dispose()
|
|
|
|
|
- {
|
|
|
|
|
- if (_isDispoising)
|
|
|
|
|
- return;
|
|
|
|
|
- _isDispoising = true;
|
|
|
|
|
- WriteDebugInfo("Dispoising called.");
|
|
|
|
|
- OnDispoising();
|
|
|
|
|
- WriteDebugInfo("Dispoising done.");
|
|
|
|
|
- }
|
|
|
|
|
- #endregion
|
|
|
|
|
-
|
|
|
|
|
- #region *** Protected Operations ***
|
|
|
|
|
- protected virtual void OnDispoising()
|
|
|
|
|
- {
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- protected virtual void OnShowHeader()
|
|
|
|
|
- {
|
|
|
|
|
- var headerText = string.IsNullOrEmpty(_configuration.ConsoleCopyright) ? @"{0} * {1}" : @"{0} * {1} * {2}";
|
|
|
|
|
- using (var writer = new ConsoleWriter(ConsoleColor.Green))
|
|
|
|
|
- {
|
|
|
|
|
- writer.WriteLine(!string.IsNullOrEmpty(_configuration.ConsoleCopyright)
|
|
|
|
|
- ? string.Format(headerText, _configuration.ConsoleName, _configuration.ConsoleVersion,
|
|
|
|
|
- _configuration.ConsoleCopyright)
|
|
|
|
|
- : string.Format(headerText, _configuration.ConsoleName, _configuration.ConsoleVersion));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- #endregion
|
|
|
|
|
-
|
|
|
|
|
- #region *** Private Operations ***
|
|
|
|
|
- private static T Construct<T>(Type instance, Type[] paramTypes, object[] paramValues)
|
|
|
|
|
- {
|
|
|
|
|
- var ci = instance.GetConstructor(
|
|
|
|
|
- BindingFlags.Instance | BindingFlags.Public,
|
|
|
|
|
- null, paramTypes, null);
|
|
|
|
|
- if (ci == null)
|
|
|
|
|
- throw new Exception($"Command class '{instance.FullName}' has not public constuctor defined or has bad constructor's format.");
|
|
|
|
|
- return (T)ci.Invoke(paramValues);
|
|
|
|
|
- }
|
|
|
|
|
- private IEnumerable<Type> GetTypesWithHelpAttribute(Attribute attribute)
|
|
|
|
|
- {
|
|
|
|
|
-
|
|
|
|
|
- WriteDebugInfo($"Loading command assemblies ...");
|
|
|
|
|
-
|
|
|
|
|
- var defaultTypes = Assembly.GetExecutingAssembly().GetTypes().Where(type => type.GetCustomAttributes(attribute.GetType(), true).Length > 0).Distinct().ToList();
|
|
|
|
|
-
|
|
|
|
|
- var customTypes = new List<Type>();
|
|
|
|
|
- if (_configuration.CommandDefinitionAssembly != null)
|
|
|
|
|
- {
|
|
|
|
|
- foreach (var assembly in _configuration.CommandDefinitionAssembly)
|
|
|
|
|
- {
|
|
|
|
|
- customTypes.AddRange(assembly.GetTypes().Where(type => type.GetCustomAttributes(attribute.GetType(), true).Length > 0).Distinct().ToArray());
|
|
|
|
|
- WriteDebugInfo(assembly.GetName().Name + " was scanned for commands.");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- WriteDebugInfo("Custom CommandDefinitionAssembly is not defined. Skip.");
|
|
|
|
|
- }
|
|
|
|
|
- WriteDebugInfo($"Command definition obtaining process found {defaultTypes.Count} default definitions and {customTypes.Count} custom definitions.");
|
|
|
|
|
- defaultTypes.AddRange(customTypes);
|
|
|
|
|
- defaultTypes = defaultTypes.Distinct().ToList();
|
|
|
|
|
-
|
|
|
|
|
- foreach (var disabledCommand in _configuration.DisabledCommands)
|
|
|
|
|
- {
|
|
|
|
|
- var disabled = defaultTypes.FirstOrDefault(x => x.Name == disabledCommand);
|
|
|
|
|
- if (disabled==null)
|
|
|
|
|
- continue;
|
|
|
|
|
- defaultTypes.Remove(disabled);
|
|
|
|
|
- WriteDebugInfo($"Commnad class '{disabled.Name}', fullname '{disabled.FullName}', assembly '{disabled.Assembly.GetName().Name}' was skipped by configuration.");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return defaultTypes.ToArray();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private void WriteDebugInfo(string text)
|
|
|
|
|
- {
|
|
|
|
|
- using (var debug = new DebugConsoleWriter())
|
|
|
|
|
- {
|
|
|
|
|
- debug.WriteDebugLine(text);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private void DumpException(ConsoleWriter writer, Exception? e)
|
|
|
|
|
- {
|
|
|
|
|
- while (e != null)
|
|
|
|
|
- {
|
|
|
|
|
- writer.WriteLine("- " + e.Message);
|
|
|
|
|
- e = e.InnerException;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- private void ShowHeader()
|
|
|
|
|
- {
|
|
|
|
|
- OnShowHeader();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- #endregion
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
|
|
+using System;
|
|
|
|
|
+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
|
|
|
|
|
+{
|
|
|
|
|
+ public class Engine : IDisposable
|
|
|
|
|
+ {
|
|
|
|
|
+ #region *** Private Fields ***
|
|
|
|
|
+ private bool _isDispoising;
|
|
|
|
|
+ private bool _isExitSignal;
|
|
|
|
|
+ private IList<AbstractCommand> _commands = new List<AbstractCommand>();
|
|
|
|
|
+ private StartupConfiguration _configuration;
|
|
|
|
|
+ private IList<ISelectionEntry> _selections = new List<ISelectionEntry>();
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region *** Properties ***
|
|
|
|
|
+ public string[] RawArguments { get; }
|
|
|
|
|
+ public StartupConfiguration Configuration => _configuration;
|
|
|
|
|
+ public AbstractCommand[] Commands => _commands.ToArray();
|
|
|
|
|
+ public ISelectionEntry[] Selections => _selections.ToArray();
|
|
|
|
|
+
|
|
|
|
|
+ public IEngineContext Context { get; }
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region *** Constructor ***
|
|
|
|
|
+ public Engine(StartupConfiguration configuration, IEngineContext context, string[] args)
|
|
|
|
|
+ {
|
|
|
|
|
+ _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
|
|
|
|
+ Context = context ?? throw new ArgumentNullException(nameof(context));
|
|
|
|
|
+
|
|
|
|
|
+ if (args == null)
|
|
|
|
|
+ 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].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))
|
|
|
|
|
+ {
|
|
|
|
|
+ configuration.EnableDebugMode();
|
|
|
|
|
+ var newArgs = new List<string>(args);
|
|
|
|
|
+ newArgs.Remove(Constants.Console.DebugModeExplicitArg);
|
|
|
|
|
+ args = newArgs.ToArray();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ WriteDebugInfo($"Console '{_configuration.ConsoleName}' initialization started.");
|
|
|
|
|
+ WriteDebugInfo($"Engine assembly version '{Assembly.GetExecutingAssembly().GetName().Version}'.");
|
|
|
|
|
+ WriteDebugInfo($"IsInitFromXml is set to '{_configuration.IsInitFromXml}'.");
|
|
|
|
|
+
|
|
|
|
|
+ ShowHeader();
|
|
|
|
|
+
|
|
|
|
|
+ RawArguments = args;
|
|
|
|
|
+ WriteDebugInfo($"Input {args.Length} arguments appended.");
|
|
|
|
|
+ WriteDebugInfo($"Context set: {Context}");
|
|
|
|
|
+
|
|
|
|
|
+ var commandClasses = GetTypesWithHelpAttribute(new CommandDefinitionAttribute());
|
|
|
|
|
+ WriteDebugInfo($"Console found {commandClasses.Count()} command definitions.");
|
|
|
|
|
+
|
|
|
|
|
+ var paramConstrTypes = new Type[] { typeof(Engine) };
|
|
|
|
|
+ var paramConstrValues = new object[] { this };
|
|
|
|
|
+
|
|
|
|
|
+ foreach (var commandClass in commandClasses)
|
|
|
|
|
+ {
|
|
|
|
|
+ var command = Construct<AbstractCommand>(commandClass, paramConstrTypes, paramConstrValues);
|
|
|
|
|
+ _commands.Add(command);
|
|
|
|
|
+ WriteDebugInfo($"Command definition '{command}' added - class '{command.GetType().FullName}', assembly '{command.GetType().Assembly.GetName().Name}'.");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ WriteDebugInfo("Console initialized.");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void Start()
|
|
|
|
|
+ {
|
|
|
|
|
+ WriteDebugInfo("Console starting.");
|
|
|
|
|
+ var commandLineArguments = string.Join(" ", RawArguments);
|
|
|
|
|
+
|
|
|
|
|
+ using (var writer = new ConsoleWriter(ConsoleColor.White))
|
|
|
|
|
+ {
|
|
|
|
|
+ while (!_isExitSignal)
|
|
|
|
|
+ {
|
|
|
|
|
+ var input = string.Empty;
|
|
|
|
|
+ if (commandLineArguments.Length == 0 && Configuration.AllowInteractive)
|
|
|
|
|
+ {
|
|
|
|
|
+ // interactive command entry
|
|
|
|
|
+ writer.Write(_configuration.CharacterLineIntroduce);
|
|
|
|
|
+ input = System.Console.ReadLine();
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ // inline command entry
|
|
|
|
|
+ input = commandLineArguments;
|
|
|
|
|
+ _isExitSignal = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var args = (input ?? String.Empty).Split([" "], StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
+ if (args.Length == 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (_isExitSignal == true && !Configuration.AllowInteractive &&
|
|
|
|
|
+ Configuration.HandlerNothingToDo != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ Configuration.HandlerNothingToDo.Invoke(this);
|
|
|
|
|
+ }
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var command = GetCommand(args[0]);
|
|
|
|
|
+ if (command == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ 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;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var result = command.Execute((input ?? String.Empty));
|
|
|
|
|
+ if (result.IsSuccess)
|
|
|
|
|
+ {
|
|
|
|
|
+ using (var writerSucc = new ConsoleWriter(ConsoleColor.Green))
|
|
|
|
|
+ {
|
|
|
|
|
+ writerSucc.WriteLine("Done.");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ using (var writerError = new ConsoleWriter(ConsoleColor.Red))
|
|
|
|
|
+ {
|
|
|
|
|
+ writerError.WriteLine("Error:");
|
|
|
|
|
+ var e = result.ThrownException;
|
|
|
|
|
+ if (Configuration.ThrowExceptionWhenNotSuccess)
|
|
|
|
|
+ throw e.ToAggregateException();
|
|
|
|
|
+
|
|
|
|
|
+ if (e is AggregateException aggr)
|
|
|
|
|
+ {
|
|
|
|
|
+ foreach (var ex in aggr.InnerExceptions)
|
|
|
|
|
+ DumpException(writerError, ex);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ while (e != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ writerError.WriteLine(e.Message);
|
|
|
|
|
+ e = e.InnerException;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!Configuration.AllowInteractive && Configuration.WaitOnKeyInNonInteractiveMode)
|
|
|
|
|
+ {
|
|
|
|
|
+ WriteDebugInfo("Waiting for the key by configuration settings...");
|
|
|
|
|
+ using (var writerGreen = new ConsoleWriter(ConsoleColor.Magenta))
|
|
|
|
|
+ {
|
|
|
|
|
+ writerGreen.WriteLine("Press any key to close...");
|
|
|
|
|
+ System.Console.ReadKey();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void Stop()
|
|
|
|
|
+ {
|
|
|
|
|
+ _isExitSignal = true;
|
|
|
|
|
+ WriteDebugInfo("Console stopped.");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public AbstractCommand? GetCommand(string commandName)
|
|
|
|
|
+ {
|
|
|
|
|
+ return _commands.FirstOrDefault(x => x.Name.ToUpper() == commandName.ToUpper());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void AppendSelection(params ISelectionEntry[] selectionEntries)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (selectionEntries == null)
|
|
|
|
|
+ throw new ArgumentNullException(nameof(selectionEntries));
|
|
|
|
|
+ foreach (var entry in selectionEntries)
|
|
|
|
|
+ {
|
|
|
|
|
+ _selections.Add(entry);
|
|
|
|
|
+ }
|
|
|
|
|
+ WriteDebugInfo($"{selectionEntries.Length} was added to global selections. Total count {_selections.Count} entries.");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void ClearSelection()
|
|
|
|
|
+ {
|
|
|
|
|
+ _selections.Clear();
|
|
|
|
|
+ WriteDebugInfo("Global selections was cleared.");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public ISelectionEntry GetSelection(int ordinal)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (ordinal==0)
|
|
|
|
|
+ throw new ArgumentOutOfRangeException(nameof(ordinal), "Ordinal value must be greater than 0.");
|
|
|
|
|
+ if (_selections.Count < ordinal)
|
|
|
|
|
+ throw new ArgumentOutOfRangeException(nameof(ordinal), $"Ordinal value must be less than {_selections.Count}.");
|
|
|
|
|
+
|
|
|
|
|
+ return _selections[ordinal - 1];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region *** Public Operations ***
|
|
|
|
|
+ public static string[] GetCurrentCommandLine()
|
|
|
|
|
+ {
|
|
|
|
|
+ return Environment.CommandLine.SplitQuoted(" ", "\"", trimValues:true, keepBracket:true).Skip(1).ToArray();
|
|
|
|
|
+ }
|
|
|
|
|
+ public void Dispose()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (_isDispoising)
|
|
|
|
|
+ return;
|
|
|
|
|
+ _isDispoising = true;
|
|
|
|
|
+ WriteDebugInfo("Dispoising called.");
|
|
|
|
|
+ OnDispoising();
|
|
|
|
|
+ WriteDebugInfo("Dispoising done.");
|
|
|
|
|
+ }
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region *** Protected Operations ***
|
|
|
|
|
+ protected virtual void OnDispoising()
|
|
|
|
|
+ {
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected virtual void OnShowHeader()
|
|
|
|
|
+ {
|
|
|
|
|
+ var headerText = string.IsNullOrEmpty(_configuration.ConsoleCopyright) ? @"{0} * {1}" : @"{0} * {1} * {2}";
|
|
|
|
|
+ using (var writer = new ConsoleWriter(ConsoleColor.Green))
|
|
|
|
|
+ {
|
|
|
|
|
+ writer.WriteLine(!string.IsNullOrEmpty(_configuration.ConsoleCopyright)
|
|
|
|
|
+ ? string.Format(headerText, _configuration.ConsoleName, _configuration.ConsoleVersion,
|
|
|
|
|
+ _configuration.ConsoleCopyright)
|
|
|
|
|
+ : string.Format(headerText, _configuration.ConsoleName, _configuration.ConsoleVersion));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region *** Private Operations ***
|
|
|
|
|
+ private static T Construct<T>(Type instance, Type[] paramTypes, object[] paramValues)
|
|
|
|
|
+ {
|
|
|
|
|
+ var ci = instance.GetConstructor(
|
|
|
|
|
+ BindingFlags.Instance | BindingFlags.Public,
|
|
|
|
|
+ null, paramTypes, null);
|
|
|
|
|
+ if (ci == null)
|
|
|
|
|
+ throw new Exception($"Command class '{instance.FullName}' has not public constuctor defined or has bad constructor's format.");
|
|
|
|
|
+ return (T)ci.Invoke(paramValues);
|
|
|
|
|
+ }
|
|
|
|
|
+ private IEnumerable<Type> GetTypesWithHelpAttribute(Attribute attribute)
|
|
|
|
|
+ {
|
|
|
|
|
+
|
|
|
|
|
+ WriteDebugInfo($"Loading command assemblies ...");
|
|
|
|
|
+
|
|
|
|
|
+ var defaultTypes = Assembly.GetExecutingAssembly().GetTypes().Where(type => type.GetCustomAttributes(attribute.GetType(), true).Length > 0).Distinct().ToList();
|
|
|
|
|
+
|
|
|
|
|
+ var customTypes = new List<Type>();
|
|
|
|
|
+ if (_configuration.CommandDefinitionAssembly != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ foreach (var assembly in _configuration.CommandDefinitionAssembly)
|
|
|
|
|
+ {
|
|
|
|
|
+ customTypes.AddRange(assembly.GetTypes().Where(type => type.GetCustomAttributes(attribute.GetType(), true).Length > 0).Distinct().ToArray());
|
|
|
|
|
+ WriteDebugInfo(assembly.GetName().Name + " was scanned for commands.");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ WriteDebugInfo("Custom CommandDefinitionAssembly is not defined. Skip.");
|
|
|
|
|
+ }
|
|
|
|
|
+ WriteDebugInfo($"Command definition obtaining process found {defaultTypes.Count} default definitions and {customTypes.Count} custom definitions.");
|
|
|
|
|
+ defaultTypes.AddRange(customTypes);
|
|
|
|
|
+ defaultTypes = defaultTypes.Distinct().ToList();
|
|
|
|
|
+
|
|
|
|
|
+ foreach (var disabledCommand in _configuration.DisabledCommands)
|
|
|
|
|
+ {
|
|
|
|
|
+ var disabled = defaultTypes.FirstOrDefault(x => x.Name == disabledCommand);
|
|
|
|
|
+ if (disabled==null)
|
|
|
|
|
+ continue;
|
|
|
|
|
+ defaultTypes.Remove(disabled);
|
|
|
|
|
+ WriteDebugInfo($"Commnad class '{disabled.Name}', fullname '{disabled.FullName}', assembly '{disabled.Assembly.GetName().Name}' was skipped by configuration.");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return defaultTypes.ToArray();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void WriteDebugInfo(string text)
|
|
|
|
|
+ {
|
|
|
|
|
+ using (var debug = new DebugConsoleWriter())
|
|
|
|
|
+ {
|
|
|
|
|
+ debug.WriteDebugLine(text);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void DumpException(ConsoleWriter writer, Exception? e)
|
|
|
|
|
+ {
|
|
|
|
|
+ while (e != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ writer.WriteLine("- " + e.Message);
|
|
|
|
|
+ e = e.InnerException;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ private void ShowHeader()
|
|
|
|
|
+ {
|
|
|
|
|
+ OnShowHeader();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+ }
|
|
|
|
|
+}
|