File size: 6,359 Bytes
18a519f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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);
        }
    }
}