1#region Copyright notice and license
2// Protocol Buffers - Google's data interchange format
3// Copyright 2015 Google Inc.  All rights reserved.
4// https://developers.google.com/protocol-buffers/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10//     * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12//     * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16//     * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31#endregion
32
33using Google.Protobuf.TestProtos;
34using Proto2 = Google.Protobuf.TestProtos.Proto2;
35using NUnit.Framework;
36using System;
37using System.Collections;
38using System.Collections.Generic;
39
40using static Google.Protobuf.TestProtos.Proto2.UnittestExtensions;
41using ProtobufUnittest;
42
43namespace Google.Protobuf.Reflection
44{
45    public class FieldAccessTest
46    {
47        [Test]
48        public void GetValue()
49        {
50            var message = SampleMessages.CreateFullTestAllTypes();
51            var fields = TestProtos.TestAllTypes.Descriptor.Fields;
52            Assert.AreEqual(message.SingleBool, fields[TestProtos.TestAllTypes.SingleBoolFieldNumber].Accessor.GetValue(message));
53            Assert.AreEqual(message.SingleBytes, fields[TestProtos.TestAllTypes.SingleBytesFieldNumber].Accessor.GetValue(message));
54            Assert.AreEqual(message.SingleDouble, fields[TestProtos.TestAllTypes.SingleDoubleFieldNumber].Accessor.GetValue(message));
55            Assert.AreEqual(message.SingleFixed32, fields[TestProtos.TestAllTypes.SingleFixed32FieldNumber].Accessor.GetValue(message));
56            Assert.AreEqual(message.SingleFixed64, fields[TestProtos.TestAllTypes.SingleFixed64FieldNumber].Accessor.GetValue(message));
57            Assert.AreEqual(message.SingleFloat, fields[TestProtos.TestAllTypes.SingleFloatFieldNumber].Accessor.GetValue(message));
58            Assert.AreEqual(message.SingleForeignEnum, fields[TestProtos.TestAllTypes.SingleForeignEnumFieldNumber].Accessor.GetValue(message));
59            Assert.AreEqual(message.SingleForeignMessage, fields[TestProtos.TestAllTypes.SingleForeignMessageFieldNumber].Accessor.GetValue(message));
60            Assert.AreEqual(message.SingleImportEnum, fields[TestProtos.TestAllTypes.SingleImportEnumFieldNumber].Accessor.GetValue(message));
61            Assert.AreEqual(message.SingleImportMessage, fields[TestProtos.TestAllTypes.SingleImportMessageFieldNumber].Accessor.GetValue(message));
62            Assert.AreEqual(message.SingleInt32, fields[TestProtos.TestAllTypes.SingleInt32FieldNumber].Accessor.GetValue(message));
63            Assert.AreEqual(message.SingleInt64, fields[TestProtos.TestAllTypes.SingleInt64FieldNumber].Accessor.GetValue(message));
64            Assert.AreEqual(message.SingleNestedEnum, fields[TestProtos.TestAllTypes.SingleNestedEnumFieldNumber].Accessor.GetValue(message));
65            Assert.AreEqual(message.SingleNestedMessage, fields[TestProtos.TestAllTypes.SingleNestedMessageFieldNumber].Accessor.GetValue(message));
66            Assert.AreEqual(message.SinglePublicImportMessage, fields[TestProtos.TestAllTypes.SinglePublicImportMessageFieldNumber].Accessor.GetValue(message));
67            Assert.AreEqual(message.SingleSint32, fields[TestProtos.TestAllTypes.SingleSint32FieldNumber].Accessor.GetValue(message));
68            Assert.AreEqual(message.SingleSint64, fields[TestProtos.TestAllTypes.SingleSint64FieldNumber].Accessor.GetValue(message));
69            Assert.AreEqual(message.SingleString, fields[TestProtos.TestAllTypes.SingleStringFieldNumber].Accessor.GetValue(message));
70            Assert.AreEqual(message.SingleSfixed32, fields[TestProtos.TestAllTypes.SingleSfixed32FieldNumber].Accessor.GetValue(message));
71            Assert.AreEqual(message.SingleSfixed64, fields[TestProtos.TestAllTypes.SingleSfixed64FieldNumber].Accessor.GetValue(message));
72            Assert.AreEqual(message.SingleUint32, fields[TestProtos.TestAllTypes.SingleUint32FieldNumber].Accessor.GetValue(message));
73            Assert.AreEqual(message.SingleUint64, fields[TestProtos.TestAllTypes.SingleUint64FieldNumber].Accessor.GetValue(message));
74            Assert.AreEqual(message.OneofBytes, fields[TestProtos.TestAllTypes.OneofBytesFieldNumber].Accessor.GetValue(message));
75            Assert.AreEqual(message.OneofString, fields[TestProtos.TestAllTypes.OneofStringFieldNumber].Accessor.GetValue(message));
76            Assert.AreEqual(message.OneofNestedMessage, fields[TestProtos.TestAllTypes.OneofNestedMessageFieldNumber].Accessor.GetValue(message));
77            Assert.AreEqual(message.OneofUint32, fields[TestProtos.TestAllTypes.OneofUint32FieldNumber].Accessor.GetValue(message));
78
79            // Just one example for repeated fields - they're all just returning the list
80            var list = (IList) fields[TestProtos.TestAllTypes.RepeatedInt32FieldNumber].Accessor.GetValue(message);
81            Assert.AreEqual(message.RepeatedInt32, list);
82            Assert.AreEqual(message.RepeatedInt32[0], list[0]); // Just in case there was any doubt...
83
84            // Just a single map field, for the same reason
85            var mapMessage = new TestMap { MapStringString = { { "key1", "value1" }, { "key2", "value2" } } };
86            fields = TestMap.Descriptor.Fields;
87            var dictionary = (IDictionary) fields[TestMap.MapStringStringFieldNumber].Accessor.GetValue(mapMessage);
88            Assert.AreEqual(mapMessage.MapStringString, dictionary);
89            Assert.AreEqual("value1", dictionary["key1"]);
90        }
91
92        [Test]
93        public void GetValue_IncorrectType()
94        {
95            IMessage message = SampleMessages.CreateFullTestAllTypes();
96            var fields = message.Descriptor.Fields;
97            Assert.Throws<InvalidCastException>(() => fields[TestProtos.TestAllTypes.SingleBoolFieldNumber].Accessor.GetValue(new TestMap()));
98        }
99
100        [Test]
101        public void HasValue_Proto3_Message()
102        {
103            var message = new TestAllTypes();
104            var accessor = ((IMessage) message).Descriptor.Fields[TestProtos.TestAllTypes.SingleForeignMessageFieldNumber].Accessor;
105            Assert.False(accessor.HasValue(message));
106            message.SingleForeignMessage = new ForeignMessage();
107            Assert.True(accessor.HasValue(message));
108            message.SingleForeignMessage = null;
109            Assert.False(accessor.HasValue(message));
110        }
111
112        [Test]
113        public void HasValue_Proto3_Oneof()
114        {
115            TestAllTypes message = new TestAllTypes();
116            var accessor = ((IMessage) message).Descriptor.Fields[TestProtos.TestAllTypes.OneofStringFieldNumber].Accessor;
117            Assert.False(accessor.HasValue(message));
118            // Even though it's the default value, we still have a value.
119            message.OneofString = "";
120            Assert.True(accessor.HasValue(message));
121            message.OneofString = "hello";
122            Assert.True(accessor.HasValue(message));
123            message.OneofUint32 = 10;
124            Assert.False(accessor.HasValue(message));
125        }
126
127        [Test]
128        public void HasValue_Proto3_Primitive_Optional()
129        {
130            var message = new TestProto3Optional();
131            var accessor = ((IMessage) message).Descriptor.Fields[TestProto3Optional.OptionalInt64FieldNumber].Accessor;
132            Assert.IsFalse(accessor.HasValue(message));
133            message.OptionalInt64 = 5L;
134            Assert.IsTrue(accessor.HasValue(message));
135            message.ClearOptionalInt64();
136            Assert.IsFalse(accessor.HasValue(message));
137            message.OptionalInt64 = 0L;
138            Assert.IsTrue(accessor.HasValue(message));
139        }
140
141        [Test]
142        public void HasValue_Proto3_Primitive_NotOptional()
143        {
144            IMessage message = SampleMessages.CreateFullTestAllTypes();
145            var fields = message.Descriptor.Fields;
146            Assert.Throws<InvalidOperationException>(() => fields[TestProtos.TestAllTypes.SingleBoolFieldNumber].Accessor.HasValue(message));
147        }
148
149        [Test]
150        public void HasValue_Proto3_Repeated()
151        {
152            var message = new TestAllTypes();
153            var accessor = ((IMessage) message).Descriptor.Fields[TestProtos.TestAllTypes.RepeatedBoolFieldNumber].Accessor;
154            Assert.Throws<InvalidOperationException>(() => accessor.HasValue(message));
155        }
156
157        [Test]
158        public void HasValue_Proto2_Primitive()
159        {
160            var message = new Proto2.TestAllTypes();
161            var accessor = ((IMessage) message).Descriptor.Fields[Proto2.TestAllTypes.OptionalInt64FieldNumber].Accessor;
162
163            Assert.IsFalse(accessor.HasValue(message));
164            message.OptionalInt64 = 5L;
165            Assert.IsTrue(accessor.HasValue(message));
166            message.ClearOptionalInt64();
167            Assert.IsFalse(accessor.HasValue(message));
168            message.OptionalInt64 = 0L;
169            Assert.IsTrue(accessor.HasValue(message));
170        }
171
172        [Test]
173        public void HasValue_Proto2_Message()
174        {
175            var message = new Proto2.TestAllTypes();
176            var field = ((IMessage) message).Descriptor.Fields[Proto2.TestAllTypes.OptionalForeignMessageFieldNumber];
177            Assert.False(field.Accessor.HasValue(message));
178            message.OptionalForeignMessage = new Proto2.ForeignMessage();
179            Assert.True(field.Accessor.HasValue(message));
180            message.OptionalForeignMessage = null;
181            Assert.False(field.Accessor.HasValue(message));
182        }
183
184        [Test]
185        public void HasValue_Proto2_Oneof()
186        {
187            var message = new Proto2.TestAllTypes();
188            var accessor = ((IMessage) message).Descriptor.Fields[Proto2.TestAllTypes.OneofStringFieldNumber].Accessor;
189            Assert.False(accessor.HasValue(message));
190            // Even though it's the default value, we still have a value.
191            message.OneofString = "";
192            Assert.True(accessor.HasValue(message));
193            message.OneofString = "hello";
194            Assert.True(accessor.HasValue(message));
195            message.OneofUint32 = 10;
196            Assert.False(accessor.HasValue(message));
197        }
198
199        [Test]
200        public void HasValue_Proto2_Repeated()
201        {
202            var message = new Proto2.TestAllTypes();
203            var accessor = ((IMessage) message).Descriptor.Fields[Proto2.TestAllTypes.RepeatedBoolFieldNumber].Accessor;
204            Assert.Throws<InvalidOperationException>(() => accessor.HasValue(message));
205        }
206
207        [Test]
208        public void SetValue_SingleFields()
209        {
210            // Just a sample (primitives, messages, enums, strings, byte strings)
211            var message = SampleMessages.CreateFullTestAllTypes();
212            var fields = TestProtos.TestAllTypes.Descriptor.Fields;
213            fields[TestProtos.TestAllTypes.SingleBoolFieldNumber].Accessor.SetValue(message, false);
214            fields[TestProtos.TestAllTypes.SingleInt32FieldNumber].Accessor.SetValue(message, 500);
215            fields[TestProtos.TestAllTypes.SingleStringFieldNumber].Accessor.SetValue(message, "It's a string");
216            fields[TestProtos.TestAllTypes.SingleBytesFieldNumber].Accessor.SetValue(message, ByteString.CopyFrom(99, 98, 97));
217            fields[TestProtos.TestAllTypes.SingleForeignEnumFieldNumber].Accessor.SetValue(message, ForeignEnum.ForeignFoo);
218            fields[TestProtos.TestAllTypes.SingleForeignMessageFieldNumber].Accessor.SetValue(message, new ForeignMessage { C = 12345 });
219            fields[TestProtos.TestAllTypes.SingleDoubleFieldNumber].Accessor.SetValue(message, 20150701.5);
220
221            var expected = new TestAllTypes(SampleMessages.CreateFullTestAllTypes())
222            {
223                SingleBool = false,
224                SingleInt32 = 500,
225                SingleString = "It's a string",
226                SingleBytes = ByteString.CopyFrom(99, 98, 97),
227                SingleForeignEnum = ForeignEnum.ForeignFoo,
228                SingleForeignMessage = new ForeignMessage { C = 12345 },
229                SingleDouble = 20150701.5
230            };
231
232            Assert.AreEqual(expected, message);
233        }
234
235        [Test]
236        public void SetValue_SingleFields_WrongType()
237        {
238            IMessage message = SampleMessages.CreateFullTestAllTypes();
239            var fields = message.Descriptor.Fields;
240            Assert.Throws<InvalidCastException>(() => fields[TestProtos.TestAllTypes.SingleBoolFieldNumber].Accessor.SetValue(message, "This isn't a bool"));
241        }
242
243        [Test]
244        public void SetValue_MapFields()
245        {
246            IMessage message = new TestMap();
247            var fields = message.Descriptor.Fields;
248            Assert.Throws<InvalidOperationException>(() => fields[TestMap.MapStringStringFieldNumber].Accessor.SetValue(message, new Dictionary<string, string>()));
249        }
250
251        [Test]
252        public void SetValue_RepeatedFields()
253        {
254            IMessage message = SampleMessages.CreateFullTestAllTypes();
255            var fields = message.Descriptor.Fields;
256            Assert.Throws<InvalidOperationException>(() => fields[TestProtos.TestAllTypes.RepeatedDoubleFieldNumber].Accessor.SetValue(message, new double[10]));
257        }
258
259        [Test]
260        public void Oneof()
261        {
262            var message = new TestAllTypes();
263            var descriptor = TestProtos.TestAllTypes.Descriptor;
264            Assert.AreEqual(1, descriptor.Oneofs.Count);
265            var oneof = descriptor.Oneofs[0];
266            Assert.AreEqual("oneof_field", oneof.Name);
267            Assert.IsNull(oneof.Accessor.GetCaseFieldDescriptor(message));
268
269            message.OneofString = "foo";
270            Assert.AreSame(descriptor.Fields[TestProtos.TestAllTypes.OneofStringFieldNumber], oneof.Accessor.GetCaseFieldDescriptor(message));
271
272            message.OneofUint32 = 10;
273            Assert.AreSame(descriptor.Fields[TestProtos.TestAllTypes.OneofUint32FieldNumber], oneof.Accessor.GetCaseFieldDescriptor(message));
274
275            oneof.Accessor.Clear(message);
276            Assert.AreEqual(TestProtos.TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
277        }
278
279        [Test]
280        public void Clear()
281        {
282            var message = SampleMessages.CreateFullTestAllTypes();
283            var fields = TestProtos.TestAllTypes.Descriptor.Fields;
284            fields[TestProtos.TestAllTypes.SingleBoolFieldNumber].Accessor.Clear(message);
285            fields[TestProtos.TestAllTypes.SingleInt32FieldNumber].Accessor.Clear(message);
286            fields[TestProtos.TestAllTypes.SingleStringFieldNumber].Accessor.Clear(message);
287            fields[TestProtos.TestAllTypes.SingleBytesFieldNumber].Accessor.Clear(message);
288            fields[TestProtos.TestAllTypes.SingleForeignEnumFieldNumber].Accessor.Clear(message);
289            fields[TestProtos.TestAllTypes.SingleForeignMessageFieldNumber].Accessor.Clear(message);
290            fields[TestProtos.TestAllTypes.RepeatedDoubleFieldNumber].Accessor.Clear(message);
291
292            var expected = new TestAllTypes(SampleMessages.CreateFullTestAllTypes())
293            {
294                SingleBool = false,
295                SingleInt32 = 0,
296                SingleString = "",
297                SingleBytes = ByteString.Empty,
298                SingleForeignEnum = 0,
299                SingleForeignMessage = null,
300            };
301            expected.RepeatedDouble.Clear();
302
303            Assert.AreEqual(expected, message);
304
305            // Separately, maps.
306            var mapMessage = new TestMap { MapStringString = { { "key1", "value1" }, { "key2", "value2" } } };
307            fields = TestMap.Descriptor.Fields;
308            fields[TestMap.MapStringStringFieldNumber].Accessor.Clear(mapMessage);
309            Assert.AreEqual(0, mapMessage.MapStringString.Count);
310        }
311
312        [Test]
313        public void Clear_Proto3Optional()
314        {
315            TestProto3Optional message = new TestProto3Optional
316            {
317                OptionalInt32 = 0,
318                OptionalNestedMessage = new TestProto3Optional.Types.NestedMessage()
319            };
320            var primitiveField = TestProto3Optional.Descriptor.Fields[TestProto3Optional.OptionalInt32FieldNumber];
321            var messageField = TestProto3Optional.Descriptor.Fields[TestProto3Optional.OptionalNestedMessageFieldNumber];
322
323            Assert.True(message.HasOptionalInt32);
324            Assert.NotNull(message.OptionalNestedMessage);
325
326            primitiveField.Accessor.Clear(message);
327            messageField.Accessor.Clear(message);
328
329            Assert.False(message.HasOptionalInt32);
330            Assert.Null(message.OptionalNestedMessage);
331        }
332
333        [Test]
334        public void Clear_Proto3_Oneof()
335        {
336            var message = new TestAllTypes();
337            var accessor = ((IMessage) message).Descriptor.Fields[TestProtos.TestAllTypes.OneofUint32FieldNumber].Accessor;
338
339            // The field accessor Clear method only affects a oneof if the current case is the one being cleared.
340            message.OneofString = "hello";
341            Assert.AreEqual(TestProtos.TestAllTypes.OneofFieldOneofCase.OneofString, message.OneofFieldCase);
342            accessor.Clear(message);
343            Assert.AreEqual(TestProtos.TestAllTypes.OneofFieldOneofCase.OneofString, message.OneofFieldCase);
344
345            message.OneofUint32 = 100;
346            Assert.AreEqual(TestProtos.TestAllTypes.OneofFieldOneofCase.OneofUint32, message.OneofFieldCase);
347            accessor.Clear(message);
348            Assert.AreEqual(TestProtos.TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
349        }
350
351        [Test]
352        public void Clear_Proto2_Oneof()
353        {
354            var message = new Proto2.TestAllTypes();
355            var accessor = ((IMessage) message).Descriptor.Fields[Proto2.TestAllTypes.OneofUint32FieldNumber].Accessor;
356
357            // The field accessor Clear method only affects a oneof if the current case is the one being cleared.
358            message.OneofString = "hello";
359            Assert.AreEqual(Proto2.TestAllTypes.OneofFieldOneofCase.OneofString, message.OneofFieldCase);
360            accessor.Clear(message);
361            Assert.AreEqual(Proto2.TestAllTypes.OneofFieldOneofCase.OneofString, message.OneofFieldCase);
362
363            message.OneofUint32 = 100;
364            Assert.AreEqual(Proto2.TestAllTypes.OneofFieldOneofCase.OneofUint32, message.OneofFieldCase);
365            accessor.Clear(message);
366            Assert.AreEqual(Proto2.TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
367        }
368
369        [Test]
370        public void FieldDescriptor_ByName()
371        {
372            var descriptor = TestProtos.TestAllTypes.Descriptor;
373            Assert.AreSame(
374                descriptor.Fields[TestProtos.TestAllTypes.SingleBoolFieldNumber],
375                descriptor.Fields["single_bool"]);
376        }
377
378        [Test]
379        public void FieldDescriptor_NotFound()
380        {
381            var descriptor = TestProtos.TestAllTypes.Descriptor;
382            Assert.Throws<KeyNotFoundException>(() => descriptor.Fields[999999].ToString());
383            Assert.Throws<KeyNotFoundException>(() => descriptor.Fields["not found"].ToString());
384        }
385
386        [Test]
387        public void GetExtensionValue()
388        {
389            var message = SampleMessages.CreateFullTestAllExtensions();
390
391            // test that the reflector works, since the reflector just runs through IExtendableMessage
392            Assert.AreEqual(message.GetExtension(OptionalBoolExtension), Proto2.TestAllExtensions.Descriptor.FindFieldByNumber(OptionalBoolExtension.FieldNumber).Accessor.GetValue(message));
393        }
394
395        [Test]
396        public void GetRepeatedExtensionValue()
397        {
398            // check to make sure repeated accessor uses GetOrRegister
399            var message = new Proto2.TestAllExtensions();
400
401            Assert.IsNull(message.GetExtension(RepeatedBoolExtension));
402            Assert.IsNotNull(Proto2.TestAllExtensions.Descriptor.FindFieldByNumber(RepeatedBoolExtension.FieldNumber).Accessor.GetValue(message));
403            Assert.IsNotNull(message.GetExtension(RepeatedBoolExtension));
404
405            message.ClearExtension(RepeatedBoolExtension);
406            Assert.IsNull(message.GetExtension(RepeatedBoolExtension));
407        }
408
409        [Test]
410        public void HasPresence()
411        {
412            // Proto3
413            var fields = TestProtos.TestAllTypes.Descriptor.Fields;
414            Assert.IsFalse(fields[TestProtos.TestAllTypes.SingleBoolFieldNumber].HasPresence);
415            Assert.IsTrue(fields[TestProtos.TestAllTypes.OneofBytesFieldNumber].HasPresence);
416            Assert.IsTrue(fields[TestProtos.TestAllTypes.SingleForeignMessageFieldNumber].HasPresence);
417            Assert.IsFalse(fields[TestProtos.TestAllTypes.RepeatedBoolFieldNumber].HasPresence);
418
419            fields = TestMap.Descriptor.Fields;
420            Assert.IsFalse(fields[TestMap.MapBoolBoolFieldNumber].HasPresence);
421
422            fields = TestProto3Optional.Descriptor.Fields;
423            Assert.IsTrue(fields[TestProto3Optional.OptionalBoolFieldNumber].HasPresence);
424
425            // Proto2
426            fields = Proto2.TestAllTypes.Descriptor.Fields;
427            Assert.IsTrue(fields[Proto2.TestAllTypes.OptionalBoolFieldNumber].HasPresence);
428            Assert.IsTrue(fields[Proto2.TestAllTypes.OneofBytesFieldNumber].HasPresence);
429            Assert.IsTrue(fields[Proto2.TestAllTypes.OptionalForeignMessageFieldNumber].HasPresence);
430            Assert.IsFalse(fields[Proto2.TestAllTypes.RepeatedBoolFieldNumber].HasPresence);
431
432            fields = Proto2.TestRequired.Descriptor.Fields;
433            Assert.IsTrue(fields[Proto2.TestRequired.AFieldNumber].HasPresence);
434        }
435    }
436}
437