blob: 638279e7134d614ea2f4c00fa90418a84db3c8b0 [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 Google.ProtocolBuffers.Descriptors;
36using Google.ProtocolBuffers.FieldAccess;
37
38namespace Google.ProtocolBuffers {
39 /// <summary>
40 /// All generated protocol message builder classes extend this class. It implements
41 /// most of the IBuilder interface using reflection. Users can ignore this class
42 /// as an implementation detail.
43 /// </summary>
44 public abstract class GeneratedBuilder<TMessage, TBuilder> : AbstractBuilder<TMessage, TBuilder>
45 where TMessage : GeneratedMessage <TMessage, TBuilder>
46 where TBuilder : GeneratedBuilder<TMessage, TBuilder> {
47
48 /// <summary>
49 /// Returns the message being built at the moment.
50 /// </summary>
51 protected abstract TMessage MessageBeingBuilt { get; }
52
53 protected internal FieldAccessorTable<TMessage, TBuilder> InternalFieldAccessors {
54 get { return MessageBeingBuilt.FieldAccessorsFromBuilder; }
55 }
56
57 public override bool IsInitialized {
58 get { return MessageBeingBuilt.IsInitialized; }
59 }
60
61 public override IDictionary<FieldDescriptor, object> AllFields {
62 get { return MessageBeingBuilt.AllFields; }
63 }
64
65 public override object this[FieldDescriptor field] {
66 get {
67 // For repeated fields, the underlying list object is still modifiable at this point.
68 // Make sure not to expose the modifiable list to the caller.
69 return field.IsRepeated
70 ? InternalFieldAccessors[field].GetRepeatedWrapper(ThisBuilder)
71 : MessageBeingBuilt[field];
72 }
73 set {
74 InternalFieldAccessors[field].SetValue(ThisBuilder, value);
75 }
76 }
77
78 /// <summary>
79 /// Adds all of the specified values to the given collection.
80 /// </summary>
Jon Skeet642a8142009-01-27 12:25:21 +000081 /// <exception cref="ArgumentNullException">Any element of the list is null</exception>
Jon Skeet68036862008-10-22 13:30:34 +010082 protected void AddRange<T>(IEnumerable<T> source, IList<T> destination) {
Jon Skeet642a8142009-01-27 12:25:21 +000083 ThrowHelper.ThrowIfNull(source);
84 // We only need to check this for nullable types.
85 if (default(T) == null) {
86 ThrowHelper.ThrowIfAnyNull(source);
87 }
Jon Skeet68036862008-10-22 13:30:34 +010088 List<T> list = destination as List<T>;
89 if (list != null) {
90 list.AddRange(source);
91 } else {
92 foreach (T element in source) {
93 destination.Add(element);
94 }
95 }
96 }
97
98 /// <summary>
99 /// Called by derived classes to parse an unknown field.
100 /// </summary>
101 /// <returns>true unless the tag is an end-group tag</returns>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100102 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100103 protected virtual bool ParseUnknownField(CodedInputStream input, UnknownFieldSet.Builder unknownFields,
104 ExtensionRegistry extensionRegistry, uint tag) {
105 return unknownFields.MergeFieldFrom(tag, input);
106 }
107
108 public override MessageDescriptor DescriptorForType {
109 get { return MessageBeingBuilt.DescriptorForType; }
110 }
111
112 public override int GetRepeatedFieldCount(FieldDescriptor field) {
113 return MessageBeingBuilt.GetRepeatedFieldCount(field);
114 }
115
116 public override object this[FieldDescriptor field, int index] {
117 get { return MessageBeingBuilt[field, index]; }
118 set { InternalFieldAccessors[field].SetRepeated(ThisBuilder, index, value); }
119 }
120
121 public override bool HasField(FieldDescriptor field) {
122 return MessageBeingBuilt.HasField(field);
123 }
124
125 public override IBuilder CreateBuilderForField(FieldDescriptor field) {
126 return InternalFieldAccessors[field].CreateBuilder();
127 }
128
129 public override TBuilder ClearField(FieldDescriptor field) {
130 InternalFieldAccessors[field].Clear(ThisBuilder);
131 return ThisBuilder;
132 }
133
134 public override TBuilder MergeFrom(TMessage other) {
135 if (other.DescriptorForType != InternalFieldAccessors.Descriptor) {
136 throw new ArgumentException("Message type mismatch");
137 }
138
Jon Skeet20bfd9b2008-10-23 21:05:58 +0100139 foreach (KeyValuePair<FieldDescriptor, object> entry in other.AllFields) {
Jon Skeet68036862008-10-22 13:30:34 +0100140 FieldDescriptor field = entry.Key;
141 if (field.IsRepeated) {
142 // Concatenate repeated fields
143 foreach (object element in (IEnumerable)entry.Value) {
144 AddRepeatedField(field, element);
145 }
146 } else if (field.MappedType == MappedType.Message && HasField(field)) {
147 // Merge singular embedded messages
148 IMessage oldValue = (IMessage)this[field];
149 this[field] = oldValue.WeakCreateBuilderForType()
150 .WeakMergeFrom(oldValue)
151 .WeakMergeFrom((IMessage)entry.Value)
152 .WeakBuildPartial();
153 } else {
154 // Just overwrite
155 this[field] = entry.Value;
156 }
157 }
158 return ThisBuilder;
159 }
160
161 public override TBuilder MergeUnknownFields(UnknownFieldSet unknownFields) {
Jon Skeet7de1aef2009-03-05 14:23:17 +0000162 if (unknownFields != UnknownFieldSet.DefaultInstance) {
163 TMessage result = MessageBeingBuilt;
164 result.SetUnknownFields(UnknownFieldSet.CreateBuilder(result.UnknownFields)
165 .MergeFrom(unknownFields)
166 .Build());
167 }
Jon Skeet68036862008-10-22 13:30:34 +0100168 return ThisBuilder;
169 }
170
171 public override TBuilder AddRepeatedField(FieldDescriptor field, object value) {
172 InternalFieldAccessors[field].AddRepeated(ThisBuilder, value);
173 return ThisBuilder;
174 }
175
176 /// <summary>
177 /// Like Build(), but will wrap UninitializedMessageException in
178 /// InvalidProtocolBufferException.
179 /// </summary>
180 public TMessage BuildParsed() {
181 if (!IsInitialized) {
182 throw new UninitializedMessageException(MessageBeingBuilt).AsInvalidProtocolBufferException();
183 }
184 return BuildPartial();
185 }
186
187 /// <summary>
188 /// Implementation of <see cref="IBuilder{TMessage, TBuilder}.Build" />.
189 /// </summary>
190 public override TMessage Build() {
Jon Skeetc298c892009-05-30 10:07:09 +0100191 // If the message is null, we'll throw a more appropriate exception in BuildPartial.
192 if (MessageBeingBuilt != null && !IsInitialized) {
Jon Skeet68036862008-10-22 13:30:34 +0100193 throw new UninitializedMessageException(MessageBeingBuilt);
194 }
195 return BuildPartial();
196 }
197
198 public override UnknownFieldSet UnknownFields {
199 get { return MessageBeingBuilt.UnknownFields; }
200 set { MessageBeingBuilt.SetUnknownFields(value); }
201 }
202 }
203}