Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.ProtobufCS/0.9.1/ProtobufCS/src/ProtocolBuffers/UnknownFieldSet.cs @ 7039

Last change on this file since 7039 was 4068, checked in by swagner, 14 years ago

Sorted usings and removed unused usings in entire solution (#1094)

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