using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Swan.Reflection;
namespace Swan.Parsers {
///
/// 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 => AttributeCache.DefaultCache.Value.RetrieveOne(p)).Where(x => x != null);
private void GetRequiredList() {
foreach(PropertyInfo targetProperty in this._properties) {
ArgumentOptionAttribute optionAttr = AttributeCache.DefaultCache.Value.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 = AttributeCache.DefaultCache.Value.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.IsEnum) {
return targetProperty.PropertyType.IsArray ? targetProperty.TrySetArray(propertyValueString.Split(optionAttr?.Separator ?? ','), result) : targetProperty.TrySetBasicType(propertyValueString, result);
}
Object parsedValue = Enum.Parse(targetProperty.PropertyType, propertyValueString, this._settings.CaseInsensitiveEnumValues);
targetProperty.SetValue(result, Enum.ToObject(targetProperty.PropertyType, parsedValue));
return true;
}
private PropertyInfo TryGetProperty(String propertyName) => this._properties.FirstOrDefault(p => String.Equals(AttributeCache.DefaultCache.Value.RetrieveOne(p)?.LongName, propertyName, this._settings.NameComparer) || String.Equals(AttributeCache.DefaultCache.Value.RetrieveOne(p)?.ShortName, propertyName, this._settings.NameComparer));
}
}
}