using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HeuristicLab.CommandLineInterface.Data;
using HeuristicLab.Common;
namespace HeuristicLab.CommandLineInterface {
///
/// Static class with output methods.
///
public static class CLIConsole {
#region Constants
private const int Padding = 2;
private const int ShortcutWidth = 4;
private const int LRowWidth = 20;
private const int MRowWidth = 20;
private const int RRowWidth = 60;
#endregion
///
/// Prints the help to the console based on the data saved in CommandData.
///
///
///
internal static void PrintHelp(CommandData cmdData, Exception exception = null) {
WriteHeader(cmdData);
if (exception != null) WriteErrors(exception);
WriteSyntax(cmdData);
if (cmdData.Options.Count > 0) {
WriteOptionBox(cmdData);
Console.WriteLine();
}
if (cmdData.Commands.Count > 0) WriteCommandBox(cmdData);
//Exit the application after every help call.
Environment.Exit((exception != null) ? 1 : 0);
}
#region PrintHelp steps
///
/// Writes the header of the help.
///
///
private static void WriteHeader(CommandData cmdData) {
Console.WriteLine($"{CLIApplication.AppName} {CLIApplication.AppVersion}");
if (cmdData.Description != null)
Console.WriteLine($"{cmdData.Description}");
Console.WriteLine();
}
///
/// Writes an list of errors.
///
///
private static void WriteErrors(Exception exception) {
Console.WriteLine($"ERROR(S):");
AggregateException ae = exception as AggregateException;
if (ae != null) {
foreach (var e in ae.InnerExceptions) {
Console.WriteLine($" -> {e} ");
}
} else {
Console.WriteLine($" -> {exception}");
}
Console.WriteLine();
}
///
/// Writes the syntax of a specific command.
///
///
private static void WriteSyntax(CommandData cmdData) {
Console.Write($"Syntax: {BuildParentCommandString(cmdData).ToLower()}{cmdData.Identifier.ToLower()}");
if (cmdData.Options.Count > 0) Console.Write(" [OPTION]");
if (cmdData.Commands.Count > 0) Console.Write(" COMMAND");
string valueSyntax = BuildValueSyntaxString(cmdData);
Console.WriteLine(" " + valueSyntax);
Console.WriteLine();
}
///
/// Writes the help box for the options of a specific command.
///
///
private static void WriteOptionBox(CommandData cmdData) {
WriteBoxSeparatorLine();
WriteBoxLine("Option", "Parameter", "Description");
WriteBoxSeparatorLine();
foreach (OptionData opt in cmdData.Options)
if (!opt.Hidden)
WriteBoxLine(
((opt.Shortcut != null) ?
$"{(OptionData.ShortcutPrefix + opt.Shortcut).ToLower() + ",",-ShortcutWidth}" :
$""
) +
$"{(OptionData.LongformPrefix + opt.Identifier).ToLower()}",
opt.Property?.PropertyType.GetPrettyName(),
opt.Description
);
WriteBoxSeparatorLine();
}
///
/// Writes the help box for the commands of a specific command.
///
///
private static void WriteCommandBox(CommandData cmdData) {
WriteBoxSeparatorLine();
WriteBoxLine("Commands", "Parameter", "Description");
WriteBoxSeparatorLine();
foreach (CommandData c in cmdData.Commands)
WriteBoxLine(c.Identifier.ToLower(), BuildValueSyntaxString(c), c.Description);
WriteBoxSeparatorLine();
Console.WriteLine();
Console.WriteLine($"Enter \"{BuildParentCommandString(cmdData).ToLower()}" +
$"{cmdData.Identifier.ToLower()} COMMAND --help\" to show more information for a command.");
}
///
/// Writes a horizontal seperator line. Used to print a help box.
///
///
///
private static void WriteBoxSeparatorLine(char edge = '+', char fill = '-') {
Console.Write($"{edge}");
WriteChar(4 * Padding + LRowWidth + MRowWidth + RRowWidth, fill);
Console.Write($"{edge}\n");
}
///
/// Writes a single line for the help box.
///
///
///
///
private static void WriteBoxLine(string lrow, string mrow, string rrow) {
IList lf = FitBoxString(lrow ?? "", LRowWidth);
IList mf = FitBoxString(mrow ?? "", MRowWidth);
IList rf = FitBoxString(rrow ?? "", RRowWidth);
// to correctly print lines.
for (int i = 0; i < lf.Count || i < mf.Count || i < rf.Count; ++i) {
Console.Write($"|{"",Padding}");
if (i < lf.Count) Console.Write($"{lf[i],-LRowWidth}");
else Console.Write($"{"",-LRowWidth}");
Console.Write($"{"",Padding}");
if (i < mf.Count) Console.Write($"{mf[i],-MRowWidth}");
else Console.Write($"{"",-MRowWidth}");
Console.Write($"{"",Padding}");
if (i < rf.Count) Console.Write($"{rf[i],-RRowWidth}");
else Console.Write($"{"",-RRowWidth}");
Console.Write($"{"",-Padding}|\n");
}
}
#endregion
#region Helper
///
/// Iterates through command values and creates a string with their type name.
///
///
/// A string with the type names of the command's values.
private static string BuildValueSyntaxString(CommandData cmdData) {
StringBuilder additionalSyntax = new StringBuilder();
int i = 0;
foreach (var x in cmdData.Values) {
additionalSyntax.Append(x.Property.PropertyType.GetPrettyName());
if ((i + 1) < cmdData.Values.Count) additionalSyntax.Append(" ");
}
return additionalSyntax.ToString();
}
///
/// Iterates through parent commends of specified commends to create a valid parent command string.
/// For example: MyRootCommand Command1 ChildOfCommand1 ...
///
///
/// A string with parent commends.
private static string BuildParentCommandString(CommandData cmdData) {
StringBuilder builder = new StringBuilder();
CommandData p = cmdData.Parent;
IList cmds = new List();
while (p != null) {
cmds.Add(p);
p = p.Parent;
}
cmds.Reverse();
foreach (CommandData parent in cmds) builder.Append($"{parent.Identifier} ");
return builder.ToString();
}
///
/// Fits a string to specified width.
///
///
///
/// Array of string, which stands for lines.
private static IList FitBoxString(string str, int width) {
string[] splits = str.Split(' ');
IList list = new List();
string line = "";
foreach (string split in splits) {
string tmp = split;
if (tmp.Length > width) // if a word is longer then the specified width, it gets "cutted" into multiple lines.
while (tmp.Length > width - line.Length) {
list.Add(line + tmp.Substring(0, width - line.Length));
tmp = tmp.Substring(width - line.Length);
line = "";
}
if (line.Length + tmp.Length <= width - 1) line += tmp + " ";
else {
list.Add(line);
line = tmp;
}
}
if (!string.IsNullOrEmpty(line)) list.Add(line);
return list;
}
///
/// Writes a specific char multiple times.
///
///
///
private static void WriteChar(int iterations = 1, char ch = '-') {
for (int i = 0; i < iterations; ++i) Console.Write(ch);
}
#endregion
}
}