blob: 2ec2762ffef3a49d33eb6c87140fad6e5d669af4 [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
csharptest7d396f92010-11-08 20:06:46 -0600151 IMessageLite oldValue = (IMessageLite)this[field];
Jon Skeet68036862008-10-22 13:30:34 +0100152 this[field] = oldValue.WeakCreateBuilderForType()
153 .WeakMergeFrom(oldValue)
csharptest7d396f92010-11-08 20:06:46 -0600154 .WeakMergeFrom((IMessageLite)entry.Value)
Jon Skeet68036862008-10-22 13:30:34 +0100155 .WeakBuildPartial();
156 } else {
157 // Just overwrite
158 this[field] = entry.Value;
159 }
160 }
csharptestcb7fc652010-11-19 10:59:50 -0600161
162 //Fix for unknown fields not merging, see java's AbstractMessage.Builder<T> line 236
163 MergeUnknownFields(other.UnknownFields);
164
Jon Skeet68036862008-10-22 13:30:34 +0100165 return ThisBuilder;
166 }
167
168 public override TBuilder MergeUnknownFields(UnknownFieldSet unknownFields) {
Jon Skeet7de1aef2009-03-05 14:23:17 +0000169 if (unknownFields != UnknownFieldSet.DefaultInstance) {
170 TMessage result = MessageBeingBuilt;
171 result.SetUnknownFields(UnknownFieldSet.CreateBuilder(result.UnknownFields)
172 .MergeFrom(unknownFields)
173 .Build());
174 }
Jon Skeet68036862008-10-22 13:30:34 +0100175 return ThisBuilder;
176 }
177
178 public override TBuilder AddRepeatedField(FieldDescriptor field, object value) {
179 InternalFieldAccessors[field].AddRepeated(ThisBuilder, value);
180 return ThisBuilder;
181 }
182
183 /// <summary>
184 /// Like Build(), but will wrap UninitializedMessageException in
185 /// InvalidProtocolBufferException.
186 /// </summary>
187 public TMessage BuildParsed() {
188 if (!IsInitialized) {
189 throw new UninitializedMessageException(MessageBeingBuilt).AsInvalidProtocolBufferException();
190 }
191 return BuildPartial();
192 }
193
194 /// <summary>
195 /// Implementation of <see cref="IBuilder{TMessage, TBuilder}.Build" />.
196 /// </summary>
197 public override TMessage Build() {
Jon Skeetc298c892009-05-30 10:07:09 +0100198 // If the message is null, we'll throw a more appropriate exception in BuildPartial.
199 if (MessageBeingBuilt != null && !IsInitialized) {
Jon Skeet68036862008-10-22 13:30:34 +0100200 throw new UninitializedMessageException(MessageBeingBuilt);
201 }
202 return BuildPartial();
203 }
204
205 public override UnknownFieldSet UnknownFields {
206 get { return MessageBeingBuilt.UnknownFields; }
207 set { MessageBeingBuilt.SetUnknownFields(value); }
208 }
209 }
210}