using System.Linq; using System.Reflection; using Unosquare.Swan.Attributes; using System; using System.Collections.Generic; namespace Unosquare.Swan.Components { /// /// Provides methods to parse command line arguments. /// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.). /// public partial class ArgumentParser { private sealed class Validator { private readonly Object _instance; private readonly IEnumerable _args; private readonly List _updatedList = new List(); private readonly ArgumentParserSettings _settings; private readonly PropertyInfo[] _properties; public Validator( PropertyInfo[] properties, IEnumerable args, Object instance, ArgumentParserSettings settings) { this._args = args; this._instance = instance; this._settings = settings; this._properties = properties; this.PopulateInstance(); this.SetDefaultValues(); this.GetRequiredList(); } public List UnknownList { get; } = new List(); public List RequiredList { get; } = new List(); public Boolean IsValid() => (this._settings.IgnoreUnknownArguments || !this.UnknownList.Any()) && !this.RequiredList.Any(); public IEnumerable GetPropertiesOptions() => this._properties.Select(p => Runtime.AttributeCache.RetrieveOne(p)) .Where(x => x != null); private void GetRequiredList() { foreach(PropertyInfo targetProperty in this._properties) { ArgumentOptionAttribute optionAttr = Runtime.AttributeCache.RetrieveOne(targetProperty); if(optionAttr == null || optionAttr.Required == false) { continue; } if(targetProperty.GetValue(this._instance) == null) { this.RequiredList.Add(optionAttr.LongName ?? optionAttr.ShortName); } } } private void SetDefaultValues() { foreach(PropertyInfo targetProperty in this._properties.Except(this._updatedList)) { ArgumentOptionAttribute optionAttr = Runtime.AttributeCache.RetrieveOne(targetProperty); Object defaultValue = optionAttr?.DefaultValue; if(defaultValue == null) { continue; } if(this.SetPropertyValue(targetProperty, defaultValue.ToString(), this._instance, optionAttr)) { this._updatedList.Add(targetProperty); } } } private void PopulateInstance() { const Char dash = '-'; String propertyName = String.Empty; foreach(String arg in this._args) { Boolean ignoreSetValue = String.IsNullOrWhiteSpace(propertyName); if(ignoreSetValue) { if(String.IsNullOrWhiteSpace(arg) || arg[0] != dash) { continue; } propertyName = arg.Substring(1); if(!String.IsNullOrWhiteSpace(propertyName) && propertyName[0] == dash) { propertyName = propertyName.Substring(1); } } PropertyInfo targetProperty = this.TryGetProperty(propertyName); if(targetProperty == null) { // Skip if the property is not found this.UnknownList.Add(propertyName); continue; } if(!ignoreSetValue && this.SetPropertyValue(targetProperty, arg, this._instance)) { this._updatedList.Add(targetProperty); propertyName = String.Empty; } else if(targetProperty.PropertyType == typeof(Boolean)) { // If the arg is a boolean property set it to true. targetProperty.SetValue(this._instance, true); this._updatedList.Add(targetProperty); propertyName = String.Empty; } } if(!String.IsNullOrEmpty(propertyName)) { this.UnknownList.Add(propertyName); } } private Boolean SetPropertyValue( PropertyInfo targetProperty, String propertyValueString, Object result, ArgumentOptionAttribute optionAttr = null) { if(targetProperty.PropertyType.GetTypeInfo().IsEnum) { Object parsedValue = Enum.Parse( targetProperty.PropertyType, propertyValueString, this._settings.CaseInsensitiveEnumValues); targetProperty.SetValue(result, Enum.ToObject(targetProperty.PropertyType, parsedValue)); return true; } return targetProperty.PropertyType.IsArray ? targetProperty.TrySetArray(propertyValueString.Split(optionAttr?.Separator ?? ','), result) : targetProperty.TrySetBasicType(propertyValueString, result); } private PropertyInfo TryGetProperty(String propertyName) => this._properties.FirstOrDefault(p => String.Equals(Runtime.AttributeCache.RetrieveOne(p)?.LongName, propertyName, this._settings.NameComparer) || String.Equals(Runtime.AttributeCache.RetrieveOne(p)?.ShortName, propertyName, this._settings.NameComparer)); } } }