Revamp to reflection.
Changes in brief: 1. Descriptor is now the entry point for all reflection. 2. IReflectedMessage has gone; there's now a Descriptor property in IMessage, which is explicitly implemented (due to the static property). 3. FieldAccessorTable has gone away 4. IFieldAccessor and OneofFieldAccessor still exist; we *could* put the functionality straight into FieldDescriptor and OneofDescriptor... I'm unsure about that. 5. There's a temporary property MessageDescriptor.FieldAccessorsByFieldNumber to make the test changes small - we probably want this to go away 6. Discovery for delegates is now via attributes applied to properties and the Clear method of a oneof I'm happy with 1-3. 4 I'm unsure about - feedback welcome. 5 will go away 6 I'm unsure about, both in design and implementation. Should we have a ProtobufMessageAttribute too? Should we find all the relevant attributes in MessageDescriptor and pass them down, to avoid an O(N^2) scenario? Generated code changes coming in the next commit.
This commit is contained in:
parent
2ee4b56655
commit
53c399a1d6
@ -604,7 +604,7 @@ namespace Google.Protobuf
|
||||
public void Reflection_GetValue()
|
||||
{
|
||||
var message = SampleMessages.CreateFullTestAllTypes();
|
||||
var fields = ((IReflectedMessage) message).Fields;
|
||||
var fields = TestAllTypes.Descriptor.FieldAccessorsByFieldNumber;
|
||||
Assert.AreEqual(message.SingleBool, fields[TestAllTypes.SingleBoolFieldNumber].GetValue(message));
|
||||
Assert.AreEqual(message.SingleBytes, fields[TestAllTypes.SingleBytesFieldNumber].GetValue(message));
|
||||
Assert.AreEqual(message.SingleDouble, fields[TestAllTypes.SingleDoubleFieldNumber].GetValue(message));
|
||||
@ -639,7 +639,7 @@ namespace Google.Protobuf
|
||||
|
||||
// Just a single map field, for the same reason
|
||||
var mapMessage = new TestMap { MapStringString = { { "key1", "value1" }, { "key2", "value2" } } };
|
||||
fields = ((IReflectedMessage) mapMessage).Fields;
|
||||
fields = TestMap.Descriptor.FieldAccessorsByFieldNumber;
|
||||
var dictionary = (IDictionary) fields[TestMap.MapStringStringFieldNumber].GetValue(mapMessage);
|
||||
Assert.AreEqual(mapMessage.MapStringString, dictionary);
|
||||
Assert.AreEqual("value1", dictionary["key1"]);
|
||||
@ -648,8 +648,8 @@ namespace Google.Protobuf
|
||||
[Test]
|
||||
public void Reflection_Clear()
|
||||
{
|
||||
IReflectedMessage message = SampleMessages.CreateFullTestAllTypes();
|
||||
var fields = message.Fields;
|
||||
var message = SampleMessages.CreateFullTestAllTypes();
|
||||
var fields = TestAllTypes.Descriptor.FieldAccessorsByFieldNumber;
|
||||
fields[TestAllTypes.SingleBoolFieldNumber].Clear(message);
|
||||
fields[TestAllTypes.SingleInt32FieldNumber].Clear(message);
|
||||
fields[TestAllTypes.SingleStringFieldNumber].Clear(message);
|
||||
@ -673,7 +673,7 @@ namespace Google.Protobuf
|
||||
|
||||
// Separately, maps.
|
||||
var mapMessage = new TestMap { MapStringString = { { "key1", "value1" }, { "key2", "value2" } } };
|
||||
fields = ((IReflectedMessage) mapMessage).Fields;
|
||||
fields = TestMap.Descriptor.FieldAccessorsByFieldNumber;
|
||||
fields[TestMap.MapStringStringFieldNumber].Clear(mapMessage);
|
||||
Assert.AreEqual(0, mapMessage.MapStringString.Count);
|
||||
}
|
||||
@ -682,8 +682,8 @@ namespace Google.Protobuf
|
||||
public void Reflection_SetValue_SingleFields()
|
||||
{
|
||||
// Just a sample (primitives, messages, enums, strings, byte strings)
|
||||
IReflectedMessage message = SampleMessages.CreateFullTestAllTypes();
|
||||
var fields = message.Fields;
|
||||
var message = SampleMessages.CreateFullTestAllTypes();
|
||||
var fields = TestAllTypes.Descriptor.FieldAccessorsByFieldNumber;
|
||||
fields[TestAllTypes.SingleBoolFieldNumber].SetValue(message, false);
|
||||
fields[TestAllTypes.SingleInt32FieldNumber].SetValue(message, 500);
|
||||
fields[TestAllTypes.SingleStringFieldNumber].SetValue(message, "It's a string");
|
||||
@ -709,51 +709,52 @@ namespace Google.Protobuf
|
||||
[Test]
|
||||
public void Reflection_SetValue_SingleFields_WrongType()
|
||||
{
|
||||
IReflectedMessage message = SampleMessages.CreateFullTestAllTypes();
|
||||
var fields = message.Fields;
|
||||
IMessage message = SampleMessages.CreateFullTestAllTypes();
|
||||
var fields = message.Descriptor.FieldAccessorsByFieldNumber;
|
||||
Assert.Throws<InvalidCastException>(() => fields[TestAllTypes.SingleBoolFieldNumber].SetValue(message, "This isn't a bool"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Reflection_SetValue_MapFields()
|
||||
{
|
||||
IReflectedMessage message = new TestMap();
|
||||
var fields = message.Fields;
|
||||
IMessage message = new TestMap();
|
||||
var fields = message.Descriptor.FieldAccessorsByFieldNumber;
|
||||
Assert.Throws<InvalidOperationException>(() => fields[TestMap.MapStringStringFieldNumber].SetValue(message, new Dictionary<string, string>()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Reflection_SetValue_RepeatedFields()
|
||||
{
|
||||
IReflectedMessage message = SampleMessages.CreateFullTestAllTypes();
|
||||
var fields = message.Fields;
|
||||
IMessage message = SampleMessages.CreateFullTestAllTypes();
|
||||
var fields = message.Descriptor.FieldAccessorsByFieldNumber;
|
||||
Assert.Throws<InvalidOperationException>(() => fields[TestAllTypes.RepeatedDoubleFieldNumber].SetValue(message, new double[10]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Reflection_GetValue_IncorrectType()
|
||||
{
|
||||
IReflectedMessage message = SampleMessages.CreateFullTestAllTypes();
|
||||
Assert.Throws<InvalidCastException>(() => message.Fields[TestAllTypes.SingleBoolFieldNumber].GetValue(new TestMap()));
|
||||
IMessage message = SampleMessages.CreateFullTestAllTypes();
|
||||
var fields = message.Descriptor.FieldAccessorsByFieldNumber;
|
||||
Assert.Throws<InvalidCastException>(() => fields[TestAllTypes.SingleBoolFieldNumber].GetValue(new TestMap()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Reflection_Oneof()
|
||||
{
|
||||
var message = new TestAllTypes();
|
||||
var fields = ((IReflectedMessage) message).Fields;
|
||||
Assert.AreEqual(1, fields.Oneofs.Count);
|
||||
var oneof = fields.Oneofs[0];
|
||||
Assert.AreEqual("oneof_field", oneof.Descriptor.Name);
|
||||
Assert.IsNull(oneof.GetCaseFieldDescriptor(message));
|
||||
var descriptor = TestAllTypes.Descriptor;
|
||||
Assert.AreEqual(1, descriptor.Oneofs.Count);
|
||||
var oneof = descriptor.Oneofs[0];
|
||||
Assert.AreEqual("oneof_field", oneof.Name);
|
||||
Assert.IsNull(oneof.Accessor.GetCaseFieldDescriptor(message));
|
||||
|
||||
message.OneofString = "foo";
|
||||
Assert.AreSame(fields[TestAllTypes.OneofStringFieldNumber].Descriptor, oneof.GetCaseFieldDescriptor(message));
|
||||
Assert.AreSame(descriptor.FieldAccessorsByFieldNumber[TestAllTypes.OneofStringFieldNumber].Descriptor, oneof.Accessor.GetCaseFieldDescriptor(message));
|
||||
|
||||
message.OneofUint32 = 10;
|
||||
Assert.AreSame(fields[TestAllTypes.OneofUint32FieldNumber].Descriptor, oneof.GetCaseFieldDescriptor(message));
|
||||
Assert.AreSame(descriptor.FieldAccessorsByFieldNumber[TestAllTypes.OneofUint32FieldNumber].Descriptor, oneof.Accessor.GetCaseFieldDescriptor(message));
|
||||
|
||||
oneof.Clear(message);
|
||||
oneof.Accessor.Clear(message);
|
||||
Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ namespace Google.Protobuf.Reflection
|
||||
Assert.AreEqual(UnittestImportProto3.Descriptor, file.Dependencies[0]);
|
||||
|
||||
MessageDescriptor messageType = TestAllTypes.Descriptor;
|
||||
Assert.AreSame(typeof(TestAllTypes), messageType.GeneratedType);
|
||||
Assert.AreEqual(messageType, file.MessageTypes[0]);
|
||||
Assert.AreEqual(messageType, file.FindTypeByName<MessageDescriptor>("TestAllTypes"));
|
||||
Assert.Null(file.FindTypeByName<MessageDescriptor>("NoSuchType"));
|
||||
|
@ -192,7 +192,7 @@ namespace Google.Protobuf.WellKnownTypes
|
||||
Uint32Field = 3,
|
||||
Uint64Field = 4
|
||||
};
|
||||
var fields = ((IReflectedMessage) message).Fields;
|
||||
var fields = TestWellKnownTypes.Descriptor.FieldAccessorsByFieldNumber;
|
||||
|
||||
Assert.AreEqual("x", fields[TestWellKnownTypes.StringFieldFieldNumber].GetValue(message));
|
||||
Assert.AreEqual(ByteString.CopyFrom(1, 2, 3), fields[TestWellKnownTypes.BytesFieldFieldNumber].GetValue(message));
|
||||
@ -216,7 +216,7 @@ namespace Google.Protobuf.WellKnownTypes
|
||||
{
|
||||
// Just a single example... note that we can't have a null value here
|
||||
var message = new RepeatedWellKnownTypes { Int32Field = { 1, 2 } };
|
||||
var fields = ((IReflectedMessage) message).Fields;
|
||||
var fields = RepeatedWellKnownTypes.Descriptor.FieldAccessorsByFieldNumber;
|
||||
var list = (IList) fields[RepeatedWellKnownTypes.Int32FieldFieldNumber].GetValue(message);
|
||||
CollectionAssert.AreEqual(new[] { 1, 2 }, list);
|
||||
}
|
||||
@ -226,7 +226,7 @@ namespace Google.Protobuf.WellKnownTypes
|
||||
{
|
||||
// Just a single example... note that we can't have a null value here
|
||||
var message = new MapWellKnownTypes { Int32Field = { { 1, 2 }, { 3, null } } };
|
||||
var fields = ((IReflectedMessage) message).Fields;
|
||||
var fields = MapWellKnownTypes.Descriptor.FieldAccessorsByFieldNumber;
|
||||
var dictionary = (IDictionary) fields[MapWellKnownTypes.Int32FieldFieldNumber].GetValue(message);
|
||||
Assert.AreEqual(2, dictionary[1]);
|
||||
Assert.IsNull(dictionary[3]);
|
||||
|
@ -30,6 +30,7 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#endregion
|
||||
|
||||
using Google.Protobuf.Reflection;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@ -559,6 +560,8 @@ namespace Google.Protobuf.Collections
|
||||
{
|
||||
return codec.keyCodec.CalculateSizeWithTag(Key) + codec.valueCodec.CalculateSizeWithTag(Value);
|
||||
}
|
||||
|
||||
MessageDescriptor IMessage.Descriptor { get { return null; } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +78,6 @@
|
||||
<Compile Include="Reflection\EnumDescriptor.cs" />
|
||||
<Compile Include="Reflection\EnumValueDescriptor.cs" />
|
||||
<Compile Include="Reflection\FieldAccessorBase.cs" />
|
||||
<Compile Include="Reflection\FieldAccessorTable.cs" />
|
||||
<Compile Include="Reflection\FieldDescriptor.cs" />
|
||||
<Compile Include="Reflection\FieldType.cs" />
|
||||
<Compile Include="Reflection\FileDescriptor.cs" />
|
||||
@ -91,6 +90,8 @@
|
||||
<Compile Include="Reflection\OneofDescriptor.cs" />
|
||||
<Compile Include="Reflection\PackageDescriptor.cs" />
|
||||
<Compile Include="Reflection\PartialClasses.cs" />
|
||||
<Compile Include="Reflection\ProtobufOneofAttribute.cs" />
|
||||
<Compile Include="Reflection\ProtobufFieldAttribute.cs" />
|
||||
<Compile Include="Reflection\ReflectionUtil.cs" />
|
||||
<Compile Include="Reflection\RepeatedFieldAccessor.cs" />
|
||||
<Compile Include="Reflection\ServiceDescriptor.cs" />
|
||||
|
@ -39,15 +39,6 @@ namespace Google.Protobuf
|
||||
// TODO(jonskeet): Do we want a "weak" (non-generic) version of IReflectedMessage?
|
||||
// TODO(jonskeet): Split these interfaces into separate files when we're happy with them.
|
||||
|
||||
/// <summary>
|
||||
/// Reflection support for accessing field values.
|
||||
/// </summary>
|
||||
public interface IReflectedMessage : IMessage
|
||||
{
|
||||
FieldAccessorTable Fields { get; }
|
||||
// TODO(jonskeet): Descriptor? Or a single property which has "all you need for reflection"?
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for a Protocol Buffers message, supporting
|
||||
/// basic operations required for serialization.
|
||||
@ -73,6 +64,13 @@ namespace Google.Protobuf
|
||||
/// <returns>The number of bytes required to write this message
|
||||
/// to a coded output stream.</returns>
|
||||
int CalculateSize();
|
||||
|
||||
/// <summary>
|
||||
/// Descriptor for this message. All instances are expected to return the same descriptor,
|
||||
/// and for generated types this will be an explicitly-implemented member, returning the
|
||||
/// same value as the static property declared on the type.
|
||||
/// </summary>
|
||||
MessageDescriptor Descriptor { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -81,7 +79,7 @@ namespace Google.Protobuf
|
||||
/// the implementation class.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The message type.</typeparam>
|
||||
public interface IMessage<T> : IReflectedMessage, IEquatable<T>, IDeepCloneable<T>, IFreezable where T : IMessage<T>
|
||||
public interface IMessage<T> : IMessage, IEquatable<T>, IDeepCloneable<T>, IFreezable where T : IMessage<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Merges the given message into this one.
|
||||
|
@ -118,7 +118,7 @@ namespace Google.Protobuf
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
public string Format(IReflectedMessage message)
|
||||
public string Format(IMessage message)
|
||||
{
|
||||
ThrowHelper.ThrowIfNull(message, "message");
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@ -129,7 +129,7 @@ namespace Google.Protobuf
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private void WriteMessage(StringBuilder builder, IReflectedMessage message)
|
||||
private void WriteMessage(StringBuilder builder, IMessage message)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
@ -137,15 +137,15 @@ namespace Google.Protobuf
|
||||
return;
|
||||
}
|
||||
builder.Append("{ ");
|
||||
var fields = message.Fields;
|
||||
var fields = message.Descriptor.Fields;
|
||||
bool first = true;
|
||||
// First non-oneof fields
|
||||
foreach (var accessor in fields.Accessors)
|
||||
foreach (var field in fields)
|
||||
{
|
||||
var descriptor = accessor.Descriptor;
|
||||
var accessor = field.Accessor;
|
||||
// Oneofs are written later
|
||||
// TODO: Change to write out fields in order, interleaving oneofs appropriately (as per binary format)
|
||||
if (descriptor.ContainingOneof != null)
|
||||
if (field.ContainingOneof != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -156,7 +156,7 @@ namespace Google.Protobuf
|
||||
continue;
|
||||
}
|
||||
// Omit awkward (single) values such as unknown enum values
|
||||
if (!descriptor.IsRepeated && !descriptor.IsMap && !CanWriteSingleValue(accessor.Descriptor, value))
|
||||
if (!field.IsRepeated && !field.IsMap && !CanWriteSingleValue(accessor.Descriptor, value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -173,15 +173,15 @@ namespace Google.Protobuf
|
||||
}
|
||||
|
||||
// Now oneofs
|
||||
foreach (var accessor in fields.Oneofs)
|
||||
foreach (var oneof in message.Descriptor.Oneofs)
|
||||
{
|
||||
var accessor = oneof.Accessor;
|
||||
var fieldDescriptor = accessor.GetCaseFieldDescriptor(message);
|
||||
if (fieldDescriptor == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var fieldAccessor = fields[fieldDescriptor.FieldNumber];
|
||||
object value = fieldAccessor.GetValue(message);
|
||||
object value = fieldDescriptor.Accessor.GetValue(message);
|
||||
// Omit awkward (single) values such as unknown enum values
|
||||
if (!fieldDescriptor.IsRepeated && !fieldDescriptor.IsMap && !CanWriteSingleValue(fieldDescriptor, value))
|
||||
{
|
||||
@ -194,7 +194,7 @@ namespace Google.Protobuf
|
||||
}
|
||||
WriteString(builder, ToCamelCase(fieldDescriptor.Name));
|
||||
builder.Append(": ");
|
||||
WriteValue(builder, fieldAccessor, value);
|
||||
WriteValue(builder, fieldDescriptor.Accessor, value);
|
||||
first = false;
|
||||
}
|
||||
builder.Append(first ? "}" : " }");
|
||||
@ -385,7 +385,7 @@ namespace Google.Protobuf
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteMessage(builder, (IReflectedMessage) value);
|
||||
WriteMessage(builder, (IMessage) value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -406,7 +406,7 @@ namespace Google.Protobuf
|
||||
WriteSingleValue(builder, descriptor.MessageType.FindFieldByNumber(1), value);
|
||||
return;
|
||||
}
|
||||
WriteMessage(builder, (IReflectedMessage) value);
|
||||
WriteMessage(builder, (IMessage) value);
|
||||
}
|
||||
|
||||
private void WriteList(StringBuilder builder, IFieldAccessor accessor, IList list)
|
||||
|
@ -50,9 +50,8 @@ namespace Google.Protobuf.Reflection
|
||||
/// Converts the given array into a read-only list, applying the specified conversion to
|
||||
/// each input element.
|
||||
/// </summary>
|
||||
internal static IList<TOutput> ConvertAndMakeReadOnly<TInput, TOutput>(IList<TInput> input,
|
||||
IndexedConverter<TInput, TOutput>
|
||||
converter)
|
||||
internal static IList<TOutput> ConvertAndMakeReadOnly<TInput, TOutput>
|
||||
(IList<TInput> input, IndexedConverter<TInput, TOutput> converter)
|
||||
{
|
||||
TOutput[] array = new TOutput[input.Count];
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
|
@ -30,6 +30,7 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
@ -42,11 +43,13 @@ namespace Google.Protobuf.Reflection
|
||||
private readonly EnumDescriptorProto proto;
|
||||
private readonly MessageDescriptor containingType;
|
||||
private readonly IList<EnumValueDescriptor> values;
|
||||
private readonly Type generatedType;
|
||||
|
||||
internal EnumDescriptor(EnumDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index)
|
||||
internal EnumDescriptor(EnumDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index, Type generatedType)
|
||||
: base(file, file.ComputeFullName(parent, proto.Name), index)
|
||||
{
|
||||
this.proto = proto;
|
||||
this.generatedType = generatedType;
|
||||
containingType = parent;
|
||||
|
||||
if (proto.Value.Count == 0)
|
||||
@ -69,6 +72,11 @@ namespace Google.Protobuf.Reflection
|
||||
/// </summary>
|
||||
public override string Name { get { return proto.Name; } }
|
||||
|
||||
/// <summary>
|
||||
/// The generated type for this enum, or <c>null</c> if the descriptor does not represent a generated type.
|
||||
/// </summary>
|
||||
public Type GeneratedType { get { return generatedType; } }
|
||||
|
||||
/// <value>
|
||||
/// If this is a nested type, get the outer descriptor, otherwise null.
|
||||
/// </value>
|
||||
|
@ -43,13 +43,8 @@ namespace Google.Protobuf.Reflection
|
||||
private readonly Func<object, object> getValueDelegate;
|
||||
private readonly FieldDescriptor descriptor;
|
||||
|
||||
internal FieldAccessorBase(Type type, string propertyName, FieldDescriptor descriptor)
|
||||
internal FieldAccessorBase(PropertyInfo property, FieldDescriptor descriptor)
|
||||
{
|
||||
PropertyInfo property = type.GetProperty(propertyName);
|
||||
if (property == null || !property.CanRead)
|
||||
{
|
||||
throw new ArgumentException("Not all required properties/methods available");
|
||||
}
|
||||
this.descriptor = descriptor;
|
||||
getValueDelegate = ReflectionUtil.CreateFuncObjectObject(property.GetGetMethod());
|
||||
}
|
||||
|
@ -1,97 +0,0 @@
|
||||
#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.ObjectModel;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to fields in generated messages via reflection.
|
||||
/// </summary>
|
||||
public sealed class FieldAccessorTable
|
||||
{
|
||||
private readonly ReadOnlyCollection<IFieldAccessor> accessors;
|
||||
private readonly ReadOnlyCollection<OneofAccessor> oneofs;
|
||||
private readonly MessageDescriptor descriptor;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a FieldAccessorTable for a particular message class.
|
||||
/// Only one FieldAccessorTable should be constructed per class.
|
||||
/// </summary>
|
||||
/// <param name="type">The CLR type for the message.</param>
|
||||
/// <param name="descriptor">The type's descriptor</param>
|
||||
/// <param name="propertyNames">The Pascal-case names of all the field-based properties in the message.</param>
|
||||
public FieldAccessorTable(Type type, MessageDescriptor descriptor, string[] propertyNames, string[] oneofPropertyNames)
|
||||
{
|
||||
this.descriptor = descriptor;
|
||||
var accessorsArray = new IFieldAccessor[descriptor.Fields.Count];
|
||||
for (int i = 0; i < accessorsArray.Length; i++)
|
||||
{
|
||||
var field = descriptor.Fields[i];
|
||||
var name = propertyNames[i];
|
||||
accessorsArray[i] =
|
||||
field.IsMap ? new MapFieldAccessor(type, name, field)
|
||||
: field.IsRepeated ? new RepeatedFieldAccessor(type, name, field)
|
||||
: (IFieldAccessor) new SingleFieldAccessor(type, name, field);
|
||||
}
|
||||
accessors = new ReadOnlyCollection<IFieldAccessor>(accessorsArray);
|
||||
var oneofsArray = new OneofAccessor[descriptor.Oneofs.Count];
|
||||
for (int i = 0; i < oneofsArray.Length; i++)
|
||||
{
|
||||
var oneof = descriptor.Oneofs[i];
|
||||
oneofsArray[i] = new OneofAccessor(type, oneofPropertyNames[i], oneof);
|
||||
}
|
||||
oneofs = new ReadOnlyCollection<OneofAccessor>(oneofsArray);
|
||||
}
|
||||
|
||||
// TODO: Validate the name here... should possibly make this type a more "general reflection access" type,
|
||||
// bearing in mind the oneof parts to come as well.
|
||||
/// <summary>
|
||||
/// Returns all of the field accessors for the message type.
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<IFieldAccessor> Accessors { get { return accessors; } }
|
||||
|
||||
public ReadOnlyCollection<OneofAccessor> Oneofs { get { return oneofs; } }
|
||||
|
||||
// TODO: Review this, as it's easy to get confused between FieldNumber and Index.
|
||||
// Currently only used to get an accessor related to a oneof... maybe just make that simpler?
|
||||
public IFieldAccessor this[int fieldNumber]
|
||||
{
|
||||
get
|
||||
{
|
||||
FieldDescriptor field = descriptor.FindFieldByNumber(fieldNumber);
|
||||
return accessors[field.Index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
@ -45,6 +46,7 @@ namespace Google.Protobuf.Reflection
|
||||
private readonly MessageDescriptor containingType;
|
||||
private readonly OneofDescriptor containingOneof;
|
||||
private FieldType fieldType;
|
||||
private IFieldAccessor accessor;
|
||||
|
||||
internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file,
|
||||
MessageDescriptor parent, int index)
|
||||
@ -82,6 +84,8 @@ namespace Google.Protobuf.Reflection
|
||||
public override string Name { get { return proto.Name; } }
|
||||
|
||||
internal FieldDescriptorProto Proto { get { return proto; } }
|
||||
|
||||
public IFieldAccessor Accessor { get { return accessor; } }
|
||||
|
||||
/// <summary>
|
||||
/// Maps a field type as included in the .proto file to a FieldType.
|
||||
@ -287,6 +291,30 @@ namespace Google.Protobuf.Reflection
|
||||
{
|
||||
throw new DescriptorValidationException(this, "MessageSet format is not supported.");
|
||||
}
|
||||
|
||||
accessor = CreateAccessor();
|
||||
}
|
||||
|
||||
private IFieldAccessor CreateAccessor()
|
||||
{
|
||||
// TODO: Check the performance of this with some large protos. Each message is O(N^2) in the number of fields,
|
||||
// which isn't great...
|
||||
if (containingType.GeneratedType == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var property = containingType
|
||||
.GeneratedType
|
||||
.GetProperties()
|
||||
.FirstOrDefault(p => p.IsDefined(typeof(ProtobufFieldAttribute), false) &&
|
||||
p.GetCustomAttributes(typeof(ProtobufFieldAttribute), false).Cast<ProtobufFieldAttribute>().Single().Number == FieldNumber);
|
||||
if (property == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return IsMap ? new MapFieldAccessor(property, this)
|
||||
: IsRepeated ? new RepeatedFieldAccessor(property, this)
|
||||
: (IFieldAccessor) new SingleFieldAccessor(property, this);
|
||||
}
|
||||
}
|
||||
}
|
@ -62,11 +62,12 @@ namespace Google.Protobuf.Reflection
|
||||
get { return proto.Syntax == "proto3" ? ProtoSyntax.Proto3 : ProtoSyntax.Proto2; }
|
||||
}
|
||||
|
||||
private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies)
|
||||
private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies, Type[] generatedTypes)
|
||||
{
|
||||
this.pool = pool;
|
||||
this.proto = proto;
|
||||
this.dependencies = new ReadOnlyCollection<FileDescriptor>((FileDescriptor[]) dependencies.Clone());
|
||||
IEnumerator<Type> generatedTypeIterator = generatedTypes == null ? null : ((IEnumerable<Type>)generatedTypes).GetEnumerator();
|
||||
|
||||
publicDependencies = DeterminePublicDependencies(this, proto, dependencies, allowUnknownDependencies);
|
||||
|
||||
@ -74,15 +75,21 @@ namespace Google.Protobuf.Reflection
|
||||
|
||||
messageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageType,
|
||||
(message, index) =>
|
||||
new MessageDescriptor(message, this, null, index));
|
||||
new MessageDescriptor(message, this, null, index, generatedTypeIterator));
|
||||
|
||||
enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType,
|
||||
(enumType, index) =>
|
||||
new EnumDescriptor(enumType, this, null, index));
|
||||
new EnumDescriptor(enumType, this, null, index, ReflectionUtil.GetNextType(generatedTypeIterator)));
|
||||
|
||||
services = DescriptorUtil.ConvertAndMakeReadOnly(proto.Service,
|
||||
(service, index) =>
|
||||
new ServiceDescriptor(service, this, index));
|
||||
|
||||
// We should now have consumed all the generated types.
|
||||
if (generatedTypeIterator != null && generatedTypeIterator.MoveNext())
|
||||
{
|
||||
throw new ArgumentException("More generated types left over after consuming all expected ones", "generatedTypes");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -265,7 +272,7 @@ namespace Google.Protobuf.Reflection
|
||||
/// <exception cref="DescriptorValidationException">If <paramref name="proto"/> is not
|
||||
/// a valid descriptor. This can occur for a number of reasons, such as a field
|
||||
/// having an undefined type or because two messages were defined with the same name.</exception>
|
||||
private static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies)
|
||||
private static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies, Type[] generatedTypes)
|
||||
{
|
||||
// Building descriptors involves two steps: translating and linking.
|
||||
// In the translation step (implemented by FileDescriptor's
|
||||
@ -282,7 +289,7 @@ namespace Google.Protobuf.Reflection
|
||||
}
|
||||
|
||||
DescriptorPool pool = new DescriptorPool(dependencies);
|
||||
FileDescriptor result = new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies);
|
||||
FileDescriptor result = new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies, generatedTypes);
|
||||
|
||||
// TODO(jonskeet): Reinstate these checks, or get rid of them entirely. They aren't in the Java code,
|
||||
// and fail for the CustomOptions test right now. (We get "descriptor.proto" vs "google/protobuf/descriptor.proto".)
|
||||
@ -319,8 +326,23 @@ namespace Google.Protobuf.Reflection
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance for generated code.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The <paramref name="generatedTypes"/> parameter should be null for descriptors which don't correspond to
|
||||
/// generated types. Otherwise, the array should be represent all the generated types in the file: messages then
|
||||
/// enums. Within each message, there can be nested messages and enums, which must be specified "inline" in the array:
|
||||
/// containing message, nested messages, nested enums - and of course each nested message may contain *more* nested messages,
|
||||
/// etc. All messages within the descriptor should be represented, even if they do not have a generated type - any
|
||||
/// type without a corresponding generated type (such as map entries) should respond to a null element.
|
||||
/// For example, a file with a messages OuterMessage and InnerMessage, and enums OuterEnum and InnerEnum (where
|
||||
/// InnerMessage and InnerEnum are nested within InnerMessage) would result in an array of
|
||||
/// OuterMessage, InnerMessage, InnerEnum, OuterEnum.
|
||||
/// </remarks>
|
||||
public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData,
|
||||
FileDescriptor[] dependencies)
|
||||
FileDescriptor[] dependencies,
|
||||
Type[] generatedTypes)
|
||||
{
|
||||
FileDescriptorProto proto;
|
||||
try
|
||||
@ -336,7 +358,7 @@ namespace Google.Protobuf.Reflection
|
||||
{
|
||||
// When building descriptors for generated code, we allow unknown
|
||||
// dependencies by default.
|
||||
return BuildFrom(proto, dependencies, true);
|
||||
return BuildFrom(proto, dependencies, true, generatedTypes);
|
||||
}
|
||||
catch (DescriptorValidationException e)
|
||||
{
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
@ -40,7 +41,7 @@ namespace Google.Protobuf.Reflection
|
||||
/// </summary>
|
||||
internal sealed class MapFieldAccessor : FieldAccessorBase
|
||||
{
|
||||
internal MapFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor)
|
||||
internal MapFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -30,8 +30,10 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#endregion
|
||||
|
||||
using Google.Protobuf.Collections;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
@ -60,11 +62,15 @@ namespace Google.Protobuf.Reflection
|
||||
private readonly IList<EnumDescriptor> enumTypes;
|
||||
private readonly IList<FieldDescriptor> fields;
|
||||
private readonly IList<OneofDescriptor> oneofs;
|
||||
// CLR representation of the type described by this descriptor, if any.
|
||||
private readonly Type generatedType;
|
||||
private IDictionary<int, IFieldAccessor> fieldAccessorsByFieldNumber;
|
||||
|
||||
internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex)
|
||||
internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex, IEnumerator<Type> generatedTypeIterator)
|
||||
: base(file, file.ComputeFullName(parent, proto.Name), typeIndex)
|
||||
{
|
||||
this.proto = proto;
|
||||
generatedType = ReflectionUtil.GetNextType(generatedTypeIterator);
|
||||
containingType = parent;
|
||||
|
||||
oneofs = DescriptorUtil.ConvertAndMakeReadOnly(proto.OneofDecl,
|
||||
@ -73,11 +79,11 @@ namespace Google.Protobuf.Reflection
|
||||
|
||||
nestedTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.NestedType,
|
||||
(type, index) =>
|
||||
new MessageDescriptor(type, file, this, index));
|
||||
new MessageDescriptor(type, file, this, index, generatedTypeIterator));
|
||||
|
||||
enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType,
|
||||
(type, index) =>
|
||||
new EnumDescriptor(type, file, this, index));
|
||||
new EnumDescriptor(type, file, this, index, ReflectionUtil.GetNextType(generatedTypeIterator)));
|
||||
|
||||
// TODO(jonskeet): Sort fields first?
|
||||
fields = DescriptorUtil.ConvertAndMakeReadOnly(proto.Field,
|
||||
@ -85,6 +91,14 @@ namespace Google.Protobuf.Reflection
|
||||
new FieldDescriptor(field, file, this, index));
|
||||
file.DescriptorPool.AddSymbol(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total number of nested types and enums, recursively.
|
||||
/// </summary>
|
||||
private int CountTotalGeneratedTypes()
|
||||
{
|
||||
return nestedTypes.Sum(nested => nested.CountTotalGeneratedTypes()) + enumTypes.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The brief name of the descriptor's target.
|
||||
@ -93,6 +107,11 @@ namespace Google.Protobuf.Reflection
|
||||
|
||||
internal DescriptorProto Proto { get { return proto; } }
|
||||
|
||||
/// <summary>
|
||||
/// The generated type for this message, or <c>null</c> if the descriptor does not represent a generated type.
|
||||
/// </summary>
|
||||
public Type GeneratedType { get { return generatedType; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether this message is one of the "well known types" which may have runtime/protoc support.
|
||||
/// </summary>
|
||||
@ -141,6 +160,13 @@ namespace Google.Protobuf.Reflection
|
||||
get { return oneofs; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a map from field number to accessor.
|
||||
/// TODO: Revisit this. It's mostly in place to make the transition from FieldAccessorTable
|
||||
/// to descriptor-based reflection simple in terms of tests. Work out what we really want.
|
||||
/// </summary>
|
||||
public IDictionary<int, IFieldAccessor> FieldAccessorsByFieldNumber { get { return fieldAccessorsByFieldNumber; } }
|
||||
|
||||
/// <summary>
|
||||
/// Finds a field by field name.
|
||||
/// </summary>
|
||||
@ -192,6 +218,8 @@ namespace Google.Protobuf.Reflection
|
||||
{
|
||||
oneof.CrossLink();
|
||||
}
|
||||
|
||||
fieldAccessorsByFieldNumber = new ReadOnlyDictionary<int, IFieldAccessor>(fields.ToDictionary(field => field.FieldNumber, field => field.Accessor));
|
||||
}
|
||||
}
|
||||
}
|
@ -44,18 +44,16 @@ namespace Google.Protobuf.Reflection
|
||||
private readonly Action<object> clearDelegate;
|
||||
private OneofDescriptor descriptor;
|
||||
|
||||
internal OneofAccessor(Type type, string propertyName, OneofDescriptor descriptor)
|
||||
internal OneofAccessor(PropertyInfo caseProperty, MethodInfo clearMethod, OneofDescriptor descriptor)
|
||||
{
|
||||
PropertyInfo property = type.GetProperty(propertyName + "Case");
|
||||
if (property == null || !property.CanRead)
|
||||
if (!caseProperty.CanRead)
|
||||
{
|
||||
throw new ArgumentException("Not all required properties/methods available");
|
||||
throw new ArgumentException("Cannot read from property");
|
||||
}
|
||||
this.descriptor = descriptor;
|
||||
caseDelegate = ReflectionUtil.CreateFuncObjectT<int>(property.GetGetMethod());
|
||||
caseDelegate = ReflectionUtil.CreateFuncObjectT<int>(caseProperty.GetGetMethod());
|
||||
|
||||
this.descriptor = descriptor;
|
||||
MethodInfo clearMethod = type.GetMethod("Clear" + propertyName);
|
||||
clearDelegate = ReflectionUtil.CreateActionObject(clearMethod);
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
@ -40,6 +41,7 @@ namespace Google.Protobuf.Reflection
|
||||
private readonly OneofDescriptorProto proto;
|
||||
private MessageDescriptor containingType;
|
||||
private IList<FieldDescriptor> fields;
|
||||
private OneofAccessor accessor;
|
||||
|
||||
internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index)
|
||||
: base(file, file.ComputeFullName(parent, proto.Name), index)
|
||||
@ -62,6 +64,8 @@ namespace Google.Protobuf.Reflection
|
||||
|
||||
public IList<FieldDescriptor> Fields { get { return fields; } }
|
||||
|
||||
public OneofAccessor Accessor { get { return accessor; } }
|
||||
|
||||
internal void CrossLink()
|
||||
{
|
||||
List<FieldDescriptor> fieldCollection = new List<FieldDescriptor>();
|
||||
@ -73,6 +77,36 @@ namespace Google.Protobuf.Reflection
|
||||
}
|
||||
}
|
||||
fields = new ReadOnlyCollection<FieldDescriptor>(fieldCollection);
|
||||
accessor = CreateAccessor();
|
||||
}
|
||||
|
||||
private OneofAccessor CreateAccessor()
|
||||
{
|
||||
if (containingType.GeneratedType == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var caseProperty = containingType
|
||||
.GeneratedType
|
||||
.GetProperties()
|
||||
.FirstOrDefault(p => p.IsDefined(typeof(ProtobufOneofAttribute), false) &&
|
||||
p.GetCustomAttributes(typeof(ProtobufOneofAttribute), false).Cast<ProtobufOneofAttribute>().Single().Name == Name);
|
||||
if (caseProperty == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var clearMethod = containingType
|
||||
.GeneratedType
|
||||
.GetMethods()
|
||||
.FirstOrDefault(p => p.IsDefined(typeof(ProtobufOneofAttribute), false) &&
|
||||
p.GetCustomAttributes(typeof(ProtobufOneofAttribute), false).Cast<ProtobufOneofAttribute>().Single().Name == Name);
|
||||
if (clearMethod == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new OneofAccessor(caseProperty, clearMethod, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,58 @@
|
||||
#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 System;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute applied to a generated property corresponding to a field in a .proto file.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
|
||||
public sealed class ProtobufFieldAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The field number in the original .proto file.
|
||||
/// </summary>
|
||||
public int Number { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The field name in the original .proto file.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
public ProtobufFieldAttribute(int number, string name)
|
||||
{
|
||||
this.Number = number;
|
||||
this.Name = name;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
#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 System;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute applied to the "case" property or "clear" method corresponding to a oneof in a .proto file.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = false)]
|
||||
public sealed class ProtobufOneofAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The oneof name in the original .proto file.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
public ProtobufOneofAttribute(string name)
|
||||
{
|
||||
this.Name = name;
|
||||
}
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
@ -102,5 +103,24 @@ namespace Google.Protobuf.Reflection
|
||||
Expression call = Expression.Call(castTarget, method);
|
||||
return Expression.Lambda<Action<object>>(call, targetParameter).Compile();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the next type from an iterator of types, unless the iterator is a null reference,
|
||||
/// in which case null is returned.
|
||||
/// </summary>
|
||||
internal static Type GetNextType(IEnumerator<Type> generatedTypeIterator)
|
||||
{
|
||||
if (generatedTypeIterator == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!generatedTypeIterator.MoveNext())
|
||||
{
|
||||
// This parameter name corresponds to any public method supplying the generated types to start with.
|
||||
throw new ArgumentException("More generated types left over after consuming all expected ones", "generatedTypes");
|
||||
}
|
||||
return generatedTypeIterator.Current;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
@ -40,7 +41,7 @@ namespace Google.Protobuf.Reflection
|
||||
/// </summary>
|
||||
internal sealed class RepeatedFieldAccessor : FieldAccessorBase
|
||||
{
|
||||
internal RepeatedFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor)
|
||||
internal RepeatedFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -48,11 +48,8 @@ namespace Google.Protobuf.Reflection
|
||||
private readonly Action<object, object> setValueDelegate;
|
||||
private readonly Action<object> clearDelegate;
|
||||
|
||||
internal SingleFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor)
|
||||
internal SingleFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor)
|
||||
{
|
||||
PropertyInfo property = type.GetProperty(propertyName);
|
||||
// We know there *is* such a property, or the base class constructor would have thrown. We should be able to write
|
||||
// to it though.
|
||||
if (!property.CanWrite)
|
||||
{
|
||||
throw new ArgumentException("Not all required properties/methods available");
|
||||
|
@ -74,6 +74,7 @@ void FieldGeneratorBase::SetCommonFieldVariables(
|
||||
|
||||
(*variables)["property_name"] = property_name();
|
||||
(*variables)["type_name"] = type_name();
|
||||
(*variables)["original_name"] = descriptor_->name();
|
||||
(*variables)["name"] = name();
|
||||
(*variables)["descriptor_name"] = descriptor_->name();
|
||||
(*variables)["default_value"] = default_value();
|
||||
@ -85,7 +86,6 @@ void FieldGeneratorBase::SetCommonFieldVariables(
|
||||
}
|
||||
(*variables)["capitalized_type_name"] = capitalized_type_name();
|
||||
(*variables)["number"] = number();
|
||||
(*variables)["field_ordinal"] = field_ordinal();
|
||||
(*variables)["has_property_check"] =
|
||||
(*variables)["property_name"] + " != " + (*variables)["default_value"];
|
||||
(*variables)["other_has_property_check"] = "other." +
|
||||
|
@ -77,6 +77,8 @@ std::string GetFullUmbrellaClassName(const FileDescriptor* descriptor);
|
||||
|
||||
std::string GetQualifiedUmbrellaClassName(const FileDescriptor* descriptor);
|
||||
|
||||
std::string GetClassName(const Descriptor* descriptor);
|
||||
|
||||
std::string GetClassName(const EnumDescriptor* descriptor);
|
||||
|
||||
std::string GetFieldName(const FieldDescriptor* descriptor);
|
||||
@ -119,10 +121,6 @@ inline bool IsDescriptorProto(const FileDescriptor* descriptor) {
|
||||
return descriptor->name() == "google/protobuf/descriptor_proto_file.proto";
|
||||
}
|
||||
|
||||
inline bool IsMapEntry(const Descriptor* descriptor) {
|
||||
return descriptor->options().map_entry();
|
||||
}
|
||||
|
||||
inline bool IsWrapperType(const FieldDescriptor* descriptor) {
|
||||
return descriptor->type() == FieldDescriptor::TYPE_MESSAGE &&
|
||||
descriptor->message_type()->file()->name() == "google/protobuf/wrappers.proto";
|
||||
|
@ -79,6 +79,7 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) {
|
||||
AddDeprecatedFlag(printer);
|
||||
printer->Print(
|
||||
variables_,
|
||||
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
|
||||
"$access_level$ pbc::MapField<$key_type_name$, $value_type_name$> $property_name$ {\n"
|
||||
" get { return $name$_; }\n"
|
||||
"}\n");
|
||||
|
@ -96,88 +96,11 @@ const std::vector<const FieldDescriptor*>& MessageGenerator::fields_by_number()
|
||||
return fields_by_number_;
|
||||
}
|
||||
|
||||
/// Get an identifier that uniquely identifies this type within the file.
|
||||
/// This is used to declare static variables related to this type at the
|
||||
/// outermost file scope.
|
||||
std::string GetUniqueFileScopeIdentifier(const Descriptor* descriptor) {
|
||||
std::string result = descriptor->full_name();
|
||||
std::replace(result.begin(), result.end(), '.', '_');
|
||||
return "static_" + result;
|
||||
}
|
||||
|
||||
void MessageGenerator::GenerateStaticVariables(io::Printer* printer) {
|
||||
// Because descriptor.proto (Google.Protobuf.DescriptorProtos) is
|
||||
// used in the construction of descriptors, we have a tricky bootstrapping
|
||||
// problem. To help control static initialization order, we make sure all
|
||||
// descriptors and other static data that depends on them are members of
|
||||
// the proto-descriptor class. This way, they will be initialized in
|
||||
// a deterministic order.
|
||||
|
||||
std::string identifier = GetUniqueFileScopeIdentifier(descriptor_);
|
||||
|
||||
// The descriptor for this type.
|
||||
printer->Print(
|
||||
"internal static pbr::FieldAccessorTable internal__$identifier$__FieldAccessorTable;\n",
|
||||
"identifier", GetUniqueFileScopeIdentifier(descriptor_),
|
||||
"full_class_name", full_class_name());
|
||||
|
||||
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
|
||||
// Don't generate accessor table fields for maps...
|
||||
if (!IsMapEntryMessage(descriptor_->nested_type(i))) {
|
||||
MessageGenerator messageGenerator(descriptor_->nested_type(i));
|
||||
messageGenerator.GenerateStaticVariables(printer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageGenerator::GenerateStaticVariableInitializers(io::Printer* printer) {
|
||||
map<string, string> vars;
|
||||
vars["identifier"] = GetUniqueFileScopeIdentifier(descriptor_);
|
||||
vars["full_class_name"] = full_class_name();
|
||||
|
||||
// Work out how to get to the message descriptor (which may be multiply nested) from the file
|
||||
// descriptor.
|
||||
string descriptor_chain;
|
||||
const Descriptor* current_descriptor = descriptor_;
|
||||
while (current_descriptor->containing_type()) {
|
||||
descriptor_chain = ".NestedTypes[" + SimpleItoa(current_descriptor->index()) + "]" + descriptor_chain;
|
||||
current_descriptor = current_descriptor->containing_type();
|
||||
}
|
||||
descriptor_chain = "descriptor.MessageTypes[" + SimpleItoa(current_descriptor->index()) + "]" + descriptor_chain;
|
||||
vars["descriptor_chain"] = descriptor_chain;
|
||||
|
||||
printer->Print(
|
||||
vars,
|
||||
"internal__$identifier$__FieldAccessorTable = \n"
|
||||
" new pbr::FieldAccessorTable(typeof($full_class_name$), $descriptor_chain$,\n");
|
||||
printer->Print(" new string[] { ");
|
||||
for (int i = 0; i < descriptor_->field_count(); i++) {
|
||||
printer->Print("\"$property_name$\", ",
|
||||
"property_name", GetPropertyName(descriptor_->field(i)));
|
||||
}
|
||||
printer->Print("}, new string[] { ");
|
||||
for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
|
||||
printer->Print("\"$oneof_name$\", ",
|
||||
"oneof_name",
|
||||
UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true));
|
||||
}
|
||||
printer->Print("});\n");
|
||||
|
||||
// Generate static member initializers for all non-map-entry nested types.
|
||||
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
|
||||
if (!IsMapEntryMessage(descriptor_->nested_type(i))) {
|
||||
MessageGenerator messageGenerator(descriptor_->nested_type(i));
|
||||
messageGenerator.GenerateStaticVariableInitializers(printer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageGenerator::Generate(io::Printer* printer) {
|
||||
map<string, string> vars;
|
||||
vars["class_name"] = class_name();
|
||||
vars["access_level"] = class_access_level();
|
||||
vars["umbrella_class_name"] = GetFullUmbrellaClassName(descriptor_->file());
|
||||
vars["identifier"] = GetUniqueFileScopeIdentifier(descriptor_);
|
||||
|
||||
printer->Print(
|
||||
"[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n");
|
||||
@ -221,8 +144,8 @@ void MessageGenerator::Generate(io::Printer* printer) {
|
||||
" get { return $descriptor_accessor$; }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"pbr::FieldAccessorTable pb::IReflectedMessage.Fields {\n"
|
||||
" get { return $umbrella_class_name$.internal__$identifier$__FieldAccessorTable; }\n"
|
||||
"pbr::MessageDescriptor pb::IMessage.Descriptor {\n"
|
||||
" get { return Descriptor; }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"private bool _frozen = false;\n"
|
||||
@ -258,6 +181,7 @@ void MessageGenerator::Generate(io::Printer* printer) {
|
||||
for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
|
||||
vars["name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false);
|
||||
vars["property_name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true);
|
||||
vars["original_name"] = descriptor_->oneof_decl(i)->name();
|
||||
printer->Print(
|
||||
vars,
|
||||
"private object $name$_;\n"
|
||||
@ -275,9 +199,11 @@ void MessageGenerator::Generate(io::Printer* printer) {
|
||||
printer->Print(
|
||||
vars,
|
||||
"private $property_name$OneofCase $name$Case_ = $property_name$OneofCase.None;\n"
|
||||
"[pbr::ProtobufOneof(\"$original_name$\")]\n"
|
||||
"public $property_name$OneofCase $property_name$Case {\n"
|
||||
" get { return $name$Case_; }\n"
|
||||
"}\n\n"
|
||||
"[pbr::ProtobufOneof(\"$original_name$\")]\n"
|
||||
"public void Clear$property_name$() {\n"
|
||||
" pb::Freezable.CheckMutable(this);\n"
|
||||
" $name$Case_ = $property_name$OneofCase.None;\n"
|
||||
|
@ -53,8 +53,6 @@ class MessageGenerator : public SourceGeneratorBase {
|
||||
void GenerateCloningCode(io::Printer* printer);
|
||||
void GenerateFreezingCode(io::Printer* printer);
|
||||
void GenerateFrameworkMethods(io::Printer* printer);
|
||||
void GenerateStaticVariables(io::Printer* printer);
|
||||
void GenerateStaticVariableInitializers(io::Printer* printer);
|
||||
void Generate(io::Printer* printer);
|
||||
|
||||
private:
|
||||
|
@ -64,6 +64,7 @@ void MessageFieldGenerator::GenerateMembers(io::Printer* printer) {
|
||||
AddDeprecatedFlag(printer);
|
||||
printer->Print(
|
||||
variables_,
|
||||
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
|
||||
"$access_level$ $type_name$ $property_name$ {\n"
|
||||
" get { return $name$_; }\n"
|
||||
" set {\n"
|
||||
@ -158,6 +159,7 @@ void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
|
||||
AddDeprecatedFlag(printer);
|
||||
printer->Print(
|
||||
variables_,
|
||||
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
|
||||
"$access_level$ $type_name$ $property_name$ {\n"
|
||||
" get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : null; }\n"
|
||||
" set {\n"
|
||||
|
@ -71,6 +71,7 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
|
||||
AddDeprecatedFlag(printer);
|
||||
printer->Print(
|
||||
variables_,
|
||||
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
|
||||
"$access_level$ $type_name$ $property_name$ {\n"
|
||||
" get { return $name$_; }\n"
|
||||
" set {\n"
|
||||
@ -174,6 +175,7 @@ void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
|
||||
AddDeprecatedFlag(printer);
|
||||
printer->Print(
|
||||
variables_,
|
||||
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
|
||||
"$access_level$ $type_name$ $property_name$ {\n"
|
||||
" get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : $default_value$; }\n"
|
||||
" set {\n"
|
||||
|
@ -59,12 +59,13 @@ void RepeatedEnumFieldGenerator::GenerateMembers(io::Printer* printer) {
|
||||
printer->Print(
|
||||
variables_,
|
||||
"private static readonly pb::FieldCodec<$type_name$> _repeated_$name$_codec\n"
|
||||
" = pb::FieldCodec.ForEnum($tag$, x => (int) x, x => ($type_name$) x);");
|
||||
" = pb::FieldCodec.ForEnum($tag$, x => (int) x, x => ($type_name$) x);\n");
|
||||
printer->Print(variables_,
|
||||
"private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n");
|
||||
AddDeprecatedFlag(printer);
|
||||
printer->Print(
|
||||
variables_,
|
||||
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
|
||||
"$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
|
||||
" get { return $name$_; }\n"
|
||||
"}\n");
|
||||
|
@ -78,6 +78,7 @@ void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) {
|
||||
AddDeprecatedFlag(printer);
|
||||
printer->Print(
|
||||
variables_,
|
||||
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
|
||||
"$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
|
||||
" get { return $name$_; }\n"
|
||||
"}\n");
|
||||
|
@ -65,6 +65,7 @@ void RepeatedPrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
|
||||
AddDeprecatedFlag(printer);
|
||||
printer->Print(
|
||||
variables_,
|
||||
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
|
||||
"$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
|
||||
" get { return $name$_; }\n"
|
||||
"}\n");
|
||||
|
@ -63,12 +63,6 @@ UmbrellaClassGenerator::~UmbrellaClassGenerator() {
|
||||
void UmbrellaClassGenerator::Generate(io::Printer* printer) {
|
||||
WriteIntroduction(printer);
|
||||
|
||||
printer->Print("#region Static variables\n");
|
||||
for (int i = 0; i < file_->message_type_count(); i++) {
|
||||
MessageGenerator messageGenerator(file_->message_type(i));
|
||||
messageGenerator.GenerateStaticVariables(printer);
|
||||
}
|
||||
printer->Print("#endregion\n");
|
||||
WriteDescriptor(printer);
|
||||
// Close the class declaration.
|
||||
printer->Outdent();
|
||||
@ -183,24 +177,43 @@ void UmbrellaClassGenerator::WriteDescriptor(io::Printer* printer) {
|
||||
// Invoke InternalBuildGeneratedFileFrom() to build the file.
|
||||
printer->Print(
|
||||
"descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,\n");
|
||||
printer->Print(" new pbr::FileDescriptor[] {\n");
|
||||
printer->Print(" new pbr::FileDescriptor[] { ");
|
||||
for (int i = 0; i < file_->dependency_count(); i++) {
|
||||
printer->Print(
|
||||
" $full_umbrella_class_name$.Descriptor, \n",
|
||||
"$full_umbrella_class_name$.Descriptor, ",
|
||||
"full_umbrella_class_name",
|
||||
GetFullUmbrellaClassName(file_->dependency(i)));
|
||||
}
|
||||
printer->Print(" });\n");
|
||||
// Then invoke any other static variable initializers, e.g. field accessors.
|
||||
// Specify all the generated types (messages and enums), recursively, as an array.
|
||||
printer->Print("},\n"
|
||||
" new global::System.Type[] { ");
|
||||
for (int i = 0; i < file_->message_type_count(); i++) {
|
||||
MessageGenerator messageGenerator(file_->message_type(i));
|
||||
messageGenerator.GenerateStaticVariableInitializers(printer);
|
||||
WriteTypeLiterals(file_->message_type(i), printer);
|
||||
}
|
||||
for (int i = 0; i < file_->enum_type_count(); i++) {
|
||||
printer->Print("typeof($type_name$), ", "type_name", GetClassName(file_->enum_type(i)));
|
||||
}
|
||||
printer->Print("});\n");
|
||||
|
||||
printer->Outdent();
|
||||
printer->Print("}\n");
|
||||
printer->Print("#endregion\n\n");
|
||||
}
|
||||
|
||||
void UmbrellaClassGenerator::WriteTypeLiterals(const Descriptor* descriptor, io::Printer* printer) {
|
||||
if (IsMapEntryMessage(descriptor)) {
|
||||
printer->Print("null, ");
|
||||
return;
|
||||
}
|
||||
printer->Print("typeof($type_name$), ", "type_name", GetClassName(descriptor));
|
||||
for (int i = 0; i < descriptor->nested_type_count(); i++) {
|
||||
WriteTypeLiterals(descriptor->nested_type(i), printer);
|
||||
}
|
||||
for (int i = 0; i < descriptor->enum_type_count(); i++) {
|
||||
printer->Print("typeof($type_name$), ", "type_name", GetClassName(descriptor->enum_type(i)));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace csharp
|
||||
} // namespace compiler
|
||||
} // namespace protobuf
|
||||
|
@ -57,6 +57,7 @@ class UmbrellaClassGenerator : public SourceGeneratorBase {
|
||||
|
||||
void WriteIntroduction(io::Printer* printer);
|
||||
void WriteDescriptor(io::Printer* printer);
|
||||
void WriteTypeLiterals(const Descriptor* descriptor, io::Printer* printer);
|
||||
|
||||
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UmbrellaClassGenerator);
|
||||
};
|
||||
|
@ -73,6 +73,7 @@ void WrapperFieldGenerator::GenerateMembers(io::Printer* printer) {
|
||||
AddDeprecatedFlag(printer);
|
||||
printer->Print(
|
||||
variables_,
|
||||
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
|
||||
"$access_level$ $type_name$ $property_name$ {\n"
|
||||
" get { return $name$_; }\n"
|
||||
" set {\n"
|
||||
@ -169,6 +170,7 @@ void WrapperOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
|
||||
AddDeprecatedFlag(printer);
|
||||
printer->Print(
|
||||
variables_,
|
||||
"[pbr::ProtobufField($number$, \"$original_name$\")]\n"
|
||||
"$access_level$ $type_name$ $property_name$ {\n"
|
||||
" get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : ($type_name$) null; }\n"
|
||||
" set {\n"
|
||||
|
Loading…
Reference in New Issue
Block a user