blob: 515637cc7d777caa69829bfbf9a421697fd1dbf5 [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.Generic;
37using System.IO;
38using Google.ProtocolBuffers.Collections;
39using Google.ProtocolBuffers.Descriptors;
40using Google.ProtocolBuffers.DescriptorProtos;
41
42namespace Google.ProtocolBuffers {
43 /// <summary>
44 /// Used to keep track of fields which were seen when parsing a protocol message
45 /// but whose field numbers or types are unrecognized. This most frequently
46 /// occurs when new fields are added to a message type and then messages containing
47 /// those fields are read by old software that was built before the new types were
48 /// added.
49 ///
50 /// Every message contains an UnknownFieldSet.
51 ///
52 /// Most users will never need to use this class directly.
53 /// </summary>
csharptestd9c59e62010-11-04 19:36:28 -050054 public sealed class UnknownFieldSet : IMessageLite {
Jon Skeet68036862008-10-22 13:30:34 +010055
56 private static readonly UnknownFieldSet defaultInstance = new UnknownFieldSet(new Dictionary<int, UnknownField>());
57
58 private readonly IDictionary<int, UnknownField> fields;
59
60 private UnknownFieldSet(IDictionary<int, UnknownField> fields) {
61 this.fields = fields;
62 }
63
64 /// <summary>
65 /// Creates a new unknown field set builder.
66 /// </summary>
67 public static Builder CreateBuilder() {
68 return new Builder();
69 }
70
71 /// <summary>
72 /// Creates a new unknown field set builder
73 /// and initialize it from <paramref name="original"/>.
74 /// </summary>
75 public static Builder CreateBuilder(UnknownFieldSet original) {
76 return new Builder().MergeFrom(original);
77 }
78
79 public static UnknownFieldSet DefaultInstance {
80 get { return defaultInstance; }
81 }
82
83 /// <summary>
84 /// Returns a read-only view of the mapping from field numbers to values.
85 /// </summary>
86 public IDictionary<int, UnknownField> FieldDictionary {
87 get { return Dictionaries.AsReadOnly(fields); }
88 }
89
90 /// <summary>
91 /// Checks whether or not the given field number is present in the set.
92 /// </summary>
93 public bool HasField(int field) {
94 return fields.ContainsKey(field);
95 }
96
97 /// <summary>
98 /// Fetches a field by number, returning an empty field if not present.
99 /// Never returns null.
100 /// </summary>
101 public UnknownField this[int number] {
102 get {
103 UnknownField ret;
104 if (!fields.TryGetValue(number, out ret)) {
105 ret = UnknownField.DefaultInstance;
106 }
107 return ret;
108 }
109 }
110
111 /// <summary>
112 /// Serializes the set and writes it to <paramref name="output"/>.
113 /// </summary>
114 public void WriteTo(CodedOutputStream output) {
115 foreach (KeyValuePair<int, UnknownField> entry in fields) {
116 entry.Value.WriteTo(entry.Key, output);
117 }
118 }
119
120 /// <summary>
121 /// Gets the number of bytes required to encode this set.
122 /// </summary>
123 public int SerializedSize {
124 get {
125 int result = 0;
126 foreach (KeyValuePair<int, UnknownField> entry in fields) {
127 result += entry.Value.GetSerializedSize(entry.Key);
128 }
129 return result;
130 }
131 }
132
133 /// <summary>
134 /// Converts the set to a string in protocol buffer text format. This
135 /// is just a trivial wrapper around TextFormat.PrintToString.
136 /// </summary>
137 public override String ToString() {
138 return TextFormat.PrintToString(this);
139 }
140
141 /// <summary>
142 /// Serializes the message to a ByteString and returns it. This is
143 /// just a trivial wrapper around WriteTo(CodedOutputStream).
144 /// </summary>
145 /// <returns></returns>
146 public ByteString ToByteString() {
147 ByteString.CodedBuilder codedBuilder = new ByteString.CodedBuilder(SerializedSize);
148 WriteTo(codedBuilder.CodedOutput);
149 return codedBuilder.Build();
150 }
151
152 /// <summary>
153 /// Serializes the message to a byte array and returns it. This is
154 /// just a trivial wrapper around WriteTo(CodedOutputStream).
155 /// </summary>
156 /// <returns></returns>
157 public byte[] ToByteArray() {
158 byte[] data = new byte[SerializedSize];
159 CodedOutputStream output = CodedOutputStream.CreateInstance(data);
160 WriteTo(output);
161 output.CheckNoSpaceLeft();
162 return data;
163 }
164
165 /// <summary>
166 /// Serializes the message and writes it to <paramref name="output"/>. This is
167 /// just a trivial wrapper around WriteTo(CodedOutputStream).
168 /// </summary>
169 /// <param name="output"></param>
170 public void WriteTo(Stream output) {
171 CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output);
172 WriteTo(codedOutput);
173 codedOutput.Flush();
174 }
175
176 /// <summary>
177 /// Serializes the set and writes it to <paramref name="output"/> using
178 /// the MessageSet wire format.
179 /// </summary>
180 public void WriteAsMessageSetTo(CodedOutputStream output) {
181 foreach (KeyValuePair<int, UnknownField> entry in fields) {
182 entry.Value.WriteAsMessageSetExtensionTo(entry.Key, output);
183 }
184 }
185
186 /// <summary>
187 /// Gets the number of bytes required to encode this set using the MessageSet
188 /// wire format.
189 /// </summary>
190 public int SerializedSizeAsMessageSet {
191 get {
192 int result = 0;
193 foreach (KeyValuePair<int, UnknownField> entry in fields) {
194 result += entry.Value.GetSerializedSizeAsMessageSetExtension(entry.Key);
195 }
196 return result;
197 }
198 }
199
Jon Skeet43da7ae2009-05-28 21:45:43 +0100200 public override bool Equals(object other) {
201 if (ReferenceEquals(this, other)) {
202 return true;
203 }
204 UnknownFieldSet otherSet = other as UnknownFieldSet;
205 return otherSet != null && Dictionaries.Equals(fields, otherSet.fields);
206 }
207
208 public override int GetHashCode() {
209 return Dictionaries.GetHashCode(fields);
210 }
211
Jon Skeet68036862008-10-22 13:30:34 +0100212 /// <summary>
213 /// Parses an UnknownFieldSet from the given input.
214 /// </summary>
215 public static UnknownFieldSet ParseFrom(CodedInputStream input) {
216 return CreateBuilder().MergeFrom(input).Build();
217 }
218
219 /// <summary>
220 /// Parses an UnknownFieldSet from the given data.
221 /// </summary>
222 public static UnknownFieldSet ParseFrom(ByteString data) {
223 return CreateBuilder().MergeFrom(data).Build();
224 }
225
226 /// <summary>
227 /// Parses an UnknownFieldSet from the given data.
228 /// </summary>
229 public static UnknownFieldSet ParseFrom(byte[] data) {
230 return CreateBuilder().MergeFrom(data).Build();
231 }
232
233 /// <summary>
234 /// Parses an UnknownFieldSet from the given input.
235 /// </summary>
236 public static UnknownFieldSet ParseFrom(Stream input) {
237 return CreateBuilder().MergeFrom(input).Build();
238 }
239
csharptestd9c59e62010-11-04 19:36:28 -0500240 #region IMessageLite Members
241
242 public bool IsInitialized {
243 get { return fields != null; }
244 }
245
246 public void WriteDelimitedTo(Stream output) {
247 CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output);
248 codedOutput.WriteRawVarint32((uint) SerializedSize);
249 WriteTo(codedOutput);
250 codedOutput.Flush();
251 }
252
253 public IBuilderLite WeakCreateBuilderForType() {
254 return new Builder();
255 }
256
257 public IBuilderLite WeakToBuilder() {
258 return new Builder(fields);
259 }
260
261 public IMessageLite WeakDefaultInstanceForType {
262 get { return defaultInstance; }
263 }
264
265 #endregion
266
Jon Skeet68036862008-10-22 13:30:34 +0100267 /// <summary>
268 /// Builder for UnknownFieldSets.
269 /// </summary>
csharptestd9c59e62010-11-04 19:36:28 -0500270 public sealed class Builder : IBuilderLite
Jon Skeet68036862008-10-22 13:30:34 +0100271 {
272 /// <summary>
Jon Skeetb49d3c72009-11-03 16:51:01 +0000273 /// Mapping from number to field. Note that by using a SortedList we ensure
Jon Skeet68036862008-10-22 13:30:34 +0100274 /// that the fields will be serialized in ascending order.
275 /// </summary>
csharptestd9c59e62010-11-04 19:36:28 -0500276 private IDictionary<int, UnknownField> fields;
Jon Skeet68036862008-10-22 13:30:34 +0100277 // Optimization: We keep around a builder for the last field that was
278 // modified so that we can efficiently add to it multiple times in a
279 // row (important when parsing an unknown repeated field).
Jon Skeet43da7ae2009-05-28 21:45:43 +0100280 private int lastFieldNumber;
281 private UnknownField.Builder lastField;
Jon Skeet68036862008-10-22 13:30:34 +0100282
283 internal Builder() {
csharptestd9c59e62010-11-04 19:36:28 -0500284 fields = new SortedList<int, UnknownField>();
285 }
286
287 internal Builder(IDictionary<int, UnknownField> dictionary) {
288 fields = new SortedList<int, UnknownField>(dictionary);
Jon Skeet68036862008-10-22 13:30:34 +0100289 }
290
291 /// <summary>
292 /// Returns a field builder for the specified field number, including any values
293 /// which already exist.
294 /// </summary>
295 private UnknownField.Builder GetFieldBuilder(int number) {
296 if (lastField != null) {
297 if (number == lastFieldNumber) {
298 return lastField;
299 }
300 // Note: AddField() will reset lastField and lastFieldNumber.
301 AddField(lastFieldNumber, lastField.Build());
302 }
303 if (number == 0) {
304 return null;
305 }
306
307 lastField = UnknownField.CreateBuilder();
308 UnknownField existing;
309 if (fields.TryGetValue(number, out existing)) {
310 lastField.MergeFrom(existing);
311 }
312 lastFieldNumber = number;
313 return lastField;
314 }
315
316 /// <summary>
317 /// Build the UnknownFieldSet and return it. Once this method has been called,
318 /// this instance will no longer be usable. Calling any method after this
319 /// will throw a NullReferenceException.
320 /// </summary>
321 public UnknownFieldSet Build() {
322 GetFieldBuilder(0); // Force lastField to be built.
323 UnknownFieldSet result = fields.Count == 0 ? DefaultInstance : new UnknownFieldSet(fields);
324 fields = null;
325 return result;
326 }
327
328 /// <summary>
329 /// Adds a field to the set. If a field with the same number already exists, it
330 /// is replaced.
331 /// </summary>
332 public Builder AddField(int number, UnknownField field) {
333 if (number == 0) {
334 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
335 }
336 if (lastField != null && lastFieldNumber == number) {
337 // Discard this.
338 lastField = null;
339 lastFieldNumber = 0;
340 }
341 fields[number] = field;
342 return this;
343 }
344
345 /// <summary>
346 /// Resets the builder to an empty set.
347 /// </summary>
348 public Builder Clear() {
349 fields.Clear();
350 lastFieldNumber = 0;
351 lastField = null;
352 return this;
353 }
354
355 /// <summary>
356 /// Parse an entire message from <paramref name="input"/> and merge
357 /// its fields into this set.
358 /// </summary>
359 public Builder MergeFrom(CodedInputStream input) {
360 while (true) {
361 uint tag = input.ReadTag();
362 if (tag == 0 || !MergeFieldFrom(tag, input)) {
363 break;
364 }
365 }
366 return this;
367 }
368
369 /// <summary>
370 /// Parse a single field from <paramref name="input"/> and merge it
371 /// into this set.
372 /// </summary>
373 /// <param name="tag">The field's tag number, which was already parsed.</param>
374 /// <param name="input">The coded input stream containing the field</param>
375 /// <returns>false if the tag is an "end group" tag, true otherwise</returns>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100376 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100377 public bool MergeFieldFrom(uint tag, CodedInputStream input) {
378 int number = WireFormat.GetTagFieldNumber(tag);
379 switch (WireFormat.GetTagWireType(tag)) {
380 case WireFormat.WireType.Varint:
381 GetFieldBuilder(number).AddVarint(input.ReadUInt64());
382 return true;
383 case WireFormat.WireType.Fixed64:
384 GetFieldBuilder(number).AddFixed64(input.ReadFixed64());
385 return true;
386 case WireFormat.WireType.LengthDelimited:
387 GetFieldBuilder(number).AddLengthDelimited(input.ReadBytes());
388 return true;
389 case WireFormat.WireType.StartGroup: {
390 Builder subBuilder = CreateBuilder();
csharptestd9c59e62010-11-04 19:36:28 -0500391#pragma warning disable 0612
Jon Skeet68036862008-10-22 13:30:34 +0100392 input.ReadUnknownGroup(number, subBuilder);
csharptestd9c59e62010-11-04 19:36:28 -0500393#pragma warning restore 0612
Jon Skeet68036862008-10-22 13:30:34 +0100394 GetFieldBuilder(number).AddGroup(subBuilder.Build());
395 return true;
396 }
397 case WireFormat.WireType.EndGroup:
398 return false;
399 case WireFormat.WireType.Fixed32:
400 GetFieldBuilder(number).AddFixed32(input.ReadFixed32());
401 return true;
402 default:
403 throw InvalidProtocolBufferException.InvalidWireType();
404 }
405 }
406
407 /// <summary>
408 /// Parses <paramref name="input"/> as an UnknownFieldSet and merge it
409 /// with the set being built. This is just a small wrapper around
410 /// MergeFrom(CodedInputStream).
411 /// </summary>
412 public Builder MergeFrom(Stream input) {
413 CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
414 MergeFrom(codedInput);
415 codedInput.CheckLastTagWas(0);
416 return this;
417 }
418
419 /// <summary>
420 /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it
421 /// with the set being built. This is just a small wrapper around
422 /// MergeFrom(CodedInputStream).
423 /// </summary>
424 public Builder MergeFrom(ByteString data) {
425 CodedInputStream input = data.CreateCodedInput();
426 MergeFrom(input);
427 input.CheckLastTagWas(0);
428 return this;
429 }
430
431 /// <summary>
432 /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it
433 /// with the set being built. This is just a small wrapper around
434 /// MergeFrom(CodedInputStream).
435 /// </summary>
436 public Builder MergeFrom(byte[] data) {
437 CodedInputStream input = CodedInputStream.CreateInstance(data);
438 MergeFrom(input);
439 input.CheckLastTagWas(0);
440 return this;
441 }
442
443 /// <summary>
444 /// Convenience method for merging a new field containing a single varint
445 /// value. This is used in particular when an unknown enum value is
446 /// encountered.
447 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100448 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100449 public Builder MergeVarintField(int number, ulong value) {
450 if (number == 0) {
451 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
452 }
453 GetFieldBuilder(number).AddVarint(value);
454 return this;
455 }
456
457 /// <summary>
458 /// Merges the fields from <paramref name="other"/> into this set.
459 /// If a field number exists in both sets, the values in <paramref name="other"/>
460 /// will be appended to the values in this set.
461 /// </summary>
462 public Builder MergeFrom(UnknownFieldSet other) {
463 if (other != DefaultInstance) {
464 foreach(KeyValuePair<int, UnknownField> entry in other.fields) {
465 MergeField(entry.Key, entry.Value);
466 }
467 }
468 return this;
469 }
470
471 /// <summary>
472 /// Checks if the given field number is present in the set.
473 /// </summary>
474 public bool HasField(int number) {
475 if (number == 0) {
476 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
477 }
478 return number == lastFieldNumber || fields.ContainsKey(number);
479 }
480
481 /// <summary>
482 /// Adds a field to the unknown field set. If a field with the same
483 /// number already exists, the two are merged.
484 /// </summary>
485 public Builder MergeField(int number, UnknownField field) {
486 if (number == 0) {
487 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
488 }
489 if (HasField(number)) {
490 GetFieldBuilder(number).MergeFrom(field);
491 } else {
492 // Optimization: We could call getFieldBuilder(number).mergeFrom(field)
493 // in this case, but that would create a copy of the Field object.
494 // We'd rather reuse the one passed to us, so call AddField() instead.
495 AddField(number, field);
496 }
497 return this;
498 }
499
csharptest80e73b92010-11-05 14:49:08 -0500500 internal void MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry, IBuilder builder) {
Jon Skeet68036862008-10-22 13:30:34 +0100501 while (true) {
502 uint tag = input.ReadTag();
503 if (tag == 0) {
504 break;
505 }
csharptestd9c59e62010-11-04 19:36:28 -0500506
Jon Skeet68036862008-10-22 13:30:34 +0100507 if (!MergeFieldFrom(input, extensionRegistry, builder, tag)) {
508 // end group tag
509 break;
510 }
511 }
512 }
513
514 /// <summary>
515 /// Like <see cref="MergeFrom(CodedInputStream, ExtensionRegistry, IBuilder)" />
516 /// but parses a single field.
517 /// </summary>
518 /// <param name="input">The input to read the field from</param>
519 /// <param name="extensionRegistry">Registry to use when an extension field is encountered</param>
520 /// <param name="builder">Builder to merge field into, if it's a known field</param>
521 /// <param name="tag">The tag, which should already have been read from the input</param>
522 /// <returns>true unless the tag is an end-group tag</returns>
csharptestd9c59e62010-11-04 19:36:28 -0500523 internal bool MergeFieldFrom(CodedInputStream input,
Jon Skeet68036862008-10-22 13:30:34 +0100524 ExtensionRegistry extensionRegistry, IBuilder builder, uint tag) {
525
Jon Skeet68036862008-10-22 13:30:34 +0100526 MessageDescriptor type = builder.DescriptorForType;
527 if (type.Options.MessageSetWireFormat && tag == WireFormat.MessageSetTag.ItemStart) {
528 MergeMessageSetExtensionFromCodedStream(input, extensionRegistry, builder);
529 return true;
530 }
531
532 WireFormat.WireType wireType = WireFormat.GetTagWireType(tag);
533 int fieldNumber = WireFormat.GetTagFieldNumber(tag);
534
535 FieldDescriptor field;
536 IMessage defaultFieldInstance = null;
537
538 if (type.IsExtensionNumber(fieldNumber)) {
539 ExtensionInfo extension = extensionRegistry[type, fieldNumber];
540 if (extension == null) {
541 field = null;
542 } else {
543 field = extension.Descriptor;
544 defaultFieldInstance = extension.DefaultInstance;
545 }
546 } else {
547 field = type.FindFieldByNumber(fieldNumber);
548 }
549
550 // Unknown field or wrong wire type. Skip.
Jon Skeet25a28582009-02-18 16:06:22 +0000551 if (field == null || wireType != WireFormat.GetWireType(field)) {
Jon Skeet68036862008-10-22 13:30:34 +0100552 return MergeFieldFrom(tag, input);
553 }
554
Jon Skeet25a28582009-02-18 16:06:22 +0000555 if (field.IsPacked) {
556 int length = (int)input.ReadRawVarint32();
557 int limit = input.PushLimit(length);
558 if (field.FieldType == FieldType.Enum) {
559 while (!input.ReachedLimit) {
Jon Skeet68036862008-10-22 13:30:34 +0100560 int rawValue = input.ReadEnum();
Jon Skeet25a28582009-02-18 16:06:22 +0000561 object value = field.EnumType.FindValueByNumber(rawValue);
Jon Skeet68036862008-10-22 13:30:34 +0100562 if (value == null) {
Jon Skeet25a28582009-02-18 16:06:22 +0000563 // If the number isn't recognized as a valid value for this
564 // enum, drop it (don't even add it to unknownFields).
Jon Skeet68036862008-10-22 13:30:34 +0100565 return true;
566 }
Jon Skeet25a28582009-02-18 16:06:22 +0000567 builder.WeakAddRepeatedField(field, value);
Jon Skeet68036862008-10-22 13:30:34 +0100568 }
Jon Skeet25a28582009-02-18 16:06:22 +0000569 } else {
570 while (!input.ReachedLimit) {
571 Object value = input.ReadPrimitiveField(field.FieldType);
572 builder.WeakAddRepeatedField(field, value);
573 }
574 }
575 input.PopLimit(limit);
Jon Skeet68036862008-10-22 13:30:34 +0100576 } else {
Jon Skeet25a28582009-02-18 16:06:22 +0000577 object value;
578 switch (field.FieldType) {
579 case FieldType.Group:
580 case FieldType.Message: {
csharptest7d396f92010-11-08 20:06:46 -0600581 IBuilderLite subBuilder;
Jon Skeet25a28582009-02-18 16:06:22 +0000582 if (defaultFieldInstance != null) {
583 subBuilder = defaultFieldInstance.WeakCreateBuilderForType();
584 } else {
585 subBuilder = builder.CreateBuilderForField(field);
586 }
587 if (!field.IsRepeated) {
csharptest7d396f92010-11-08 20:06:46 -0600588 subBuilder.WeakMergeFrom((IMessageLite)builder[field]);
Jon Skeet25a28582009-02-18 16:06:22 +0000589 }
590 if (field.FieldType == FieldType.Group) {
591 input.ReadGroup(field.FieldNumber, subBuilder, extensionRegistry);
592 } else {
593 input.ReadMessage(subBuilder, extensionRegistry);
594 }
595 value = subBuilder.WeakBuild();
596 break;
597 }
598 case FieldType.Enum: {
599 int rawValue = input.ReadEnum();
600 value = field.EnumType.FindValueByNumber(rawValue);
601 // If the number isn't recognized as a valid value for this enum,
602 // drop it.
603 if (value == null) {
604 MergeVarintField(fieldNumber, (ulong)rawValue);
605 return true;
606 }
607 break;
608 }
609 default:
610 value = input.ReadPrimitiveField(field.FieldType);
611 break;
612 }
613 if (field.IsRepeated) {
614 builder.WeakAddRepeatedField(field, value);
615 } else {
616 builder[field] = value;
617 }
Jon Skeet68036862008-10-22 13:30:34 +0100618 }
619 return true;
620 }
621
622 /// <summary>
623 /// Called by MergeFieldFrom to parse a MessageSet extension.
624 /// </summary>
625 private void MergeMessageSetExtensionFromCodedStream(CodedInputStream input,
626 ExtensionRegistry extensionRegistry, IBuilder builder) {
627 MessageDescriptor type = builder.DescriptorForType;
628
629 // The wire format for MessageSet is:
630 // message MessageSet {
631 // repeated group Item = 1 {
632 // required int32 typeId = 2;
633 // required bytes message = 3;
634 // }
635 // }
636 // "typeId" is the extension's field number. The extension can only be
637 // a message type, where "message" contains the encoded bytes of that
638 // message.
639 //
640 // In practice, we will probably never see a MessageSet item in which
641 // the message appears before the type ID, or where either field does not
642 // appear exactly once. However, in theory such cases are valid, so we
643 // should be prepared to accept them.
644
645 int typeId = 0;
646 ByteString rawBytes = null; // If we encounter "message" before "typeId"
647 IBuilder subBuilder = null;
648 FieldDescriptor field = null;
649
650 while (true) {
651 uint tag = input.ReadTag();
652 if (tag == 0) {
653 break;
654 }
655
656 if (tag == WireFormat.MessageSetTag.TypeID) {
657 typeId = input.ReadInt32();
658 // Zero is not a valid type ID.
659 if (typeId != 0) {
660 ExtensionInfo extension = extensionRegistry[type, typeId];
661 if (extension != null) {
662 field = extension.Descriptor;
663 subBuilder = extension.DefaultInstance.WeakCreateBuilderForType();
csharptest7d396f92010-11-08 20:06:46 -0600664 IMessageLite originalMessage = (IMessageLite)builder[field];
Jon Skeet68036862008-10-22 13:30:34 +0100665 if (originalMessage != null) {
666 subBuilder.WeakMergeFrom(originalMessage);
667 }
668 if (rawBytes != null) {
669 // We already encountered the message. Parse it now.
670 // TODO(jonskeet): Check this is okay. It's subtly different from the Java, as it doesn't create an input stream from rawBytes.
671 // In fact, why don't we just call MergeFrom(rawBytes)? And what about the extension registry?
672 subBuilder.WeakMergeFrom(rawBytes.CreateCodedInput());
673 rawBytes = null;
674 }
675 } else {
676 // Unknown extension number. If we already saw data, put it
677 // in rawBytes.
678 if (rawBytes != null) {
679 MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(rawBytes).Build());
680 rawBytes = null;
681 }
682 }
683 }
684 } else if (tag == WireFormat.MessageSetTag.Message) {
685 if (typeId == 0) {
686 // We haven't seen a type ID yet, so we have to store the raw bytes for now.
687 rawBytes = input.ReadBytes();
688 } else if (subBuilder == null) {
689 // We don't know how to parse this. Ignore it.
690 MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(input.ReadBytes()).Build());
691 } else {
692 // We already know the type, so we can parse directly from the input
693 // with no copying. Hooray!
694 input.ReadMessage(subBuilder, extensionRegistry);
695 }
696 } else {
697 // Unknown tag. Skip it.
698 if (!input.SkipField(tag)) {
699 break; // end of group
700 }
701 }
702 }
703
704 input.CheckLastTagWas(WireFormat.MessageSetTag.ItemEnd);
705
706 if (subBuilder != null) {
707 builder[field] = subBuilder.WeakBuild();
708 }
709 }
csharptestd9c59e62010-11-04 19:36:28 -0500710
711 #region IBuilderLite Members
712
713 bool IBuilderLite.IsInitialized {
714 get { return fields != null; }
715 }
716
717 IBuilderLite IBuilderLite.WeakClear() {
718 return Clear();
719 }
720
721 IBuilderLite IBuilderLite.WeakMergeFrom(IMessageLite message) {
722 return MergeFrom((UnknownFieldSet)message);
723 }
724
725 IBuilderLite IBuilderLite.WeakMergeFrom(ByteString data) {
726 return MergeFrom(data);
727 }
728
csharptest80e73b92010-11-05 14:49:08 -0500729 IBuilderLite IBuilderLite.WeakMergeFrom(ByteString data, ExtensionRegistry registry) {
csharptestd9c59e62010-11-04 19:36:28 -0500730 return MergeFrom(data);
731 }
732
733 IBuilderLite IBuilderLite.WeakMergeFrom(CodedInputStream input) {
734 return MergeFrom(input);
735 }
736
csharptest80e73b92010-11-05 14:49:08 -0500737 IBuilderLite IBuilderLite.WeakMergeFrom(CodedInputStream input, ExtensionRegistry registry) {
csharptestd9c59e62010-11-04 19:36:28 -0500738 return MergeFrom(input);
739 }
740
741 IMessageLite IBuilderLite.WeakBuild() {
742 return Build();
743 }
744
745 IMessageLite IBuilderLite.WeakBuildPartial() {
746 return Build();
747 }
748
749 IBuilderLite IBuilderLite.WeakClone() {
750 return Build().WeakToBuilder();
751 }
752
753 IMessageLite IBuilderLite.WeakDefaultInstanceForType {
754 get { return DefaultInstance; }
755 }
756
757 #endregion
Jon Skeet68036862008-10-22 13:30:34 +0100758 }
759 }
760}