using System;
using System.Collections.Generic;
using System.Reflection;
namespace UnityEngine.InputSystem.Utilities
{
///
/// A combination of a name and a value assignment for it.
///
public struct NamedValue : IEquatable
{
public const string Separator = ",";
///
/// Name of the parameter.
///
public string name { get; set; }
///
/// Value of the parameter.
///
public PrimitiveValue value { get; set; }
public TypeCode type => value.type;
public NamedValue ConvertTo(TypeCode type)
{
return new NamedValue
{
name = name,
value = value.ConvertTo(type)
};
}
public static NamedValue From(string name, TValue value)
where TValue : struct
{
return new NamedValue
{
name = name,
value = PrimitiveValue.From(value)
};
}
public override string ToString()
{
return $"{name}={value}";
}
public bool Equals(NamedValue other)
{
return string.Equals(name, other.name, StringComparison.InvariantCultureIgnoreCase)
&& value == other.value;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
return obj is NamedValue parameterValue && Equals(parameterValue);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (name != null ? name.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ value.GetHashCode();
return hashCode;
}
}
public static bool operator==(NamedValue left, NamedValue right)
{
return left.Equals(right);
}
public static bool operator!=(NamedValue left, NamedValue right)
{
return !left.Equals(right);
}
public static NamedValue[] ParseMultiple(string parameterString)
{
if (parameterString == null)
throw new ArgumentNullException(nameof(parameterString));
parameterString = parameterString.Trim();
if (string.IsNullOrEmpty(parameterString))
return null;
var parameterCount = parameterString.CountOccurrences(Separator[0]) + 1;
var parameters = new NamedValue[parameterCount];
var index = 0;
for (var i = 0; i < parameterCount; ++i)
{
var parameter = ParseParameter(parameterString, ref index);
parameters[i] = parameter;
}
return parameters;
}
public static NamedValue Parse(string str)
{
var index = 0;
return ParseParameter(str, ref index);
}
private static NamedValue ParseParameter(string parameterString, ref int index)
{
var parameter = new NamedValue();
var parameterStringLength = parameterString.Length;
// Skip whitespace.
while (index < parameterStringLength && char.IsWhiteSpace(parameterString[index]))
++index;
// Parse name.
var nameStart = index;
while (index < parameterStringLength)
{
var nextChar = parameterString[index];
if (nextChar == '=' || nextChar == Separator[0] || char.IsWhiteSpace(nextChar))
break;
++index;
}
parameter.name = parameterString.Substring(nameStart, index - nameStart);
// Skip whitespace.
while (index < parameterStringLength && char.IsWhiteSpace(parameterString[index]))
++index;
if (index == parameterStringLength || parameterString[index] != '=')
{
// No value given so take "=true" as implied.
parameter.value = true;
}
else
{
++index; // Skip over '='.
// Skip whitespace.
while (index < parameterStringLength && char.IsWhiteSpace(parameterString[index]))
++index;
// Parse value.
var valueStart = index;
while (index < parameterStringLength &&
!(parameterString[index] == Separator[0] || char.IsWhiteSpace(parameterString[index])))
++index;
////TODO: use Substring struct here so that we don't allocate lots of useless strings
var value = parameterString.Substring(valueStart, index - valueStart);
parameter.value = PrimitiveValue.FromString(value);
}
if (index < parameterStringLength && parameterString[index] == Separator[0])
++index;
return parameter;
}
public void ApplyToObject(object instance)
{
if (instance == null)
throw new System.ArgumentNullException(nameof(instance));
var instanceType = instance.GetType();
////REVIEW: what about properties?
var field = instanceType.GetField(name,
BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field == null)
throw new ArgumentException(
$"Cannot find public field '{name}' in '{instanceType.Name}' (while trying to apply parameter)", nameof(instance));
////REVIEW: would be awesome to be able to do this without boxing
var fieldTypeCode = Type.GetTypeCode(field.FieldType);
field.SetValue(instance, value.ConvertTo(fieldTypeCode).ToObject());
}
public static void ApplyAllToObject(object instance, TParameterList parameters)
where TParameterList : IEnumerable
{
foreach (var parameter in parameters)
parameter.ApplyToObject(instance);
}
}
}