blob: 52401acd2f4f5082a7c8112c764d9adcc2a61ccd [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
csharptestf2925232011-06-11 10:41:57 -0500124 public string ToJson()
125 {
126 Serialization.JsonFormatWriter w = Serialization.JsonFormatWriter.CreateInstance();
127 w.WriteMessage(this);
128 return w.ToString();
129 }
130
131 public string ToXml()
132 {
133 StringWriter w = new StringWriter(new System.Text.StringBuilder(4096));
134 Serialization.XmlFormatWriter.CreateInstance(w).WriteMessage(this);
135 return w.ToString();
136 }
137
138 public string ToXml(string rootElementName)
139 {
140 StringWriter w = new StringWriter(new System.Text.StringBuilder(4096));
141 Serialization.XmlFormatWriter.CreateInstance(w).WriteMessage(rootElementName, this);
142 return w.ToString();
143 }
144
csharptest71f662c2011-05-20 15:15:34 -0500145 public override sealed void PrintTo(TextWriter writer)
146 {
147 TextFormat.Print(this, writer);
148 }
149
150 /// <summary>
151 /// Serializes the message and writes it to the given output stream.
152 /// This does not flush or close the stream.
153 /// </summary>
154 /// <remarks>
155 /// Protocol Buffers are not self-delimiting. Therefore, if you write
156 /// any more data to the stream after the message, you must somehow ensure
157 /// that the parser on the receiving end does not interpret this as being
158 /// part of the protocol message. One way of doing this is by writing the size
159 /// of the message before the data, then making sure you limit the input to
160 /// that size when receiving the data. Alternatively, use WriteDelimitedTo(Stream).
161 /// </remarks>
csharptestffafdaa2011-06-03 12:58:14 -0500162 public override void WriteTo(ICodedOutputStream output)
csharptest71f662c2011-05-20 15:15:34 -0500163 {
164 foreach (KeyValuePair<FieldDescriptor, object> entry in AllFields)
165 {
166 FieldDescriptor field = entry.Key;
167 if (field.IsRepeated)
168 {
169 // We know it's an IList<T>, but not the exact type - so
170 // IEnumerable is the best we can do. (C# generics aren't covariant yet.)
171 IEnumerable valueList = (IEnumerable) entry.Value;
172 if (field.IsPacked)
csharptestffafdaa2011-06-03 12:58:14 -0500173 output.WritePackedArray(field.FieldType, field.FieldNumber, field.Name, valueList);
174 //{
175 // output.WriteTag(field.FieldNumber, WireFormat.WireType.LengthDelimited);
176 // int dataSize = 0;
177 // foreach (object element in valueList)
178 // {
179 // dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element);
180 // }
181 // output.WriteRawVarint32((uint) dataSize);
182 // foreach (object element in valueList)
183 // {
184 // output.WriteFieldNoTag(field.FieldType, element);
185 // }
186 //}
csharptest71f662c2011-05-20 15:15:34 -0500187 else
csharptestffafdaa2011-06-03 12:58:14 -0500188 output.WriteArray(field.FieldType, field.FieldNumber, field.Name, valueList);
189 //{
190 // foreach (object element in valueList)
191 // {
192 // output.WriteField(field.FieldType, field.FieldNumber, field.Name, element);
193 // }
194 //}
csharptest71f662c2011-05-20 15:15:34 -0500195 }
196 else
197 {
csharptest90922db2011-06-03 11:57:47 -0500198 output.WriteField(field.FieldType, field.FieldNumber, field.Name, entry.Value);
csharptest71f662c2011-05-20 15:15:34 -0500199 }
200 }
201
202 UnknownFieldSet unknownFields = UnknownFields;
203 if (DescriptorForType.Options.MessageSetWireFormat)
204 {
205 unknownFields.WriteAsMessageSetTo(output);
206 }
207 else
208 {
209 unknownFields.WriteTo(output);
210 }
211 }
212
213 /// <summary>
214 /// Returns the number of bytes required to encode this message.
215 /// The result is only computed on the first call and memoized after that.
216 /// </summary>
217 public override int SerializedSize
218 {
219 get
220 {
221 if (memoizedSize != null)
222 {
223 return memoizedSize.Value;
224 }
225
226 int size = 0;
227 foreach (KeyValuePair<FieldDescriptor, object> entry in AllFields)
228 {
229 FieldDescriptor field = entry.Key;
230 if (field.IsRepeated)
231 {
232 IEnumerable valueList = (IEnumerable) entry.Value;
233 if (field.IsPacked)
234 {
235 int dataSize = 0;
236 foreach (object element in valueList)
237 {
238 dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element);
239 }
240 size += dataSize;
241 size += CodedOutputStream.ComputeTagSize(field.FieldNumber);
242 size += CodedOutputStream.ComputeRawVarint32Size((uint) dataSize);
243 }
244 else
245 {
246 foreach (object element in valueList)
247 {
248 size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, element);
249 }
250 }
251 }
252 else
253 {
254 size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, entry.Value);
255 }
256 }
257
258 UnknownFieldSet unknownFields = UnknownFields;
259 if (DescriptorForType.Options.MessageSetWireFormat)
260 {
261 size += unknownFields.SerializedSizeAsMessageSet;
262 }
263 else
264 {
265 size += unknownFields.SerializedSize;
266 }
267
268 memoizedSize = size;
269 return size;
270 }
271 }
272
273 /// <summary>
274 /// Compares the specified object with this message for equality.
275 /// Returns true iff the given object is a message of the same type
276 /// (as defined by DescriptorForType) and has identical values
277 /// for all its fields.
278 /// </summary>
279 public override bool Equals(object other)
280 {
281 if (other == this)
282 {
283 return true;
284 }
285 IMessage otherMessage = other as IMessage;
286 if (otherMessage == null || otherMessage.DescriptorForType != DescriptorForType)
287 {
288 return false;
289 }
290 return Dictionaries.Equals(AllFields, otherMessage.AllFields) &&
291 UnknownFields.Equals(otherMessage.UnknownFields);
292 }
293
294 /// <summary>
295 /// Returns the hash code value for this message.
296 /// TODO(jonskeet): Specify the hash algorithm, but better than the Java one!
297 /// </summary>
298 public override int GetHashCode()
299 {
300 int hash = 41;
301 hash = (19*hash) + DescriptorForType.GetHashCode();
302 hash = (53*hash) + Dictionaries.GetHashCode(AllFields);
303 hash = (29*hash) + UnknownFields.GetHashCode();
304 return hash;
305 }
306
307 #region Explicit Members
308
309 IBuilder IMessage.WeakCreateBuilderForType()
310 {
311 return CreateBuilderForType();
312 }
313
314 IBuilder IMessage.WeakToBuilder()
315 {
316 return ToBuilder();
317 }
318
319 IMessage IMessage.WeakDefaultInstanceForType
320 {
321 get { return DefaultInstanceForType; }
322 }
323
324 #endregion
325 }
326}