blob: a4a6e17efc9094e1b2a16fffd6b1d1bbaf321a06 [file] [log] [blame]
Jon Skeet68036862008-10-22 13:30:34 +01001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.
3// http://code.google.com/p/protobuf/
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16using System;
17using System.Collections.Generic;
18using Google.ProtocolBuffers.Collections;
19using Google.ProtocolBuffers.Descriptors;
20
21namespace Google.ProtocolBuffers {
22 public abstract class ExtendableMessage<TMessage, TBuilder> : GeneratedMessage<TMessage, TBuilder>
23 where TMessage : GeneratedMessage<TMessage, TBuilder>
24 where TBuilder : GeneratedBuilder<TMessage, TBuilder> {
25
26 protected ExtendableMessage() {}
27 private readonly FieldSet extensions = FieldSet.CreateInstance();
28
29 /// <summary>
30 /// Access for the builder.
31 /// </summary>
32 internal FieldSet Extensions {
33 get { return extensions; }
34 }
35
36 /// <summary>
37 /// Checks if a singular extension is present.
38 /// </summary>
39 public bool HasExtension<TExtension>(GeneratedExtensionBase<TExtension> extension) {
40 return extensions.HasField(extension.Descriptor);
41 }
42
43 /// <summary>
44 /// Returns the number of elements in a repeated extension.
45 /// </summary>
46 public int GetExtensionCount<TExtension>(GeneratedExtensionBase<IList<TExtension>> extension) {
47 return extensions.GetRepeatedFieldCount(extension.Descriptor);
48 }
49
50 /// <summary>
51 /// Returns the value of an extension.
52 /// </summary>
53 public TExtension GetExtension<TExtension>(GeneratedExtensionBase<TExtension> extension) {
54 object value = extensions[extension.Descriptor];
55 if (value == null) {
56 return (TExtension) extension.MessageDefaultInstance;
57 } else {
58 return (TExtension) extension.FromReflectionType(value);
59 }
60 }
61
62 /// <summary>
63 /// Returns one element of a repeated extension.
64 /// </summary>
65 public TExtension GetExtension<TExtension>(GeneratedExtensionBase<IList<TExtension>> extension, int index) {
66 return (TExtension) extension.SingularFromReflectionType(extensions[extension.Descriptor, index]);
67 }
68
69 /// <summary>
70 /// Called to check if all extensions are initialized.
71 /// </summary>
72 protected bool ExtensionsAreInitialized {
73 get { return extensions.IsInitialized; }
74 }
75
76 public override bool IsInitialized {
77 get {
78 return base.IsInitialized && ExtensionsAreInitialized;
79 }
80 }
81
82 #region Reflection
83 public override IDictionary<FieldDescriptor, object> AllFields {
84 get {
85 IDictionary<FieldDescriptor, object> result = GetMutableFieldMap();
86 foreach(KeyValuePair<FieldDescriptor, object> entry in extensions.AllFields) {
87 result[entry.Key] = entry.Value;
88 }
89 return Dictionaries.AsReadOnly(result);
90 }
91 }
92
93 public override bool HasField(FieldDescriptor field) {
94 if (field.IsExtension) {
95 VerifyContainingType(field);
96 return extensions.HasField(field);
97 } else {
98 return base.HasField(field);
99 }
100 }
101
102 public override object this[FieldDescriptor field] {
103 get {
104 if (field.IsExtension) {
105 VerifyContainingType(field);
106 object value = extensions[field];
107 if (value == null) {
108 // Lacking an ExtensionRegistry, we have no way to determine the
109 // extension's real type, so we return a DynamicMessage.
110 // TODO(jonskeet): Work out what this means
111 return DynamicMessage.GetDefaultInstance(field.MessageType);
112 } else {
113 return value;
114 }
115 } else {
116 return base[field];
117 }
118 }
119 }
120
121 public override int GetRepeatedFieldCount(FieldDescriptor field) {
122 if (field.IsExtension) {
123 VerifyContainingType(field);
124 return extensions.GetRepeatedFieldCount(field);
125 } else {
126 return base.GetRepeatedFieldCount(field);
127 }
128 }
129
130 public override object this[FieldDescriptor field, int index] {
131 get {
132 if (field.IsExtension) {
133 VerifyContainingType(field);
134 return extensions[field, index];
135 } else {
136 return base[field, index];
137 }
138 }
139 }
140
141 internal void VerifyContainingType(FieldDescriptor field) {
142 if (field.ContainingType != DescriptorForType) {
143 throw new ArgumentException("FieldDescriptor does not match message type.");
144 }
145 }
146 #endregion
147
148 /// <summary>
149 /// Used by subclasses to serialize extensions. Extension ranges may be
150 /// interleaves with field numbers, but we must write them in canonical
151 /// (sorted by field number) order. This class helps us to write individual
152 /// ranges of extensions at once.
153 ///
154 /// TODO(jonskeet): See if we can improve this in terms of readability.
155 /// </summary>
156 protected class ExtensionWriter {
157 readonly IEnumerator<KeyValuePair<FieldDescriptor, object>> iterator;
158 readonly FieldSet extensions;
159 KeyValuePair<FieldDescriptor, object>? next = null;
160
161 internal ExtensionWriter(ExtendableMessage<TMessage, TBuilder> message) {
162 extensions = message.extensions;
163 iterator = message.extensions.GetEnumerator();
164 if (iterator.MoveNext()) {
165 next = iterator.Current;
166 }
167 }
168
169 public void WriteUntil(int end, CodedOutputStream output) {
170 while (next != null && next.Value.Key.FieldNumber < end) {
171 extensions.WriteField(next.Value.Key, next.Value.Value, output);
172 if (iterator.MoveNext()) {
173 next = iterator.Current;
174 } else {
175 next = null;
176 }
177 }
178 }
179 }
180
181 protected ExtensionWriter CreateExtensionWriter(ExtendableMessage<TMessage, TBuilder> message) {
182 return new ExtensionWriter(message);
183 }
184
185 /// <summary>
186 /// Called by subclasses to compute the size of extensions.
187 /// </summary>
188 protected int ExtensionsSerializedSize {
189 get { return extensions.SerializedSize; }
190 }
191
192 internal void VerifyExtensionContainingType<TExtension>(GeneratedExtensionBase<TExtension> extension) {
193 if (extension.Descriptor.ContainingType != DescriptorForType) {
194 // This can only happen if someone uses unchecked operations.
195 throw new ArgumentException("Extension is for type \"" + extension.Descriptor.ContainingType.FullName
196 + "\" which does not match message type \"" + DescriptorForType.FullName + "\".");
197 }
198 }
199 }
200}