aboutsummaryrefslogtreecommitdiff
path: root/csharp/src/ProtocolBuffers/FieldAccess/SingleFieldAccessor.cs
diff options
context:
space:
mode:
authorJon Skeet <skeet@pobox.com>2015-06-09 19:30:44 +0100
committerJon Skeet <skeet@pobox.com>2015-06-09 19:30:44 +0100
commite38294a62d7f37c0661273a9a26fda16d557423f (patch)
tree316989251907553408e7b32a12792f496333e075 /csharp/src/ProtocolBuffers/FieldAccess/SingleFieldAccessor.cs
parentf52426827e4d5e8da7d205af538799740b5199b9 (diff)
downloadprotobuf-e38294a62d7f37c0661273a9a26fda16d557423f.tar.gz
protobuf-e38294a62d7f37c0661273a9a26fda16d557423f.tar.bz2
protobuf-e38294a62d7f37c0661273a9a26fda16d557423f.zip
First pass at the mutable API. Quite a bit more to do - in particular, it's pretty slow right now.
Diffstat (limited to 'csharp/src/ProtocolBuffers/FieldAccess/SingleFieldAccessor.cs')
-rw-r--r--csharp/src/ProtocolBuffers/FieldAccess/SingleFieldAccessor.cs89
1 files changed, 89 insertions, 0 deletions
diff --git a/csharp/src/ProtocolBuffers/FieldAccess/SingleFieldAccessor.cs b/csharp/src/ProtocolBuffers/FieldAccess/SingleFieldAccessor.cs
new file mode 100644
index 00000000..a352d3a2
--- /dev/null
+++ b/csharp/src/ProtocolBuffers/FieldAccess/SingleFieldAccessor.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Google.Protobuf;
+using Google.Protobuf.Descriptors;
+using Google.Protobuf.FieldAccess;
+
+namespace Google.Protobuf.FieldAccess
+{
+ /// <summary>
+ /// Accessor for single fields.
+ /// </summary>
+ /// <typeparam name="T">The type of message containing the field.</typeparam>
+ internal sealed class SingleFieldAccessor<T> : FieldAccessorBase<T> where T : IMessage<T>
+ {
+ // All the work here is actually done in the constructor - it creates the appropriate delegates.
+ // There are various cases to consider, based on the property type (message, string/bytes, or "genuine" primitive)
+ // and proto2 vs proto3 for non-message types, as proto3 doesn't support "full" presence detection or default
+ // values.
+
+ private readonly Action<T, object> setValueDelegate;
+ private readonly Action<T> clearDelegate;
+ private readonly Func<T, bool> hasValueDelegate;
+
+ internal SingleFieldAccessor(FieldDescriptor descriptor, string name, bool supportsFieldPresence) : base(name)
+ {
+ PropertyInfo property = typeof(T).GetProperty(name);
+ // 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");
+ }
+ setValueDelegate = ReflectionUtil.CreateDowncastDelegate<T>(property.GetSetMethod());
+
+ var clrType = property.PropertyType;
+
+ if (typeof(IMessage).IsAssignableFrom(clrType))
+ {
+ // Message types are simple - we only need to detect nullity.
+ clearDelegate = message => SetValue(message, null);
+ hasValueDelegate = message => GetValue(message) == null;
+ }
+
+ if (supportsFieldPresence)
+ {
+ // Proto2: we expect a HasFoo property and a ClearFoo method.
+ // For strings and byte arrays, setting the property to null would have the equivalent effect,
+ // but we generate the method for consistency, which makes this simpler.
+ PropertyInfo hasProperty = typeof(T).GetProperty("Has" + name);
+ MethodInfo clearMethod = typeof(T).GetMethod("Clear" + name);
+ if (hasProperty == null || clearMethod == null || !hasProperty.CanRead)
+ {
+ throw new ArgumentException("Not all required properties/methods available");
+ }
+ hasValueDelegate = ReflectionUtil.CreateDelegateFunc<T, bool>(hasProperty.GetGetMethod());
+ clearDelegate = ReflectionUtil.CreateDelegateAction<T>(clearMethod);
+ }
+ else
+ {
+ /*
+ // TODO(jonskeet): Reimplement. We need a better way of working out default values.
+ // Proto3: for field detection, we just need the default value of the field (0, "", byte[0] etc)
+ // To clear a field, we set the value to that default.
+ object defaultValue = descriptor.DefaultValue;
+ hasValueDelegate = message => GetValue(message).Equals(defaultValue);
+ clearDelegate = message => SetValue(message, defaultValue);
+ */
+ }
+ }
+
+ public override bool HasValue(T message)
+ {
+ return hasValueDelegate(message);
+ }
+
+ public override void Clear(T message)
+ {
+ clearDelegate(message);
+ }
+
+ public override void SetValue(T message, object value)
+ {
+ setValueDelegate(message, value);
+ }
+ }
+}