blob: 3ac3e3d3034d03ca023ca23a522885dc4772eae9 [file] [log] [blame]
Jon Skeet0aac0e42009-09-09 18:48:02 +01001#region Copyright notice and license
Jon Skeet60c059b2008-10-23 21:17:56 +01002// Protocol Buffers - Google's data interchange format
3// Copyright 2008 Google Inc. All rights reserved.
4// http://github.com/jskeet/dotnet-protobufs/
5// Original C++/Java/Python code:
Jon Skeet68036862008-10-22 13:30:34 +01006// http://code.google.com/p/protobuf/
7//
Jon Skeet60c059b2008-10-23 21:17:56 +01008// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are
10// met:
Jon Skeet68036862008-10-22 13:30:34 +010011//
Jon Skeet60c059b2008-10-23 21:17:56 +010012// * Redistributions of source code must retain the above copyright
13// notice, this list of conditions and the following disclaimer.
14// * Redistributions in binary form must reproduce the above
15// copyright notice, this list of conditions and the following disclaimer
16// in the documentation and/or other materials provided with the
17// distribution.
18// * Neither the name of Google Inc. nor the names of its
19// contributors may be used to endorse or promote products derived from
20// this software without specific prior written permission.
Jon Skeet68036862008-10-22 13:30:34 +010021//
Jon Skeet60c059b2008-10-23 21:17:56 +010022// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Jon Skeet0aac0e42009-09-09 18:48:02 +010033#endregion
34
Jon Skeet68036862008-10-22 13:30:34 +010035using System;
36using System.Collections;
37using System.Collections.Generic;
38using Google.ProtocolBuffers.Collections;
39using Google.ProtocolBuffers.Descriptors;
40
41namespace Google.ProtocolBuffers {
csharptest804b6d82010-11-07 10:49:33 -060042
43 public interface IFieldDescriptorLite : IComparable<IFieldDescriptorLite> {
44 bool IsRepeated { get; }
45 bool IsRequired { get; }
46 bool IsPacked { get; }
47 bool IsExtension { get; }
48 bool MessageSetWireFormat { get; } //field.ContainingType.Options.MessageSetWireFormat
49 int FieldNumber { get; }
50 IEnumLiteMap EnumType { get; }
51 FieldType FieldType { get; }
52 MappedType MappedType { get; }
53 object DefaultValue { get; }
54 }
55
Jon Skeet68036862008-10-22 13:30:34 +010056 /// <summary>
57 /// A class which represents an arbitrary set of fields of some message type.
58 /// This is used to implement DynamicMessage, and also to represent extensions
59 /// in GeneratedMessage. This class is internal, since outside users should probably
60 /// be using DynamicMessage.
61 ///
62 /// As in the Java implementation, this class goes against the rest of the framework
63 /// in terms of mutability. Instead of having a mutable Builder class and an immutable
64 /// FieldSet class, FieldSet just has a MakeImmutable() method. This is safe so long as
65 /// all callers are careful not to let a mutable FieldSet escape into the open. This would
66 /// be impossible to guarantee if this were a public class, of course.
67 ///
68 /// All repeated fields are stored as IList[object] even
69 /// TODO(jonskeet): Finish this comment!
70 /// </summary>
71 internal sealed class FieldSet {
72
csharptest804b6d82010-11-07 10:49:33 -060073 private static readonly FieldSet defaultInstance = new FieldSet(new Dictionary<IFieldDescriptorLite, object>()).MakeImmutable();
Jon Skeet68036862008-10-22 13:30:34 +010074
csharptest804b6d82010-11-07 10:49:33 -060075 private IDictionary<IFieldDescriptorLite, object> fields;
Jon Skeet68036862008-10-22 13:30:34 +010076
csharptest804b6d82010-11-07 10:49:33 -060077 private FieldSet(IDictionary<IFieldDescriptorLite, object> fields) {
Jon Skeet68036862008-10-22 13:30:34 +010078 this.fields = fields;
79 }
80
81 public static FieldSet CreateInstance() {
Jon Skeetb49d3c72009-11-03 16:51:01 +000082 // Use SortedList to keep fields in the canonical order
csharptest804b6d82010-11-07 10:49:33 -060083 return new FieldSet(new SortedList<IFieldDescriptorLite, object>());
Jon Skeet68036862008-10-22 13:30:34 +010084 }
85
86 /// <summary>
87 /// Makes this FieldSet immutable, and returns it for convenience. Any
88 /// mutable repeated fields are made immutable, as well as the map itself.
89 /// </summary>
90 internal FieldSet MakeImmutable() {
91 // First check if we have any repeated values
92 bool hasRepeats = false;
93 foreach (object value in fields.Values) {
94 IList<object> list = value as IList<object>;
95 if (list != null && !list.IsReadOnly) {
96 hasRepeats = true;
97 break;
98 }
99 }
100
101 if (hasRepeats) {
csharptest804b6d82010-11-07 10:49:33 -0600102 var tmp = new SortedList<IFieldDescriptorLite, object>();
103 foreach (KeyValuePair<IFieldDescriptorLite, object> entry in fields) {
Jon Skeet68036862008-10-22 13:30:34 +0100104 IList<object> list = entry.Value as IList<object>;
105 tmp[entry.Key] = list == null ? entry.Value : Lists.AsReadOnly(list);
106 }
107 fields = tmp;
108 }
109
110 fields = Dictionaries.AsReadOnly(fields);
111
112 return this;
113 }
114
115 /// <summary>
116 /// Returns the default, immutable instance with no fields defined.
117 /// </summary>
118 internal static FieldSet DefaultInstance {
119 get { return defaultInstance; }
120 }
121
122 /// <summary>
123 /// Returns an immutable mapping of fields. Note that although the mapping itself
124 /// is immutable, the entries may not be (i.e. any repeated values are represented by
125 /// mutable lists). The behaviour is not specified if the contents are mutated.
126 /// </summary>
csharptest804b6d82010-11-07 10:49:33 -0600127 internal IDictionary<IFieldDescriptorLite, object> AllFields {
Jon Skeet68036862008-10-22 13:30:34 +0100128 get { return Dictionaries.AsReadOnly(fields); }
129 }
csharptest804b6d82010-11-07 10:49:33 -0600130#if !LITE
Jon Skeet68036862008-10-22 13:30:34 +0100131 /// <summary>
csharptest804b6d82010-11-07 10:49:33 -0600132 /// Force coercion to full descriptor dictionary.
Jon Skeet68036862008-10-22 13:30:34 +0100133 /// </summary>
csharptest804b6d82010-11-07 10:49:33 -0600134 internal IDictionary<Descriptors.FieldDescriptor, object> AllFieldDescriptors {
135 get {
136 SortedList<Descriptors.FieldDescriptor, object> copy = new SortedList<Google.ProtocolBuffers.Descriptors.FieldDescriptor, object>();
137 foreach (KeyValuePair<IFieldDescriptorLite, object> fd in fields)
138 copy.Add((Descriptors.FieldDescriptor)fd.Key, fd.Value);
139 return Dictionaries.AsReadOnly(copy);
140 }
141 }
142#endif
143 /// <summary>
144 /// See <see cref="IMessageLite.HasField"/>.
145 /// </summary>
146 public bool HasField(IFieldDescriptorLite field) {
Jon Skeet68036862008-10-22 13:30:34 +0100147 if (field.IsRepeated) {
148 throw new ArgumentException("HasField() can only be called on non-repeated fields.");
149 }
150
151 return fields.ContainsKey(field);
152 }
153
154 /// <summary>
155 /// Clears all fields.
156 /// </summary>
157 internal void Clear() {
158 fields.Clear();
159 }
160
161 /// <summary>
csharptest804b6d82010-11-07 10:49:33 -0600162 /// See <see cref="IMessageLite.Item(IFieldDescriptorLite)"/>
Jon Skeet68036862008-10-22 13:30:34 +0100163 /// </summary>
164 /// <remarks>
165 /// If the field is not set, the behaviour when fetching this property varies by field type:
166 /// <list>
167 /// <item>For singular message values, null is returned.</item>
168 /// <item>For singular non-message values, the default value of the field is returned.</item>
169 /// <item>For repeated values, an empty immutable list is returned. This will be compatible
170 /// with IList[object], regardless of the type of the repeated item.</item>
171 /// </list>
172 /// This method returns null if the field is a singular message type
173 /// and is not set; in this case it is up to the caller to fetch the
174 /// message's default instance. For repeated fields of message types,
175 /// an empty collection is returned. For repeated fields of non-message
176 /// types, null is returned.
177 /// <para />
178 /// When setting this property, any list values are copied, and each element is checked
179 /// to ensure it is of an appropriate type.
180 /// </remarks>
181 ///
csharptest804b6d82010-11-07 10:49:33 -0600182 internal object this[IFieldDescriptorLite field] {
Jon Skeet68036862008-10-22 13:30:34 +0100183 get {
184 object result;
185 if (fields.TryGetValue(field, out result)) {
186 return result;
187 }
188 if (field.MappedType == MappedType.Message) {
189 if (field.IsRepeated) {
190 return new List<object>();
191 } else {
192 return null;
193 }
194 }
195 return field.DefaultValue;
196 }
197 set {
198 if (field.IsRepeated) {
199 List<object> list = value as List<object>;
200 if (list == null) {
201 throw new ArgumentException("Wrong object type used with protocol message reflection.");
202 }
203
204 // Wrap the contents in a new list so that the caller cannot change
205 // the list's contents after setting it.
206 List<object> newList = new List<object>(list);
207 foreach (object element in newList) {
208 VerifyType(field, element);
209 }
210 value = newList;
211 }
212 else {
213 VerifyType(field, value);
214 }
215 fields[field] = value;
216 }
217 }
218
219 /// <summary>
csharptest804b6d82010-11-07 10:49:33 -0600220 /// See <see cref="IMessageLite.Item(IFieldDescriptorLite,int)" />
Jon Skeet68036862008-10-22 13:30:34 +0100221 /// </summary>
csharptest804b6d82010-11-07 10:49:33 -0600222 internal object this[IFieldDescriptorLite field, int index] {
Jon Skeet68036862008-10-22 13:30:34 +0100223 get {
224 if (!field.IsRepeated) {
225 throw new ArgumentException("Indexer specifying field and index can only be called on repeated fields.");
226 }
227
228 return ((IList<object>) this[field])[index];
229 }
230 set {
231 if (!field.IsRepeated) {
232 throw new ArgumentException("Indexer specifying field and index can only be called on repeated fields.");
233 }
234 VerifyType(field, value);
235 object list;
236 if (!fields.TryGetValue(field, out list)) {
237 throw new ArgumentOutOfRangeException();
238 }
239 ((IList<object>) list)[index] = value;
240 }
241 }
242
243 /// <summary>
244 /// See <see cref="IBuilder{TMessage, TBuilder}.AddRepeatedField" />
245 /// </summary>
csharptest804b6d82010-11-07 10:49:33 -0600246 internal void AddRepeatedField(IFieldDescriptorLite field, object value) {
Jon Skeet68036862008-10-22 13:30:34 +0100247 if (!field.IsRepeated) {
248 throw new ArgumentException("AddRepeatedField can only be called on repeated fields.");
249 }
250 VerifyType(field, value);
251 object list;
252 if (!fields.TryGetValue(field, out list)) {
253 list = new List<object>();
254 fields[field] = list;
255 }
256 ((IList<object>) list).Add(value);
257 }
258
259 /// <summary>
260 /// Returns an enumerator for the field map. Used to write the fields out.
261 /// </summary>
csharptest804b6d82010-11-07 10:49:33 -0600262 internal IEnumerator<KeyValuePair<IFieldDescriptorLite, object>> GetEnumerator() {
Jon Skeet68036862008-10-22 13:30:34 +0100263 return fields.GetEnumerator();
264 }
265
266 /// <summary>
csharptest804b6d82010-11-07 10:49:33 -0600267 /// See <see cref="IMessageLite.IsInitialized" />
Jon Skeet68036862008-10-22 13:30:34 +0100268 /// </summary>
269 /// <remarks>
270 /// Since FieldSet itself does not have any way of knowing about
271 /// required fields that aren't actually present in the set, it is up
272 /// to the caller to check for genuinely required fields. This property
273 /// merely checks that any messages present are themselves initialized.
274 /// </remarks>
275 internal bool IsInitialized {
276 get {
csharptest804b6d82010-11-07 10:49:33 -0600277 foreach (KeyValuePair<IFieldDescriptorLite, object> entry in fields) {
278 IFieldDescriptorLite field = entry.Key;
Jon Skeet68036862008-10-22 13:30:34 +0100279 if (field.MappedType == MappedType.Message) {
280 if (field.IsRepeated) {
csharptest804b6d82010-11-07 10:49:33 -0600281 foreach(IMessageLite message in (IEnumerable) entry.Value) {
Jon Skeet68036862008-10-22 13:30:34 +0100282 if (!message.IsInitialized) {
283 return false;
284 }
285 }
286 } else {
csharptest804b6d82010-11-07 10:49:33 -0600287 if (!((IMessageLite)entry.Value).IsInitialized) {
Jon Skeet68036862008-10-22 13:30:34 +0100288 return false;
289 }
290 }
291 }
292 }
293 return true;
294 }
295 }
296
297 /// <summary>
298 /// Verifies whether all the required fields in the specified message
299 /// descriptor are present in this field set, as well as whether
300 /// all the embedded messages are themselves initialized.
301 /// </summary>
csharptest804b6d82010-11-07 10:49:33 -0600302 internal bool IsInitializedWithRespectTo(IEnumerable typeFields) {
303 foreach (IFieldDescriptorLite field in typeFields) {
Jon Skeet68036862008-10-22 13:30:34 +0100304 if (field.IsRequired && !HasField(field)) {
305 return false;
306 }
307 }
308 return IsInitialized;
309 }
310
311 /// <summary>
312 /// See <see cref="IBuilder{TMessage, TBuilder}.ClearField" />
313 /// </summary>
csharptest804b6d82010-11-07 10:49:33 -0600314 public void ClearField(IFieldDescriptorLite field) {
Jon Skeet68036862008-10-22 13:30:34 +0100315 fields.Remove(field);
316 }
317
318 /// <summary>
csharptest804b6d82010-11-07 10:49:33 -0600319 /// See <see cref="IMessageLite.GetRepeatedFieldCount" />
Jon Skeet68036862008-10-22 13:30:34 +0100320 /// </summary>
csharptest804b6d82010-11-07 10:49:33 -0600321 public int GetRepeatedFieldCount(IFieldDescriptorLite field) {
Jon Skeet68036862008-10-22 13:30:34 +0100322 if (!field.IsRepeated) {
323 throw new ArgumentException("GetRepeatedFieldCount() can only be called on repeated fields.");
324 }
325
326 return ((IList<object>) this[field]).Count;
327 }
328
csharptest804b6d82010-11-07 10:49:33 -0600329#if !LITE
330 /// <summary>
331 /// See <see cref="IBuilder{TMessage, TBuilder}.MergeFrom(IMessageLite)" />
332 /// </summary>
333 public void MergeFrom(IMessage other) {
334 foreach (KeyValuePair<Descriptors.FieldDescriptor, object> fd in other.AllFields)
335 MergeField(fd.Key, fd.Value);
336 }
337#endif
338
Jon Skeet68036862008-10-22 13:30:34 +0100339 /// <summary>
340 /// Implementation of both <c>MergeFrom</c> methods.
341 /// </summary>
342 /// <param name="otherFields"></param>
csharptest804b6d82010-11-07 10:49:33 -0600343 public void MergeFrom(FieldSet other) {
Jon Skeet68036862008-10-22 13:30:34 +0100344 // Note: We don't attempt to verify that other's fields have valid
345 // types. Doing so would be a losing battle. We'd have to verify
346 // all sub-messages as well, and we'd have to make copies of all of
347 // them to insure that they don't change after verification (since
csharptest804b6d82010-11-07 10:49:33 -0600348 // the IMessageLite interface itself cannot enforce immutability of
Jon Skeet68036862008-10-22 13:30:34 +0100349 // implementations).
350 // TODO(jonskeet): Provide a function somewhere called MakeDeepCopy()
351 // which allows people to make secure deep copies of messages.
352
csharptest804b6d82010-11-07 10:49:33 -0600353 foreach (KeyValuePair<IFieldDescriptorLite, object> entry in other.fields) {
354 MergeField(entry.Key, entry.Value);
355 }
356 }
357
358 private void MergeField(IFieldDescriptorLite field, object mergeValue) {
359 object existingValue;
360 fields.TryGetValue(field, out existingValue);
361 if (field.IsRepeated) {
362 if (existingValue == null) {
363 existingValue = new List<object>();
364 fields[field] = existingValue;
Jon Skeet68036862008-10-22 13:30:34 +0100365 }
csharptest804b6d82010-11-07 10:49:33 -0600366 IList<object> list = (IList<object>) existingValue;
367 foreach (object otherValue in (IEnumerable)mergeValue) {
368 list.Add(otherValue);
369 }
370 } else if (field.MappedType == MappedType.Message && existingValue != null) {
371 IMessageLite existingMessage = (IMessageLite)existingValue;
372 IMessageLite merged = existingMessage.WeakToBuilder()
373 .WeakMergeFrom((IMessageLite)mergeValue)
374 .WeakBuild();
375 this[field] = merged;
376 } else {
377 this[field] = mergeValue;
Jon Skeet68036862008-10-22 13:30:34 +0100378 }
379 }
380
381 /// <summary>
csharptest804b6d82010-11-07 10:49:33 -0600382 /// See <see cref="IMessageLite.WriteTo(CodedOutputStream)" />.
Jon Skeet68036862008-10-22 13:30:34 +0100383 /// </summary>
384 public void WriteTo(CodedOutputStream output) {
csharptest804b6d82010-11-07 10:49:33 -0600385 foreach (KeyValuePair<IFieldDescriptorLite, object> entry in fields) {
Jon Skeet68036862008-10-22 13:30:34 +0100386 WriteField(entry.Key, entry.Value, output);
387 }
388 }
389
390 /// <summary>
391 /// Writes a single field to a CodedOutputStream.
392 /// </summary>
csharptest804b6d82010-11-07 10:49:33 -0600393 public void WriteField(IFieldDescriptorLite field, Object value, CodedOutputStream output) {
394 if (field.IsExtension && field.MessageSetWireFormat) {
395 output.WriteMessageSetExtension(field.FieldNumber, (IMessageLite) value);
Jon Skeet68036862008-10-22 13:30:34 +0100396 } else {
397 if (field.IsRepeated) {
Jon Skeet25a28582009-02-18 16:06:22 +0000398 IEnumerable valueList = (IEnumerable) value;
399 if (field.IsPacked) {
400 output.WriteTag(field.FieldNumber, WireFormat.WireType.LengthDelimited);
401 // Compute the total data size so the length can be written.
402 int dataSize = 0;
403 foreach (object element in valueList) {
404 dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element);
405 }
406 output.WriteRawVarint32((uint)dataSize);
407 // Write the data itself, without any tags.
408 foreach (object element in valueList) {
409 output.WriteFieldNoTag(field.FieldType, element);
410 }
411 } else {
412 foreach (object element in valueList) {
413 output.WriteField(field.FieldType, field.FieldNumber, element);
414 }
Jon Skeet68036862008-10-22 13:30:34 +0100415 }
416 } else {
417 output.WriteField(field.FieldType, field.FieldNumber, value);
418 }
419 }
420 }
421
422 /// <summary>
csharptest804b6d82010-11-07 10:49:33 -0600423 /// See <see cref="IMessageLite.SerializedSize" />. It's up to the caller to
Jon Skeet68036862008-10-22 13:30:34 +0100424 /// cache the resulting size if desired.
425 /// </summary>
426 public int SerializedSize {
427 get {
428 int size = 0;
csharptest804b6d82010-11-07 10:49:33 -0600429 foreach (KeyValuePair<IFieldDescriptorLite, object> entry in fields) {
430 IFieldDescriptorLite field = entry.Key;
Jon Skeet68036862008-10-22 13:30:34 +0100431 object value = entry.Value;
432
csharptest804b6d82010-11-07 10:49:33 -0600433 if (field.IsExtension && field.MessageSetWireFormat) {
434 size += CodedOutputStream.ComputeMessageSetExtensionSize(field.FieldNumber, (IMessageLite)value);
Jon Skeet68036862008-10-22 13:30:34 +0100435 } else {
436 if (field.IsRepeated) {
Jon Skeet25a28582009-02-18 16:06:22 +0000437 IEnumerable valueList = (IEnumerable)value;
438 if (field.IsPacked) {
439 int dataSize = 0;
440 foreach (object element in valueList) {
441 dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element);
442 }
443 size += dataSize + CodedOutputStream.ComputeTagSize(field.FieldNumber) + CodedOutputStream.ComputeRawVarint32Size((uint)dataSize);
444 } else {
445 foreach (object element in valueList) {
446 size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, element);
447 }
Jon Skeet68036862008-10-22 13:30:34 +0100448 }
449 } else {
450 size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, value);
451 }
452 }
453 }
454 return size;
455 }
456 }
457
458 /// <summary>
459 /// Verifies that the given object is of the correct type to be a valid
460 /// value for the given field.
461 /// </summary>
462 /// <remarks>
463 /// For repeated fields, this checks if the object is of the right
464 /// element type, not whether it's a list.
465 /// </remarks>
466 /// <exception cref="ArgumentException">The value is not of the right type.</exception>
Jon Skeet642a8142009-01-27 12:25:21 +0000467 /// <exception cref="ArgumentNullException">The value is null.</exception>
csharptest804b6d82010-11-07 10:49:33 -0600468 private static void VerifyType(IFieldDescriptorLite field, object value) {
Jon Skeet642a8142009-01-27 12:25:21 +0000469 ThrowHelper.ThrowIfNull(value, "value");
Jon Skeet68036862008-10-22 13:30:34 +0100470 bool isValid = false;
471 switch (field.MappedType) {
472 case MappedType.Int32: isValid = value is int; break;
473 case MappedType.Int64: isValid = value is long; break;
474 case MappedType.UInt32: isValid = value is uint; break;
475 case MappedType.UInt64: isValid = value is ulong; break;
476 case MappedType.Single: isValid = value is float; break;
477 case MappedType.Double: isValid = value is double; break;
478 case MappedType.Boolean: isValid = value is bool; break;
479 case MappedType.String: isValid = value is string; break;
480 case MappedType.ByteString: isValid = value is ByteString; break;
481 case MappedType.Enum:
csharptest804b6d82010-11-07 10:49:33 -0600482 IEnumLite enumValue = value as IEnumLite;
483 isValid = enumValue != null && field.EnumType.IsValidValue(enumValue);
Jon Skeet68036862008-10-22 13:30:34 +0100484 break;
485 case MappedType.Message:
csharptest804b6d82010-11-07 10:49:33 -0600486 IMessageLite messageValue = value as IMessageLite;
487 isValid = messageValue != null;
488#if !LITE
489 isValid = isValid && ((IMessage)messageValue).DescriptorForType == ((Google.ProtocolBuffers.Descriptors.FieldDescriptor)field).MessageType;
490#endif
Jon Skeet68036862008-10-22 13:30:34 +0100491 break;
492 }
493
494 if (!isValid) {
495 // When chaining calls to SetField(), it can be hard to tell from
496 // the stack trace which exact call failed, since the whole chain is
497 // considered one line of code. So, let's make sure to include the
498 // field name and other useful info in the exception.
csharptest804b6d82010-11-07 10:49:33 -0600499 string message = "Wrong object type used with protocol message reflection.";
500#if !LITE
501 Google.ProtocolBuffers.Descriptors.FieldDescriptor fieldinfo = field as Google.ProtocolBuffers.Descriptors.FieldDescriptor;
502 if (fieldinfo != null) {
503 message += "Message type \"" + fieldinfo.ContainingType.FullName;
504 message += "\", field \"" + (fieldinfo.IsExtension ? fieldinfo.FullName : fieldinfo.Name);
505 message += "\", value was type \"" + value.GetType().Name + "\".";
506 }
507#endif
508 throw new ArgumentException(message);
Jon Skeet68036862008-10-22 13:30:34 +0100509 }
510 }
511 }
512}