namespace Unosquare.Swan.Components
{
using System.Linq;
using System.Reflection;
using Attributes;
using System;
using System.Collections.Generic;
///
/// 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)
{
_args = args;
_instance = instance;
_settings = settings;
_properties = properties;
PopulateInstance();
SetDefaultValues();
GetRequiredList();
}
public List UnknownList { get; } = new List();
public List RequiredList { get; } = new List();
public bool IsValid() => (_settings.IgnoreUnknownArguments || !UnknownList.Any()) && !RequiredList.Any();
public IEnumerable GetPropertiesOptions()
=> _properties.Select(p => Runtime.AttributeCache.RetrieveOne(p))
.Where(x => x != null);
private void GetRequiredList()
{
foreach (var targetProperty in _properties)
{
var optionAttr = Runtime.AttributeCache.RetrieveOne(targetProperty);
if (optionAttr == null || optionAttr.Required == false)
continue;
if (targetProperty.GetValue(_instance) == null)
{
RequiredList.Add(optionAttr.LongName ?? optionAttr.ShortName);
}
}
}
private void SetDefaultValues()
{
foreach (var targetProperty in _properties.Except(_updatedList))
{
var optionAttr = Runtime.AttributeCache.RetrieveOne(targetProperty);
var defaultValue = optionAttr?.DefaultValue;
if (defaultValue == null)
continue;
if (SetPropertyValue(targetProperty, defaultValue.ToString(), _instance, optionAttr))
_updatedList.Add(targetProperty);
}
}
private void PopulateInstance()
{
const char dash = '-';
var propertyName = string.Empty;
foreach (var arg in _args)
{
var 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);
}
var targetProperty = TryGetProperty(propertyName);
if (targetProperty == null)
{
// Skip if the property is not found
UnknownList.Add(propertyName);
continue;
}
if (!ignoreSetValue && SetPropertyValue(targetProperty, arg, _instance))
{
_updatedList.Add(targetProperty);
propertyName = string.Empty;
}
else if (targetProperty.PropertyType == typeof(bool))
{
// If the arg is a boolean property set it to true.
targetProperty.SetValue(_instance, true);
_updatedList.Add(targetProperty);
propertyName = string.Empty;
}
}
if (!string.IsNullOrEmpty(propertyName))
{
UnknownList.Add(propertyName);
}
}
private bool SetPropertyValue(
PropertyInfo targetProperty,
string propertyValueString,
object result,
ArgumentOptionAttribute optionAttr = null)
{
if (targetProperty.PropertyType.GetTypeInfo().IsEnum)
{
var parsedValue = Enum.Parse(
targetProperty.PropertyType,
propertyValueString,
_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)
=> _properties.FirstOrDefault(p =>
string.Equals(Runtime.AttributeCache.RetrieveOne(p)?.LongName, propertyName, _settings.NameComparer) ||
string.Equals(Runtime.AttributeCache.RetrieveOne(p)?.ShortName, propertyName, _settings.NameComparer));
}
}
}