blob: 13443b1cbc78d58126cb6fc28056cea47b563cee [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;
csharptest74c5e0c2011-07-14 13:06:22 -050040using System.Text;
csharptest71f662c2011-05-20 15:15:34 -050041using Google.ProtocolBuffers.Collections;
42using Google.ProtocolBuffers.Descriptors;
43
44namespace Google.ProtocolBuffers
45{
46 /// <summary>
47 /// Implementation of the non-generic IMessage interface as far as possible.
48 /// </summary>
49 public abstract class AbstractMessage<TMessage, TBuilder> : AbstractMessageLite<TMessage, TBuilder>,
50 IMessage<TMessage, TBuilder>
51 where TMessage : AbstractMessage<TMessage, TBuilder>
52 where TBuilder : AbstractBuilder<TMessage, TBuilder>
53 {
54 /// <summary>
55 /// The serialized size if it's already been computed, or null
56 /// if we haven't computed it yet.
57 /// </summary>
58 private int? memoizedSize = null;
59
60 #region Unimplemented members of IMessage
61
62 public abstract MessageDescriptor DescriptorForType { get; }
63 public abstract IDictionary<FieldDescriptor, object> AllFields { get; }
64 public abstract bool HasField(FieldDescriptor field);
65 public abstract object this[FieldDescriptor field] { get; }
66 public abstract int GetRepeatedFieldCount(FieldDescriptor field);
67 public abstract object this[FieldDescriptor field, int index] { get; }
68 public abstract UnknownFieldSet UnknownFields { get; }
69
70 #endregion
71
72 /// <summary>
73 /// Returns true iff all required fields in the message and all embedded
74 /// messages are set.
75 /// </summary>
76 public override bool IsInitialized
77 {
78 get
79 {
80 // Check that all required fields are present.
81 foreach (FieldDescriptor field in DescriptorForType.Fields)
82 {
83 if (field.IsRequired && !HasField(field))
84 {
85 return false;
86 }
87 }
88
89 // Check that embedded messages are initialized.
90 foreach (KeyValuePair<FieldDescriptor, object> entry in AllFields)
91 {
92 FieldDescriptor field = entry.Key;
93 if (field.MappedType == MappedType.Message)
94 {
95 if (field.IsRepeated)
96 {
97 // We know it's an IList<T>, but not the exact type - so
98 // IEnumerable is the best we can do. (C# generics aren't covariant yet.)
99 foreach (IMessageLite element in (IEnumerable) entry.Value)
100 {
101 if (!element.IsInitialized)
102 {
103 return false;
104 }
105 }
106 }
107 else
108 {
109 if (!((IMessageLite) entry.Value).IsInitialized)
110 {
111 return false;
112 }
113 }
114 }
115 }
116 return true;
117 }
118 }
119
120 public override sealed string ToString()
121 {
122 return TextFormat.PrintToString(this);
123 }
124
125 public override sealed void PrintTo(TextWriter writer)
126 {
127 TextFormat.Print(this, writer);
128 }
129
130 /// <summary>
131 /// Serializes the message and writes it to the given output stream.
132 /// This does not flush or close the stream.
133 /// </summary>
134 /// <remarks>
135 /// Protocol Buffers are not self-delimiting. Therefore, if you write
136 /// any more data to the stream after the message, you must somehow ensure
137 /// that the parser on the receiving end does not interpret this as being
138 /// part of the protocol message. One way of doing this is by writing the size
139 /// of the message before the data, then making sure you limit the input to
140 /// that size when receiving the data. Alternatively, use WriteDelimitedTo(Stream).
141 /// </remarks>
csharptestffafdaa2011-06-03 12:58:14 -0500142 public override void WriteTo(ICodedOutputStream output)
csharptest71f662c2011-05-20 15:15:34 -0500143 {
144 foreach (KeyValuePair<FieldDescriptor, object> entry in AllFields)
145 {
146 FieldDescriptor field = entry.Key;
147 if (field.IsRepeated)
148 {
149 // We know it's an IList<T>, but not the exact type - so
150 // IEnumerable is the best we can do. (C# generics aren't covariant yet.)
151 IEnumerable valueList = (IEnumerable) entry.Value;
152 if (field.IsPacked)
csharptest74c5e0c2011-07-14 13:06:22 -0500153 {
csharptestffafdaa2011-06-03 12:58:14 -0500154 output.WritePackedArray(field.FieldType, field.FieldNumber, field.Name, valueList);
csharptest74c5e0c2011-07-14 13:06:22 -0500155 }
csharptest71f662c2011-05-20 15:15:34 -0500156 else
csharptest74c5e0c2011-07-14 13:06:22 -0500157 {
csharptestffafdaa2011-06-03 12:58:14 -0500158 output.WriteArray(field.FieldType, field.FieldNumber, field.Name, valueList);
csharptest74c5e0c2011-07-14 13:06:22 -0500159 }
csharptest71f662c2011-05-20 15:15:34 -0500160 }
161 else
162 {
csharptest90922db2011-06-03 11:57:47 -0500163 output.WriteField(field.FieldType, field.FieldNumber, field.Name, entry.Value);
csharptest71f662c2011-05-20 15:15:34 -0500164 }
165 }
166
167 UnknownFieldSet unknownFields = UnknownFields;
168 if (DescriptorForType.Options.MessageSetWireFormat)
169 {
170 unknownFields.WriteAsMessageSetTo(output);
171 }
172 else
173 {
174 unknownFields.WriteTo(output);
175 }
176 }
177
178 /// <summary>
179 /// Returns the number of bytes required to encode this message.
180 /// The result is only computed on the first call and memoized after that.
181 /// </summary>
182 public override int SerializedSize
183 {
184 get
185 {
186 if (memoizedSize != null)
187 {
188 return memoizedSize.Value;
189 }
190
191 int size = 0;
192 foreach (KeyValuePair<FieldDescriptor, object> entry in AllFields)
193 {
194 FieldDescriptor field = entry.Key;
195 if (field.IsRepeated)
196 {
197 IEnumerable valueList = (IEnumerable) entry.Value;
198 if (field.IsPacked)
199 {
200 int dataSize = 0;
201 foreach (object element in valueList)
202 {
203 dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element);
204 }
205 size += dataSize;
206 size += CodedOutputStream.ComputeTagSize(field.FieldNumber);
207 size += CodedOutputStream.ComputeRawVarint32Size((uint) dataSize);
208 }
209 else
210 {
211 foreach (object element in valueList)
212 {
213 size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, element);
214 }
215 }
216 }
217 else
218 {
219 size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, entry.Value);
220 }
221 }
222
223 UnknownFieldSet unknownFields = UnknownFields;
224 if (DescriptorForType.Options.MessageSetWireFormat)
225 {
226 size += unknownFields.SerializedSizeAsMessageSet;
227 }
228 else
229 {
230 size += unknownFields.SerializedSize;
231 }
232
233 memoizedSize = size;
234 return size;
235 }
236 }
237
238 /// <summary>
239 /// Compares the specified object with this message for equality.
240 /// Returns true iff the given object is a message of the same type
241 /// (as defined by DescriptorForType) and has identical values
242 /// for all its fields.
243 /// </summary>
244 public override bool Equals(object other)
245 {
246 if (other == this)
247 {
248 return true;
249 }
250 IMessage otherMessage = other as IMessage;
251 if (otherMessage == null || otherMessage.DescriptorForType != DescriptorForType)
252 {
253 return false;
254 }
255 return Dictionaries.Equals(AllFields, otherMessage.AllFields) &&
256 UnknownFields.Equals(otherMessage.UnknownFields);
257 }
258
259 /// <summary>
260 /// Returns the hash code value for this message.
261 /// TODO(jonskeet): Specify the hash algorithm, but better than the Java one!
262 /// </summary>
263 public override int GetHashCode()
264 {
265 int hash = 41;
266 hash = (19*hash) + DescriptorForType.GetHashCode();
267 hash = (53*hash) + Dictionaries.GetHashCode(AllFields);
268 hash = (29*hash) + UnknownFields.GetHashCode();
269 return hash;
270 }
271
272 #region Explicit Members
273
274 IBuilder IMessage.WeakCreateBuilderForType()
275 {
276 return CreateBuilderForType();
277 }
278
279 IBuilder IMessage.WeakToBuilder()
280 {
281 return ToBuilder();
282 }
283
284 IMessage IMessage.WeakDefaultInstanceForType
285 {
286 get { return DefaultInstanceForType; }
287 }
288
289 #endregion
290 }
291}