blob: 1124fc865fd194921879f433068200c660920acb [file] [log] [blame]
Jon Skeet0aac0e42009-09-09 18:48:02 +01001#region Copyright notice and license
Jon Skeet60c059b2008-10-23 21:17:56 +01002// Protocol Buffers - Google's data interchange format
3// Copyright 2008 Google Inc. All rights reserved.
4// http://github.com/jskeet/dotnet-protobufs/
5// Original C++/Java/Python code:
Jon Skeet68036862008-10-22 13:30:34 +01006// http://code.google.com/p/protobuf/
7//
Jon Skeet60c059b2008-10-23 21:17:56 +01008// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are
10// met:
Jon Skeet68036862008-10-22 13:30:34 +010011//
Jon Skeet60c059b2008-10-23 21:17:56 +010012// * Redistributions of source code must retain the above copyright
13// notice, this list of conditions and the following disclaimer.
14// * Redistributions in binary form must reproduce the above
15// copyright notice, this list of conditions and the following disclaimer
16// in the documentation and/or other materials provided with the
17// distribution.
18// * Neither the name of Google Inc. nor the names of its
19// contributors may be used to endorse or promote products derived from
20// this software without specific prior written permission.
Jon Skeet68036862008-10-22 13:30:34 +010021//
Jon Skeet60c059b2008-10-23 21:17:56 +010022// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Jon Skeet0aac0e42009-09-09 18:48:02 +010033#endregion
34
Jon Skeet68036862008-10-22 13:30:34 +010035using System;
36using System.Collections;
37using System.Collections.Generic;
38using Google.ProtocolBuffers.Descriptors;
39using Google.ProtocolBuffers.FieldAccess;
40
41namespace Google.ProtocolBuffers {
42 /// <summary>
43 /// All generated protocol message builder classes extend this class. It implements
44 /// most of the IBuilder interface using reflection. Users can ignore this class
45 /// as an implementation detail.
46 /// </summary>
47 public abstract class GeneratedBuilder<TMessage, TBuilder> : AbstractBuilder<TMessage, TBuilder>
48 where TMessage : GeneratedMessage <TMessage, TBuilder>
49 where TBuilder : GeneratedBuilder<TMessage, TBuilder> {
50
51 /// <summary>
52 /// Returns the message being built at the moment.
53 /// </summary>
54 protected abstract TMessage MessageBeingBuilt { get; }
55
56 protected internal FieldAccessorTable<TMessage, TBuilder> InternalFieldAccessors {
57 get { return MessageBeingBuilt.FieldAccessorsFromBuilder; }
58 }
59
60 public override bool IsInitialized {
61 get { return MessageBeingBuilt.IsInitialized; }
62 }
63
64 public override IDictionary<FieldDescriptor, object> AllFields {
65 get { return MessageBeingBuilt.AllFields; }
66 }
67
68 public override object this[FieldDescriptor field] {
69 get {
70 // For repeated fields, the underlying list object is still modifiable at this point.
71 // Make sure not to expose the modifiable list to the caller.
72 return field.IsRepeated
73 ? InternalFieldAccessors[field].GetRepeatedWrapper(ThisBuilder)
74 : MessageBeingBuilt[field];
75 }
76 set {
77 InternalFieldAccessors[field].SetValue(ThisBuilder, value);
78 }
79 }
80
81 /// <summary>
82 /// Adds all of the specified values to the given collection.
83 /// </summary>
Jon Skeet642a8142009-01-27 12:25:21 +000084 /// <exception cref="ArgumentNullException">Any element of the list is null</exception>
Jon Skeet68036862008-10-22 13:30:34 +010085 protected void AddRange<T>(IEnumerable<T> source, IList<T> destination) {
Jon Skeet642a8142009-01-27 12:25:21 +000086 ThrowHelper.ThrowIfNull(source);
87 // We only need to check this for nullable types.
88 if (default(T) == null) {
89 ThrowHelper.ThrowIfAnyNull(source);
90 }
Jon Skeet68036862008-10-22 13:30:34 +010091 List<T> list = destination as List<T>;
92 if (list != null) {
93 list.AddRange(source);
94 } else {
95 foreach (T element in source) {
96 destination.Add(element);
97 }
98 }
99 }
100
101 /// <summary>
102 /// Called by derived classes to parse an unknown field.
103 /// </summary>
104 /// <returns>true unless the tag is an end-group tag</returns>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100105 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100106 protected virtual bool ParseUnknownField(CodedInputStream input, UnknownFieldSet.Builder unknownFields,
107 ExtensionRegistry extensionRegistry, uint tag) {
108 return unknownFields.MergeFieldFrom(tag, input);
109 }
110
111 public override MessageDescriptor DescriptorForType {
112 get { return MessageBeingBuilt.DescriptorForType; }
113 }
114
115 public override int GetRepeatedFieldCount(FieldDescriptor field) {
116 return MessageBeingBuilt.GetRepeatedFieldCount(field);
117 }
118
119 public override object this[FieldDescriptor field, int index] {
120 get { return MessageBeingBuilt[field, index]; }
121 set { InternalFieldAccessors[field].SetRepeated(ThisBuilder, index, value); }
122 }
123
124 public override bool HasField(FieldDescriptor field) {
125 return MessageBeingBuilt.HasField(field);
126 }
127
128 public override IBuilder CreateBuilderForField(FieldDescriptor field) {
129 return InternalFieldAccessors[field].CreateBuilder();
130 }
131
132 public override TBuilder ClearField(FieldDescriptor field) {
133 InternalFieldAccessors[field].Clear(ThisBuilder);
134 return ThisBuilder;
135 }
136
137 public override TBuilder MergeFrom(TMessage other) {
138 if (other.DescriptorForType != InternalFieldAccessors.Descriptor) {
139 throw new ArgumentException("Message type mismatch");
140 }
141
Jon Skeet20bfd9b2008-10-23 21:05:58 +0100142 foreach (KeyValuePair<FieldDescriptor, object> entry in other.AllFields) {
Jon Skeet68036862008-10-22 13:30:34 +0100143 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 && HasField(field)) {
150 // Merge singular embedded messages
151 IMessage oldValue = (IMessage)this[field];
152 this[field] = oldValue.WeakCreateBuilderForType()
153 .WeakMergeFrom(oldValue)
154 .WeakMergeFrom((IMessage)entry.Value)
155 .WeakBuildPartial();
156 } else {
157 // Just overwrite
158 this[field] = entry.Value;
159 }
160 }
161 return ThisBuilder;
162 }
163
164 public override TBuilder MergeUnknownFields(UnknownFieldSet unknownFields) {
Jon Skeet7de1aef2009-03-05 14:23:17 +0000165 if (unknownFields != UnknownFieldSet.DefaultInstance) {
166 TMessage result = MessageBeingBuilt;
167 result.SetUnknownFields(UnknownFieldSet.CreateBuilder(result.UnknownFields)
168 .MergeFrom(unknownFields)
169 .Build());
170 }
Jon Skeet68036862008-10-22 13:30:34 +0100171 return ThisBuilder;
172 }
173
174 public override TBuilder AddRepeatedField(FieldDescriptor field, object value) {
175 InternalFieldAccessors[field].AddRepeated(ThisBuilder, value);
176 return ThisBuilder;
177 }
178
179 /// <summary>
180 /// Like Build(), but will wrap UninitializedMessageException in
181 /// InvalidProtocolBufferException.
182 /// </summary>
183 public TMessage BuildParsed() {
184 if (!IsInitialized) {
185 throw new UninitializedMessageException(MessageBeingBuilt).AsInvalidProtocolBufferException();
186 }
187 return BuildPartial();
188 }
189
190 /// <summary>
191 /// Implementation of <see cref="IBuilder{TMessage, TBuilder}.Build" />.
192 /// </summary>
193 public override TMessage Build() {
Jon Skeetc298c892009-05-30 10:07:09 +0100194 // If the message is null, we'll throw a more appropriate exception in BuildPartial.
195 if (MessageBeingBuilt != null && !IsInitialized) {
Jon Skeet68036862008-10-22 13:30:34 +0100196 throw new UninitializedMessageException(MessageBeingBuilt);
197 }
198 return BuildPartial();
199 }
200
201 public override UnknownFieldSet UnknownFields {
202 get { return MessageBeingBuilt.UnknownFields; }
203 set { MessageBeingBuilt.SetUnknownFields(value); }
204 }
205 }
206}