parent
57599ef16d
commit
c07571a79b
3 changed files with 798 additions and 0 deletions
@ -0,0 +1,245 @@ |
|||||||
|
#region Copyright notice and license |
||||||
|
// Protocol Buffers - Google's data interchange format |
||||||
|
// Copyright 2008 Google Inc. All rights reserved. |
||||||
|
// http://github.com/jskeet/dotnet-protobufs/ |
||||||
|
// Original C++/Java/Python code: |
||||||
|
// http://code.google.com/p/protobuf/ |
||||||
|
// |
||||||
|
// Redistribution and use in source and binary forms, with or without |
||||||
|
// modification, are permitted provided that the following conditions are |
||||||
|
// met: |
||||||
|
// |
||||||
|
// * Redistributions of source code must retain the above copyright |
||||||
|
// notice, this list of conditions and the following disclaimer. |
||||||
|
// * Redistributions in binary form must reproduce the above |
||||||
|
// copyright notice, this list of conditions and the following disclaimer |
||||||
|
// in the documentation and/or other materials provided with the |
||||||
|
// distribution. |
||||||
|
// * Neither the name of Google Inc. nor the names of its |
||||||
|
// contributors may be used to endorse or promote products derived from |
||||||
|
// this software without specific prior written permission. |
||||||
|
// |
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
#endregion |
||||||
|
|
||||||
|
using System.Collections.Generic; |
||||||
|
using Google.ProtocolBuffers.Descriptors; |
||||||
|
using System; |
||||||
|
|
||||||
|
namespace Google.ProtocolBuffers { |
||||||
|
/// <summary> |
||||||
|
/// A table of known extensions, searchable by name or field number. When |
||||||
|
/// parsing a protocol message that might have extensions, you must provide |
||||||
|
/// an <see cref="ExtensionRegistry"/> in which you have registered any extensions |
||||||
|
/// that you want to be able to parse. Otherwise, those extensions will just |
||||||
|
/// be treated like unknown fields. |
||||||
|
/// </summary> |
||||||
|
/// <example> |
||||||
|
/// For example, if you had the <c>.proto</c> file: |
||||||
|
/// <code> |
||||||
|
/// option java_class = "MyProto"; |
||||||
|
/// |
||||||
|
/// message Foo { |
||||||
|
/// extensions 1000 to max; |
||||||
|
/// } |
||||||
|
/// |
||||||
|
/// extend Foo { |
||||||
|
/// optional int32 bar; |
||||||
|
/// } |
||||||
|
/// </code> |
||||||
|
/// |
||||||
|
/// Then you might write code like: |
||||||
|
/// |
||||||
|
/// <code> |
||||||
|
/// ExtensionRegistry registry = ExtensionRegistry.CreateInstance(); |
||||||
|
/// registry.Add(MyProto.Bar); |
||||||
|
/// MyProto.Foo message = MyProto.Foo.ParseFrom(input, registry); |
||||||
|
/// </code> |
||||||
|
/// </example> |
||||||
|
/// |
||||||
|
/// <remarks> |
||||||
|
/// <para>You might wonder why this is necessary. Two alternatives might come to |
||||||
|
/// mind. First, you might imagine a system where generated extensions are |
||||||
|
/// automatically registered when their containing classes are loaded. This |
||||||
|
/// is a popular technique, but is bad design; among other things, it creates a |
||||||
|
/// situation where behavior can change depending on what classes happen to be |
||||||
|
/// loaded. It also introduces a security vulnerability, because an |
||||||
|
/// unprivileged class could cause its code to be called unexpectedly from a |
||||||
|
/// privileged class by registering itself as an extension of the right type. |
||||||
|
/// </para> |
||||||
|
/// <para>Another option you might consider is lazy parsing: do not parse an |
||||||
|
/// extension until it is first requested, at which point the caller must |
||||||
|
/// provide a type to use. This introduces a different set of problems. First, |
||||||
|
/// it would require a mutex lock any time an extension was accessed, which |
||||||
|
/// would be slow. Second, corrupt data would not be detected until first |
||||||
|
/// access, at which point it would be much harder to deal with it. Third, it |
||||||
|
/// could violate the expectation that message objects are immutable, since the |
||||||
|
/// type provided could be any arbitrary message class. An unprivileged user |
||||||
|
/// could take advantage of this to inject a mutable object into a message |
||||||
|
/// belonging to privileged code and create mischief.</para> |
||||||
|
/// </remarks> |
||||||
|
public sealed class ExtensionRegistry { |
||||||
|
|
||||||
|
private static readonly ExtensionRegistry empty = new ExtensionRegistry( |
||||||
|
new Dictionary<string, ExtensionInfo>(), |
||||||
|
new Dictionary<DescriptorIntPair, ExtensionInfo>(), |
||||||
|
true); |
||||||
|
|
||||||
|
private readonly IDictionary<string, ExtensionInfo> extensionsByName; |
||||||
|
private readonly IDictionary<DescriptorIntPair, ExtensionInfo> extensionsByNumber; |
||||||
|
private readonly bool readOnly; |
||||||
|
|
||||||
|
private ExtensionRegistry(IDictionary<String, ExtensionInfo> extensionsByName, |
||||||
|
IDictionary<DescriptorIntPair, ExtensionInfo> extensionsByNumber, |
||||||
|
bool readOnly) { |
||||||
|
this.extensionsByName = extensionsByName; |
||||||
|
this.extensionsByNumber = extensionsByNumber; |
||||||
|
this.readOnly = readOnly; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Construct a new, empty instance. |
||||||
|
/// </summary> |
||||||
|
public static ExtensionRegistry CreateInstance() { |
||||||
|
return new ExtensionRegistry(new Dictionary<string, ExtensionInfo>(), |
||||||
|
new Dictionary<DescriptorIntPair, ExtensionInfo>(), false); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Get the unmodifiable singleton empty instance. |
||||||
|
/// </summary> |
||||||
|
public static ExtensionRegistry Empty { |
||||||
|
get { return empty; } |
||||||
|
} |
||||||
|
|
||||||
|
public ExtensionRegistry AsReadOnly() { |
||||||
|
return new ExtensionRegistry(extensionsByName, extensionsByNumber, true); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Finds an extension by fully-qualified field name, in the |
||||||
|
/// proto namespace, i.e. result.Descriptor.FullName will match |
||||||
|
/// <paramref name="fullName"/> if a match is found. A null |
||||||
|
/// reference is returned if the extension can't be found. |
||||||
|
/// </summary> |
||||||
|
public ExtensionInfo this[string fullName] { |
||||||
|
get { |
||||||
|
ExtensionInfo ret; |
||||||
|
extensionsByName.TryGetValue(fullName, out ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Finds an extension by containing type and field number. |
||||||
|
/// A null reference is returned if the extension can't be found. |
||||||
|
/// </summary> |
||||||
|
public ExtensionInfo this[MessageDescriptor containingType, int fieldNumber] { |
||||||
|
get { |
||||||
|
ExtensionInfo ret; |
||||||
|
extensionsByNumber.TryGetValue(new DescriptorIntPair(containingType, fieldNumber), out ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Add an extension from a generated file to the registry. |
||||||
|
/// </summary> |
||||||
|
public void Add<TExtension> (GeneratedExtensionBase<TExtension> extension) { |
||||||
|
if (extension.Descriptor.MappedType == MappedType.Message) { |
||||||
|
Add(new ExtensionInfo(extension.Descriptor, extension.MessageDefaultInstance)); |
||||||
|
} else { |
||||||
|
Add(new ExtensionInfo(extension.Descriptor, null)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Adds a non-message-type extension to the registry by descriptor. |
||||||
|
/// </summary> |
||||||
|
/// <param name="type"></param> |
||||||
|
public void Add(FieldDescriptor type) { |
||||||
|
if (type.MappedType == MappedType.Message) { |
||||||
|
throw new ArgumentException("ExtensionRegistry.Add() must be provided a default instance " |
||||||
|
+ "when adding an embedded message extension."); |
||||||
|
} |
||||||
|
Add(new ExtensionInfo(type, null)); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Adds a message-type-extension to the registry by descriptor. |
||||||
|
/// </summary> |
||||||
|
/// <param name="type"></param> |
||||||
|
/// <param name="defaultInstance"></param> |
||||||
|
public void Add(FieldDescriptor type, IMessage defaultInstance) { |
||||||
|
if (type.MappedType != MappedType.Message) { |
||||||
|
throw new ArgumentException("ExtensionRegistry.Add() provided a default instance for a " |
||||||
|
+ "non-message extension."); |
||||||
|
} |
||||||
|
Add(new ExtensionInfo(type, defaultInstance)); |
||||||
|
} |
||||||
|
|
||||||
|
private void Add(ExtensionInfo extension) { |
||||||
|
if (readOnly) { |
||||||
|
throw new InvalidOperationException("Cannot add entries to a read-only extension registry"); |
||||||
|
} |
||||||
|
if (!extension.Descriptor.IsExtension) { |
||||||
|
throw new ArgumentException("ExtensionRegistry.add() was given a FieldDescriptor for a " |
||||||
|
+ "regular (non-extension) field."); |
||||||
|
} |
||||||
|
|
||||||
|
extensionsByName[extension.Descriptor.FullName] = extension; |
||||||
|
extensionsByNumber[new DescriptorIntPair(extension.Descriptor.ContainingType, |
||||||
|
extension.Descriptor.FieldNumber)] = extension; |
||||||
|
|
||||||
|
FieldDescriptor field = extension.Descriptor; |
||||||
|
if (field.ContainingType.Options.MessageSetWireFormat |
||||||
|
&& field.FieldType == FieldType.Message |
||||||
|
&& field.IsOptional |
||||||
|
&& field.ExtensionScope == field.MessageType) { |
||||||
|
// This is an extension of a MessageSet type defined within the extension |
||||||
|
// type's own scope. For backwards-compatibility, allow it to be looked |
||||||
|
// up by type name. |
||||||
|
extensionsByName[field.MessageType.FullName] = extension; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Nested type just used to represent a pair of MessageDescriptor and int, as |
||||||
|
/// the key into the "by number" map. |
||||||
|
/// </summary> |
||||||
|
private struct DescriptorIntPair : IEquatable<DescriptorIntPair> { |
||||||
|
readonly MessageDescriptor descriptor; |
||||||
|
readonly int number; |
||||||
|
|
||||||
|
internal DescriptorIntPair(MessageDescriptor descriptor, int number) { |
||||||
|
this.descriptor = descriptor; |
||||||
|
this.number = number; |
||||||
|
} |
||||||
|
|
||||||
|
public override int GetHashCode() { |
||||||
|
return descriptor.GetHashCode() * ((1 << 16) - 1) + number; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool Equals(object obj) { |
||||||
|
if (!(obj is DescriptorIntPair)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
return Equals((DescriptorIntPair)obj); |
||||||
|
} |
||||||
|
|
||||||
|
public bool Equals(DescriptorIntPair other) { |
||||||
|
return descriptor == other.descriptor && number == other.number; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,317 @@ |
|||||||
|
#region Copyright notice and license |
||||||
|
// Protocol Buffers - Google's data interchange format |
||||||
|
// Copyright 2008 Google Inc. All rights reserved. |
||||||
|
// http://github.com/jskeet/dotnet-protobufs/ |
||||||
|
// Original C++/Java/Python code: |
||||||
|
// http://code.google.com/p/protobuf/ |
||||||
|
// |
||||||
|
// Redistribution and use in source and binary forms, with or without |
||||||
|
// modification, are permitted provided that the following conditions are |
||||||
|
// met: |
||||||
|
// |
||||||
|
// * Redistributions of source code must retain the above copyright |
||||||
|
// notice, this list of conditions and the following disclaimer. |
||||||
|
// * Redistributions in binary form must reproduce the above |
||||||
|
// copyright notice, this list of conditions and the following disclaimer |
||||||
|
// in the documentation and/or other materials provided with the |
||||||
|
// distribution. |
||||||
|
// * Neither the name of Google Inc. nor the names of its |
||||||
|
// contributors may be used to endorse or promote products derived from |
||||||
|
// this software without specific prior written permission. |
||||||
|
// |
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
#endregion |
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.IO; |
||||||
|
using Google.ProtocolBuffers.Descriptors; |
||||||
|
|
||||||
|
namespace Google.ProtocolBuffers { |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Non-generic interface for all members whose signatures don't require knowledge of |
||||||
|
/// the type being built. The generic interface extends this one. Some methods return |
||||||
|
/// either an IBuilder or an IMessage; in these cases the generic interface redeclares |
||||||
|
/// the same method with a type-specific signature. Implementations are encouraged to |
||||||
|
/// use explicit interface implemenation for the non-generic form. This mirrors |
||||||
|
/// how IEnumerable and IEnumerable<T> work. |
||||||
|
/// </summary> |
||||||
|
public interface IBuilder { |
||||||
|
/// <summary> |
||||||
|
/// Returns true iff all required fields in the message and all |
||||||
|
/// embedded messages are set. |
||||||
|
/// </summary> |
||||||
|
bool IsInitialized { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Only present in the nongeneric interface - useful for tests, but |
||||||
|
/// not as much in real life. |
||||||
|
/// </summary> |
||||||
|
IBuilder SetField(FieldDescriptor field, object value); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Only present in the nongeneric interface - useful for tests, but |
||||||
|
/// not as much in real life. |
||||||
|
/// </summary> |
||||||
|
IBuilder SetRepeatedField(FieldDescriptor field, int index, object value); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Behaves like the equivalent property in IMessage<T>. |
||||||
|
/// The returned map may or may not reflect future changes to the builder. |
||||||
|
/// Either way, the returned map is unmodifiable. |
||||||
|
/// </summary> |
||||||
|
IDictionary<FieldDescriptor, object> AllFields { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Allows getting and setting of a field. |
||||||
|
/// <see cref="IMessage{TMessage, TBuilder}.Item(FieldDescriptor)"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="field"></param> |
||||||
|
/// <returns></returns> |
||||||
|
object this[FieldDescriptor field] { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Get the message's type descriptor. |
||||||
|
/// <see cref="IMessage{TMessage, TBuilder}.DescriptorForType"/> |
||||||
|
/// </summary> |
||||||
|
MessageDescriptor DescriptorForType { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <see cref="IMessage{TMessage, TBuilder}.GetRepeatedFieldCount"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="field"></param> |
||||||
|
/// <returns></returns> |
||||||
|
int GetRepeatedFieldCount(FieldDescriptor field); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Allows getting and setting of a repeated field value. |
||||||
|
/// <see cref="IMessage{TMessage, TBuilder}.Item(FieldDescriptor, int)"/> |
||||||
|
/// </summary> |
||||||
|
object this[FieldDescriptor field, int index] { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <see cref="IMessage{TMessage, TBuilder}.HasField"/> |
||||||
|
/// </summary> |
||||||
|
bool HasField(FieldDescriptor field); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <see cref="IMessage{TMessage, TBuilder}.UnknownFields"/> |
||||||
|
/// </summary> |
||||||
|
UnknownFieldSet UnknownFields { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Create a builder for messages of the appropriate type for the given field. |
||||||
|
/// Messages built with this can then be passed to the various mutation properties |
||||||
|
/// and methods. |
||||||
|
/// </summary> |
||||||
|
IBuilder CreateBuilderForField(FieldDescriptor field); |
||||||
|
|
||||||
|
#region Methods which are like those of the generic form, but without any knowledge of the type parameters |
||||||
|
IBuilder WeakAddRepeatedField(FieldDescriptor field, object value); |
||||||
|
IBuilder WeakClear(); |
||||||
|
IBuilder WeakClearField(FieldDescriptor field); |
||||||
|
IBuilder WeakMergeFrom(IMessage message); |
||||||
|
IBuilder WeakMergeFrom(ByteString data); |
||||||
|
IBuilder WeakMergeFrom(ByteString data, ExtensionRegistry registry); |
||||||
|
IBuilder WeakMergeFrom(CodedInputStream input); |
||||||
|
IBuilder WeakMergeFrom(CodedInputStream input, ExtensionRegistry registry); |
||||||
|
IMessage WeakBuild(); |
||||||
|
IMessage WeakBuildPartial(); |
||||||
|
IBuilder WeakClone(); |
||||||
|
IMessage WeakDefaultInstanceForType { get; } |
||||||
|
#endregion |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Interface implemented by Protocol Message builders. |
||||||
|
/// TODO(jonskeet): Consider "SetXXX" methods returning the builder, as well as the properties. |
||||||
|
/// </summary> |
||||||
|
/// <typeparam name="TMessage">Type of message</typeparam> |
||||||
|
/// <typeparam name="TBuilder">Type of builder</typeparam> |
||||||
|
public interface IBuilder<TMessage, TBuilder> : IBuilder |
||||||
|
where TMessage : IMessage<TMessage, TBuilder> |
||||||
|
where TBuilder : IBuilder<TMessage, TBuilder> { |
||||||
|
|
||||||
|
TBuilder SetUnknownFields(UnknownFieldSet unknownFields); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Resets all fields to their default values. |
||||||
|
/// </summary> |
||||||
|
TBuilder Clear(); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Merge the specified other message into the message being |
||||||
|
/// built. Merging occurs as follows. For each field: |
||||||
|
/// For singular primitive fields, if the field is set in <paramref name="other"/>, |
||||||
|
/// then <paramref name="other"/>'s value overwrites the value in this message. |
||||||
|
/// For singular message fields, if the field is set in <paramref name="other"/>, |
||||||
|
/// it is merged into the corresponding sub-message of this message using the same |
||||||
|
/// merging rules. |
||||||
|
/// For repeated fields, the elements in <paramref name="other"/> are concatenated |
||||||
|
/// with the elements in this message. |
||||||
|
/// </summary> |
||||||
|
/// <param name="other"></param> |
||||||
|
/// <returns></returns> |
||||||
|
TBuilder MergeFrom(TMessage other); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Merge the specified other message which may be a different implementation of |
||||||
|
/// the same message descriptor. |
||||||
|
/// </summary> |
||||||
|
TBuilder MergeFrom(IMessage other); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Constructs the final message. Once this is called, this Builder instance |
||||||
|
/// is no longer valid, and calling any other method may throw a |
||||||
|
/// NullReferenceException. If you need to continue working with the builder |
||||||
|
/// after calling Build, call Clone first. |
||||||
|
/// </summary> |
||||||
|
/// <exception cref="UninitializedMessageException">the message |
||||||
|
/// is missing one or more required fields; use BuildPartial to bypass |
||||||
|
/// this check</exception> |
||||||
|
TMessage Build(); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Like Build(), but does not throw an exception if the message is missing |
||||||
|
/// required fields. Instead, a partial message is returned. |
||||||
|
/// </summary> |
||||||
|
TMessage BuildPartial(); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Clones this builder. |
||||||
|
/// TODO(jonskeet): Explain depth of clone. |
||||||
|
/// </summary> |
||||||
|
TBuilder Clone(); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Parses a message of this type from the input and merges it with this |
||||||
|
/// message, as if using MergeFrom(IMessage<T>). |
||||||
|
/// </summary> |
||||||
|
/// <remarks> |
||||||
|
/// Warning: This does not verify that all required fields are present |
||||||
|
/// in the input message. If you call Build() without setting all |
||||||
|
/// required fields, it will throw an UninitializedMessageException. |
||||||
|
/// There are a few good ways to deal with this: |
||||||
|
/// <list> |
||||||
|
/// <item>Call IsInitialized to verify to verify that all required fields are |
||||||
|
/// set before building.</item> |
||||||
|
/// <item>Parse the message separately using one of the static ParseFrom |
||||||
|
/// methods, then use MergeFrom(IMessage<T>) to merge it with |
||||||
|
/// this one. ParseFrom will throw an InvalidProtocolBufferException |
||||||
|
/// (an IOException) if some required fields are missing. |
||||||
|
/// Use BuildPartial to build, which ignores missing required fields. |
||||||
|
/// </list> |
||||||
|
/// </remarks> |
||||||
|
TBuilder MergeFrom(CodedInputStream input); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Like MergeFrom(CodedInputStream), but also parses extensions. |
||||||
|
/// The extensions that you want to be able to parse must be registered |
||||||
|
/// in <paramref name="extensionRegistry"/>. Extensions not in the registry |
||||||
|
/// will be treated as unknown fields. |
||||||
|
/// </summary> |
||||||
|
TBuilder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Get's the message's type's default instance. |
||||||
|
/// <see cref="IMessage{TMessage}.DefaultInstanceForType" /> |
||||||
|
/// </summary> |
||||||
|
TMessage DefaultInstanceForType { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Clears the field. This is exactly equivalent to calling the generated |
||||||
|
/// Clear method corresponding to the field. |
||||||
|
/// </summary> |
||||||
|
/// <param name="field"></param> |
||||||
|
/// <returns></returns> |
||||||
|
TBuilder ClearField(FieldDescriptor field); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Appends the given value as a new element for the specified repeated field. |
||||||
|
/// </summary> |
||||||
|
/// <exception cref="ArgumentException">the field is not a repeated field, |
||||||
|
/// the field does not belong to this builder's type, or the value is |
||||||
|
/// of the incorrect type |
||||||
|
/// </exception> |
||||||
|
TBuilder AddRepeatedField(FieldDescriptor field, object value); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Merge some unknown fields into the set for this message. |
||||||
|
/// </summary> |
||||||
|
TBuilder MergeUnknownFields(UnknownFieldSet unknownFields); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Like MergeFrom(Stream), but does not read until the end of the file. |
||||||
|
/// Instead, the size of the message (encoded as a varint) is read first, |
||||||
|
/// then the message data. Use Message.WriteDelimitedTo(Stream) to |
||||||
|
/// write messages in this format. |
||||||
|
/// </summary> |
||||||
|
/// <param name="input"></param> |
||||||
|
TBuilder MergeDelimitedFrom(Stream input); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Like MergeDelimitedFrom(Stream) but supporting extensions. |
||||||
|
/// </summary> |
||||||
|
TBuilder MergeDelimitedFrom(Stream input, ExtensionRegistry extensionRegistry); |
||||||
|
|
||||||
|
#region Convenience methods |
||||||
|
/// <summary> |
||||||
|
/// Parse <paramref name="data"/> as a message of this type and merge |
||||||
|
/// it with the message being built. This is just a small wrapper around |
||||||
|
/// MergeFrom(CodedInputStream). |
||||||
|
/// </summary> |
||||||
|
TBuilder MergeFrom(ByteString data); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Parse <paramref name="data"/> as a message of this type and merge |
||||||
|
/// it with the message being built. This is just a small wrapper around |
||||||
|
/// MergeFrom(CodedInputStream, ExtensionRegistry). |
||||||
|
/// </summary> |
||||||
|
TBuilder MergeFrom(ByteString data, ExtensionRegistry extensionRegistry); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Parse <paramref name="data"/> as a message of this type and merge |
||||||
|
/// it with the message being built. This is just a small wrapper around |
||||||
|
/// MergeFrom(CodedInputStream). |
||||||
|
/// </summary> |
||||||
|
TBuilder MergeFrom(byte[] data); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Parse <paramref name="data"/> as a message of this type and merge |
||||||
|
/// it with the message being built. This is just a small wrapper around |
||||||
|
/// MergeFrom(CodedInputStream, ExtensionRegistry). |
||||||
|
/// </summary> |
||||||
|
TBuilder MergeFrom(byte[] data, ExtensionRegistry extensionRegistry); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Parse <paramref name="input"/> as a message of this type and merge |
||||||
|
/// it with the message being built. This is just a small wrapper around |
||||||
|
/// MergeFrom(CodedInputStream). Note that this method always reads |
||||||
|
/// the entire input (unless it throws an exception). If you want it to |
||||||
|
/// stop earlier, you will need to wrap the input in a wrapper |
||||||
|
/// stream which limits reading. Or, use IMessage.WriteDelimitedTo(Stream) |
||||||
|
/// to write your message and MmergeDelimitedFrom(Stream) to read it. |
||||||
|
/// Despite usually reading the entire stream, this method never closes the stream. |
||||||
|
/// </summary> |
||||||
|
TBuilder MergeFrom(Stream input); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Parse <paramref name="input"/> as a message of this type and merge |
||||||
|
/// it with the message being built. This is just a small wrapper around |
||||||
|
/// MergeFrom(CodedInputStream, ExtensionRegistry). |
||||||
|
/// </summary> |
||||||
|
TBuilder MergeFrom(Stream input, ExtensionRegistry extensionRegistry); |
||||||
|
#endregion |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,236 @@ |
|||||||
|
#region Copyright notice and license |
||||||
|
// Protocol Buffers - Google's data interchange format |
||||||
|
// Copyright 2008 Google Inc. All rights reserved. |
||||||
|
// http://github.com/jskeet/dotnet-protobufs/ |
||||||
|
// Original C++/Java/Python code: |
||||||
|
// http://code.google.com/p/protobuf/ |
||||||
|
// |
||||||
|
// Redistribution and use in source and binary forms, with or without |
||||||
|
// modification, are permitted provided that the following conditions are |
||||||
|
// met: |
||||||
|
// |
||||||
|
// * Redistributions of source code must retain the above copyright |
||||||
|
// notice, this list of conditions and the following disclaimer. |
||||||
|
// * Redistributions in binary form must reproduce the above |
||||||
|
// copyright notice, this list of conditions and the following disclaimer |
||||||
|
// in the documentation and/or other materials provided with the |
||||||
|
// distribution. |
||||||
|
// * Neither the name of Google Inc. nor the names of its |
||||||
|
// contributors may be used to endorse or promote products derived from |
||||||
|
// this software without specific prior written permission. |
||||||
|
// |
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
#endregion |
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.IO; |
||||||
|
using Google.ProtocolBuffers.Descriptors; |
||||||
|
|
||||||
|
namespace Google.ProtocolBuffers { |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Non-generic interface used for all parts of the API which don't require |
||||||
|
/// any type knowledge. |
||||||
|
/// </summary> |
||||||
|
public interface IMessage { |
||||||
|
/// <summary> |
||||||
|
/// Returns the message's type's descriptor. This differs from the |
||||||
|
/// Descriptor property of each generated message class in that this |
||||||
|
/// method is an abstract method of IMessage whereas Descriptor is |
||||||
|
/// a static property of a specific class. They return the same thing. |
||||||
|
/// </summary> |
||||||
|
MessageDescriptor DescriptorForType { get; } |
||||||
|
/// <summary> |
||||||
|
/// Returns a collection of all the fields in this message which are set |
||||||
|
/// and their corresponding values. A singular ("required" or "optional") |
||||||
|
/// field is set iff HasField() returns true for that field. A "repeated" |
||||||
|
/// field is set iff GetRepeatedFieldSize() is greater than zero. The |
||||||
|
/// values are exactly what would be returned by calling |
||||||
|
/// GetField(FieldDescriptor) for each field. The map |
||||||
|
/// is guaranteed to be a sorted map, so iterating over it will return fields |
||||||
|
/// in order by field number. |
||||||
|
/// </summary> |
||||||
|
IDictionary<FieldDescriptor, object> AllFields { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Returns true if the given field is set. This is exactly equivalent |
||||||
|
/// to calling the generated "Has" property corresponding to the field. |
||||||
|
/// </summary> |
||||||
|
/// <exception cref="ArgumentException">the field is a repeated field, |
||||||
|
/// or it's not a field of this type</exception> |
||||||
|
bool HasField(FieldDescriptor field); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Obtains the value of the given field, or the default value if |
||||||
|
/// it isn't set. For value type fields, the boxed value is returned. |
||||||
|
/// For enum fields, the EnumValueDescriptor for the enum is returned. |
||||||
|
/// For embedded message fields, the sub-message |
||||||
|
/// is returned. For repeated fields, an IList<T> is returned. |
||||||
|
/// </summary> |
||||||
|
object this[FieldDescriptor field] { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Returns the number of elements of a repeated field. This is |
||||||
|
/// exactly equivalent to calling the generated "Count" property |
||||||
|
/// corresponding to the field. |
||||||
|
/// </summary> |
||||||
|
/// <exception cref="ArgumentException">the field is not a repeated field, |
||||||
|
/// or it's not a field of this type</exception> |
||||||
|
int GetRepeatedFieldCount(FieldDescriptor field); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Gets an element of a repeated field. For value type fields |
||||||
|
/// excluding enums, the boxed value is returned. For embedded |
||||||
|
/// message fields, the sub-message is returned. For enums, the |
||||||
|
/// relevant EnumValueDescriptor is returned. |
||||||
|
/// </summary> |
||||||
|
/// <exception cref="ArgumentException">the field is not a repeated field, |
||||||
|
/// or it's not a field of this type</exception> |
||||||
|
/// <exception cref="ArgumentOutOfRangeException">the index is out of |
||||||
|
/// range for the repeated field's value</exception> |
||||||
|
object this[FieldDescriptor field, int index] { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Returns the unknown fields for this message. |
||||||
|
/// </summary> |
||||||
|
UnknownFieldSet UnknownFields { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Returns true iff all required fields in the message and all embedded |
||||||
|
/// messages are set. |
||||||
|
/// </summary> |
||||||
|
bool IsInitialized { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Serializes the message and writes it to the given output stream. |
||||||
|
/// This does not flush or close the stream. |
||||||
|
/// </summary> |
||||||
|
/// <remarks> |
||||||
|
/// Protocol Buffers are not self-delimiting. Therefore, if you write |
||||||
|
/// any more data to the stream after the message, you must somehow ensure |
||||||
|
/// that the parser on the receiving end does not interpret this as being |
||||||
|
/// part of the protocol message. One way of doing this is by writing the size |
||||||
|
/// of the message before the data, then making sure you limit the input to |
||||||
|
/// that size when receiving the data. Alternatively, use WriteDelimitedTo(Stream). |
||||||
|
/// </remarks> |
||||||
|
void WriteTo(CodedOutputStream output); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Like WriteTo(Stream) but writes the size of the message as a varint before |
||||||
|
/// writing the data. This allows more data to be written to the stream after the |
||||||
|
/// message without the need to delimit the message data yourself. Use |
||||||
|
/// IBuilder.MergeDelimitedFrom(Stream) or the static method |
||||||
|
/// YourMessageType.ParseDelimitedFrom(Stream) to parse messages written by this method. |
||||||
|
/// </summary> |
||||||
|
/// <param name="output"></param> |
||||||
|
void WriteDelimitedTo(Stream output); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Returns the number of bytes required to encode this message. |
||||||
|
/// The result is only computed on the first call and memoized after that. |
||||||
|
/// </summary> |
||||||
|
int SerializedSize { get; } |
||||||
|
|
||||||
|
#region Comparison and hashing |
||||||
|
/// <summary> |
||||||
|
/// Compares the specified object with this message for equality. |
||||||
|
/// Returns true iff the given object is a message of the same type |
||||||
|
/// (as defined by DescriptorForType) and has identical values |
||||||
|
/// for all its fields. |
||||||
|
/// </summary> |
||||||
|
bool Equals(object other); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Returns the hash code value for this message. |
||||||
|
/// TODO(jonskeet): Specify the hash algorithm, but better than the Java one! |
||||||
|
/// </summary> |
||||||
|
int GetHashCode(); |
||||||
|
#endregion |
||||||
|
|
||||||
|
#region Convenience methods |
||||||
|
/// <summary> |
||||||
|
/// Converts the message to a string in protocol buffer text format. |
||||||
|
/// This is just a trivial wrapper around TextFormat.PrintToString. |
||||||
|
/// </summary> |
||||||
|
string ToString(); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Serializes the message to a ByteString. This is a trivial wrapper |
||||||
|
/// around WriteTo(CodedOutputStream). |
||||||
|
/// </summary> |
||||||
|
ByteString ToByteString(); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Serializes the message to a byte array. This is a trivial wrapper |
||||||
|
/// around WriteTo(CodedOutputStream). |
||||||
|
/// </summary> |
||||||
|
byte[] ToByteArray(); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Serializes the message and writes it to the given stream. |
||||||
|
/// This is just a wrapper around WriteTo(CodedOutputStream). This |
||||||
|
/// does not flush or close the stream. |
||||||
|
/// </summary> |
||||||
|
/// <param name="output"></param> |
||||||
|
void WriteTo(Stream output); |
||||||
|
#endregion |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Creates a builder for the type, but in a weakly typed manner. This |
||||||
|
/// is typically implemented by strongly typed messages by just returning |
||||||
|
/// the result of CreateBuilderForType. |
||||||
|
/// </summary> |
||||||
|
IBuilder WeakCreateBuilderForType(); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Creates a builder with the same contents as this message. This |
||||||
|
/// is typically implemented by strongly typed messages by just returning |
||||||
|
/// the result of ToBuilder. |
||||||
|
/// </summary> |
||||||
|
IBuilder WeakToBuilder(); |
||||||
|
|
||||||
|
IMessage WeakDefaultInstanceForType { get; } |
||||||
|
} |
||||||
|
|
||||||
|
public interface IMessage<TMessage> : IMessage { |
||||||
|
/// <summary> |
||||||
|
/// Returns an instance of this message type with all fields set to |
||||||
|
/// their default values. This may or may not be a singleton. This differs |
||||||
|
/// from the DefaultInstance property of each generated message class in that this |
||||||
|
/// method is an abstract method of IMessage whereas DefaultInstance is |
||||||
|
/// a static property of a specific class. They return the same thing. |
||||||
|
/// </summary> |
||||||
|
TMessage DefaultInstanceForType { get; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Type-safe interface for all generated messages to implement. |
||||||
|
/// </summary> |
||||||
|
public interface IMessage<TMessage, TBuilder> : IMessage<TMessage> |
||||||
|
where TMessage : IMessage<TMessage, TBuilder> |
||||||
|
where TBuilder : IBuilder<TMessage, TBuilder> { |
||||||
|
#region Builders |
||||||
|
/// <summary> |
||||||
|
/// Constructs a new builder for a message of the same type as this message. |
||||||
|
/// </summary> |
||||||
|
TBuilder CreateBuilderForType(); |
||||||
|
/// <summary> |
||||||
|
/// Creates a builder with the same contents as this current instance. |
||||||
|
/// This is typically implemented by strongly typed messages by just |
||||||
|
/// returning the result of ToBuilder(). |
||||||
|
/// </summary> |
||||||
|
TBuilder ToBuilder(); |
||||||
|
#endregion |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue