blob: 8c8525174b1d67a8a124469fe7289014be4485a9 [file] [log] [blame]
Jon Skeet60c059b2008-10-23 21:17:56 +01001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// http://github.com/jskeet/dotnet-protobufs/
4// Original C++/Java/Python code:
Jon Skeet68036862008-10-22 13:30:34 +01005// http://code.google.com/p/protobuf/
6//
Jon Skeet60c059b2008-10-23 21:17:56 +01007// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are
9// met:
Jon Skeet68036862008-10-22 13:30:34 +010010//
Jon Skeet60c059b2008-10-23 21:17:56 +010011// * Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13// * Redistributions in binary form must reproduce the above
14// copyright notice, this list of conditions and the following disclaimer
15// in the documentation and/or other materials provided with the
16// distribution.
17// * Neither the name of Google Inc. nor the names of its
18// contributors may be used to endorse or promote products derived from
19// this software without specific prior written permission.
Jon Skeet68036862008-10-22 13:30:34 +010020//
Jon Skeet60c059b2008-10-23 21:17:56 +010021// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Jon Skeet68036862008-10-22 13:30:34 +010032using System;
33using System.Collections;
34using System.Collections.Generic;
35using System.IO;
36using Google.ProtocolBuffers.Descriptors;
37
38namespace Google.ProtocolBuffers {
39 /// <summary>
40 /// Implementation of the non-generic IMessage interface as far as possible.
41 /// </summary>
42 public abstract class AbstractBuilder<TMessage, TBuilder> : IBuilder<TMessage, TBuilder>
43 where TMessage : AbstractMessage<TMessage, TBuilder>
44 where TBuilder : AbstractBuilder<TMessage, TBuilder> {
45
46 protected abstract TBuilder ThisBuilder { get; }
47
48 #region Unimplemented members of IBuilder
49 public abstract UnknownFieldSet UnknownFields { get; set; }
50 public abstract TBuilder MergeFrom(TMessage other);
51 public abstract bool IsInitialized { get; }
52 public abstract IDictionary<FieldDescriptor, object> AllFields { get; }
53 public abstract object this[FieldDescriptor field] { get; set; }
54 public abstract MessageDescriptor DescriptorForType { get; }
55 public abstract int GetRepeatedFieldCount(FieldDescriptor field);
56 public abstract object this[FieldDescriptor field, int index] { get; set; }
57 public abstract bool HasField(FieldDescriptor field);
58 public abstract TMessage Build();
59 public abstract TMessage BuildPartial();
60 public abstract TBuilder Clone();
61 public abstract TMessage DefaultInstanceForType { get; }
62 public abstract IBuilder CreateBuilderForField(FieldDescriptor field);
63 public abstract TBuilder ClearField(FieldDescriptor field);
64 public abstract TBuilder AddRepeatedField(FieldDescriptor field, object value);
65 #endregion
66
67 #region Implementation of methods which don't require type parameter information
68 public IMessage WeakBuild() {
69 return Build();
70 }
71
72 public IBuilder WeakAddRepeatedField(FieldDescriptor field, object value) {
73 return AddRepeatedField(field, value);
74 }
75
76 public IBuilder WeakClear() {
77 return Clear();
78 }
79
80 public IBuilder WeakMergeFrom(IMessage message) {
81 return MergeFrom(message);
82 }
83
84 public IBuilder WeakMergeFrom(CodedInputStream input) {
85 return MergeFrom(input);
86 }
87
88 public IBuilder WeakMergeFrom(CodedInputStream input, ExtensionRegistry registry) {
89 return MergeFrom(input, registry);
90 }
91
92 public IBuilder WeakMergeFrom(ByteString data) {
93 return MergeFrom(data);
94 }
95
96 public IBuilder WeakMergeFrom(ByteString data, ExtensionRegistry registry) {
97 return MergeFrom(data, registry);
98 }
99
100 public IMessage WeakBuildPartial() {
101 return BuildPartial();
102 }
103
104 public IBuilder WeakClone() {
105 return Clone();
106 }
107
108 public IMessage WeakDefaultInstanceForType {
109 get { return DefaultInstanceForType; }
110 }
111
112 public IBuilder WeakClearField(FieldDescriptor field) {
113 return ClearField(field);
114 }
115 #endregion
116
117 public TBuilder SetUnknownFields(UnknownFieldSet fields) {
118 UnknownFields = fields;
119 return ThisBuilder;
120 }
121
122 public virtual TBuilder Clear() {
123 foreach(FieldDescriptor field in AllFields.Keys) {
124 ClearField(field);
125 }
126 return ThisBuilder;
127 }
128
129 public virtual TBuilder MergeFrom(IMessage other) {
130 if (other.DescriptorForType != DescriptorForType) {
131 throw new ArgumentException("MergeFrom(IMessage) can only merge messages of the same type.");
132 }
133
134 // Note: We don't attempt to verify that other's fields have valid
135 // types. Doing so would be a losing battle. We'd have to verify
136 // all sub-messages as well, and we'd have to make copies of all of
137 // them to insure that they don't change after verification (since
138 // the Message interface itself cannot enforce immutability of
139 // implementations).
140 // TODO(jonskeet): Provide a function somewhere called MakeDeepCopy()
141 // which allows people to make secure deep copies of messages.
142 foreach (KeyValuePair<FieldDescriptor, object> entry in other.AllFields) {
143 FieldDescriptor field = entry.Key;
144 if (field.IsRepeated) {
145 // Concatenate repeated fields
146 foreach (object element in (IEnumerable) entry.Value) {
147 AddRepeatedField(field, element);
148 }
149 } else if (field.MappedType == MappedType.Message) {
150 // Merge singular messages
151 IMessage existingValue = (IMessage) this[field];
152 if (existingValue == existingValue.WeakDefaultInstanceForType) {
153 this[field] = entry.Value;
154 } else {
155 this[field] = existingValue.WeakCreateBuilderForType()
156 .WeakMergeFrom(existingValue)
157 .WeakMergeFrom((IMessage) entry.Value)
158 .WeakBuild();
159 }
160 } else {
161 // Overwrite simple values
162 this[field] = entry.Value;
163 }
164 }
165 return ThisBuilder;
166 }
167
168 public virtual TBuilder MergeFrom(CodedInputStream input) {
169 return MergeFrom(input, ExtensionRegistry.Empty);
170 }
171
172 public virtual TBuilder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry) {
173 UnknownFieldSet.Builder unknownFields = UnknownFieldSet.CreateBuilder(UnknownFields);
174 unknownFields.MergeFrom(input, extensionRegistry, this);
175 UnknownFields = unknownFields.Build();
176 return ThisBuilder;
177 }
178
179 public virtual TBuilder MergeUnknownFields(UnknownFieldSet unknownFields) {
180 UnknownFields = UnknownFieldSet.CreateBuilder(UnknownFields)
181 .MergeFrom(unknownFields)
182 .Build();
183 return ThisBuilder;
184 }
185
186 public virtual TBuilder MergeFrom(ByteString data) {
187 CodedInputStream input = data.CreateCodedInput();
188 MergeFrom(input);
189 input.CheckLastTagWas(0);
190 return ThisBuilder;
191 }
192
193 public virtual TBuilder MergeFrom(ByteString data, ExtensionRegistry extensionRegistry) {
194 CodedInputStream input = data.CreateCodedInput();
195 MergeFrom(input, extensionRegistry);
196 input.CheckLastTagWas(0);
197 return ThisBuilder;
198 }
199
200 public virtual TBuilder MergeFrom(byte[] data) {
201 CodedInputStream input = CodedInputStream.CreateInstance(data);
202 MergeFrom(input);
203 input.CheckLastTagWas(0);
204 return ThisBuilder;
205 }
206
207 public virtual TBuilder MergeFrom(byte[] data, ExtensionRegistry extensionRegistry) {
208 CodedInputStream input = CodedInputStream.CreateInstance(data);
209 MergeFrom(input, extensionRegistry);
210 input.CheckLastTagWas(0);
211 return ThisBuilder;
212 }
213
214 public virtual TBuilder MergeFrom(Stream input) {
215 CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
216 MergeFrom(codedInput);
217 codedInput.CheckLastTagWas(0);
218 return ThisBuilder;
219 }
220
221 public virtual TBuilder MergeFrom(Stream input, ExtensionRegistry extensionRegistry) {
222 CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
223 MergeFrom(codedInput, extensionRegistry);
224 codedInput.CheckLastTagWas(0);
225 return ThisBuilder;
226 }
227
Jon Skeet2e6dc122009-05-29 06:34:52 +0100228 public TBuilder MergeDelimitedFrom(Stream input, ExtensionRegistry extensionRegistry) {
Jon Skeetc298c892009-05-30 10:07:09 +0100229 int size = (int) CodedInputStream.ReadRawVarint32(input);
Jon Skeet2e6dc122009-05-29 06:34:52 +0100230 Stream limitedStream = new LimitedInputStream(input, size);
231 return MergeFrom(limitedStream, extensionRegistry);
232 }
233
234 public TBuilder MergeDelimitedFrom(Stream input) {
235 return MergeDelimitedFrom(input, ExtensionRegistry.Empty);
236 }
237
Jon Skeet68036862008-10-22 13:30:34 +0100238 public virtual IBuilder SetField(FieldDescriptor field, object value) {
239 this[field] = value;
240 return ThisBuilder;
241 }
242
243 public virtual IBuilder SetRepeatedField(FieldDescriptor field, int index, object value) {
244 this[field, index] = value;
245 return ThisBuilder;
246 }
Jon Skeet2e6dc122009-05-29 06:34:52 +0100247
248 /// <summary>
249 /// Stream implementation which proxies another stream, only allowing a certain amount
250 /// of data to be read. Note that this is only used to read delimited streams, so it
251 /// doesn't attempt to implement everything.
252 /// </summary>
253 private class LimitedInputStream : Stream {
254
255 private readonly Stream proxied;
256 private int bytesLeft;
257
258 internal LimitedInputStream(Stream proxied, int size) {
259 this.proxied = proxied;
260 bytesLeft = size;
261 }
262
263 public override bool CanRead {
264 get { return true; }
265 }
266
267 public override bool CanSeek {
268 get { return false; }
269 }
270
271 public override bool CanWrite {
272 get { return false; }
273 }
274
275 public override void Flush() {
276 }
277
278 public override long Length {
279 get { throw new NotImplementedException(); }
280 }
281
282 public override long Position {
283 get {
284 throw new NotImplementedException();
285 }
286 set {
287 throw new NotImplementedException();
288 }
289 }
290
291 public override int Read(byte[] buffer, int offset, int count) {
292 if (bytesLeft > 0) {
293 int bytesRead = proxied.Read(buffer, offset, Math.Min(bytesLeft, count));
294 bytesLeft -= bytesRead;
295 return bytesRead;
296 }
297 return 0;
298 }
299
300 public override long Seek(long offset, SeekOrigin origin) {
301 throw new NotImplementedException();
302 }
303
304 public override void SetLength(long value) {
305 throw new NotImplementedException();
306 }
307
308 public override void Write(byte[] buffer, int offset, int count) {
309 throw new NotImplementedException();
310 }
311 }
Jon Skeet68036862008-10-22 13:30:34 +0100312 }
313}