Free cookie consent management tool by TermsFeed Policy Generator

source: tags/3.3.3/HeuristicLab.ExtLibs/HeuristicLab.ProtobufCS/0.9.1/ProtobufCS/src/ProtocolBuffers/FieldSet.cs @ 13398

Last change on this file since 13398 was 3857, checked in by abeham, 14 years ago

#866

  • Added protobuf-csharp-port project source to ExtLibs
File size: 18.9 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;
37using System.Collections.Generic;
38using Google.ProtocolBuffers.Collections;
39using Google.ProtocolBuffers.Descriptors;
40
41namespace Google.ProtocolBuffers {
42  /// <summary>
43  /// A class which represents an arbitrary set of fields of some message type.
44  /// This is used to implement DynamicMessage, and also to represent extensions
45  /// in GeneratedMessage. This class is internal, since outside users should probably
46  /// be using DynamicMessage.
47  ///
48  /// As in the Java implementation, this class goes against the rest of the framework
49  /// in terms of mutability. Instead of having a mutable Builder class and an immutable
50  /// FieldSet class, FieldSet just has a MakeImmutable() method. This is safe so long as
51  /// all callers are careful not to let a mutable FieldSet escape into the open. This would
52  /// be impossible to guarantee if this were a public class, of course.
53  ///
54  /// All repeated fields are stored as IList[object] even
55  /// TODO(jonskeet): Finish this comment!
56  /// </summary>
57  internal sealed class FieldSet {
58
59    private static readonly FieldSet defaultInstance = new FieldSet(new Dictionary<FieldDescriptor, object>()).MakeImmutable();
60
61    private IDictionary<FieldDescriptor, object> fields;
62
63    private FieldSet(IDictionary<FieldDescriptor, object> fields) {
64      this.fields = fields;
65    }
66
67    public static FieldSet CreateInstance() {
68      // Use SortedList to keep fields in the canonical order
69      return new FieldSet(new SortedList<FieldDescriptor, object>());
70    }
71
72    /// <summary>
73    /// Makes this FieldSet immutable, and returns it for convenience. Any
74    /// mutable repeated fields are made immutable, as well as the map itself.
75    /// </summary>
76    internal FieldSet MakeImmutable() {
77      // First check if we have any repeated values
78      bool hasRepeats = false;
79      foreach (object value in fields.Values) {
80        IList<object> list = value as IList<object>;
81        if (list != null && !list.IsReadOnly) {
82          hasRepeats = true;
83          break;
84        }
85      }
86
87      if (hasRepeats) {
88        var tmp = new SortedList<FieldDescriptor, object>();
89        foreach (KeyValuePair<FieldDescriptor, object> entry in fields) {
90          IList<object> list = entry.Value as IList<object>;
91          tmp[entry.Key] = list == null ? entry.Value : Lists.AsReadOnly(list);
92        }
93        fields = tmp;
94      }
95
96      fields = Dictionaries.AsReadOnly(fields);
97
98      return this;
99    }
100
101    /// <summary>
102    /// Returns the default, immutable instance with no fields defined.
103    /// </summary>
104    internal static FieldSet DefaultInstance {
105      get { return defaultInstance; }
106    }
107
108    /// <summary>
109    /// Returns an immutable mapping of fields. Note that although the mapping itself
110    /// is immutable, the entries may not be (i.e. any repeated values are represented by
111    /// mutable lists). The behaviour is not specified if the contents are mutated.
112    /// </summary>
113    internal IDictionary<FieldDescriptor, object> AllFields {
114      get { return Dictionaries.AsReadOnly(fields); }
115    }
116
117    /// <summary>
118    /// See <see cref="IMessage.HasField"/>.
119    /// </summary>
120    public bool HasField(FieldDescriptor field) {
121      if (field.IsRepeated) {
122        throw new ArgumentException("HasField() can only be called on non-repeated fields.");
123      }
124
125      return fields.ContainsKey(field);
126    }
127
128    /// <summary>
129    /// Clears all fields.
130    /// </summary>
131    internal void Clear() {
132      fields.Clear();
133    }
134
135    /// <summary>
136    /// See <see cref="IMessage.Item(FieldDescriptor)"/>
137    /// </summary>
138    /// <remarks>
139    /// If the field is not set, the behaviour when fetching this property varies by field type:
140    /// <list>
141    /// <item>For singular message values, null is returned.</item>
142    /// <item>For singular non-message values, the default value of the field is returned.</item>
143    /// <item>For repeated values, an empty immutable list is returned. This will be compatible
144    /// with IList[object], regardless of the type of the repeated item.</item>
145    /// </list>
146    /// This method returns null if the field is a singular message type
147    /// and is not set; in this case it is up to the caller to fetch the
148    /// message's default instance. For repeated fields of message types,
149    /// an empty collection is returned. For repeated fields of non-message
150    /// types, null is returned.
151    /// <para />
152    /// When setting this property, any list values are copied, and each element is checked
153    /// to ensure it is of an appropriate type.
154    /// </remarks>
155    ///
156    internal object this[FieldDescriptor field] {
157      get {
158        object result;
159        if (fields.TryGetValue(field, out result)) {
160          return result;
161        }
162        if (field.MappedType == MappedType.Message) {
163          if (field.IsRepeated) {
164            return new List<object>();
165          } else {
166            return null;
167          }
168        }
169        return field.DefaultValue;
170      }
171      set {
172        if (field.IsRepeated) {
173          List<object> list = value as List<object>;
174          if (list == null) {
175            throw new ArgumentException("Wrong object type used with protocol message reflection.");
176          }
177
178          // Wrap the contents in a new list so that the caller cannot change
179          // the list's contents after setting it.
180          List<object> newList = new List<object>(list);
181          foreach (object element in newList) {
182            VerifyType(field, element);
183          }
184          value = newList;
185        }
186        else {
187          VerifyType(field, value);
188        }
189        fields[field] = value;
190      }
191    }
192
193    /// <summary>
194    /// See <see cref="IMessage.Item(FieldDescriptor,int)" />
195    /// </summary>
196    internal object this[FieldDescriptor field, int index] {
197      get {
198        if (!field.IsRepeated) {
199          throw new ArgumentException("Indexer specifying field and index can only be called on repeated fields.");
200        }
201
202        return ((IList<object>) this[field])[index];
203      }
204      set {
205        if (!field.IsRepeated) {
206          throw new ArgumentException("Indexer specifying field and index can only be called on repeated fields.");
207        }
208        VerifyType(field, value);
209        object list;
210        if (!fields.TryGetValue(field, out list)) {
211          throw new ArgumentOutOfRangeException();
212        }
213        ((IList<object>) list)[index] = value;
214      }
215    }
216
217    /// <summary>
218    /// See <see cref="IBuilder{TMessage, TBuilder}.AddRepeatedField" />
219    /// </summary>
220    internal void AddRepeatedField(FieldDescriptor field, object value) {
221      if (!field.IsRepeated) {
222        throw new ArgumentException("AddRepeatedField can only be called on repeated fields.");
223      }
224      VerifyType(field, value);
225      object list;
226      if (!fields.TryGetValue(field, out list)) {
227        list = new List<object>();
228        fields[field] = list;
229      }
230      ((IList<object>) list).Add(value);
231    }
232
233    /// <summary>
234    /// Returns an enumerator for the field map. Used to write the fields out.
235    /// </summary>
236    internal IEnumerator<KeyValuePair<FieldDescriptor, object>> GetEnumerator() {
237      return fields.GetEnumerator();
238    }
239
240    /// <summary>
241    /// See <see cref="IMessage.IsInitialized" />
242    /// </summary>
243    /// <remarks>
244    /// Since FieldSet itself does not have any way of knowing about
245    /// required fields that aren't actually present in the set, it is up
246    /// to the caller to check for genuinely required fields. This property
247    /// merely checks that any messages present are themselves initialized.
248    /// </remarks>
249    internal bool IsInitialized {
250      get {
251        foreach (KeyValuePair<FieldDescriptor, object> entry in fields) {
252          FieldDescriptor field = entry.Key;
253          if (field.MappedType == MappedType.Message) {
254            if (field.IsRepeated) {
255              foreach(IMessage message in (IEnumerable) entry.Value) {
256                if (!message.IsInitialized) {
257                  return false;
258                }
259              }
260            } else {
261              if (!((IMessage) entry.Value).IsInitialized) {
262                return false;
263              }
264            }
265          }
266        }
267        return true;
268      }
269    }
270
271    /// <summary>
272    /// Verifies whether all the required fields in the specified message
273    /// descriptor are present in this field set, as well as whether
274    /// all the embedded messages are themselves initialized.
275    /// </summary>
276    internal bool IsInitializedWithRespectTo(MessageDescriptor type) {
277      foreach (FieldDescriptor field in type.Fields) {
278        if (field.IsRequired && !HasField(field)) {
279          return false;
280        }
281      }
282      return IsInitialized;
283    }
284
285    /// <summary>
286    /// See <see cref="IBuilder{TMessage, TBuilder}.ClearField" />
287    /// </summary>
288    public void ClearField(FieldDescriptor field) {
289      fields.Remove(field);
290    }
291
292    /// <summary>
293    /// See <see cref="IMessage.GetRepeatedFieldCount" />
294    /// </summary>
295    public int GetRepeatedFieldCount(FieldDescriptor field) {
296      if (!field.IsRepeated) {
297        throw new ArgumentException("GetRepeatedFieldCount() can only be called on repeated fields.");
298      }
299
300      return ((IList<object>) this[field]).Count;
301    }
302
303    /// <summary>
304    /// Implementation of both <c>MergeFrom</c> methods.
305    /// </summary>
306    /// <param name="otherFields"></param>
307    private void MergeFields(IEnumerable<KeyValuePair<FieldDescriptor, object>> otherFields) {
308      // Note:  We don't attempt to verify that other's fields have valid
309      //   types.  Doing so would be a losing battle.  We'd have to verify
310      //   all sub-messages as well, and we'd have to make copies of all of
311      //   them to insure that they don't change after verification (since
312      //   the IMessage interface itself cannot enforce immutability of
313      //   implementations).
314      // TODO(jonskeet):  Provide a function somewhere called MakeDeepCopy()
315      //   which allows people to make secure deep copies of messages.
316
317      foreach (KeyValuePair<FieldDescriptor, object> entry in otherFields) {
318        FieldDescriptor field = entry.Key;
319        object existingValue;
320        fields.TryGetValue(field, out existingValue);
321        if (field.IsRepeated) {
322          if (existingValue == null) {
323            existingValue = new List<object>();
324            fields[field] = existingValue;
325          }
326          IList<object> list = (IList<object>) existingValue;
327          foreach (object otherValue in (IEnumerable) entry.Value) {
328            list.Add(otherValue);
329          }
330        } else if (field.MappedType == MappedType.Message && existingValue != null) {
331          IMessage existingMessage = (IMessage)existingValue;
332          IMessage merged = existingMessage.WeakToBuilder()
333              .WeakMergeFrom((IMessage) entry.Value)
334              .WeakBuild();
335          this[field] = merged;
336        } else {
337          this[field] = entry.Value;
338        }
339      }
340    }
341
342    /// <summary>
343    /// See <see cref="IBuilder{TMessage, TBuilder}.MergeFrom(IMessage)" />
344    /// </summary>
345    public void MergeFrom(IMessage other) {
346      MergeFields(other.AllFields);
347    }
348
349    /// <summary>
350    /// Like <see cref="MergeFrom(IMessage)"/>, but merges from another <c>FieldSet</c>.
351    /// </summary>
352    public void MergeFrom(FieldSet other) {
353      MergeFields(other.fields);
354    }
355
356    /// <summary>
357    /// See <see cref="IMessage.WriteTo(CodedOutputStream)" />.
358    /// </summary>
359    public void WriteTo(CodedOutputStream output) {
360      foreach (KeyValuePair<FieldDescriptor, object> entry in fields) {
361        WriteField(entry.Key, entry.Value, output);
362      }
363    }
364
365    /// <summary>
366    /// Writes a single field to a CodedOutputStream.
367    /// </summary>
368    public void WriteField(FieldDescriptor field, Object value, CodedOutputStream output) {
369      if (field.IsExtension && field.ContainingType.Options.MessageSetWireFormat) {
370        output.WriteMessageSetExtension(field.FieldNumber, (IMessage) value);
371      } else {
372        if (field.IsRepeated) {
373          IEnumerable valueList = (IEnumerable) value;
374          if (field.IsPacked) {
375            output.WriteTag(field.FieldNumber, WireFormat.WireType.LengthDelimited);
376            // Compute the total data size so the length can be written.
377            int dataSize = 0;
378            foreach (object element in valueList) {
379              dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element);
380            }
381            output.WriteRawVarint32((uint)dataSize);
382            // Write the data itself, without any tags.
383            foreach (object element in valueList) {
384              output.WriteFieldNoTag(field.FieldType, element);
385            }
386          } else {
387            foreach (object element in valueList) {
388              output.WriteField(field.FieldType, field.FieldNumber, element);
389            }
390          }
391        } else {
392          output.WriteField(field.FieldType, field.FieldNumber, value);
393        }
394      }
395    }
396
397    /// <summary>
398    /// See <see cref="IMessage.SerializedSize" />. It's up to the caller to
399    /// cache the resulting size if desired.
400    /// </summary>
401    public int SerializedSize {
402      get {
403        int size = 0;
404        foreach (KeyValuePair<FieldDescriptor, object> entry in fields) {
405          FieldDescriptor field = entry.Key;
406          object value = entry.Value;
407
408          if (field.IsExtension && field.ContainingType.Options.MessageSetWireFormat) {
409            size += CodedOutputStream.ComputeMessageSetExtensionSize(field.FieldNumber, (IMessage)value);
410          } else {
411            if (field.IsRepeated) {
412              IEnumerable valueList = (IEnumerable)value;
413              if (field.IsPacked) {
414                int dataSize = 0;
415                foreach (object element in valueList) {
416                  dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element);
417                }
418                size += dataSize + CodedOutputStream.ComputeTagSize(field.FieldNumber) + CodedOutputStream.ComputeRawVarint32Size((uint)dataSize);
419              } else {
420                foreach (object element in valueList) {
421                  size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, element);
422                }
423              }
424            } else {
425              size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, value);
426            }
427          }
428        }
429        return size;
430      }
431    }
432
433    /// <summary>
434    /// Verifies that the given object is of the correct type to be a valid
435    /// value for the given field.
436    /// </summary>
437    /// <remarks>
438    /// For repeated fields, this checks if the object is of the right
439    /// element type, not whether it's a list.
440    /// </remarks>
441    /// <exception cref="ArgumentException">The value is not of the right type.</exception>
442    /// <exception cref="ArgumentNullException">The value is null.</exception>
443    private static void VerifyType(FieldDescriptor field, object value) {
444      ThrowHelper.ThrowIfNull(value, "value");
445      bool isValid = false;
446      switch (field.MappedType) {
447        case MappedType.Int32:       isValid = value is int;    break;
448        case MappedType.Int64:       isValid = value is long;   break;
449        case MappedType.UInt32:      isValid = value is uint;   break;
450        case MappedType.UInt64:      isValid = value is ulong;  break;
451        case MappedType.Single:      isValid = value is float;  break;
452        case MappedType.Double:      isValid = value is double; break;
453        case MappedType.Boolean:     isValid = value is bool;   break;
454        case MappedType.String:      isValid = value is string; break;
455        case MappedType.ByteString:  isValid = value is ByteString; break;       
456        case MappedType.Enum:
457          EnumValueDescriptor enumValue = value as EnumValueDescriptor;
458          isValid = enumValue != null && enumValue.EnumDescriptor == field.EnumType;
459          break;
460        case MappedType.Message:
461          IMessage messageValue = value as IMessage;
462          isValid = messageValue != null && messageValue.DescriptorForType == field.MessageType;
463          break;
464      }
465
466      if (!isValid) {
467        // When chaining calls to SetField(), it can be hard to tell from
468        // the stack trace which exact call failed, since the whole chain is
469        // considered one line of code.  So, let's make sure to include the
470        // field name and other useful info in the exception.
471        throw new ArgumentException("Wrong object type used with protocol message reflection. "
472            + "Message type \"" + field.ContainingType.FullName
473            + "\", field \"" + (field.IsExtension ? field.FullName : field.Name)
474            + "\", value was type \"" + value.GetType().Name + "\".");
475      }
476    }     
477  }
478}
Note: See TracBrowser for help on using the repository browser.