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); } } }