Commit 9e89b6e7 authored by Sydney Acksman's avatar Sydney Acksman Committed by Jie Luo

C# Proto2 feature : Extensions (#5350)

* Compiler changes (extensions)

* Generated changes (extensions)

* Library changes (extensions)

* Adjusted a summary to indicate ContainingType can be null for extensions

* Compiler changes (custom option review + access level review)

* Generated code changes (custom options + access review)

* Library changes (custom options + access review)

* Support C# 6 with library changes

* Access HasValue by property

* Set access level of all extension classes to internal (revert in next PR)

* Added null checks to custom options

* Rebase on master and regenerate Conformance

* Removed second dictionary from ExtensionSet

* Rebased compiler changes

* Rebased generated code changes

* Rebased library changes + review changes

* Add more safety checks to extension accessors

* Remove instances where extension sets were unnecessarily allocated

* Remove cleared items from sets
Empty sets are now made null
IExtensionMessage -> IExtendableMessage

* Remove dead code from IExtensionValue impls

* Clean both repeated and single value extensions

* Add GetOrRegister method for repeated fields and allow clearing repeated extensions

* Add type safe ClearExtension methods, remove non-generic IExtendableMessage interface.

* Simplify ExtensionSet.TryMergeFieldFrom

* Rebase on master to resolve conflicts

* Fix Makefile.am

