blob: a4d980451f8aa4674752a01503f772c3d9ee326b [file] [log] [blame]
csharptest71f662c2011-05-20 15:15:34 -05001#region Copyright notice and license
2
3// Protocol Buffers - Google's data interchange format
4// Copyright 2008 Google Inc. All rights reserved.
5// http://github.com/jskeet/dotnet-protobufs/
6// Original C++/Java/Python code:
7// http://code.google.com/p/protobuf/
8//
9// Redistribution and use in source and binary forms, with or without
10// modification, are permitted provided that the following conditions are
11// met:
12//
13// * Redistributions of source code must retain the above copyright
14// notice, this list of conditions and the following disclaimer.
15// * Redistributions in binary form must reproduce the above
16// copyright notice, this list of conditions and the following disclaimer
17// in the documentation and/or other materials provided with the
18// distribution.
19// * Neither the name of Google Inc. nor the names of its
20// contributors may be used to endorse or promote products derived from
21// this software without specific prior written permission.
22//
23// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
35#endregion
36
37using System.Collections;
38using System.Collections.Generic;
39using System.IO;
40using Google.ProtocolBuffers.Collections;
41using Google.ProtocolBuffers.Descriptors;
42
43namespace Google.ProtocolBuffers
44{
45 /// <summary>
46 /// Implementation of the non-generic IMessage interface as far as possible.
47 /// </summary>
48 public abstract class AbstractMessage<TMessage, TBuilder> : AbstractMessageLite<TMessage, TBuilder>,
49 IMessage<TMessage, TBuilder>
50 where TMessage : AbstractMessage<TMessage, TBuilder>
51 where TBuilder : AbstractBuilder<TMessage, TBuilder>
52 {
53 /// <summary>
54 /// The serialized size if it's already been computed, or null
55 /// if we haven't computed it yet.
56 /// </summary>
57 private int? memoizedSize = null;
58
59 #region Unimplemented members of IMessage
60
61 public abstract MessageDescriptor DescriptorForType { get; }
62 public abstract IDictionary<FieldDescriptor, object> AllFields { get; }
63 public abstract bool HasField(FieldDescriptor field);
64 public abstract object this[FieldDescriptor field] { get; }
65 public abstract int GetRepeatedFieldCount(FieldDescriptor field);
66 public abstract object this[FieldDescriptor field, int index] { get; }
67 public abstract UnknownFieldSet UnknownFields { get; }
68
69 #endregion
70
71 /// <summary>
72 /// Returns true iff all required fields in the message and all embedded
73 /// messages are set.
74 /// </summary>
75 public override bool IsInitialized
76 {
77 get
78 {
79 // Check that all required fields are present.
80 foreach (FieldDescriptor field in DescriptorForType.Fields)
81 {
82 if (field.IsRequired && !HasField(field))
83 {
84 return false;
85 }
86 }
87
88 // Check that embedded messages are initialized.
89 foreach (KeyValuePair<FieldDescriptor, object> entry in AllFields)
90 {
91 FieldDescriptor field = entry.Key;
92 if (field.MappedType == MappedType.Message)
93 {
94 if (field.IsRepeated)
95 {
96 // We know it's an IList<T>, but not the exact type - so
97 // IEnumerable is the best we can do. (C# generics aren't covariant yet.)
98 foreach (IMessageLite element in (IEnumerable) entry.Value)
99 {
100 if (!element.IsInitialized)
101 {
102 return false;
103 }
104 }
105 }
106 else
107 {
108 if (!((IMessageLite) entry.Value).IsInitialized)
109 {
110 return false;
111 }
112 }
113 }
114 }
115 return true;
116 }
117 }
118
119 public override sealed string ToString()
120 {
121 return TextFormat.PrintToString(this);
122 }
123
124 public override sealed void PrintTo(TextWriter writer)
125 {
126 TextFormat.Print(this, writer);
127 }
128
129 /// <summary>
130 /// Serializes the message and writes it to the given output stream.
131 /// This does not flush or close the stream.
132 /// </summary>
133 /// <remarks>
134 /// Protocol Buffers are not self-delimiting. Therefore, if you write
135 /// any more data to the stream after the message, you must somehow ensure
136 /// that the parser on the receiving end does not interpret this as being
137 /// part of the protocol message. One way of doing this is by writing the size
138 /// of the message before the data, then making sure you limit the input to
139 /// that size when receiving the data. Alternatively, use WriteDelimitedTo(Stream).
140 /// </remarks>
141 public override void WriteTo(CodedOutputStream output)
142 {
143 foreach (KeyValuePair<FieldDescriptor, object> entry in AllFields)
144 {
145 FieldDescriptor field = entry.Key;
146 if (field.IsRepeated)
147 {
148 // We know it's an IList<T>, but not the exact type - so
149 // IEnumerable is the best we can do. (C# generics aren't covariant yet.)
150 IEnumerable valueList = (IEnumerable) entry.Value;
151 if (field.IsPacked)
152 {
153 output.WriteTag(field.FieldNumber, WireFormat.WireType.LengthDelimited);
154 int dataSize = 0;
155 foreach (object element in valueList)
156 {
157 dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element);
158 }
159 output.WriteRawVarint32((uint) dataSize);
160 foreach (object element in valueList)
161 {
162 output.WriteFieldNoTag(field.FieldType, element);
163 }
164 }
165 else
166 {
167 foreach (object element in valueList)
168 {
169 output.WriteField(field.FieldType, field.FieldNumber, element);
170 }
171 }
172 }
173 else
174 {
175 output.WriteField(field.FieldType, field.FieldNumber, entry.Value);
176 }
177 }
178
179 UnknownFieldSet unknownFields = UnknownFields;
180 if (DescriptorForType.Options.MessageSetWireFormat)
181 {
182 unknownFields.WriteAsMessageSetTo(output);
183 }
184 else
185 {
186 unknownFields.WriteTo(output);
187 }
188 }
189
190 /// <summary>
191 /// Returns the number of bytes required to encode this message.
192 /// The result is only computed on the first call and memoized after that.
193 /// </summary>
194 public override int SerializedSize
195 {
196 get
197 {
198 if (memoizedSize != null)
199 {
200 return memoizedSize.Value;
201 }
202
203 int size = 0;
204 foreach (KeyValuePair<FieldDescriptor, object> entry in AllFields)
205 {
206 FieldDescriptor field = entry.Key;
207 if (field.IsRepeated)
208 {
209 IEnumerable valueList = (IEnumerable) entry.Value;
210 if (field.IsPacked)
211 {
212 int dataSize = 0;
213 foreach (object element in valueList)
214 {
215 dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element);
216 }
217 size += dataSize;
218 size += CodedOutputStream.ComputeTagSize(field.FieldNumber);
219 size += CodedOutputStream.ComputeRawVarint32Size((uint) dataSize);
220 }
221 else
222 {
223 foreach (object element in valueList)
224 {
225 size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, element);
226 }
227 }
228 }
229 else
230 {
231 size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, entry.Value);
232 }
233 }
234
235 UnknownFieldSet unknownFields = UnknownFields;
236 if (DescriptorForType.Options.MessageSetWireFormat)
237 {
238 size += unknownFields.SerializedSizeAsMessageSet;
239 }
240 else
241 {
242 size += unknownFields.SerializedSize;
243 }
244
245 memoizedSize = size;
246 return size;
247 }
248 }
249
250 /// <summary>
251 /// Compares the specified object with this message for equality.
252 /// Returns true iff the given object is a message of the same type
253 /// (as defined by DescriptorForType) and has identical values
254 /// for all its fields.
255 /// </summary>
256 public override bool Equals(object other)
257 {
258 if (other == this)
259 {
260 return true;
261 }
262 IMessage otherMessage = other as IMessage;
263 if (otherMessage == null || otherMessage.DescriptorForType != DescriptorForType)
264 {
265 return false;
266 }
267 return Dictionaries.Equals(AllFields, otherMessage.AllFields) &&
268 UnknownFields.Equals(otherMessage.UnknownFields);
269 }
270
271 /// <summary>
272 /// Returns the hash code value for this message.
273 /// TODO(jonskeet): Specify the hash algorithm, but better than the Java one!
274 /// </summary>
275 public override int GetHashCode()
276 {
277 int hash = 41;
278 hash = (19*hash) + DescriptorForType.GetHashCode();
279 hash = (53*hash) + Dictionaries.GetHashCode(AllFields);
280 hash = (29*hash) + UnknownFields.GetHashCode();
281 return hash;
282 }
283
284 #region Explicit Members
285
286 IBuilder IMessage.WeakCreateBuilderForType()
287 {
288 return CreateBuilderForType();
289 }
290
291 IBuilder IMessage.WeakToBuilder()
292 {
293 return ToBuilder();
294 }
295
296 IMessage IMessage.WeakDefaultInstanceForType
297 {
298 get { return DefaultInstanceForType; }
299 }
300
301 #endregion
302 }
303}