/// <summary> /// Implementation of the abstract base function 'Validate'. /// </summary> /// <param name="propertyMetaInfo"></param> /// <param name="parameterObject"></param> public override void Validate(PropertyMetaInfo propertyMetaInfo, ParameterBase parameterObject) { MethodBase? methodBase = MethodBase.GetCurrentMethod(); // // Make sure the 'ValidationAttribute' is assigned to the right // property type. (A string in this case) // #pragma warning disable CS8604 if (propertyMetaInfo.Type.ToLower().IndexOf("string") < 0) { propertyMetaInfo.ValidationError = new ValidationError(propertyMetaInfo, propertyMetaInfo.PropertyInfo.GetValue(parameterObject)?.ToString(), $"The attribute '{methodBase?.DeclaringType}' is not allowed on properties of type: '{propertyMetaInfo.Type}'."); return; } // // The actual validation. // if (propertyMetaInfo.PropertyInfo.GetValue(parameterObject)?.ToString()?.Length < this.MinLength) { propertyMetaInfo.ValidationError = new ValidationError(propertyMetaInfo, propertyMetaInfo.PropertyInfo.GetValue(parameterObject)?.ToString(), $"{ValidationErrorMessage}"); return; } #pragma warning restore CS8604 // // The validation passed. // propertyMetaInfo.ValidationError = null; }
/// <summary> /// Creates a summary of the validation errors of the parameter object /// provided in argument 'parameterObject'. /// <para> /// The message provided in argument 'message' will be shown on top of the summary. /// </para> /// <para> /// The summary will be returned as a string with a line length matching with the /// value of argument screenWidth. /// </para> /// <para> /// The result string is supposed to be rendered on the command line. /// </para> /// </summary> /// <param name="parameterObject"></param> /// <param name="message"></param> /// <param name="screenWidth"></param> /// <returns>The resulting string</returns> public virtual String CreateValidationSummary(ParameterBase parameterObject, string message = "One or more of the command line arguments are invalid.", int screenWidth = 80) { string result; result = String.Empty; if (parameterObject == null) { return result; } result = CreateValidationSummaryMessage(message, screenWidth); result += CreateValidationSummaryBody(parameterObject, screenWidth); return result; }
/// <summary> /// Processes the command line by calling the parse and validation /// function. /// <para> /// If the 'showUsageOnEmptyArgs' argument is true and the 'args' /// argument has no argument which qualifies as parameter argument, /// the usage screen will be rendered to the console error stream /// and the return value will be false; /// </para> /// <para> /// If the validation fails because it was a help request, the /// help screen will be rendered to the console standard stream /// and the return value will be false. /// </para> /// <para> /// If the validation fails because it was a version request, the /// version screen will be rendered to the console standard stream /// and the return value will be false. /// </para> /// <para> /// If the validation fails because of invalid arguments, the /// validation summary screen will be rendered to the console error /// stream the return value will be false. /// </para> /// <para> /// The function returns true if the process was successful and /// no screen was rendered. /// </para> /// </summary> /// <param name="args"> /// The command line arguments which will be processed. /// </param> /// <param name="showUsageOnEmptyArgs"> /// Determines whether the usage screen will be rendered if the /// args array has no argument which qualifies as parameter argument. /// </param> /// <returns>true, if the process succeeded, otherwise false</returns> public virtual bool Process(String[] args, bool showUsageOnEmptyArgs = true) { bool IsValid; string output; this.Parse(args); // // The usage screen will be rendered to the // console error stream. // if (!this.IsHelpRequest && !this.IsVersionRequest && showUsageOnEmptyArgs && this.Arguments.Count() == 0) { output = this.CreateUsage(); ParameterBase.PrintToConsole(output, consoleOutputStream : ConsoleOutputStream.ERROR); return false; } IsValid = this.Validate(); if (!IsValid) { if (this.IsHelpRequest) { output = this.CreateHelp(); Console.Write(output); return IsValid; } if (this.IsVersionRequest) { output = this.CreateVersion(this); Console.Write(output); return IsValid; } output = this.CreateValidationSummary(); //ParameterBase.PrintToConsole(output, ConsoleColor.Red); // // The validation summary screen will be rendered to the // console error stream. // ParameterBase.PrintToConsole(output, consoleOutputStream: ConsoleOutputStream.ERROR, foregroundColor: ConsoleColor.Red); return IsValid; } return IsValid; }
/// <summary> /// Creates a usage description for the command associated with the provided /// 'parameterObject'. /// <para> /// The usage description is either the one provided with /// the 'UsageAttribute' or a generic description created from the known /// parameter attributes. /// </para> /// <para> /// The result string is supposed to be rendered on the command line. /// </para> /// </summary> /// <param name="parameterObject"></param> /// <param name="screenWidth"></param> /// <returns>The resulting string</returns> public virtual string CreateUsage(ParameterBase parameterObject, int screenWidth = 80) { string result; string usage; result = String.Empty; result += CreateUsageHeader(screenWidth); result += "Usage:\r\n\r\n"; UsageAttribute? usageAttribute = (UsageAttribute?)parameterObject.GetType().GetCustomAttribute(typeof(UsageAttribute)); // // If a usage description is provided in the command attributes return that description. // if ((usageAttribute != null) && !String.IsNullOrWhiteSpace(usageAttribute.Usage)) { usage = usageAttribute.Usage; } // // Create a usage description from the known parameter attributes. // else { usage = parameterObject.CommandName + " "; usage += "[" + parameterObject.HelpIndicatorList.First() + "] "; usage += "[" + parameterObject.VersionIndicatorList.First() + "] "; foreach (var metaInfo in parameterObject.PropertyMetaInfoList) { if (metaInfo.IsMandatory) { usage += metaInfo.Name + "=" + "<" + metaInfo.Type + "> "; } else { usage += "[" + metaInfo.Name + "=" + "<" + metaInfo.Type + ">" + "] "; } } } List<string> lines = CreateWrappedLines(usage, screenWidth, 0); return result + String.Join("\r\n", lines); }
/// <summary> /// Implementation of the abstract base function 'Validate'. /// </summary> /// <param name="propertyMetaInfo"></param> /// <param name="parameterObject"></param> public override void Validate(PropertyMetaInfo propertyMetaInfo, ParameterBase parameterObject) { MethodBase? methodBase = MethodBase.GetCurrentMethod(); #pragma warning disable CS8600, CS8602, CS8604 var propertyValue = propertyMetaInfo.PropertyInfo.GetValue(parameterObject); if(propertyValue == null) { propertyMetaInfo.ValidationError = new ValidationError(propertyMetaInfo, propertyMetaInfo.PropertyInfo.GetValue(parameterObject)?.ToString(), $"{ValidationErrorMessage}"); return; } else { switch(propertyMetaInfo.Type) { case "UInt16|Null": case "UInt16": case "Int16|Null": case "Int16": { if(Convert.ToInt16(this.MaxValue) >= ((Int16) propertyValue)) { propertyMetaInfo.ValidationError = null; return; } break; } case "UInt32|Null": case "UInt32": case "Int32|Null": case "Int32": { if(((Int32) this.MaxValue) >= ((Int32) propertyValue)) { propertyMetaInfo.ValidationError = null; return; } break; } case "UInt64|Null": case "UInt64": case "Int64|Null": case "Int64": { if(Convert.ToInt64(this.MaxValue) >= ((Int64) propertyValue)) { propertyMetaInfo.ValidationError = null; return; } break; } case "Single|Null": case "Single": { if(Convert.ToSingle(this.MaxValue) >= ((Single) propertyValue)) { propertyMetaInfo.ValidationError = null; return; } break; } case "Double|Null": case "Double": { if(((Double) this.MaxValue) >= ((Double) propertyValue)) { propertyMetaInfo.ValidationError = null; return; } break; } case "Decimal|Null": case "Decimal": { if(Convert.ToDecimal(this.MaxValue) >= ((Decimal) propertyValue)) { propertyMetaInfo.ValidationError = null; return; } break; } default: { // // The 'ValidationAttribute' is assigned to the wrong // property type. (Must be one of the number types above) // propertyMetaInfo.ValidationError = new ValidationError(propertyMetaInfo, propertyMetaInfo.PropertyInfo.GetValue(parameterObject)?.ToString(), $"The attribute '{methodBase?.DeclaringType}' is not allowed on properties of type: '{propertyMetaInfo.Type}'."); return; } } propertyMetaInfo.ValidationError = new ValidationError(propertyMetaInfo, propertyMetaInfo.PropertyInfo.GetValue(parameterObject)?.ToString(), $"{ValidationErrorMessage}"); } #pragma warning restore CS8600, CS8602, CS8604 }
public void ParameterBase_ConstructorTest() { ParameterBase parameter = new ParameterBase("ParameterBaseTest", Assembly.GetExecutingAssembly(), new DisplayHelper()); Assert.IsNotNull(parameter, "The 'Parameter' constructor should return a valid instance."); }
/// <summary> /// This function must be implemented by every subclass. /// <para> /// The function must set the 'propertyMetaInfo.ValidationError' property if the validation failed. /// </para> /// <para> /// If the validation succeeded, the 'propertyMetaInfo.ValidationError' should be set to 'null'. /// </para> /// <example> <para>An example string length validation attribute implementation:</para> /// <code> /// public class MaxStringLengthAttribute : ValidationAttributeBase /// { /// public int MaxLength /// { /// get; /// private set; /// } /// /// public MaxStringLengthAttribute(int maxLength, string validationErrorMessage) : base(validationErrorMessage) /// { /// this.MaxLength = maxLength; /// this.ValidationErrorMessage = validationErrorMessage; /// } /// /// // /// // Create an implementation of the abstact base function 'Validate' /// // /// public override void Validate(PropertyMetaInfo propertyMetaInfo, ParameterBase parameterObject) /// { /// MethodBase? methodBase = MethodBase.GetCurrentMethod(); /// // /// // Make sure the 'ValidationAttribute' is assigned to the right /// // property type. (A string in this case) /// // /// if(propertyMetaInfo.Type.ToLower().IndexOf("string") < 0) /// { /// propertyMetaInfo.ValidationError = new ValidationError(propertyMetaInfo, propertyMetaInfo.PropertyInfo.GetValue(parameterObject)?.ToString(), $"The attribute '{methodBase?.DeclaringType}' is not allowed on properties of type: '{propertyMetaInfo.Type}'."); /// return; /// } /// /// // /// // The actual validation. Set the 'ValidationError' if the validation fails. /// // /// if(propertyMetaInfo.PropertyInfo.GetValue(parameterObject)?.ToString()?.Length > this.MaxLength) /// { /// propertyMetaInfo.ValidationError = new ValidationError(propertyMetaInfo, propertyMetaInfo.PropertyInfo.GetValue(parameterObject)?.ToString(), $"{ValidationErrorMessage}"); /// return; /// } /// /// // /// // The validation passed. Clear the 'ValidationError'. /// // /// propertyMetaInfo.ValidationError = null; /// } /// /// }// END class /// </code> /// </example> /// </summary> /// <param name="propertyMetaInfo"></param> /// <param name="parameterObject"></param> public abstract void Validate(PropertyMetaInfo propertyMetaInfo, ParameterBase parameterObject);
const char Asterisk = '*'; // * // ************************************************************************ // IDisplayHelper implementation START // ************************************************************************ /// <summary> /// Creates the help screen for the parameter object provided in argument /// 'parameterObject'. /// <para> /// The line length of the resulting help text will match with value provide in argument 'screenWidth' /// as long a the value is greater than the minimum screen line length. Otherwise the minimum screen /// line length will be used. /// </para> /// <para> /// The result string is supposed to be rendered on the command line. /// </para> /// </summary> /// <param name="parameterObject"></param> /// <param name="screenWidth"></param> /// <returns>The resulting string</returns> public virtual string CreateHelp(ParameterBase parameterObject, int screenWidth = 80) { string result; int longestArgumentLength; int longestTypeLength; int longestDefaultLength; string help; List<string> helpLines; result = string.Empty; longestArgumentLength = 0; if (parameterObject == null) { return result; } HelpAttribute? helpAttribute = (HelpAttribute?)parameterObject.GetType().GetCustomAttribute(typeof(HelpAttribute)); // // If a help text is provided in the command attributes show that text first. // if ((helpAttribute != null) && !String.IsNullOrWhiteSpace(helpAttribute.Help)) { help = helpAttribute.Help; } else { help = string.Empty; } if (parameterObject.PropertyMetaInfoList.Count == 0) { result = "The current parameter object has no parameter property!"; } else { longestArgumentLength = parameterObject.PropertyMetaInfoList.Max(item => item.Name.Length); longestTypeLength = parameterObject.PropertyMetaInfoList.Max(item => item.Type.Length); longestDefaultLength = parameterObject.PropertyMetaInfoList.Max(item => (item.DefaultValue?.ToString()?.Length ?? 0)); longestArgumentLength = Math.Max(longestArgumentLength, "[Parameter]".Length); // [Parameter] = 11 characters min longestTypeLength = Math.Max(longestTypeLength, "[Type]".Length); // [Type] = 6 characters min longestDefaultLength = Math.Max(longestDefaultLength, "[Default]".Length); // [Default] = 9 characters min // // Expand the screen width if there isn't at least 20 characters left for the description. // Minimum length for the help screen is 11 + 6 + 9 + 20 for parameter, type default and description // plus additional 15 characters for spaces and borders. That is 61 characters minimum. // if((longestArgumentLength + longestTypeLength + longestDefaultLength + 20) > screenWidth) { screenWidth = (longestArgumentLength + longestTypeLength + longestDefaultLength + 20); } if(!String.IsNullOrWhiteSpace(help)) { // // Format the help text from the help attribute. // helpLines = CreateWrappedLines(help, screenWidth, leadingSpaces:0, keepEmptyLines:true); result += "\r\n" + String.Join("\r\n", helpLines) + "\r\n\r\n"; } result += CreateHelpHeader(screenWidth); result += CreateHelpTop(screenWidth, longestArgumentLength, longestTypeLength, longestDefaultLength); result += CreateHelpAndVersionArgumentHelpLines(parameterObject.HelpIndicatorList.First(), "Shows the help screen. (This screen).", screenWidth, longestArgumentLength, longestTypeLength, longestDefaultLength); result += CreateHelpSeparatorLine(screenWidth, longestArgumentLength, longestTypeLength, longestDefaultLength); result += CreateHelpAndVersionArgumentHelpLines(parameterObject.VersionIndicatorList.First(), "Shows the command version number if available or an empty string. (Nothing)", screenWidth, longestArgumentLength, longestTypeLength, longestDefaultLength); result += CreateHelpSeparatorLine(screenWidth, longestArgumentLength, longestTypeLength, longestDefaultLength); for (int index = 0; index < parameterObject.PropertyMetaInfoList.Count; index++) { result += CreateHelpLines(parameterObject.PropertyMetaInfoList[index], screenWidth, longestArgumentLength, longestTypeLength, longestDefaultLength); if (index < parameterObject.PropertyMetaInfoList.Count - 1) { result += CreateHelpSeparatorLine(screenWidth, longestArgumentLength, longestTypeLength, longestDefaultLength); } } result += CreateHelpBottom(screenWidth, longestArgumentLength, longestTypeLength, longestDefaultLength); } result += "\r\n\r\n"; result += CreateUsage(parameterObject); return result; }
private static string CreateValidationSummaryBody(ParameterBase parameterObject, int screenWidth) { string result; int longestArgumentLength; int errorMessageSpace; //╔═[Argument]═╦═[Error message]════════════════════════════════════════════════════════╗ //║_abcdefg_. ║_abcdefghijklmnop_......................................................║ //╚════════════╩════════════════════════════════════════════════════════════════════════╝ // |-------------------- errorMessageSpace -------------------------| // // Create top with column names // longestArgumentLength = parameterObject.ValidationErrorList.Max(item => item.PropertyMetaInfo.Name.Length); longestArgumentLength = Math.Max(longestArgumentLength, "[Argument]".Length); errorMessageSpace = screenWidth - longestArgumentLength - 5; // screenWidth - longestArgumentLength - 2xSPACE -3x║ result = string.Empty; result += CornerTopLeft; result += HorizontalLine + "[Argument]" + new string(HorizontalLine, longestArgumentLength - "[Argument]".Length) + HorizontalLine; result += TJunctionUp; result += HorizontalLine + "[Error message]" + new string(HorizontalLine, errorMessageSpace - "[Error message]".Length - 1); result += CornerTopRight + "\r\n"; for (int index = 0; index < parameterObject.ValidationErrorList.Count; index++) { // // Create line // string argumentName = parameterObject.ValidationErrorList[index].PropertyMetaInfo.Name; string errorMessage = parameterObject.ValidationErrorList[index].PropertyMetaInfo.ValidationError?.Message ?? ""; List<string> errorMessageLines = CreateWrappedLines(errorMessage, errorMessageSpace - 1); result += VerticalLine; result += " " + argumentName + new string(' ', longestArgumentLength - argumentName.Length) + " "; result += VerticalLine; result += errorMessageLines[0] + " "; result += VerticalLine + "\r\n"; if (errorMessageLines.Count > 1) { for (int linesIndex = 1; linesIndex < errorMessageLines.Count; linesIndex++) { result += VerticalLine; result += " " + new string(' ', longestArgumentLength) + " "; result += VerticalLine; result += errorMessageLines[linesIndex] + " "; result += VerticalLine + "\r\n"; } } if (index < parameterObject.ValidationErrorList.Count - 1) { // // Create separator // result += TJunctionLeft; result += new string(HorizontalLine, longestArgumentLength + 2); result += CrossJunction; result += new string(HorizontalLine, errorMessageSpace); result += TJunctionRight + "\r\n"; } } // // Create bottom // result += CornerBottomLeft; result += new string(HorizontalLine, longestArgumentLength + 2); result += TJunctionDown; result += new string(HorizontalLine, errorMessageSpace); result += CornerBottomRight + "\r\n"; return result; }