* Add ObjectIntPair to Makefile.am
parent 3ee24bca
......@@ -148,12 +148,17 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf/Compatibility/PropertyInfoExtensions.cs \
csharp/src/Google.Protobuf/Compatibility/StreamExtensions.cs \
csharp/src/Google.Protobuf/Compatibility/TypeExtensions.cs \
csharp/src/Google.Protobuf/Extension.cs \
csharp/src/Google.Protobuf/ExtensionRegistry.cs \
csharp/src/Google.Protobuf/ExtensionSet.cs \
csharp/src/Google.Protobuf/ExtensionValue.cs \
csharp/src/Google.Protobuf/FieldCodec.cs \
csharp/src/Google.Protobuf/FieldMaskTree.cs \
csharp/src/Google.Protobuf/FrameworkPortability.cs \
csharp/src/Google.Protobuf/Google.Protobuf.csproj \
csharp/src/Google.Protobuf/ICustomDiagnosticMessage.cs \
csharp/src/Google.Protobuf/IDeepCloneable.cs \
csharp/src/Google.Protobuf/IExtendableMessage.cs \
csharp/src/Google.Protobuf/IMessage.cs \
csharp/src/Google.Protobuf/InvalidJsonException.cs \
csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs \
......@@ -164,6 +169,7 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf/LimitedInputStream.cs \
csharp/src/Google.Protobuf/MessageExtensions.cs \
csharp/src/Google.Protobuf/MessageParser.cs \
csharp/src/Google.Protobuf/ObjectIntPair.cs \
csharp/src/Google.Protobuf/ProtoPreconditions.cs \
csharp/src/Google.Protobuf/Properties/AssemblyInfo.cs \
csharp/src/Google.Protobuf/Reflection/CustomOptions.cs \
......@@ -175,6 +181,8 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf/Reflection/DescriptorValidationException.cs \
csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs \
csharp/src/Google.Protobuf/Reflection/EnumValueDescriptor.cs \
csharp/src/Google.Protobuf/Reflection/ExtensionAccessor.cs \
csharp/src/Google.Protobuf/Reflection/ExtensionCollection.cs \
csharp/src/Google.Protobuf/Reflection/FieldAccessorBase.cs \
csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs \
csharp/src/Google.Protobuf/Reflection/FieldType.cs \
......
......@@ -37,9 +37,9 @@ namespace Google.Protobuf.Examples.AddressBook {
"ZHJlc3NCb29rYgZwcm90bzM="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, },
new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.Person), global::Google.Protobuf.Examples.AddressBook.Person.Parser, new[]{ "Name", "Id", "Email", "Phones", "LastUpdated" }, null, new[]{ typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType) }, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber), global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber.Parser, new[]{ "Number", "Type" }, null, null, null)}),
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.AddressBook), global::Google.Protobuf.Examples.AddressBook.AddressBook.Parser, new[]{ "People" }, null, null, null)
new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.Person), global::Google.Protobuf.Examples.AddressBook.Person.Parser, new[]{ "Name", "Id", "Email", "Phones", "LastUpdated" }, null, new[]{ typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType) }, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber), global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber.Parser, new[]{ "Number", "Type" }, null, null, null, null)}),
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.AddressBook), global::Google.Protobuf.Examples.AddressBook.AddressBook.Parser, new[]{ "People" }, null, null, null, null)
}));
}
#endregion
......@@ -347,7 +347,7 @@ namespace Google.Protobuf.Examples.AddressBook {
/// <summary>Field number for the "type" field.</summary>
public const int TypeFieldNumber = 2;
private global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType type_ = 0;
private global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType type_ = global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.Mobile;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType Type {
get { return type_; }
......@@ -378,7 +378,7 @@ namespace Google.Protobuf.Examples.AddressBook {
public override int GetHashCode() {
int hash = 1;
if (Number.Length != 0) hash ^= Number.GetHashCode();
if (Type != 0) hash ^= Type.GetHashCode();
if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.Mobile) hash ^= Type.GetHashCode();
if (_unknownFields != null) {
hash ^= _unknownFields.GetHashCode();
}
......@@ -396,7 +396,7 @@ namespace Google.Protobuf.Examples.AddressBook {
output.WriteRawTag(10);
output.WriteString(Number);
}
if (Type != 0) {
if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.Mobile) {
output.WriteRawTag(16);
output.WriteEnum((int) Type);
}
......@@ -411,7 +411,7 @@ namespace Google.Protobuf.Examples.AddressBook {
if (Number.Length != 0) {
size += 1 + pb::CodedOutputStream.ComputeStringSize(Number);
}
if (Type != 0) {
if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.Mobile) {
size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Type);
}
if (_unknownFields != null) {
......@@ -428,7 +428,7 @@ namespace Google.Protobuf.Examples.AddressBook {
if (other.Number.Length != 0) {
Number = other.Number;
}
if (other.Type != 0) {
if (other.Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.Mobile) {
Type = other.Type;
}
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
......
......@@ -55,9 +55,9 @@ namespace Benchmarks.Proto3 {
"YmVuY2htYXJrc0gB+AEBYgZwcm90bzM="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Benchmarks.Proto3.GoogleMessage1), global::Benchmarks.Proto3.GoogleMessage1.Parser, new[]{ "Field1", "Field9", "Field18", "Field80", "Field81", "Field2", "Field3", "Field280", "Field6", "Field22", "Field4", "Field5", "Field59", "Field7", "Field16", "Field130", "Field12", "Field17", "Field13", "Field14", "Field104", "Field100", "Field101", "Field102", "Field103", "Field29", "Field30", "Field60", "Field271", "Field272", "Field150", "Field23", "Field24", "Field25", "Field15", "Field78", "Field67", "Field68", "Field128", "Field129", "Field131" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Benchmarks.Proto3.GoogleMessage1SubMessage), global::Benchmarks.Proto3.GoogleMessage1SubMessage.Parser, new[]{ "Field1", "Field2", "Field3", "Field15", "Field12", "Field13", "Field14", "Field16", "Field19", "Field20", "Field28", "Field21", "Field22", "Field23", "Field206", "Field203", "Field204", "Field205", "Field207", "Field300" }, null, null, null)
new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Benchmarks.Proto3.GoogleMessage1), global::Benchmarks.Proto3.GoogleMessage1.Parser, new[]{ "Field1", "Field9", "Field18", "Field80", "Field81", "Field2", "Field3", "Field280", "Field6", "Field22", "Field4", "Field5", "Field59", "Field7", "Field16", "Field130", "Field12", "Field17", "Field13", "Field14", "Field104", "Field100", "Field101", "Field102", "Field103", "Field29", "Field30", "Field60", "Field271", "Field272", "Field150", "Field23", "Field24", "Field25", "Field15", "Field78", "Field67", "Field68", "Field128", "Field129", "Field131" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Benchmarks.Proto3.GoogleMessage1SubMessage), global::Benchmarks.Proto3.GoogleMessage1SubMessage.Parser, new[]{ "Field1", "Field2", "Field3", "Field15", "Field12", "Field13", "Field14", "Field16", "Field19", "Field20", "Field28", "Field21", "Field22", "Field23", "Field206", "Field203", "Field204", "Field205", "Field207", "Field300" }, null, null, null, null)
}));
}
#endregion
......
......@@ -30,8 +30,8 @@ namespace Benchmarks {
"a3NiBnByb3RvMw=="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Benchmarks.BenchmarkDataset), global::Benchmarks.BenchmarkDataset.Parser, new[]{ "Name", "MessageName", "Payload" }, null, null, null)
new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Benchmarks.BenchmarkDataset), global::Benchmarks.BenchmarkDataset.Parser, new[]{ "Name", "MessageName", "Payload" }, null, null, null, null)
}));
}
#endregion
......
......@@ -48,11 +48,11 @@ namespace Conformance {
"Z2xlLnByb3RvYnVmLmNvbmZvcm1hbmNlYgZwcm90bzM="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Conformance.WireFormat), typeof(global::Conformance.TestCategory), }, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.FailureSet), global::Conformance.FailureSet.Parser, new[]{ "Failure" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceRequest), global::Conformance.ConformanceRequest.Parser, new[]{ "ProtobufPayload", "JsonPayload", "JspbPayload", "TextPayload", "RequestedOutputFormat", "MessageType", "TestCategory", "JspbEncodingOptions", "PrintUnknownFields" }, new[]{ "Payload" }, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceResponse), global::Conformance.ConformanceResponse.Parser, new[]{ "ParseError", "SerializeError", "RuntimeError", "ProtobufPayload", "JsonPayload", "Skipped", "JspbPayload", "TextPayload" }, new[]{ "Result" }, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.JspbEncodingConfig), global::Conformance.JspbEncodingConfig.Parser, new[]{ "UseJspbArrayAnyFormat" }, null, null, null)
new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Conformance.WireFormat), typeof(global::Conformance.TestCategory), }, null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.FailureSet), global::Conformance.FailureSet.Parser, new[]{ "Failure" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceRequest), global::Conformance.ConformanceRequest.Parser, new[]{ "ProtobufPayload", "JsonPayload", "JspbPayload", "TextPayload", "RequestedOutputFormat", "MessageType", "TestCategory", "JspbEncodingOptions", "PrintUnknownFields" }, new[]{ "Payload" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceResponse), global::Conformance.ConformanceResponse.Parser, new[]{ "ParseError", "SerializeError", "RuntimeError", "ProtobufPayload", "JsonPayload", "Skipped", "JspbPayload", "TextPayload" }, new[]{ "Result" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.JspbEncodingConfig), global::Conformance.JspbEncodingConfig.Parser, new[]{ "UseJspbArrayAnyFormat" }, null, null, null, null)
}));
}
#endregion
......@@ -337,7 +337,7 @@ namespace Conformance {
/// <summary>Field number for the "requested_output_format" field.</summary>
public const int RequestedOutputFormatFieldNumber = 3;
private global::Conformance.WireFormat requestedOutputFormat_ = 0;
private global::Conformance.WireFormat requestedOutputFormat_ = global::Conformance.WireFormat.Unspecified;
/// <summary>
/// Which format should the testee serialize its message to?
/// </summary>
......@@ -367,7 +367,7 @@ namespace Conformance {
/// <summary>Field number for the "test_category" field.</summary>
public const int TestCategoryFieldNumber = 5;
private global::Conformance.TestCategory testCategory_ = 0;
private global::Conformance.TestCategory testCategory_ = global::Conformance.TestCategory.UnspecifiedTest;
/// <summary>
/// Each test is given a specific test category. Some category may need
/// spedific support in testee programs. Refer to the defintion of TestCategory
......@@ -464,9 +464,9 @@ namespace Conformance {
if (payloadCase_ == PayloadOneofCase.JsonPayload) hash ^= JsonPayload.GetHashCode();
if (payloadCase_ == PayloadOneofCase.JspbPayload) hash ^= JspbPayload.GetHashCode();
if (payloadCase_ == PayloadOneofCase.TextPayload) hash ^= TextPayload.GetHashCode();
if (RequestedOutputFormat != 0) hash ^= RequestedOutputFormat.GetHashCode();
if (RequestedOutputFormat != global::Conformance.WireFormat.Unspecified) hash ^= RequestedOutputFormat.GetHashCode();
if (MessageType.Length != 0) hash ^= MessageType.GetHashCode();
if (TestCategory != 0) hash ^= TestCategory.GetHashCode();
if (TestCategory != global::Conformance.TestCategory.UnspecifiedTest) hash ^= TestCategory.GetHashCode();
if (jspbEncodingOptions_ != null) hash ^= JspbEncodingOptions.GetHashCode();
if (PrintUnknownFields != false) hash ^= PrintUnknownFields.GetHashCode();
hash ^= (int) payloadCase_;
......@@ -491,7 +491,7 @@ namespace Conformance {
output.WriteRawTag(18);
output.WriteString(JsonPayload);
}
if (RequestedOutputFormat != 0) {
if (RequestedOutputFormat != global::Conformance.WireFormat.Unspecified) {
output.WriteRawTag(24);
output.WriteEnum((int) RequestedOutputFormat);
}
......@@ -499,7 +499,7 @@ namespace Conformance {
output.WriteRawTag(34);
output.WriteString(MessageType);
}
if (TestCategory != 0) {
if (TestCategory != global::Conformance.TestCategory.UnspecifiedTest) {
output.WriteRawTag(40);
output.WriteEnum((int) TestCategory);
}
......@@ -539,13 +539,13 @@ namespace Conformance {
if (payloadCase_ == PayloadOneofCase.TextPayload) {
size += 1 + pb::CodedOutputStream.ComputeStringSize(TextPayload);
}
if (RequestedOutputFormat != 0) {
if (RequestedOutputFormat != global::Conformance.WireFormat.Unspecified) {
size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) RequestedOutputFormat);
}
if (MessageType.Length != 0) {
size += 1 + pb::CodedOutputStream.ComputeStringSize(MessageType);
}
if (TestCategory != 0) {
if (TestCategory != global::Conformance.TestCategory.UnspecifiedTest) {
size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) TestCategory);
}
if (jspbEncodingOptions_ != null) {
......@@ -565,13 +565,13 @@ namespace Conformance {
if (other == null) {
return;
}
if (other.RequestedOutputFormat != 0) {
if (other.RequestedOutputFormat != global::Conformance.WireFormat.Unspecified) {
RequestedOutputFormat = other.RequestedOutputFormat;
}
if (other.MessageType.Length != 0) {
MessageType = other.MessageType;
}
if (other.TestCategory != 0) {
if (other.TestCategory != global::Conformance.TestCategory.UnspecifiedTest) {
TestCategory = other.TestCategory;
}
if (other.jspbEncodingOptions_ != null) {
......
......@@ -99,66 +99,6 @@ namespace Google.Protobuf.Test.Reflection
{
delegate bool OptionFetcher<T>(int field, out T value);
[Test]
public void EmptyOptionsIsShared()
{
var structOptions = Struct.Descriptor.CustomOptions;
var timestampOptions = Struct.Descriptor.CustomOptions;
Assert.AreSame(structOptions, timestampOptions);
}
[Test]
public void SimpleIntegerTest()
{
var stream = new MemoryStream();
var output = new CodedOutputStream(stream);
output.WriteTag(MakeTag(1, WireType.Varint));
output.WriteInt32(1234567);
output.Flush();
stream.Position = 0;
var input = new CodedInputStream(stream);
input.ReadTag();
var options = CustomOptions.Empty;
options = options.ReadOrSkipUnknownField(input);
int intValue;
Assert.True(options.TryGetInt32(1, out intValue));
Assert.AreEqual(1234567, intValue);
string stringValue;
// No ByteString stored values
Assert.False(options.TryGetString(1, out stringValue));
// Nothing stored for field 2
Assert.False(options.TryGetInt32(2, out intValue));
}
[Test]
public void SimpleStringTest()
{
var stream = new MemoryStream();
var output = new CodedOutputStream(stream);
output.WriteTag(MakeTag(1, WireType.LengthDelimited));
output.WriteString("value");
output.Flush();
stream.Position = 0;
var input = new CodedInputStream(stream);
input.ReadTag();
var options = CustomOptions.Empty;
options = options.ReadOrSkipUnknownField(input);
string stringValue;
Assert.True(options.TryGetString(1, out stringValue));
Assert.AreEqual("value", stringValue);
int intValue;
// No numeric stored values
Assert.False(options.TryGetInt32(1, out intValue));
// Nothing stored for field 2
Assert.False(options.TryGetString(2, out stringValue));
}
[Test]
public void ScalarOptions()
{
......@@ -168,7 +108,7 @@ namespace Google.Protobuf.Test.Reflection
AssertOption(1.234567890123456789d, options.TryGetDouble, DoubleOpt);
AssertOption("Hello, \"World\"", options.TryGetString, StringOpt);
AssertOption(ByteString.CopyFromUtf8("Hello\0World"), options.TryGetBytes, BytesOpt);
AssertOption((int) TestEnumType.TestOptionEnumType2, options.TryGetInt32, EnumOpt);
AssertOption((int)TestEnumType.TestOptionEnumType2, options.TryGetInt32, EnumOpt);
}
[Test]
......@@ -177,11 +117,12 @@ namespace Google.Protobuf.Test.Reflection
var options = VariousComplexOptions.Descriptor.CustomOptions;
AssertOption(new ComplexOptionType1 { Foo = 42, Foo4 = { 99, 88 } }, options.TryGetMessage, ComplexOpt1);
AssertOption(new ComplexOptionType2
{
Baz = 987, Bar = new ComplexOptionType1 { Foo = 743 },
Fred = new ComplexOptionType4 { Waldo = 321 },
Barney = { new ComplexOptionType4 { Waldo = 101 }, new ComplexOptionType4 { Waldo = 212 } }
},
{
Baz = 987,
Bar = new ComplexOptionType1 { Foo = 743 },
Fred = new ComplexOptionType4 { Waldo = 321 },
Barney = { new ComplexOptionType4 { Waldo = 101 }, new ComplexOptionType4 { Waldo = 212 } }
},
options.TryGetMessage, ComplexOpt2);
AssertOption(new ComplexOptionType3 { Qux = 9 }, options.TryGetMessage, ComplexOpt3);
}
......@@ -195,7 +136,7 @@ namespace Google.Protobuf.Test.Reflection
var messageOptions = TestMessageWithCustomOptions.Descriptor.CustomOptions;
AssertOption(-56, messageOptions.TryGetInt32, MessageOpt1);
var fieldOptions = TestMessageWithCustomOptions.Descriptor.Fields["field1"] .CustomOptions;
var fieldOptions = TestMessageWithCustomOptions.Descriptor.Fields["field1"].CustomOptions;
AssertOption(8765432109UL, fieldOptions.TryGetFixed64, FieldOpt1);
var oneofOptions = TestMessageWithCustomOptions.Descriptor.Oneofs[0].CustomOptions;
......@@ -213,7 +154,7 @@ namespace Google.Protobuf.Test.Reflection
AssertOption(-9876543210, serviceOptions.TryGetSInt64, ServiceOpt1);
var methodOptions = service.Methods[0].CustomOptions;
AssertOption((int) UnitTest.Issues.TestProtos.MethodOpt1.Val2, methodOptions.TryGetInt32, CustomOptionNumber.MethodOpt1);
AssertOption((int)UnitTest.Issues.TestProtos.MethodOpt1.Val2, methodOptions.TryGetInt32, CustomOptionNumber.MethodOpt1);
}
[Test]
......@@ -264,7 +205,7 @@ namespace Google.Protobuf.Test.Reflection
private void AssertOption<T>(T expected, OptionFetcher<T> fetcher, CustomOptionNumber field)
{
T actual;
Assert.IsTrue(fetcher((int) field, out actual));
Assert.IsTrue(fetcher((int)field, out actual));
Assert.AreEqual(expected, actual);
}
}
......
......@@ -32,8 +32,8 @@ namespace Google.Protobuf.TestProtos {
"cm90b2J1Zi5UZXN0UHJvdG9zUABiBnByb3RvMw=="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.TestProtos.UnittestImportPublicProto3Reflection.Descriptor, },
new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.TestProtos.ImportEnum), }, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.ImportMessage), global::Google.Protobuf.TestProtos.ImportMessage.Parser, new[]{ "D" }, null, null, null)
new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.TestProtos.ImportEnum), }, null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.ImportMessage), global::Google.Protobuf.TestProtos.ImportMessage.Parser, new[]{ "D" }, null, null, null, null)
}));
}
#endregion
......
......@@ -30,8 +30,8 @@ namespace Google.Protobuf.TestProtos {
"Mw=="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.PublicImportMessage), global::Google.Protobuf.TestProtos.PublicImportMessage.Parser, new[]{ "E" }, null, null, null)
new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.PublicImportMessage), global::Google.Protobuf.TestProtos.PublicImportMessage.Parser, new[]{ "E" }, null, null, null, null)
}));
}
#endregion
......
......@@ -272,6 +272,11 @@ namespace Google.Protobuf
/// </summary>
internal bool DiscardUnknownFields { get; set; }
/// <summary>
/// Internal-only property; provides extension identifiers to compatible messages while parsing.
/// </summary>
internal ExtensionRegistry ExtensionRegistry { get; set; }
/// <summary>
/// Disposes of this instance, potentially closing any underlying stream.
/// </summary>
......@@ -574,7 +579,7 @@ namespace Google.Protobuf
/// <summary>
/// Reads an embedded message field value from the stream.
/// </summary>
/// </summary>
public void ReadMessage(IMessage builder)
{
int length = ReadLength();
......
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// 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;
namespace Google.Protobuf
{
/// <summary>
/// Represents a non-generic extension definition
/// </summary>
public abstract class Extension
{
internal abstract Type TargetType { get; }
/// <summary>
/// Internal use. Creates a new extension with the specified field number.
/// </summary>
protected Extension(int number)
{
FieldNumber = number;
}
internal abstract IExtensionValue CreateValue();
/// <summary>
/// Gets the field number of this extension
/// </summary>
public int FieldNumber { get; }
}
/// <summary>
/// Represents a type-safe extension identifier used for getting and setting single extension values in <see cref="IExtendableMessage{T}"/> instances
/// </summary>
/// <typeparam name="TTarget">The message type this field applies to</typeparam>
/// <typeparam name="TValue">The field value type of this extension</typeparam>
public sealed class Extension<TTarget, TValue> : Extension where TTarget : IExtendableMessage<TTarget>
{
private readonly FieldCodec<TValue> codec;
/// <summary>
/// Creates a new extension identifier with the specified field number and codec
/// </summary>
public Extension(int number, FieldCodec<TValue> codec) : base(number)
{
this.codec = codec;
}
internal TValue DefaultValue => codec.DefaultValue;
internal override Type TargetType => typeof(TTarget);
internal override IExtensionValue CreateValue()
{
return new ExtensionValue<TValue>(codec);
}
}
/// <summary>
/// Represents a type-safe extension identifier used for getting repeated extension values in <see cref="IExtendableMessage{T}"/> instances
/// </summary>
/// <typeparam name="TTarget">The message type this field applies to</typeparam>
/// <typeparam name="TValue">The repeated field value type of this extension</typeparam>
public sealed class RepeatedExtension<TTarget, TValue> : Extension where TTarget : IExtendableMessage<TTarget>
{
private readonly FieldCodec<TValue> codec;
/// <summary>
/// Creates a new repeated extension identifier with the specified field number and codec
/// </summary>
public RepeatedExtension(int number, FieldCodec<TValue> codec) : base(number)
{
this.codec = codec;
}
internal override Type TargetType => typeof(TTarget);
internal override IExtensionValue CreateValue()
{
return new RepeatedExtensionValue<TValue>(codec);
}
}
}
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// 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;
using System.Collections.Generic;
using System.Linq;
namespace Google.Protobuf
{
/// <summary>
/// Provides extensions to messages while parsing
/// </summary>
public sealed class ExtensionRegistry : ICollection<Extension>, IDeepCloneable<ExtensionRegistry>
{
private IDictionary<ObjectIntPair<Type>, Extension> extensions;
/// <summary>
/// Creates a new empty extension registry
/// </summary>
public ExtensionRegistry()
{
extensions = new Dictionary<ObjectIntPair<Type>, Extension>();
}
private ExtensionRegistry(IDictionary<ObjectIntPair<Type>, Extension> collection)
{
extensions = collection.ToDictionary(k => k.Key, v => v.Value);
}
/// <summary>
/// Gets the total number of extensions in this extension registry
/// </summary>
public int Count => extensions.Count;
/// <summary>
/// Returns whether the registry is readonly
/// </summary>
bool ICollection<Extension>.IsReadOnly => false;
internal bool ContainsInputField(CodedInputStream stream, Type target, out Extension extension)
{
return extensions.TryGetValue(new ObjectIntPair<Type>(target, WireFormat.GetTagFieldNumber(stream.LastTag)), out extension);
}
/// <summary>
/// Adds the specified extension to the registry
/// </summary>
public void Add(Extension extension)
{
ProtoPreconditions.CheckNotNull(extension, nameof(extension));
extensions.Add(new ObjectIntPair<Type>(extension.TargetType, extension.FieldNumber), extension);
}
/// <summary>
/// Adds the specified extensions to the registry
/// </summary>
public void Add(params Extension[] newExtensions)
{
ProtoPreconditions.CheckNotNull(newExtensions, nameof(newExtensions));
Add((IEnumerable<Extension>)newExtensions);
}
/// <summary>
/// Adds the specified extensions to the reigstry
/// </summary>
public void Add(IEnumerable<Extension> newExtensions)
{
ProtoPreconditions.CheckNotNull(newExtensions, nameof(newExtensions));
foreach (var extension in newExtensions)
Add(extension);
}
/// <summary>
/// Clears the registry of all values
/// </summary>
public void Clear()
{
extensions.Clear();
}
/// <summary>
/// Gets whether the extension registry contains the specified extension
/// </summary>
public bool Contains(Extension item)
{
ProtoPreconditions.CheckNotNull(item, nameof(item));
return extensions.ContainsKey(new ObjectIntPair<Type>(item.TargetType, item.FieldNumber));
}
/// <summary>
/// Copies the arrays in the registry set to the specified array at the specified index
/// </summary>
/// <param name="array">The array to copy to</param>
/// <param name="arrayIndex">The array index to start at</param>
void ICollection<Extension>.CopyTo(Extension[] array, int arrayIndex)
{
ProtoPreconditions.CheckNotNull(array, nameof(array));
if (arrayIndex < 0 || arrayIndex >= array.Length)
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
if (array.Length - arrayIndex < Count)
throw new ArgumentException("The provided array is shorter than the number of elements in the registry");
for (int i = 0; i < array.Length; i++)
{
Extension extension = array[i];
extensions.Add(new ObjectIntPair<Type>(extension.TargetType, extension.FieldNumber), extension);
}
}
/// <summary>
/// Returns an enumerator to enumerate through the items in the registry
/// </summary>
/// <returns>Returns an enumerator for the extensions in this registry</returns>
public IEnumerator<Extension> GetEnumerator()
{
return extensions.Values.GetEnumerator();
}
/// <summary>
/// Removes the specified extension from the set
/// </summary>
/// <param name="item">The extension</param>
/// <returns><c>true</c> if the extension was removed, otherwise <c>false</c></returns>
public bool Remove(Extension item)
{
ProtoPreconditions.CheckNotNull(item, nameof(item));
return extensions.Remove(new ObjectIntPair<Type>(item.TargetType, item.FieldNumber));
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
/// Clones the registry into a new registry
/// </summary>
public ExtensionRegistry Clone()
{
return new ExtensionRegistry(extensions);
}
}
}
This diff is collapsed.
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// 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 Google.Protobuf.Collections;
using System;
namespace Google.Protobuf
{
internal interface IExtensionValue : IEquatable<IExtensionValue>, IDeepCloneable<IExtensionValue>
{
void MergeFrom(CodedInputStream input);
void MergeFrom(IExtensionValue value);
void WriteTo(CodedOutputStream output);
int CalculateSize();
}
internal sealed class ExtensionValue<T> : IExtensionValue
{
private bool hasValue;
private T field;
private FieldCodec<T> codec;
internal ExtensionValue(FieldCodec<T> codec)
{
this.codec = codec;
field = codec.DefaultValue;
}
public int CalculateSize()
{
if (!hasValue)
{
return 0;
}
return codec.CalculateSizeWithTag(field);
}
public IExtensionValue Clone()
{
return new ExtensionValue<T>(codec)
{
hasValue = hasValue,
field = field is IDeepCloneable<T> ? (field as IDeepCloneable<T>).Clone() : field
};
}
public bool Equals(IExtensionValue other)
{
if (ReferenceEquals(this, other))
return true;
return other is ExtensionValue<T>
&& codec.Equals((other as ExtensionValue<T>).codec)
&& hasValue.Equals((other as ExtensionValue<T>).hasValue)
&& Equals(field, (other as ExtensionValue<T>).field);
// we check for equality in the codec since we could have equal field values however the values could be written in different ways
}
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 31 + hasValue.GetHashCode();
hash = hash * 31 + field.GetHashCode();
hash = hash * 31 + codec.GetHashCode();
return hash;
}
}
public void MergeFrom(CodedInputStream input)
{
hasValue = true;
codec.ValueMerger(input, ref field);
}
public void MergeFrom(IExtensionValue value)
{
if (value is ExtensionValue<T>)
{
var extensionValue = value as ExtensionValue<T>;
if (extensionValue.hasValue)
{
hasValue |= codec.FieldMerger(ref field, extensionValue.field);
}
}
}
public void WriteTo(CodedOutputStream output)
{
if (hasValue)
{
output.WriteTag(codec.Tag);
codec.ValueWriter(output, field);
if (codec.EndTag != 0)
{
output.WriteTag(codec.EndTag);
}
}
}
public T GetValue() => field;
public void SetValue(T value)
{
hasValue = true;
field = value;
}
public bool HasValue => hasValue;
}
internal sealed class RepeatedExtensionValue<T> : IExtensionValue
{
private RepeatedField<T> field;
private readonly FieldCodec<T> codec;
internal RepeatedExtensionValue(FieldCodec<T> codec)
{
this.codec = codec;
field = new RepeatedField<T>();
}
public int CalculateSize()
{
return field.CalculateSize(codec);
}
public IExtensionValue Clone()
{
return new RepeatedExtensionValue<T>(codec)
{
field = field.Clone()
};
}
public bool Equals(IExtensionValue other)
{
if (ReferenceEquals(this, other))
return true;
return other is RepeatedExtensionValue<T>
&& field.Equals((other as RepeatedExtensionValue<T>).field)
&& codec.Equals((other as RepeatedExtensionValue<T>).codec);
}
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 31 + field.GetHashCode();
hash = hash * 31 + codec.GetHashCode();
return hash;
}
}
public void MergeFrom(CodedInputStream input)
{
field.AddEntriesFrom(input, codec);
}
public void MergeFrom(IExtensionValue value)
{
if (value is RepeatedExtensionValue<T>)
{
field.Add((value as RepeatedExtensionValue<T>).field);
}
}
public void WriteTo(CodedOutputStream output)
{
field.WriteTo(output, codec);
}
public RepeatedField<T> GetValue() => field;
}
}
This diff is collapsed.
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2015 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// 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 Google.Protobuf.Collections;
namespace Google.Protobuf
{
/// <summary>
/// Generic interface for a Protocol Buffers message containing one or more extensions, where the type parameter is expected to be the same type as the implementation class
/// </summary>
public interface IExtendableMessage<T> : IMessage<T> where T : IExtendableMessage<T>
{
/// <summary>
/// Gets the value of the specified extension
/// </summary>
TValue GetExtension<TValue>(Extension<T, TValue> extension);
/// <summary>
/// Gets the value of the specified repeated extension or null if the extension isn't registered in this set.
/// For a version of this method that never returns null, use <see cref="IExtendableMessage{T}.GetOrRegisterExtension{TValue}(RepeatedExtension{T, TValue})"/>
/// </summary>
RepeatedField<TValue> GetExtension<TValue>(RepeatedExtension<T, TValue> extension);
/// <summary>
/// Gets the value of the specified repeated extension, registering it if it isn't
/// </summary>
RepeatedField<TValue> GetOrRegisterExtension<TValue>(RepeatedExtension<T, TValue> extension);
/// <summary>
/// Sets the value of the specified extension
/// </summary>
void SetExtension<TValue>(Extension<T, TValue> extension, TValue value);
/// <summary>
/// Gets whether the value of the specified extension is set
/// </summary>
bool HasExtension<TValue>(Extension<T, TValue> extension);
/// <summary>
/// Clears the value of the specified extension
/// </summary>
void ClearExtension<TValue>(Extension<T, TValue> extension);
/// <summary>
/// Clears the value of the specified repeated extension
/// </summary>
void ClearExtension<TValue>(RepeatedExtension<T, TValue> extension);
}
}
......@@ -48,7 +48,7 @@ namespace Google.Protobuf
/// <param name="message">The message to merge the data into.</param>
/// <param name="data">The data to merge, which must be protobuf-encoded binary data.</param>
public static void MergeFrom(this IMessage message, byte[] data) =>
MergeFrom(message, data, false);
MergeFrom(message, data, false, null);
/// <summary>
/// Merges data from the given byte array slice into an existing message.
......@@ -58,7 +58,7 @@ namespace Google.Protobuf
/// <param name="offset">The offset of the slice to merge.</param>
/// <param name="length">The length of the slice to merge.</param>
public static void MergeFrom(this IMessage message, byte[] data, int offset, int length) =>
MergeFrom(message, data, offset, length, false);
MergeFrom(message, data, offset, length, false, null);
/// <summary>
/// Merges data from the given byte string into an existing message.
......@@ -66,7 +66,7 @@ namespace Google.Protobuf
/// <param name="message">The message to merge the data into.</param>
/// <param name="data">The data to merge, which must be protobuf-encoded binary data.</param>
public static void MergeFrom(this IMessage message, ByteString data) =>
MergeFrom(message, data, false);
MergeFrom(message, data, false, null);
/// <summary>
/// Merges data from the given stream into an existing message.
......@@ -74,7 +74,7 @@ namespace Google.Protobuf
/// <param name="message">The message to merge the data into.</param>
/// <param name="input">Stream containing the data to merge, which must be protobuf-encoded binary data.</param>
public static void MergeFrom(this IMessage message, Stream input) =>
MergeFrom(message, input, false);
MergeFrom(message, input, false, null);
/// <summary>
/// Merges length-delimited data from the given stream into an existing message.
......@@ -86,7 +86,7 @@ namespace Google.Protobuf
/// <param name="message">The message to merge the data into.</param>
/// <param name="input">Stream containing the data to merge, which must be protobuf-encoded binary data.</param>
public static void MergeDelimitedFrom(this IMessage message, Stream input) =>
MergeDelimitedFrom(message, input, false);
MergeDelimitedFrom(message, input, false, null);
/// <summary>
/// Converts the given message into a byte array in protobuf encoding.
......@@ -191,53 +191,57 @@ namespace Google.Protobuf
}
// Implementations allowing unknown fields to be discarded.
internal static void MergeFrom(this IMessage message, byte[] data, bool discardUnknownFields)
internal static void MergeFrom(this IMessage message, byte[] data, bool discardUnknownFields, ExtensionRegistry registry)
{
ProtoPreconditions.CheckNotNull(message, "message");
ProtoPreconditions.CheckNotNull(data, "data");
CodedInputStream input = new CodedInputStream(data);
input.DiscardUnknownFields = discardUnknownFields;
input.ExtensionRegistry = registry;
message.MergeFrom(input);
input.CheckReadEndOfStreamTag();
}
internal static void MergeFrom(this IMessage message, byte[] data, int offset, int length, bool discardUnknownFields)
internal static void MergeFrom(this IMessage message, byte[] data, int offset, int length, bool discardUnknownFields, ExtensionRegistry registry)
{
ProtoPreconditions.CheckNotNull(message, "message");
ProtoPreconditions.CheckNotNull(data, "data");
CodedInputStream input = new CodedInputStream(data, offset, length);
input.DiscardUnknownFields = discardUnknownFields;
input.ExtensionRegistry = registry;
message.MergeFrom(input);
input.CheckReadEndOfStreamTag();
}
internal static void MergeFrom(this IMessage message, ByteString data, bool discardUnknownFields)
internal static void MergeFrom(this IMessage message, ByteString data, bool discardUnknownFields, ExtensionRegistry registry)
{
ProtoPreconditions.CheckNotNull(message, "message");
ProtoPreconditions.CheckNotNull(data, "data");
CodedInputStream input = data.CreateCodedInput();
input.DiscardUnknownFields = discardUnknownFields;
input.ExtensionRegistry = registry;
message.MergeFrom(input);
input.CheckReadEndOfStreamTag();
}
internal static void MergeFrom(this IMessage message, Stream input, bool discardUnknownFields)
internal static void MergeFrom(this IMessage message, Stream input, bool discardUnknownFields, ExtensionRegistry registry)
{
ProtoPreconditions.CheckNotNull(message, "message");
ProtoPreconditions.CheckNotNull(input, "input");
CodedInputStream codedInput = new CodedInputStream(input);
codedInput.DiscardUnknownFields = discardUnknownFields;
codedInput.ExtensionRegistry = registry;
message.MergeFrom(codedInput);
codedInput.CheckReadEndOfStreamTag();
}
internal static void MergeDelimitedFrom(this IMessage message, Stream input, bool discardUnknownFields)
internal static void MergeDelimitedFrom(this IMessage message, Stream input, bool discardUnknownFields, ExtensionRegistry registry)
{
ProtoPreconditions.CheckNotNull(message, "message");
ProtoPreconditions.CheckNotNull(input, "input");
int size = (int) CodedInputStream.ReadRawVarint32(input);
Stream limitedStream = new LimitedInputStream(input, size);
MergeFrom(message, limitedStream, discardUnknownFields);
MergeFrom(message, limitedStream, discardUnknownFields, registry);
}
}
}
......@@ -45,10 +45,13 @@ namespace Google.Protobuf
// TODO: When we use a C# 7.1 compiler, make this private protected.
internal bool DiscardUnknownFields { get; }
internal MessageParser(Func<IMessage> factory, bool discardUnknownFields)
internal ExtensionRegistry Extensions { get; }
internal MessageParser(Func<IMessage> factory, bool discardUnknownFields, ExtensionRegistry extensions)
{
this.factory = factory;
DiscardUnknownFields = discardUnknownFields;
Extensions = extensions;
}
/// <summary>
......@@ -68,7 +71,7 @@ namespace Google.Protobuf
public IMessage ParseFrom(byte[] data)
{
IMessage message = factory();
message.MergeFrom(data, DiscardUnknownFields);
message.MergeFrom(data, DiscardUnknownFields, Extensions);
CheckMergedRequiredFields(message);
return message;
}
......@@ -83,7 +86,7 @@ namespace Google.Protobuf
public IMessage ParseFrom(byte[] data, int offset, int length)
{
IMessage message = factory();
message.MergeFrom(data, offset, length, DiscardUnknownFields);
message.MergeFrom(data, offset, length, DiscardUnknownFields, Extensions);
CheckMergedRequiredFields(message);
return message;
}
......@@ -96,7 +99,7 @@ namespace Google.Protobuf
public IMessage ParseFrom(ByteString data)
{
IMessage message = factory();
message.MergeFrom(data, DiscardUnknownFields);
message.MergeFrom(data, DiscardUnknownFields, Extensions);
CheckMergedRequiredFields(message);
return message;
}
......@@ -109,7 +112,7 @@ namespace Google.Protobuf
public IMessage ParseFrom(Stream input)
{
IMessage message = factory();
message.MergeFrom(input, DiscardUnknownFields);
message.MergeFrom(input, DiscardUnknownFields, Extensions);
CheckMergedRequiredFields(message);
return message;
}
......@@ -126,7 +129,7 @@ namespace Google.Protobuf
public IMessage ParseDelimitedFrom(Stream input)
{
IMessage message = factory();
message.MergeDelimitedFrom(input, DiscardUnknownFields);
message.MergeDelimitedFrom(input, DiscardUnknownFields, Extensions);
CheckMergedRequiredFields(message);
return message;
}
......@@ -185,7 +188,15 @@ namespace Google.Protobuf
/// <param name="discardUnknownFields">Whether or not to discard unknown fields when parsing.</param>
/// <returns>A newly configured message parser.</returns>
public MessageParser WithDiscardUnknownFields(bool discardUnknownFields) =>
new MessageParser(factory, discardUnknownFields);
new MessageParser(factory, discardUnknownFields, Extensions);
/// <summary>
/// Creates a new message parser which registers extensions from the specified registry upon creating the message instance
/// </summary>
/// <param name="registry">The extensions to register</param>
/// <returns>A newly configured message parser.</returns>
public MessageParser WithExtensionRegistry(ExtensionRegistry registry) =>
new MessageParser(factory, DiscardUnknownFields, registry);
}
/// <summary>
......@@ -220,11 +231,11 @@ namespace Google.Protobuf
/// to require a parameterless constructor: delegates are significantly faster to execute.
/// </remarks>
/// <param name="factory">Function to invoke when a new, empty message is required.</param>
public MessageParser(Func<T> factory) : this(factory, false)
public MessageParser(Func<T> factory) : this(factory, false, null)
{
}
internal MessageParser(Func<T> factory, bool discardUnknownFields) : base(() => factory(), discardUnknownFields)
internal MessageParser(Func<T> factory, bool discardUnknownFields, ExtensionRegistry extensions) : base(() => factory(), discardUnknownFields, extensions)
{
this.factory = factory;
}
......@@ -246,7 +257,7 @@ namespace Google.Protobuf
public new T ParseFrom(byte[] data)
{
T message = factory();
message.MergeFrom(data, DiscardUnknownFields);
message.MergeFrom(data, DiscardUnknownFields, Extensions);
return message;
}
......@@ -260,7 +271,7 @@ namespace Google.Protobuf
public new T ParseFrom(byte[] data, int offset, int length)
{
T message = factory();
message.MergeFrom(data, offset, length, DiscardUnknownFields);
message.MergeFrom(data, offset, length, DiscardUnknownFields, Extensions);
return message;
}
......@@ -272,7 +283,7 @@ namespace Google.Protobuf
public new T ParseFrom(ByteString data)
{
T message = factory();
message.MergeFrom(data, DiscardUnknownFields);
message.MergeFrom(data, DiscardUnknownFields, Extensions);
return message;
}
......@@ -284,7 +295,7 @@ namespace Google.Protobuf
public new T ParseFrom(Stream input)
{
T message = factory();
message.MergeFrom(input, DiscardUnknownFields);
message.MergeFrom(input, DiscardUnknownFields, Extensions);
return message;
}
......@@ -300,7 +311,7 @@ namespace Google.Protobuf
public new T ParseDelimitedFrom(Stream input)
{
T message = factory();
message.MergeDelimitedFrom(input, DiscardUnknownFields);
message.MergeDelimitedFrom(input, DiscardUnknownFields, Extensions);
return message;
}
......@@ -336,6 +347,14 @@ namespace Google.Protobuf
/// <param name="discardUnknownFields">Whether or not to discard unknown fields when parsing.</param>
/// <returns>A newly configured message parser.</returns>
public new MessageParser<T> WithDiscardUnknownFields(bool discardUnknownFields) =>
new MessageParser<T>(factory, discardUnknownFields);
new MessageParser<T>(factory, discardUnknownFields, Extensions);
/// <summary>
/// Creates a new message parser which registers extensions from the specified registry upon creating the message instance
/// </summary>
/// <param name="registry">The extensions to register</param>
/// <returns>A newly configured message parser.</returns>
public new MessageParser<T> WithExtensionRegistry(ExtensionRegistry registry) =>
new MessageParser<T>(factory, DiscardUnknownFields, registry);
}
}
using System;
namespace Google.Protobuf
{
/// <summary>
/// Struct used to hold the keys for the fieldByNumber table in DescriptorPool and the keys for the
/// extensionByNumber table in ExtensionRegistry.
/// </summary>
internal struct ObjectIntPair<T> : IEquatable<ObjectIntPair<T>> where T : class
{
private readonly int number;
private readonly T obj;
internal ObjectIntPair(T obj, int number)
{
this.number = number;
this.obj = obj;
}
public bool Equals(ObjectIntPair<T> other)
{
return obj == other.obj
&& number == other.number;
}
public override bool Equals(object obj)
{
if (obj is ObjectIntPair<T>)
{
return Equals((ObjectIntPair<T>)obj);
}
return false;
}
public override int GetHashCode()
{
return obj.GetHashCode() * ((1 << 16) - 1) + number;
}
}
}
......@@ -45,11 +45,11 @@ namespace Google.Protobuf.Reflection
private readonly IDictionary<string, IDescriptor> descriptorsByName =
new Dictionary<string, IDescriptor>();
private readonly IDictionary<DescriptorIntPair, FieldDescriptor> fieldsByNumber =
new Dictionary<DescriptorIntPair, FieldDescriptor>();
private readonly IDictionary<ObjectIntPair<IDescriptor>, FieldDescriptor> fieldsByNumber =
new Dictionary<ObjectIntPair<IDescriptor>, FieldDescriptor>();
private readonly IDictionary<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber =
new Dictionary<DescriptorIntPair, EnumValueDescriptor>();
private readonly IDictionary<ObjectIntPair<IDescriptor>, EnumValueDescriptor> enumValuesByNumber =
new Dictionary<ObjectIntPair<IDescriptor>, EnumValueDescriptor>();
private readonly HashSet<FileDescriptor> dependencies;
......@@ -209,14 +209,14 @@ namespace Google.Protobuf.Reflection
internal FieldDescriptor FindFieldByNumber(MessageDescriptor messageDescriptor, int number)
{
FieldDescriptor ret;
fieldsByNumber.TryGetValue(new DescriptorIntPair(messageDescriptor, number), out ret);
fieldsByNumber.TryGetValue(new ObjectIntPair<IDescriptor>(messageDescriptor, number), out ret);
return ret;
}
internal EnumValueDescriptor FindEnumValueByNumber(EnumDescriptor enumDescriptor, int number)
{
EnumValueDescriptor ret;
enumValuesByNumber.TryGetValue(new DescriptorIntPair(enumDescriptor, number), out ret);
enumValuesByNumber.TryGetValue(new ObjectIntPair<IDescriptor>(enumDescriptor, number), out ret);
return ret;
}
......@@ -227,7 +227,8 @@ namespace Google.Protobuf.Reflection
/// containing type and number already exists.</exception>
internal void AddFieldByNumber(FieldDescriptor field)
{
DescriptorIntPair key = new DescriptorIntPair(field.ContainingType, field.FieldNumber);
// for extensions, we use the extended type, otherwise we use the containing type
ObjectIntPair<IDescriptor> key = new ObjectIntPair<IDescriptor>(field.Proto.HasExtendee ? field.ExtendeeType : field.ContainingType, field.FieldNumber);
FieldDescriptor old;
if (fieldsByNumber.TryGetValue(key, out old))
{
......@@ -246,7 +247,7 @@ namespace Google.Protobuf.Reflection
/// </summary>
internal void AddEnumValueByNumber(EnumValueDescriptor enumValue)
{
DescriptorIntPair key = new DescriptorIntPair(enumValue.EnumDescriptor, enumValue.Number);
ObjectIntPair<IDescriptor> key = new ObjectIntPair<IDescriptor>(enumValue.EnumDescriptor, enumValue.Number);
if (!enumValuesByNumber.ContainsKey(key))
{
enumValuesByNumber[key] = enumValue;
......@@ -329,40 +330,5 @@ namespace Google.Protobuf.Reflection
return result;
}
}
/// <summary>
/// Struct used to hold the keys for the fieldByNumber table.
/// </summary>
private struct DescriptorIntPair : IEquatable<DescriptorIntPair>
{
private readonly int number;
private readonly IDescriptor descriptor;
internal DescriptorIntPair(IDescriptor descriptor, int number)
{
this.number = number;
this.descriptor = descriptor;
}
public bool Equals(DescriptorIntPair other)
{
return descriptor == other.descriptor
&& number == other.number;
}
public override bool Equals(object obj)
{
if (obj is DescriptorIntPair)
{
return Equals((DescriptorIntPair) obj);
}
return false;
}
public override int GetHashCode()
{
return descriptor.GetHashCode()*((1 << 16) - 1) + number;
}
}
}
}
\ No newline at end of file
......@@ -30,6 +30,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using Google.Protobuf.Collections;
using System;
using System.Collections.Generic;
......@@ -127,6 +128,26 @@ namespace Google.Protobuf.Reflection
/// <summary>
/// The (possibly empty) set of custom options for this enum.
/// </summary>
public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
//[Obsolete("CustomOptions are obsolete. Use GetOption")]
public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
/* // uncomment this in the full proto2 support PR
/// <summary>
/// Gets a single value enum option for this descriptor
/// </summary>
public T GetOption<T>(Extension<EnumOptions, T> extension)
{
var value = Proto.Options.GetExtension(extension);
return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
}
/// <summary>
/// Gets a repeated value enum option for this descriptor
/// </summary>
public RepeatedField<T> GetOption<T>(RepeatedExtension<EnumOptions, T> extension)
{
return Proto.Options.GetExtension(extension).Clone();
}
*/
}
}
\ No newline at end of file
......@@ -30,6 +30,9 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using Google.Protobuf.Collections;
using System;
namespace Google.Protobuf.Reflection
{
/// <summary>
......@@ -70,6 +73,27 @@ namespace Google.Protobuf.Reflection
/// <summary>
/// The (possibly empty) set of custom options for this enum value.
/// </summary>
public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
//[Obsolete("CustomOptions are obsolete. Use GetOption")]
public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
/* // uncomment this in the full proto2 support PR
/// <summary>
/// Gets a single value enum option for this descriptor
/// </summary>
public T GetOption<T>(Extension<EnumValueOptions, T> extension)
{
var value = Proto.Options.GetExtension(extension);
return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
}
/// <summary>
/// Gets a repeated value enum option for this descriptor
/// </summary>
public RepeatedField<T> GetOption<T>(RepeatedExtension<EnumValueOptions, T> extension)
{
return Proto.Options.GetExtension(extension).Clone();
}
*/
}
}
\ No newline at end of file
}
\ No newline at end of file
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// 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
namespace Google.Protobuf.Reflection
{
internal sealed class ExtensionAccessor : IFieldAccessor
{
private readonly Extension extension;
private readonly ReflectionUtil.IExtensionReflectionHelper helper;
internal ExtensionAccessor(FieldDescriptor descriptor)
{
Descriptor = descriptor;
extension = descriptor.Extension;
helper = ReflectionUtil.CreateExtensionHelper(extension);
}
public FieldDescriptor Descriptor { get; }
public void Clear(IMessage message)
{
helper.ClearExtension(message);
}
public bool HasValue(IMessage message)
{
return helper.HasExtension(message);
}
public object GetValue(IMessage message)
{
return helper.GetExtension(message);
}
public void SetValue(IMessage message, object value)
{
helper.SetExtension(message, value);
}
}
}
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// 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 System.Collections.ObjectModel;
using System.Linq;
namespace Google.Protobuf.Reflection
{
/// <summary>
/// A collection to simplify retrieving the descriptors of extensions in a descriptor for a message
/// </summary>
public class ExtensionCollection
{
private readonly FileDescriptor file;
private readonly MessageDescriptor message;
private IDictionary<MessageDescriptor, IList<FieldDescriptor>> extensionsByTypeInDeclarationOrder;
private IDictionary<MessageDescriptor, IList<FieldDescriptor>> extensionsByTypeInNumberOrder;
internal ExtensionCollection(FileDescriptor file, Extension[] extensions)
{
this.file = file;
UnorderedExtensions = DescriptorUtil.ConvertAndMakeReadOnly(
file.Proto.Extension,
(extension, i) => new FieldDescriptor(extension, file, null, i, null, extensions?[i]));
}
internal ExtensionCollection(MessageDescriptor message, Extension[] extensions)
{
this.message = message;
UnorderedExtensions = DescriptorUtil.ConvertAndMakeReadOnly(
message.Proto.Extension,
(extension, i) => new FieldDescriptor(extension, message.File, message, i, null, extensions?[i]));
}
/// <summary>
/// Returns a readonly list of all the extensions defined in this type in
/// the order they were defined in the source .proto file
/// </summary>
public IList<FieldDescriptor> UnorderedExtensions { get; }
/// <summary>
/// Returns a readonly list of all the extensions define in this type that extend
/// the provided descriptor type in the order they were defined in the source .proto file
/// </summary>
public IList<FieldDescriptor> GetExtensionsInDeclarationOrder(MessageDescriptor descriptor)
{
return extensionsByTypeInDeclarationOrder[descriptor];
}
/// <summary>
/// Returns a readonly list of all the extensions define in this type that extend
/// the provided descriptor type in accending field order
/// </summary>
public IList<FieldDescriptor> GetExtensionsInNumberOrder(MessageDescriptor descriptor)
{
return extensionsByTypeInNumberOrder[descriptor];
}
internal void CrossLink()
{
Dictionary<MessageDescriptor, IList<FieldDescriptor>> declarationOrder = new Dictionary<MessageDescriptor, IList<FieldDescriptor>>();
foreach (FieldDescriptor descriptor in UnorderedExtensions)
{
descriptor.CrossLink();
IList<FieldDescriptor> _;
if (!declarationOrder.TryGetValue(descriptor.ExtendeeType, out _))
declarationOrder.Add(descriptor.ExtendeeType, new List<FieldDescriptor>());
declarationOrder[descriptor.ExtendeeType].Add(descriptor);
}
extensionsByTypeInDeclarationOrder = declarationOrder
.ToDictionary(kvp => kvp.Key, kvp => (IList<FieldDescriptor>)new ReadOnlyCollection<FieldDescriptor>(kvp.Value));
extensionsByTypeInNumberOrder = declarationOrder
.ToDictionary(kvp => kvp.Key, kvp => (IList<FieldDescriptor>)new ReadOnlyCollection<FieldDescriptor>(kvp.Value.OrderBy(field => field.FieldNumber).ToArray()));
}
}
}
......@@ -30,6 +30,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using Google.Protobuf.Collections;
using Google.Protobuf.Compatibility;
using System;
......@@ -41,13 +42,14 @@ namespace Google.Protobuf.Reflection
public sealed class FieldDescriptor : DescriptorBase, IComparable<FieldDescriptor>
{
private EnumDescriptor enumType;
private MessageDescriptor extendeeType;
private MessageDescriptor messageType;
private FieldType fieldType;
private readonly string propertyName; // Annoyingly, needed in Crosslink.
private IFieldAccessor accessor;
/// <summary>
/// Get the field's containing message type.
/// Get the field's containing message type, or <c>null</c> if it is a field defined at the top level of a file as an extension.
/// </summary>
public MessageDescriptor ContainingType { get; }
......@@ -64,8 +66,10 @@ namespace Google.Protobuf.Reflection
internal FieldDescriptorProto Proto { get; }
internal Extension Extension { get; }
internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file,
MessageDescriptor parent, int index, string propertyName)
MessageDescriptor parent, int index, string propertyName, Extension extension)
: base(file, file.ComputeFullName(parent, proto.Name), index)
{
Proto = proto;
......@@ -96,6 +100,7 @@ namespace Google.Protobuf.Reflection
// We could trust the generated code and check whether the type of the property is
// a MapField, but that feels a tad nasty.
this.propertyName = propertyName;
Extension = extension;
JsonName = Proto.JsonName == "" ? JsonFormatter.ToJsonName(Proto.Name) : Proto.JsonName;
}
......@@ -183,8 +188,8 @@ namespace Google.Protobuf.Reflection
/// </summary>
public bool IsRepeated => Proto.Label == FieldDescriptorProto.Types.Label.Repeated;
/// <summary>
/// Returns <c>true</c> if this field is a required field; <c>false</c> otherwise.
/// <summary>
/// Returns <c>true</c> if this field is a required field; <c>false</c> otherwise.
/// </summary>
public bool IsRequired => Proto.Label == FieldDescriptorProto.Types.Label.Required;
......@@ -196,8 +201,8 @@ namespace Google.Protobuf.Reflection
/// <summary>
/// Returns <c>true</c> if this field is a packed, repeated field; <c>false</c> otherwise.
/// </summary>
public bool IsPacked => File.Proto.Syntax == "proto2" ? Proto.Options?.Packed ?? false : !Proto.Options.HasPacked || Proto.Options.Packed;
public bool IsPacked => File.Proto.Syntax == "proto2" ? Proto.Options?.Packed ?? false : !Proto.Options.HasPacked || Proto.Options.Packed;
/// <summary>
/// Returns the type of the field.
/// </summary>
......@@ -254,10 +259,45 @@ namespace Google.Protobuf.Reflection
}
}
/// <summary>
/// For extension fields, returns the extended type
/// </summary>
public MessageDescriptor ExtendeeType
{
get
{
if (!Proto.HasExtendee)
{
throw new InvalidOperationException("ExtendeeType is only valid for extension fields.");
}
return extendeeType;
}
}
/// <summary>
/// The (possibly empty) set of custom options for this field.
/// </summary>
public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
//[Obsolete("CustomOptions are obsolete. Use GetOption")]
public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
/* // uncomment this in the full proto2 support PR
/// <summary>
/// Gets a single value enum option for this descriptor
/// </summary>
public T GetOption<T>(Extension<FieldOptions, T> extension)
{
var value = Proto.Options.GetExtension(extension);
return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
}
/// <summary>
/// Gets a repeated value enum option for this descriptor
/// </summary>
public RepeatedField<T> GetOption<T>(RepeatedExtension<FieldOptions, T> extension)
{
return Proto.Options.GetExtension(extension).Clone();
}
*/
/// <summary>
/// Look up and cross-link all field types etc.
......@@ -320,6 +360,11 @@ namespace Google.Protobuf.Reflection
}
}
if (Proto.HasExtendee)
{
extendeeType = File.DescriptorPool.LookupSymbol(Proto.Extendee, this) as MessageDescriptor;
}
// Note: no attempt to perform any default value parsing
File.DescriptorPool.AddFieldByNumber(this);
......@@ -340,6 +385,11 @@ namespace Google.Protobuf.Reflection
{
return null;
}
if (Extension != null)
{
return new ExtensionAccessor(this);
}
var property = ContainingType.ClrType.GetProperty(propertyName);
if (property == null)
{
......@@ -350,4 +400,5 @@ namespace Google.Protobuf.Reflection
: (IFieldAccessor) new SingleFieldAccessor(property, this);
}
}
}
\ No newline at end of file
}
\ No newline at end of file
......@@ -31,8 +31,8 @@ namespace Google.Protobuf.WellKnownTypes {
"dWYuV2VsbEtub3duVHlwZXNiBnByb3RvMw=="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Any), global::Google.Protobuf.WellKnownTypes.Any.Parser, new[]{ "TypeUrl", "Value" }, null, null, null)
new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Any), global::Google.Protobuf.WellKnownTypes.Any.Parser, new[]{ "TypeUrl", "Value" }, null, null, null, null)
}));
}
#endregion
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment