aboutsummaryrefslogtreecommitdiff
path: root/csharp/src/ProtocolBuffers
diff options
context:
space:
mode:
authorJon Skeet <jonskeet@google.com>2015-07-10 14:04:53 +0100
committerJon Skeet <jonskeet@google.com>2015-07-10 14:04:53 +0100
commitef3464dff648362683a75ddf593c9d47ec3c33ce (patch)
tree7ab7c866fbbe912249c9011c923fcfe0e1b890eb /csharp/src/ProtocolBuffers
parent5b9288e47d7add219717d472aa95a5cfe1141ac9 (diff)
downloadprotobuf-ef3464dff648362683a75ddf593c9d47ec3c33ce.tar.gz
protobuf-ef3464dff648362683a75ddf593c9d47ec3c33ce.tar.bz2
protobuf-ef3464dff648362683a75ddf593c9d47ec3c33ce.zip
Oneof reflection support. (Generated code changes in next commit.)
Diffstat (limited to 'csharp/src/ProtocolBuffers')
-rw-r--r--csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs29
-rw-r--r--csharp/src/ProtocolBuffers/FieldAccess/OneofAccessor.cs57
-rw-r--r--csharp/src/ProtocolBuffers/FieldAccess/ReflectionUtil.cs15
3 files changed, 66 insertions, 35 deletions
diff --git a/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs b/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs
index 57ea9c87..c69612fb 100644
--- a/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs
+++ b/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs
@@ -42,6 +42,7 @@ namespace Google.Protobuf.FieldAccess
public sealed class FieldAccessorTable
{
private readonly ReadOnlyCollection<IFieldAccessor> accessors;
+ private readonly ReadOnlyCollection<OneofAccessor> oneofs;
private readonly MessageDescriptor descriptor;
/// <summary>
@@ -51,7 +52,7 @@ namespace Google.Protobuf.FieldAccess
/// <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)
+ public FieldAccessorTable(Type type, MessageDescriptor descriptor, string[] propertyNames, string[] oneofPropertyNames)
{
this.descriptor = descriptor;
var accessorsArray = new IFieldAccessor[descriptor.Fields.Count];
@@ -65,7 +66,13 @@ namespace Google.Protobuf.FieldAccess
: (IFieldAccessor) new SingleFieldAccessor(type, name, field);
}
accessors = new ReadOnlyCollection<IFieldAccessor>(accessorsArray);
- // TODO(jonskeet): Oneof support
+ 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,
@@ -75,6 +82,10 @@ namespace Google.Protobuf.FieldAccess
/// </summary>
public ReadOnlyCollection<IFieldAccessor> Accessors { get { return accessors; } }
+ public ReadOnlyCollection<OneofAccessor> Oneofs { get { return oneofs; } }
+
+ // TODO: Review the API for the indexers. Now that we have fields and oneofs, it's not as clear...
+
public IFieldAccessor this[int fieldNumber]
{
get
@@ -84,7 +95,7 @@ namespace Google.Protobuf.FieldAccess
}
}
- internal IFieldAccessor this[FieldDescriptor field]
+ public IFieldAccessor this[FieldDescriptor field]
{
get
{
@@ -95,5 +106,17 @@ namespace Google.Protobuf.FieldAccess
return accessors[field.Index];
}
}
+
+ public OneofAccessor this[OneofDescriptor oneof]
+ {
+ get
+ {
+ if (oneof.ContainingType != descriptor)
+ {
+ throw new ArgumentException("OneofDescriptor does not match message type.");
+ }
+ return oneofs[oneof.Index];
+ }
+ }
}
} \ No newline at end of file
diff --git a/csharp/src/ProtocolBuffers/FieldAccess/OneofAccessor.cs b/csharp/src/ProtocolBuffers/FieldAccess/OneofAccessor.cs
index feaa6232..590b6309 100644
--- a/csharp/src/ProtocolBuffers/FieldAccess/OneofAccessor.cs
+++ b/csharp/src/ProtocolBuffers/FieldAccess/OneofAccessor.cs
@@ -30,62 +30,57 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
+using Google.Protobuf.Descriptors;
+using System;
+using System.Reflection;
+
namespace Google.Protobuf.FieldAccess
{
- // TODO(jonskeet): Add "new" oneof API support
-
/// <summary>
- /// Access for an oneof
+ /// Reflection access for a oneof, allowing clear and "get case" actions.
/// </summary>
- internal class OneofAccessor<TMessage> where TMessage : IMessage<TMessage>
+ public sealed class OneofAccessor
{
- /*
- private readonly Func<TMessage, object> caseDelegate;
- private readonly Func<TBuilder, IBuilder> clearDelegate;
- private MessageDescriptor descriptor;
+ private readonly Func<object, int> caseDelegate;
+ private readonly Action<object> clearDelegate;
+ private OneofDescriptor descriptor;
- internal OneofAccessor(MessageDescriptor descriptor, string name)
+ internal OneofAccessor(Type type, string propertyName, OneofDescriptor descriptor)
{
- this.descriptor = descriptor;
- MethodInfo clearMethod = typeof(TBuilder).GetMethod("Clear" + name);
- PropertyInfo caseProperty = typeof(TMessage).GetProperty(name + "Case");
- if (clearMethod == null || caseProperty == null)
+ PropertyInfo property = type.GetProperty(propertyName + "Case");
+ if (property == null || !property.CanRead)
{
- throw new ArgumentException("Not all required properties/methods available for oneof");
+ throw new ArgumentException("Not all required properties/methods available");
}
-
+ this.descriptor = descriptor;
+ caseDelegate = ReflectionUtil.CreateFuncObjectT<int>(property.GetGetMethod());
- clearDelegate = ReflectionUtil.CreateDelegateFunc<TBuilder, IBuilder>(clearMethod);
- caseDelegate = ReflectionUtil.CreateUpcastDelegate<TMessage>(caseProperty.GetGetMethod());
+ this.descriptor = descriptor;
+ MethodInfo clearMethod = type.GetMethod("Clear" + propertyName);
+ clearDelegate = ReflectionUtil.CreateActionObject(clearMethod);
}
- /// <summary>
- /// Indicates whether the specified message has set any field in the oneof.
- /// </summary>
- public bool Has(TMessage message)
- {
- return ((int) caseDelegate(message) != 0);
- }
+ public OneofDescriptor Descriptor { get { return descriptor; } }
/// <summary>
- /// Clears the oneof in the specified builder.
+ /// Clears the oneof in the specified message.
/// </summary>
- public void Clear(TBuilder builder)
+ public void Clear(object message)
{
- clearDelegate(builder);
+ clearDelegate(message);
}
/// <summary>
/// Indicates which field in the oneof is set for specified message
/// </summary>
- public virtual FieldDescriptor GetOneofFieldDescriptor(TMessage message)
+ public FieldDescriptor GetCaseFieldDescriptor(object message)
{
- int fieldNumber = (int) caseDelegate(message);
+ int fieldNumber = caseDelegate(message);
if (fieldNumber > 0)
{
- return descriptor.FindFieldByNumber(fieldNumber);
+ return descriptor.ContainingType.FindFieldByNumber(fieldNumber);
}
return null;
- }*/
+ }
}
}
diff --git a/csharp/src/ProtocolBuffers/FieldAccess/ReflectionUtil.cs b/csharp/src/ProtocolBuffers/FieldAccess/ReflectionUtil.cs
index d3053920..08ef6c0c 100644
--- a/csharp/src/ProtocolBuffers/FieldAccess/ReflectionUtil.cs
+++ b/csharp/src/ProtocolBuffers/FieldAccess/ReflectionUtil.cs
@@ -63,7 +63,20 @@ namespace Google.Protobuf.FieldAccess
Expression upcast = Expression.Convert(call, typeof(object));
return Expression.Lambda<Func<object, object>>(upcast, parameter).Compile();
}
-
+
+ /// <summary>
+ /// Creates a delegate which will cast the argument to the appropriate method target type,
+ /// call the method on it, then convert the result to the specified type.
+ /// </summary>
+ internal static Func<object, T> CreateFuncObjectT<T>(MethodInfo method)
+ {
+ ParameterExpression parameter = Expression.Parameter(typeof(object), "p");
+ Expression downcast = Expression.Convert(parameter, method.DeclaringType);
+ Expression call = Expression.Call(downcast, method);
+ Expression upcast = Expression.Convert(call, typeof(T));
+ return Expression.Lambda<Func<object, T>>(upcast, parameter).Compile();
+ }
+
/// <summary>
/// Creates a delegate which will execute the given method after casting the first argument to
/// the target type of the method, and the second argument to the first parameter type of the method.