blob: cd4c47d742e721dd3b82cae3227ba67ad03e31ed [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.Generic;
34using Google.ProtocolBuffers.Collections;
35using Google.ProtocolBuffers.Descriptors;
36
37namespace Google.ProtocolBuffers {
38 public abstract class ExtendableMessage<TMessage, TBuilder> : GeneratedMessage<TMessage, TBuilder>
39 where TMessage : GeneratedMessage<TMessage, TBuilder>
40 where TBuilder : GeneratedBuilder<TMessage, TBuilder> {
41
42 protected ExtendableMessage() {}
43 private readonly FieldSet extensions = FieldSet.CreateInstance();
44
45 /// <summary>
46 /// Access for the builder.
47 /// </summary>
48 internal FieldSet Extensions {
49 get { return extensions; }
50 }
51
52 /// <summary>
53 /// Checks if a singular extension is present.
54 /// </summary>
55 public bool HasExtension<TExtension>(GeneratedExtensionBase<TExtension> extension) {
56 return extensions.HasField(extension.Descriptor);
57 }
58
59 /// <summary>
60 /// Returns the number of elements in a repeated extension.
61 /// </summary>
62 public int GetExtensionCount<TExtension>(GeneratedExtensionBase<IList<TExtension>> extension) {
63 return extensions.GetRepeatedFieldCount(extension.Descriptor);
64 }
65
66 /// <summary>
67 /// Returns the value of an extension.
68 /// </summary>
69 public TExtension GetExtension<TExtension>(GeneratedExtensionBase<TExtension> extension) {
70 object value = extensions[extension.Descriptor];
71 if (value == null) {
72 return (TExtension) extension.MessageDefaultInstance;
73 } else {
74 return (TExtension) extension.FromReflectionType(value);
75 }
76 }
77
78 /// <summary>
79 /// Returns one element of a repeated extension.
80 /// </summary>
81 public TExtension GetExtension<TExtension>(GeneratedExtensionBase<IList<TExtension>> extension, int index) {
82 return (TExtension) extension.SingularFromReflectionType(extensions[extension.Descriptor, index]);
83 }
84
85 /// <summary>
86 /// Called to check if all extensions are initialized.
87 /// </summary>
88 protected bool ExtensionsAreInitialized {
89 get { return extensions.IsInitialized; }
90 }
91
92 public override bool IsInitialized {
93 get {
94 return base.IsInitialized && ExtensionsAreInitialized;
95 }
96 }
97
98 #region Reflection
99 public override IDictionary<FieldDescriptor, object> AllFields {
100 get {
101 IDictionary<FieldDescriptor, object> result = GetMutableFieldMap();
102 foreach(KeyValuePair<FieldDescriptor, object> entry in extensions.AllFields) {
103 result[entry.Key] = entry.Value;
104 }
105 return Dictionaries.AsReadOnly(result);
106 }
107 }
108
109 public override bool HasField(FieldDescriptor field) {
110 if (field.IsExtension) {
111 VerifyContainingType(field);
112 return extensions.HasField(field);
113 } else {
114 return base.HasField(field);
115 }
116 }
117
118 public override object this[FieldDescriptor field] {
119 get {
120 if (field.IsExtension) {
121 VerifyContainingType(field);
122 object value = extensions[field];
123 if (value == null) {
124 // Lacking an ExtensionRegistry, we have no way to determine the
125 // extension's real type, so we return a DynamicMessage.
126 // TODO(jonskeet): Work out what this means
127 return DynamicMessage.GetDefaultInstance(field.MessageType);
128 } else {
129 return value;
130 }
131 } else {
132 return base[field];
133 }
134 }
135 }
136
137 public override int GetRepeatedFieldCount(FieldDescriptor field) {
138 if (field.IsExtension) {
139 VerifyContainingType(field);
140 return extensions.GetRepeatedFieldCount(field);
141 } else {
142 return base.GetRepeatedFieldCount(field);
143 }
144 }
145
146 public override object this[FieldDescriptor field, int index] {
147 get {
148 if (field.IsExtension) {
149 VerifyContainingType(field);
150 return extensions[field, index];
151 } else {
152 return base[field, index];
153 }
154 }
155 }
156
157 internal void VerifyContainingType(FieldDescriptor field) {
158 if (field.ContainingType != DescriptorForType) {
159 throw new ArgumentException("FieldDescriptor does not match message type.");
160 }
161 }
162 #endregion
163
164 /// <summary>
165 /// Used by subclasses to serialize extensions. Extension ranges may be
166 /// interleaves with field numbers, but we must write them in canonical
167 /// (sorted by field number) order. This class helps us to write individual
168 /// ranges of extensions at once.
169 ///
170 /// TODO(jonskeet): See if we can improve this in terms of readability.
171 /// </summary>
172 protected class ExtensionWriter {
173 readonly IEnumerator<KeyValuePair<FieldDescriptor, object>> iterator;
174 readonly FieldSet extensions;
175 KeyValuePair<FieldDescriptor, object>? next = null;
176
177 internal ExtensionWriter(ExtendableMessage<TMessage, TBuilder> message) {
178 extensions = message.extensions;
179 iterator = message.extensions.GetEnumerator();
180 if (iterator.MoveNext()) {
181 next = iterator.Current;
182 }
183 }
184
185 public void WriteUntil(int end, CodedOutputStream output) {
186 while (next != null && next.Value.Key.FieldNumber < end) {
187 extensions.WriteField(next.Value.Key, next.Value.Value, output);
188 if (iterator.MoveNext()) {
189 next = iterator.Current;
190 } else {
191 next = null;
192 }
193 }
194 }
195 }
196
197 protected ExtensionWriter CreateExtensionWriter(ExtendableMessage<TMessage, TBuilder> message) {
198 return new ExtensionWriter(message);
199 }
200
201 /// <summary>
202 /// Called by subclasses to compute the size of extensions.
203 /// </summary>
204 protected int ExtensionsSerializedSize {
205 get { return extensions.SerializedSize; }
206 }
207
208 internal void VerifyExtensionContainingType<TExtension>(GeneratedExtensionBase<TExtension> extension) {
209 if (extension.Descriptor.ContainingType != DescriptorForType) {
210 // This can only happen if someone uses unchecked operations.
211 throw new ArgumentException("Extension is for type \"" + extension.Descriptor.ContainingType.FullName
212 + "\" which does not match message type \"" + DescriptorForType.FullName + "\".");
213 }
214 }
215 }
216}