193 lines
6.2 KiB
C#
193 lines
6.2 KiB
C#
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Reflection;
|
||
|
|
||
|
namespace UnityEngine.InputSystem.Utilities
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// A combination of a name and a value assignment for it.
|
||
|
/// </summary>
|
||
|
public struct NamedValue : IEquatable<NamedValue>
|
||
|
{
|
||
|
public const string Separator = ",";
|
||
|
|
||
|
/// <summary>
|
||
|
/// Name of the parameter.
|
||
|
/// </summary>
|
||
|
public string name { get; set; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// Value of the parameter.
|
||
|
/// </summary>
|
||
|
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<TValue>(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<TParameterList>(object instance, TParameterList parameters)
|
||
|
where TParameterList : IEnumerable<NamedValue>
|
||
|
{
|
||
|
foreach (var parameter in parameters)
|
||
|
parameter.ApplyToObject(instance);
|
||
|
}
|
||
|
}
|
||
|
}
|