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