parent
023d7398d6
commit
b84310e110
12 changed files with 437 additions and 10 deletions
@ -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)); |
||||
} |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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 |
||||
} |
||||
} |
Loading…
Reference in new issue