Field accessor implementations complete (hopefully)
This commit is contained in:
parent
023d7398d6
commit
b84310e110
@ -84,7 +84,7 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
|
|||||||
0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x0b, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x59, 0x54,
|
0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x0b, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x59, 0x54,
|
||||||
0x45, 0x53, 0x10, 0x0c, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10,
|
0x45, 0x53, 0x10, 0x0c, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10,
|
||||||
0x0d, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x10, 0x0e, 0x12, 0x11, 0x0a, 0x0d,
|
0x0d, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x10, 0x0e, 0x12, 0x11, 0x0a, 0x0d,
|
||||||
0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x46, 0x49, 0x58, 0x45, 0x44, 0x33, 0x32, 0x10, 0x0f, 0x12, 0x11, 0x0a, 0x0d, 0x54,
|
0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x46, 0x49, 0x58, 0x45, 0x44, 0x33, 0x32, 0x10, 0x14, 0x12, 0x11, 0x0a, 0x0d, 0x54,
|
||||||
0x59, 0x50, 0x45, 0x5f, 0x53, 0x46, 0x49, 0x58, 0x45, 0x44, 0x36, 0x34, 0x10, 0x10, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59,
|
0x59, 0x50, 0x45, 0x5f, 0x53, 0x46, 0x49, 0x58, 0x45, 0x44, 0x36, 0x34, 0x10, 0x10, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59,
|
||||||
0x50, 0x45, 0x5f, 0x53, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x11, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f,
|
0x50, 0x45, 0x5f, 0x53, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x11, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f,
|
||||||
0x53, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x12, 0x22, 0x43, 0x0a, 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x12, 0x0a,
|
0x53, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x12, 0x22, 0x43, 0x0a, 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x12, 0x0a,
|
||||||
@ -1908,7 +1908,7 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
|
|||||||
[pbd::EnumDescriptorIndex(13)]
|
[pbd::EnumDescriptorIndex(13)]
|
||||||
TYPE_ENUM = 14,
|
TYPE_ENUM = 14,
|
||||||
[pbd::EnumDescriptorIndex(14)]
|
[pbd::EnumDescriptorIndex(14)]
|
||||||
TYPE_SFIXED32 = 15,
|
TYPE_SFIXED32 = 20,
|
||||||
[pbd::EnumDescriptorIndex(15)]
|
[pbd::EnumDescriptorIndex(15)]
|
||||||
TYPE_SFIXED64 = 16,
|
TYPE_SFIXED64 = 16,
|
||||||
[pbd::EnumDescriptorIndex(16)]
|
[pbd::EnumDescriptorIndex(16)]
|
||||||
|
@ -43,8 +43,6 @@ namespace Google.ProtocolBuffers.Descriptors {
|
|||||||
/// Finds an enum value by number. If multiple enum values have the
|
/// Finds an enum value by number. If multiple enum values have the
|
||||||
/// same number, this returns the first defined value with that number.
|
/// same number, this returns the first defined value with that number.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="number"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
internal EnumValueDescriptor FindValueByNumber(int number) {
|
internal EnumValueDescriptor FindValueByNumber(int number) {
|
||||||
return File.DescriptorPool.FindEnumValueByNumber(this, number);
|
return File.DescriptorPool.FindEnumValueByNumber(this, number);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace Google.ProtocolBuffers.FieldAccess {
|
|||||||
/// Declarations of delegate types used for field access. Can't
|
/// Declarations of delegate types used for field access. Can't
|
||||||
/// use Func and Action (other than one parameter) as we can't guarantee .NET 3.5.
|
/// use Func and Action (other than one parameter) as we can't guarantee .NET 3.5.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
delegate bool HasFunction<TMessage>(TMessage message);
|
internal delegate bool HasFunction<TMessage>(TMessage message);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,20 +2,68 @@
|
|||||||
using Google.ProtocolBuffers.Descriptors;
|
using Google.ProtocolBuffers.Descriptors;
|
||||||
|
|
||||||
namespace Google.ProtocolBuffers.FieldAccess {
|
namespace Google.ProtocolBuffers.FieldAccess {
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to fields in generated messages via reflection.
|
||||||
|
/// This type is public to allow it to be used by generated messages, which
|
||||||
|
/// create appropriate instances in the .proto file description class.
|
||||||
|
/// TODO(jonskeet): See if we can hide it somewhere...
|
||||||
|
/// </summary>
|
||||||
public class FieldAccessorTable {
|
public class FieldAccessorTable {
|
||||||
|
|
||||||
|
readonly IFieldAccessor[] accessors;
|
||||||
|
|
||||||
readonly MessageDescriptor descriptor;
|
readonly MessageDescriptor descriptor;
|
||||||
|
|
||||||
public MessageDescriptor Descriptor {
|
public MessageDescriptor Descriptor {
|
||||||
get { return descriptor; }
|
get { return descriptor; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public FieldAccessorTable(MessageDescriptor descriptor, String[] pascalCaseFieldNames, Type messageType, Type builderType) {
|
/// <summary>
|
||||||
|
/// Constructs a FieldAccessorTable for a particular message class.
|
||||||
|
/// Only one FieldAccessorTable should be constructed per class.
|
||||||
|
/// </summary>
|
||||||
|
/// <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>
|
||||||
|
/// <param name="messageType">The .NET type representing the message</param>
|
||||||
|
/// <param name="builderType">The .NET type representing the message's builder type</param>
|
||||||
|
public FieldAccessorTable(MessageDescriptor descriptor, String[] propertyNames, Type messageType, Type builderType) {
|
||||||
this.descriptor = descriptor;
|
this.descriptor = descriptor;
|
||||||
|
accessors = new IFieldAccessor[descriptor.Fields.Count];
|
||||||
|
for (int i=0; i < accessors.Length; i++) {
|
||||||
|
accessors[i] = CreateAccessor(descriptor.Fields[i], propertyNames[i], messageType, builderType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an accessor for a single field
|
||||||
|
/// </summary>
|
||||||
|
private static IFieldAccessor CreateAccessor(FieldDescriptor field, string name, Type messageType, Type builderType) {
|
||||||
|
if (field.IsRepeated) {
|
||||||
|
switch (field.MappedType) {
|
||||||
|
case MappedType.Message: return new RepeatedMessageAccessor(name, messageType, builderType);
|
||||||
|
case MappedType.Enum: return new RepeatedEnumAccessor(field, name, messageType, builderType);
|
||||||
|
default: return new RepeatedPrimitiveAccessor(name, messageType, builderType);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (field.MappedType) {
|
||||||
|
case MappedType.Message: return new SingleMessageAccessor(name, messageType, builderType);
|
||||||
|
case MappedType.Enum: return new SingleEnumAccessor(field, name, messageType, builderType);
|
||||||
|
default: return new SinglePrimitiveAccessor(name, messageType, builderType);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IFieldAccessor this[FieldDescriptor field] {
|
internal IFieldAccessor this[FieldDescriptor field] {
|
||||||
get { return null; }
|
get {
|
||||||
|
if (field.ContainingType != descriptor) {
|
||||||
|
throw new ArgumentException("FieldDescriptor does not match message type.");
|
||||||
|
} else if (field.IsExtension) {
|
||||||
|
// If this type had extensions, it would subclass ExtendableMessage,
|
||||||
|
// which overrides the reflection interface to handle extensions.
|
||||||
|
throw new ArgumentException("This type does not have extensions.");
|
||||||
|
}
|
||||||
|
return accessors[field.Index];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
45
csharp/ProtocolBuffers/FieldAccess/RepeatedEnumAccessor.cs
Normal file
45
csharp/ProtocolBuffers/FieldAccess/RepeatedEnumAccessor.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Google.ProtocolBuffers.Collections;
|
||||||
|
using Google.ProtocolBuffers.Descriptors;
|
||||||
|
|
||||||
|
namespace Google.ProtocolBuffers.FieldAccess {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Accessor for a repeated enum field.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class RepeatedEnumAccessor : RepeatedPrimitiveAccessor {
|
||||||
|
|
||||||
|
private readonly EnumDescriptor enumDescriptor;
|
||||||
|
|
||||||
|
internal RepeatedEnumAccessor(FieldDescriptor field, string name, Type messageType, Type builderType)
|
||||||
|
: base(name, messageType, builderType) {
|
||||||
|
|
||||||
|
enumDescriptor = field.EnumType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object GetValue(IMessage message) {
|
||||||
|
List<EnumValueDescriptor> ret = new List<EnumValueDescriptor>();
|
||||||
|
foreach (int rawValue in (IEnumerable) base.GetValue(message)) {
|
||||||
|
ret.Add(enumDescriptor.FindValueByNumber(rawValue));
|
||||||
|
}
|
||||||
|
return Lists.AsReadOnly(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object GetRepeatedValue(IMessage message, int index) {
|
||||||
|
// Note: This relies on the fact that the CLR allows unboxing from an enum to
|
||||||
|
// its underlying value
|
||||||
|
int rawValue = (int) base.GetRepeatedValue(message, index);
|
||||||
|
return enumDescriptor.FindValueByNumber(rawValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void AddRepeated(IBuilder builder, object value) {
|
||||||
|
base.AddRepeated(builder, ((EnumValueDescriptor) value).Number);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetRepeated(IBuilder builder, int index, object value) {
|
||||||
|
base.SetRepeated(builder, index, ((EnumValueDescriptor) value).Number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using Google.ProtocolBuffers.Descriptors;
|
||||||
|
|
||||||
|
namespace Google.ProtocolBuffers.FieldAccess {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Accessor for a repeated message field.
|
||||||
|
///
|
||||||
|
/// TODO(jonskeet): Try to extract the commonality between this and SingleMessageAccessor.
|
||||||
|
/// We almost want multiple inheritance...
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class RepeatedMessageAccessor : RepeatedPrimitiveAccessor {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The static method to create a builder for the property type. For example,
|
||||||
|
/// in a message type "Foo", a field called "bar" might be of type "Baz". This
|
||||||
|
/// method is Baz.CreateBuilder.
|
||||||
|
/// </summary>
|
||||||
|
private readonly MethodInfo createBuilderMethod;
|
||||||
|
|
||||||
|
internal RepeatedMessageAccessor(string name, Type messageType, Type builderType)
|
||||||
|
: base(name, messageType, builderType) {
|
||||||
|
createBuilderMethod = ClrType.GetMethod("CreateBuilder", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
if (createBuilderMethod == null) {
|
||||||
|
throw new ArgumentException("No public static CreateBuilder method declared in " + ClrType.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a message of the appropriate CLR type from the given value,
|
||||||
|
/// which may already be of the right type or may be a dynamic message.
|
||||||
|
/// </summary>
|
||||||
|
private object CoerceType(object value) {
|
||||||
|
|
||||||
|
// If it's already of the right type, we're done
|
||||||
|
if (ClrType.IsInstanceOfType(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No... so let's create a builder of the right type, and merge the value in.
|
||||||
|
IMessage message = (IMessage) value;
|
||||||
|
return CreateBuilder().MergeFrom(message).Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetRepeated(IBuilder builder, int index, object value) {
|
||||||
|
base.SetRepeated(builder, index, CoerceType(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IBuilder CreateBuilder() {
|
||||||
|
return (IBuilder) createBuilderMethod.Invoke(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void AddRepeated(IBuilder builder, object value) {
|
||||||
|
base.AddRepeated(builder, CoerceType(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
102
csharp/ProtocolBuffers/FieldAccess/RepeatedPrimitiveAccessor.cs
Normal file
102
csharp/ProtocolBuffers/FieldAccess/RepeatedPrimitiveAccessor.cs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Reflection;
|
||||||
|
using Google.ProtocolBuffers.Descriptors;
|
||||||
|
|
||||||
|
namespace Google.ProtocolBuffers.FieldAccess {
|
||||||
|
/// <summary>
|
||||||
|
/// Accesor for a repeated field of type int, ByteString etc.
|
||||||
|
/// </summary>
|
||||||
|
internal class RepeatedPrimitiveAccessor : IFieldAccessor {
|
||||||
|
|
||||||
|
private readonly PropertyInfo messageProperty;
|
||||||
|
private readonly PropertyInfo builderProperty;
|
||||||
|
private readonly PropertyInfo hasProperty;
|
||||||
|
private readonly PropertyInfo countProperty;
|
||||||
|
private readonly MethodInfo clearMethod;
|
||||||
|
private readonly MethodInfo addMethod;
|
||||||
|
private readonly MethodInfo getElementMethod;
|
||||||
|
private readonly MethodInfo setElementMethod;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The CLR type of the field (int, the enum type, ByteString, the message etc).
|
||||||
|
/// This is taken from the return type of the method used to retrieve a single
|
||||||
|
/// value.
|
||||||
|
/// </summary>
|
||||||
|
protected Type ClrType {
|
||||||
|
get { return getElementMethod.ReturnType; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal RepeatedPrimitiveAccessor(string name, Type messageType, Type builderType) {
|
||||||
|
messageProperty = messageType.GetProperty(name + "List");
|
||||||
|
builderProperty = builderType.GetProperty(name + "List");
|
||||||
|
hasProperty = messageType.GetProperty("Has" + name);
|
||||||
|
countProperty = messageType.GetProperty(name + "Count");
|
||||||
|
clearMethod = builderType.GetMethod("Clear" + name);
|
||||||
|
addMethod = builderType.GetMethod("Add" + name);
|
||||||
|
getElementMethod = messageType.GetMethod("Get" + name, new Type[] { typeof(int) });
|
||||||
|
setElementMethod = builderType.GetMethod("Set" + name, new Type[] { typeof(int) });
|
||||||
|
if (messageProperty == null
|
||||||
|
|| builderProperty == null
|
||||||
|
|| hasProperty == null
|
||||||
|
|| countProperty == null
|
||||||
|
|| clearMethod == null
|
||||||
|
|| addMethod == null
|
||||||
|
|| getElementMethod == null
|
||||||
|
|| setElementMethod == null) {
|
||||||
|
throw new ArgumentException("Not all required properties/methods available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Has(IMessage message) {
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IBuilder CreateBuilder() {
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual object GetValue(IMessage message) {
|
||||||
|
return messageProperty.GetValue(message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetValue(IBuilder builder, object value) {
|
||||||
|
// Add all the elements individually. This serves two purposes:
|
||||||
|
// 1) Verifies that each element has the correct type.
|
||||||
|
// 2) Insures that the caller cannot modify the list later on and
|
||||||
|
// have the modifications be reflected in the message.
|
||||||
|
Clear(builder);
|
||||||
|
foreach (object element in (IEnumerable) value) {
|
||||||
|
AddRepeated(builder, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear(IBuilder builder) {
|
||||||
|
clearMethod.Invoke(builder, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetRepeatedCount(IMessage message) {
|
||||||
|
return (int) countProperty.GetValue(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual object GetRepeatedValue(IMessage message, int index) {
|
||||||
|
return getElementMethod.Invoke(message, new object[] {index } );
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void SetRepeated(IBuilder builder, int index, object value) {
|
||||||
|
setElementMethod.Invoke(builder, new object[] {index, value} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void AddRepeated(IBuilder builder, object value) {
|
||||||
|
addMethod.Invoke(builder, new object[] { value });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The builder class's accessor already builds a read-only wrapper for
|
||||||
|
/// us, which is exactly what we want.
|
||||||
|
/// </summary>
|
||||||
|
public object GetRepeatedWrapper(IBuilder builder) {
|
||||||
|
return builderProperty.GetValue(builder, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
csharp/ProtocolBuffers/FieldAccess/SingleEnumAccessor.cs
Normal file
39
csharp/ProtocolBuffers/FieldAccess/SingleEnumAccessor.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Google.ProtocolBuffers.Descriptors;
|
||||||
|
|
||||||
|
namespace Google.ProtocolBuffers.FieldAccess {
|
||||||
|
/// <summary>
|
||||||
|
/// Accessor for fields representing a non-repeated enum value.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class SingleEnumAccessor : SinglePrimitiveAccessor {
|
||||||
|
|
||||||
|
private readonly EnumDescriptor enumDescriptor;
|
||||||
|
|
||||||
|
internal SingleEnumAccessor(FieldDescriptor field, string name, Type messageType, Type builderType)
|
||||||
|
: base(name, messageType, builderType) {
|
||||||
|
enumDescriptor = field.EnumType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an EnumValueDescriptor representing the value in the builder.
|
||||||
|
/// Note that if an enum has multiple values for the same number, the descriptor
|
||||||
|
/// for the first value with that number will be returned.
|
||||||
|
/// </summary>
|
||||||
|
public override object GetValue(IMessage message) {
|
||||||
|
// Note: This relies on the fact that the CLR allows unboxing from an enum to
|
||||||
|
// its underlying value
|
||||||
|
int rawValue = (int) base.GetValue(message);
|
||||||
|
return enumDescriptor.FindValueByNumber(rawValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the value as an enum (via an int) in the builder,
|
||||||
|
/// from an EnumValueDescriptor parameter.
|
||||||
|
/// </summary>
|
||||||
|
public override void SetValue(IBuilder builder, object value) {
|
||||||
|
EnumValueDescriptor valueDescriptor = (EnumValueDescriptor) value;
|
||||||
|
base.SetValue(builder, valueDescriptor.Number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
csharp/ProtocolBuffers/FieldAccess/SingleMessageAccessor.cs
Normal file
52
csharp/ProtocolBuffers/FieldAccess/SingleMessageAccessor.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using Google.ProtocolBuffers.Descriptors;
|
||||||
|
|
||||||
|
namespace Google.ProtocolBuffers.FieldAccess {
|
||||||
|
/// <summary>
|
||||||
|
/// Accessor for fields representing a non-repeated message value.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class SingleMessageAccessor : SinglePrimitiveAccessor {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The static method to create a builder for the property type. For example,
|
||||||
|
/// in a message type "Foo", a field called "bar" might be of type "Baz". This
|
||||||
|
/// method is Baz.CreateBuilder.
|
||||||
|
/// </summary>
|
||||||
|
private readonly MethodInfo createBuilderMethod;
|
||||||
|
|
||||||
|
|
||||||
|
internal SingleMessageAccessor(string name, Type messageType, Type builderType)
|
||||||
|
: base(name, messageType, builderType) {
|
||||||
|
|
||||||
|
createBuilderMethod = ClrType.GetMethod("CreateBuilder", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
if (createBuilderMethod == null) {
|
||||||
|
throw new ArgumentException("No public static CreateBuilder method declared in " + ClrType.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a message of the appropriate CLR type from the given value,
|
||||||
|
/// which may already be of the right type or may be a dynamic message.
|
||||||
|
/// </summary>
|
||||||
|
private object CoerceType(object value) {
|
||||||
|
|
||||||
|
// If it's already of the right type, we're done
|
||||||
|
if (ClrType.IsInstanceOfType(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No... so let's create a builder of the right type, and merge the value in.
|
||||||
|
IMessage message = (IMessage) value;
|
||||||
|
return CreateBuilder().MergeFrom(message).Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetValue(IBuilder builder, object value) {
|
||||||
|
base.SetValue(builder, CoerceType(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IBuilder CreateBuilder() {
|
||||||
|
return (IBuilder) createBuilderMethod.Invoke(null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using Google.ProtocolBuffers.Descriptors;
|
||||||
|
|
||||||
|
namespace Google.ProtocolBuffers.FieldAccess {
|
||||||
|
/// <summary>
|
||||||
|
/// Access for a non-repeated field of a "primitive" type (i.e. not another message or an enum).
|
||||||
|
/// </summary>
|
||||||
|
internal class SinglePrimitiveAccessor : IFieldAccessor {
|
||||||
|
|
||||||
|
private readonly PropertyInfo messageProperty;
|
||||||
|
private readonly PropertyInfo builderProperty;
|
||||||
|
private readonly PropertyInfo hasProperty;
|
||||||
|
private readonly MethodInfo clearMethod;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The CLR type of the field (int, the enum type, ByteString, the message etc).
|
||||||
|
/// As declared by the property.
|
||||||
|
/// </summary>
|
||||||
|
protected Type ClrType {
|
||||||
|
get { return messageProperty.PropertyType; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal SinglePrimitiveAccessor(string name, Type messageType, Type builderType) {
|
||||||
|
messageProperty = messageType.GetProperty(name);
|
||||||
|
builderProperty = builderType.GetProperty(name);
|
||||||
|
hasProperty = messageType.GetProperty("Has" + name);
|
||||||
|
clearMethod = builderType.GetMethod("Clear" + name);
|
||||||
|
if (messageProperty == null || builderProperty == null || hasProperty == null || clearMethod == null) {
|
||||||
|
throw new ArgumentException("Not all required properties/methods available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Has(IMessage message) {
|
||||||
|
return (bool) hasProperty.GetValue(message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear(IBuilder builder) {
|
||||||
|
clearMethod.Invoke(builder, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Only valid for message types - this implementation throws InvalidOperationException.
|
||||||
|
/// </summary>
|
||||||
|
public virtual IBuilder CreateBuilder() {
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual object GetValue(IMessage message) {
|
||||||
|
return messageProperty.GetValue(message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void SetValue(IBuilder builder, object value) {
|
||||||
|
builderProperty.SetValue(builder, value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Methods only related to repeated values
|
||||||
|
public int GetRepeatedCount(IMessage message) {
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GetRepeatedValue(IMessage message, int index) {
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetRepeated(IBuilder builder, int index, object value) {
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddRepeated(IBuilder builder, object value) {
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GetRepeatedWrapper(IBuilder builder) {
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -57,8 +57,9 @@ namespace Google.ProtocolBuffers {
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Obtains the value of the given field, or the default value if
|
/// Obtains the value of the given field, or the default value if
|
||||||
/// it isn't set. For value type fields including enums, the boxed
|
/// it isn't set. For value type fields, the boxed value is returned.
|
||||||
/// value is returned. For embedded message fields, the sub-message
|
/// For enum fields, the EnumValueDescriptor for the enum is returned.
|
||||||
|
/// For embedded message fields, the sub-message
|
||||||
/// is returned. For repeated fields, an IList<T> is returned.
|
/// is returned. For repeated fields, an IList<T> is returned.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
object this[FieldDescriptor field] { get; }
|
object this[FieldDescriptor field] { get; }
|
||||||
|
@ -69,9 +69,15 @@
|
|||||||
<Compile Include="Descriptors\ServiceDescriptor.cs" />
|
<Compile Include="Descriptors\ServiceDescriptor.cs" />
|
||||||
<Compile Include="ExtensionInfo.cs" />
|
<Compile Include="ExtensionInfo.cs" />
|
||||||
<Compile Include="ExtensionRegistry.cs" />
|
<Compile Include="ExtensionRegistry.cs" />
|
||||||
|
<Compile Include="FieldAccess\SingleEnumAccessor.cs" />
|
||||||
|
<Compile Include="FieldAccess\SingleMessageAccessor.cs" />
|
||||||
|
<Compile Include="FieldAccess\SinglePrimitiveAccessor.cs" />
|
||||||
|
<Compile Include="FieldAccess\RepeatedPrimitiveAccessor.cs" />
|
||||||
|
<Compile Include="FieldAccess\RepeatedEnumAccessor.cs" />
|
||||||
<Compile Include="FieldAccess\Delegates.cs" />
|
<Compile Include="FieldAccess\Delegates.cs" />
|
||||||
<Compile Include="FieldAccess\IFieldAccessor.cs" />
|
<Compile Include="FieldAccess\IFieldAccessor.cs" />
|
||||||
<Compile Include="FieldAccess\FieldAccessorTable.cs" />
|
<Compile Include="FieldAccess\FieldAccessorTable.cs" />
|
||||||
|
<Compile Include="FieldAccess\RepeatedMessageAccessor.cs" />
|
||||||
<Compile Include="FieldSet.cs" />
|
<Compile Include="FieldSet.cs" />
|
||||||
<Compile Include="GeneratedBuilder.cs" />
|
<Compile Include="GeneratedBuilder.cs" />
|
||||||
<Compile Include="GeneratedExtension.cs" />
|
<Compile Include="GeneratedExtension.cs" />
|
||||||
|
Loading…
Reference in New Issue
Block a user