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