[16985] | 1 | using System;
|
---|
| 2 | using System.Collections.Generic;
|
---|
| 3 | using System.Linq;
|
---|
| 4 | using System.Text;
|
---|
| 5 | using HeuristicLab.CommandLineInterface.Data;
|
---|
| 6 | using HeuristicLab.Common;
|
---|
| 7 |
|
---|
| 8 | namespace HeuristicLab.CommandLineInterface {
|
---|
| 9 | /// <summary>
|
---|
| 10 | /// Static class with output methods.
|
---|
| 11 | /// </summary>
|
---|
| 12 | public static class CLIConsole {
|
---|
| 13 |
|
---|
| 14 | #region Constants
|
---|
| 15 | private const int Padding = 2;
|
---|
| 16 | private const int ShortcutWidth = 4;
|
---|
| 17 | private const int LRowWidth = 20;
|
---|
| 18 | private const int MRowWidth = 20;
|
---|
| 19 | private const int RRowWidth = 60;
|
---|
| 20 | #endregion
|
---|
| 21 |
|
---|
| 22 | /// <summary>
|
---|
| 23 | /// Prints the help to the console based on the data saved in CommandData.
|
---|
| 24 | /// </summary>
|
---|
| 25 | /// <param name="cmdData"></param>
|
---|
| 26 | /// <param name="errors"></param>
|
---|
| 27 | internal static void PrintHelp(CommandData cmdData, Exception exception = null) {
|
---|
| 28 | WriteHeader(cmdData);
|
---|
| 29 |
|
---|
[16998] | 30 | if (exception != null) WriteErrors(exception);
|
---|
| 31 |
|
---|
[16985] | 32 | WriteSyntax(cmdData);
|
---|
| 33 |
|
---|
| 34 | if (cmdData.Options.Count > 0) {
|
---|
| 35 | WriteOptionBox(cmdData);
|
---|
| 36 | Console.WriteLine();
|
---|
| 37 | }
|
---|
| 38 |
|
---|
| 39 | if (cmdData.Commands.Count > 0) WriteCommandBox(cmdData);
|
---|
| 40 |
|
---|
| 41 | //Exit the application after every help call.
|
---|
| 42 | Environment.Exit((exception != null) ? 1 : 0);
|
---|
| 43 | }
|
---|
| 44 |
|
---|
| 45 | #region PrintHelp steps
|
---|
| 46 | /// <summary>
|
---|
| 47 | /// Writes the header of the help.
|
---|
| 48 | /// </summary>
|
---|
| 49 | /// <param name="cmdData"></param>
|
---|
| 50 | private static void WriteHeader(CommandData cmdData) {
|
---|
| 51 | Console.WriteLine($"{CLIApplication.AppName} {CLIApplication.AppVersion}");
|
---|
| 52 | if (cmdData.Description != null)
|
---|
| 53 | Console.WriteLine($"{cmdData.Description}");
|
---|
| 54 | Console.WriteLine();
|
---|
| 55 | }
|
---|
| 56 |
|
---|
| 57 | /// <summary>
|
---|
| 58 | /// Writes an list of errors.
|
---|
| 59 | /// </summary>
|
---|
| 60 | /// <param name="errors"></param>
|
---|
| 61 | private static void WriteErrors(Exception exception) {
|
---|
| 62 | Console.WriteLine($"ERROR(S):");
|
---|
| 63 | AggregateException ae = exception as AggregateException;
|
---|
[16998] | 64 | if (ae != null) {
|
---|
| 65 | foreach (var e in ae.InnerExceptions) {
|
---|
[16985] | 66 | Console.WriteLine($" -> {e} ");
|
---|
| 67 | }
|
---|
| 68 | } else {
|
---|
| 69 | Console.WriteLine($" -> {exception}");
|
---|
[16998] | 70 | }
|
---|
[16985] | 71 | Console.WriteLine();
|
---|
| 72 | }
|
---|
| 73 |
|
---|
| 74 | /// <summary>
|
---|
| 75 | /// Writes the syntax of a specific command.
|
---|
| 76 | /// </summary>
|
---|
| 77 | /// <param name="cmdData"></param>
|
---|
| 78 | private static void WriteSyntax(CommandData cmdData) {
|
---|
| 79 | Console.Write($"Syntax: {BuildParentCommandString(cmdData).ToLower()}{cmdData.Identifier.ToLower()}");
|
---|
| 80 | if (cmdData.Options.Count > 0) Console.Write(" [OPTION]");
|
---|
| 81 | if (cmdData.Commands.Count > 0) Console.Write(" COMMAND");
|
---|
| 82 | string valueSyntax = BuildValueSyntaxString(cmdData);
|
---|
| 83 | Console.WriteLine(" " + valueSyntax);
|
---|
| 84 | Console.WriteLine();
|
---|
| 85 | }
|
---|
| 86 |
|
---|
| 87 | /// <summary>
|
---|
| 88 | /// Writes the help box for the options of a specific command.
|
---|
| 89 | /// </summary>
|
---|
| 90 | /// <param name="cmdData"></param>
|
---|
| 91 | private static void WriteOptionBox(CommandData cmdData) {
|
---|
| 92 | WriteBoxSeparatorLine();
|
---|
| 93 | WriteBoxLine("Option", "Parameter", "Description");
|
---|
| 94 | WriteBoxSeparatorLine();
|
---|
| 95 | foreach (OptionData opt in cmdData.Options)
|
---|
[16998] | 96 | if (!opt.Hidden)
|
---|
| 97 | WriteBoxLine(
|
---|
| 98 | ((opt.Shortcut != null) ?
|
---|
| 99 | $"{(OptionData.ShortcutPrefix + opt.Shortcut).ToLower() + ",",-ShortcutWidth}" :
|
---|
| 100 | $""
|
---|
| 101 | ) +
|
---|
| 102 | $"{(OptionData.LongformPrefix + opt.Identifier).ToLower()}",
|
---|
| 103 | opt.Property?.PropertyType.GetPrettyName(),
|
---|
| 104 | opt.Description
|
---|
| 105 | );
|
---|
[16985] | 106 | WriteBoxSeparatorLine();
|
---|
| 107 | }
|
---|
| 108 |
|
---|
| 109 | /// <summary>
|
---|
| 110 | /// Writes the help box for the commands of a specific command.
|
---|
| 111 | /// </summary>
|
---|
| 112 | /// <param name="cmdData"></param>
|
---|
| 113 | private static void WriteCommandBox(CommandData cmdData) {
|
---|
| 114 | WriteBoxSeparatorLine();
|
---|
| 115 | WriteBoxLine("Commands", "Parameter", "Description");
|
---|
| 116 | WriteBoxSeparatorLine();
|
---|
| 117 | foreach (CommandData c in cmdData.Commands)
|
---|
| 118 | WriteBoxLine(c.Identifier.ToLower(), BuildValueSyntaxString(c), c.Description);
|
---|
| 119 | WriteBoxSeparatorLine();
|
---|
| 120 | Console.WriteLine();
|
---|
| 121 | Console.WriteLine($"Enter \"{BuildParentCommandString(cmdData).ToLower()}" +
|
---|
| 122 | $"{cmdData.Identifier.ToLower()} COMMAND --help\" to show more information for a command.");
|
---|
| 123 | }
|
---|
| 124 |
|
---|
| 125 | /// <summary>
|
---|
| 126 | /// Writes a horizontal seperator line. Used to print a help box.
|
---|
| 127 | /// </summary>
|
---|
| 128 | /// <param name="edge"></param>
|
---|
| 129 | /// <param name="fill"></param>
|
---|
| 130 | private static void WriteBoxSeparatorLine(char edge = '+', char fill = '-') {
|
---|
| 131 | Console.Write($"{edge}");
|
---|
| 132 | WriteChar(4 * Padding + LRowWidth + MRowWidth + RRowWidth, fill);
|
---|
| 133 | Console.Write($"{edge}\n");
|
---|
| 134 | }
|
---|
| 135 |
|
---|
| 136 | /// <summary>
|
---|
| 137 | /// Writes a single line for the help box.
|
---|
| 138 | /// </summary>
|
---|
| 139 | /// <param name="lrow"></param>
|
---|
| 140 | /// <param name="mrow"></param>
|
---|
| 141 | /// <param name="rrow"></param>
|
---|
| 142 | private static void WriteBoxLine(string lrow, string mrow, string rrow) {
|
---|
| 143 | IList<string> lf = FitBoxString(lrow ?? "", LRowWidth);
|
---|
| 144 | IList<string> mf = FitBoxString(mrow ?? "", MRowWidth);
|
---|
| 145 | IList<string> rf = FitBoxString(rrow ?? "", RRowWidth);
|
---|
| 146 |
|
---|
| 147 | // to correctly print lines.
|
---|
| 148 | for (int i = 0; i < lf.Count || i < mf.Count || i < rf.Count; ++i) {
|
---|
| 149 | Console.Write($"|{"",Padding}");
|
---|
| 150 | if (i < lf.Count) Console.Write($"{lf[i],-LRowWidth}");
|
---|
| 151 | else Console.Write($"{"",-LRowWidth}");
|
---|
| 152 | Console.Write($"{"",Padding}");
|
---|
| 153 | if (i < mf.Count) Console.Write($"{mf[i],-MRowWidth}");
|
---|
| 154 | else Console.Write($"{"",-MRowWidth}");
|
---|
| 155 | Console.Write($"{"",Padding}");
|
---|
| 156 | if (i < rf.Count) Console.Write($"{rf[i],-RRowWidth}");
|
---|
| 157 | else Console.Write($"{"",-RRowWidth}");
|
---|
| 158 | Console.Write($"{"",-Padding}|\n");
|
---|
| 159 | }
|
---|
| 160 | }
|
---|
| 161 | #endregion
|
---|
| 162 |
|
---|
| 163 | #region Helper
|
---|
| 164 | /// <summary>
|
---|
| 165 | /// Iterates through command values and creates a string with their type name.
|
---|
| 166 | /// </summary>
|
---|
| 167 | /// <param name="cmdData"></param>
|
---|
| 168 | /// <returns>A string with the type names of the command's values.</returns>
|
---|
| 169 | private static string BuildValueSyntaxString(CommandData cmdData) {
|
---|
| 170 | StringBuilder additionalSyntax = new StringBuilder();
|
---|
| 171 | int i = 0;
|
---|
| 172 | foreach (var x in cmdData.Values) {
|
---|
| 173 | additionalSyntax.Append(x.Property.PropertyType.GetPrettyName());
|
---|
| 174 | if ((i + 1) < cmdData.Values.Count) additionalSyntax.Append(" ");
|
---|
| 175 | }
|
---|
| 176 | return additionalSyntax.ToString();
|
---|
| 177 | }
|
---|
| 178 |
|
---|
| 179 | /// <summary>
|
---|
| 180 | /// Iterates through parent commends of specified commends to create a valid parent command string.
|
---|
| 181 | /// For example: MyRootCommand Command1 ChildOfCommand1 ...
|
---|
| 182 | /// </summary>
|
---|
| 183 | /// <param name="cmdData"></param>
|
---|
| 184 | /// <returns>A string with parent commends.</returns>
|
---|
| 185 | private static string BuildParentCommandString(CommandData cmdData) {
|
---|
| 186 | StringBuilder builder = new StringBuilder();
|
---|
| 187 |
|
---|
| 188 | CommandData p = cmdData.Parent;
|
---|
| 189 | IList<CommandData> cmds = new List<CommandData>();
|
---|
| 190 | while (p != null) {
|
---|
| 191 | cmds.Add(p);
|
---|
| 192 | p = p.Parent;
|
---|
| 193 | }
|
---|
| 194 | cmds.Reverse();
|
---|
| 195 |
|
---|
| 196 | foreach (CommandData parent in cmds) builder.Append($"{parent.Identifier} ");
|
---|
| 197 | return builder.ToString();
|
---|
| 198 | }
|
---|
| 199 |
|
---|
| 200 | /// <summary>
|
---|
| 201 | /// Fits a string to specified width.
|
---|
| 202 | /// </summary>
|
---|
| 203 | /// <param name="str"></param>
|
---|
| 204 | /// <param name="width"></param>
|
---|
| 205 | /// <returns>Array of string, which stands for lines.</returns>
|
---|
| 206 | private static IList<string> FitBoxString(string str, int width) {
|
---|
| 207 | string[] splits = str.Split(' ');
|
---|
| 208 | IList<string> list = new List<string>();
|
---|
| 209 | string line = "";
|
---|
| 210 | foreach (string split in splits) {
|
---|
| 211 | string tmp = split;
|
---|
| 212 | if (tmp.Length > width) // if a word is longer then the specified width, it gets "cutted" into multiple lines.
|
---|
| 213 | while (tmp.Length > width - line.Length) {
|
---|
| 214 | list.Add(line + tmp.Substring(0, width - line.Length));
|
---|
| 215 | tmp = tmp.Substring(width - line.Length);
|
---|
| 216 | line = "";
|
---|
| 217 | }
|
---|
| 218 | if (line.Length + tmp.Length <= width - 1) line += tmp + " ";
|
---|
| 219 | else {
|
---|
| 220 | list.Add(line);
|
---|
| 221 | line = tmp;
|
---|
| 222 | }
|
---|
| 223 | }
|
---|
| 224 | if (!string.IsNullOrEmpty(line)) list.Add(line);
|
---|
| 225 | return list;
|
---|
| 226 | }
|
---|
| 227 |
|
---|
| 228 | /// <summary>
|
---|
| 229 | /// Writes a specific char multiple times.
|
---|
| 230 | /// </summary>
|
---|
| 231 | /// <param name="iterations"></param>
|
---|
| 232 | /// <param name="ch"></param>
|
---|
| 233 | private static void WriteChar(int iterations = 1, char ch = '-') {
|
---|
| 234 | for (int i = 0; i < iterations; ++i) Console.Write(ch);
|
---|
| 235 | }
|
---|
| 236 | #endregion
|
---|
| 237 | }
|
---|
| 238 | }
|
---|