From f7b417ddfe63cb4d39775e5fd4560894cc547d65 Mon Sep 17 00:00:00 2001 From: Jie Luo Date: Fri, 29 May 2015 12:48:25 -0700 Subject: Add oneof support for C# --- csharp/src/ProtocolBuffers/AbstractBuilder.cs | 8 ++ csharp/src/ProtocolBuffers/AbstractMessage.cs | 2 + .../ProtocolBuffers/Descriptors/FieldDescriptor.cs | 18 ++- .../Descriptors/MessageDescriptor.cs | 28 +++++ .../ProtocolBuffers/Descriptors/OneofDescriptor.cs | 78 +++++++++++++ csharp/src/ProtocolBuffers/DynamicMessage.cs | 122 ++++++++++++++++++++- .../FieldAccess/FieldAccessorTable.cs | 46 +++++++- .../ProtocolBuffers/FieldAccess/OneofAccessor.cs | 91 +++++++++++++++ .../FieldAccess/SingleEnumAccessor.cs | 3 +- .../FieldAccess/SingleMessageAccessor.cs | 4 +- .../FieldAccess/SinglePrimitiveAccessor.cs | 22 +++- csharp/src/ProtocolBuffers/GeneratedBuilder.cs | 16 +++ csharp/src/ProtocolBuffers/GeneratedMessage.cs | 10 ++ csharp/src/ProtocolBuffers/IBuilder.cs | 14 +++ csharp/src/ProtocolBuffers/IMessage.cs | 4 + csharp/src/ProtocolBuffers/ProtocolBuffers.csproj | 4 +- 16 files changed, 452 insertions(+), 18 deletions(-) create mode 100644 csharp/src/ProtocolBuffers/Descriptors/OneofDescriptor.cs create mode 100644 csharp/src/ProtocolBuffers/FieldAccess/OneofAccessor.cs (limited to 'csharp/src/ProtocolBuffers') diff --git a/csharp/src/ProtocolBuffers/AbstractBuilder.cs b/csharp/src/ProtocolBuffers/AbstractBuilder.cs index e7a41fb3..8e33e931 100644 --- a/csharp/src/ProtocolBuffers/AbstractBuilder.cs +++ b/csharp/src/ProtocolBuffers/AbstractBuilder.cs @@ -58,8 +58,11 @@ namespace Google.ProtocolBuffers public abstract int GetRepeatedFieldCount(FieldDescriptor field); public abstract object this[FieldDescriptor field, int index] { get; set; } public abstract bool HasField(FieldDescriptor field); + public abstract bool HasOneof(OneofDescriptor oneof); + public abstract FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof); public abstract IBuilder CreateBuilderForField(FieldDescriptor field); public abstract TBuilder ClearField(FieldDescriptor field); + public abstract TBuilder ClearOneof(OneofDescriptor oneof); public abstract TBuilder AddRepeatedField(FieldDescriptor field, object value); #endregion @@ -248,6 +251,11 @@ namespace Google.ProtocolBuffers return ClearField(field); } + IBuilder IBuilder.WeakClearOneof(OneofDescriptor oneof) + { + return ClearOneof(oneof); + } + #endregion /// diff --git a/csharp/src/ProtocolBuffers/AbstractMessage.cs b/csharp/src/ProtocolBuffers/AbstractMessage.cs index 16c8c786..ef057e6f 100644 --- a/csharp/src/ProtocolBuffers/AbstractMessage.cs +++ b/csharp/src/ProtocolBuffers/AbstractMessage.cs @@ -62,6 +62,8 @@ namespace Google.ProtocolBuffers public abstract MessageDescriptor DescriptorForType { get; } public abstract IDictionary AllFields { get; } public abstract bool HasField(FieldDescriptor field); + public abstract bool HasOneof(OneofDescriptor oneof); + public abstract FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof); public abstract object this[FieldDescriptor field] { get; } public abstract int GetRepeatedFieldCount(FieldDescriptor field); public abstract object this[FieldDescriptor field, int index] { get; } diff --git a/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs b/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs index 076dc852..e7f5a3c1 100644 --- a/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs +++ b/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs @@ -47,6 +47,7 @@ namespace Google.ProtocolBuffers.Descriptors private EnumDescriptor enumType; private MessageDescriptor messageType; private MessageDescriptor containingType; + private OneofDescriptor containingOneof; private object defaultValue; private FieldType fieldType; private MappedType mappedType; @@ -94,6 +95,16 @@ namespace Google.ProtocolBuffers.Descriptors "FieldDescriptorProto.Extendee set for non-extension field."); } containingType = parent; + if (proto.HasOneofIndex) + { + if (proto.OneofIndex < 0 || proto.OneofIndex >= parent.Proto.OneofDeclCount) + { + throw new DescriptorValidationException(this, + "FieldDescriptorProto.oneof_index is out of range for type " + parent.Name); + } + containingOneof = parent.Oneofs[proto.OneofIndex]; + containingOneof.fieldCount ++; + } extensionScope = null; } @@ -253,7 +264,12 @@ namespace Google.ProtocolBuffers.Descriptors { get { return containingType; } } - + + public OneofDescriptor ContainingOneof + { + get { return containingOneof; } + } + /// /// For extensions defined nested within message types, gets /// the outer type. Not valid for non-extension fields. diff --git a/csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs b/csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs index 5b29849c..c00711b9 100644 --- a/csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs +++ b/csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs @@ -45,6 +45,7 @@ namespace Google.ProtocolBuffers.Descriptors private readonly IList enumTypes; private readonly IList fields; private readonly IList extensions; + private readonly IList oneofs; private bool hasRequiredFields; internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex) @@ -52,6 +53,10 @@ namespace Google.ProtocolBuffers.Descriptors { containingType = parent; + oneofs = DescriptorUtil.ConvertAndMakeReadOnly(proto.OneofDeclList, + (oneof, index) => + new OneofDescriptor(oneof, file, this, index)); + nestedTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.NestedTypeList, (type, index) => new MessageDescriptor(type, file, this, index)); @@ -69,6 +74,19 @@ namespace Google.ProtocolBuffers.Descriptors (field, index) => new FieldDescriptor(field, file, this, index, true)); + for (int i = 0; i < proto.OneofDeclCount; i++) + { + oneofs[i].fields = new FieldDescriptor[oneofs[i].FieldCount]; + oneofs[i].fieldCount = 0; + } + for (int i = 0; i< proto.FieldCount; i++) + { + OneofDescriptor oneofDescriptor = fields[i].ContainingOneof; + if (oneofDescriptor != null) + { + oneofDescriptor.fields[oneofDescriptor.fieldCount++] = fields[i]; + } + } file.DescriptorPool.AddSymbol(this); } @@ -112,6 +130,11 @@ namespace Google.ProtocolBuffers.Descriptors get { return enumTypes; } } + public IList Oneofs + { + get { return oneofs; } + } + /// /// Returns a pre-computed result as to whether this message /// has required fields. This includes optional fields which are @@ -189,6 +212,11 @@ namespace Google.ProtocolBuffers.Descriptors { extension.CrossLink(); } + + foreach (OneofDescriptor oneof in oneofs) + { + // oneof.C + } } internal void CheckRequiredFields() diff --git a/csharp/src/ProtocolBuffers/Descriptors/OneofDescriptor.cs b/csharp/src/ProtocolBuffers/Descriptors/OneofDescriptor.cs new file mode 100644 index 00000000..aa62853b --- /dev/null +++ b/csharp/src/ProtocolBuffers/Descriptors/OneofDescriptor.cs @@ -0,0 +1,78 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2015 Google Inc. All rights reserved. +// Author: jieluo@google.com (Jie Luo) +// +// 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. +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Google.ProtocolBuffers.DescriptorProtos; + +namespace Google.ProtocolBuffers.Descriptors +{ + public sealed class OneofDescriptor + { + private int index; + private OneofDescriptorProto proto; + private FileDescriptor file; + private MessageDescriptor containingType; + internal int fieldCount; + internal IList fields; + + internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file, + MessageDescriptor parent, int index) + { + this.proto = proto; + this.file = file; + this.index = index; + + containingType = parent; + fieldCount = 0; + } + + public int Index + { + get { return index; } + } + + public MessageDescriptor ContainingType + { + get { return containingType; } + } + + public int FieldCount + { + get { return fieldCount; } + } + + public FieldDescriptor Field(int index) + { + return fields[index]; + } + } +} diff --git a/csharp/src/ProtocolBuffers/DynamicMessage.cs b/csharp/src/ProtocolBuffers/DynamicMessage.cs index e39efb12..0a8772ca 100644 --- a/csharp/src/ProtocolBuffers/DynamicMessage.cs +++ b/csharp/src/ProtocolBuffers/DynamicMessage.cs @@ -48,6 +48,7 @@ namespace Google.ProtocolBuffers { private readonly MessageDescriptor type; private readonly FieldSet fields; + private readonly FieldDescriptor[] oneofCase; private readonly UnknownFieldSet unknownFields; private int memoizedSize = -1; @@ -57,10 +58,12 @@ namespace Google.ProtocolBuffers /// /// /// - private DynamicMessage(MessageDescriptor type, FieldSet fields, UnknownFieldSet unknownFields) + private DynamicMessage(MessageDescriptor type, FieldSet fields, + FieldDescriptor[] oneofCase, UnknownFieldSet unknownFields) { this.type = type; this.fields = fields; + this.oneofCase = oneofCase; this.unknownFields = unknownFields; } @@ -71,7 +74,9 @@ namespace Google.ProtocolBuffers /// public static DynamicMessage GetDefaultInstance(MessageDescriptor type) { - return new DynamicMessage(type, FieldSet.DefaultInstance, UnknownFieldSet.DefaultInstance); + int oneofDescriptorCount = type.Proto.OneofDeclCount; + FieldDescriptor[] oneofCase = new FieldDescriptor[oneofDescriptorCount]; + return new DynamicMessage(type, FieldSet.DefaultInstance, oneofCase, UnknownFieldSet.DefaultInstance); } /// @@ -201,6 +206,23 @@ namespace Google.ProtocolBuffers get { return fields.AllFieldDescriptors; } } + public override bool HasOneof(OneofDescriptor oneof) + { + VerifyContainingOneofType(oneof); + FieldDescriptor field = oneofCase[oneof.Index]; + if (field == null) + { + return false; + } + return true; + } + + public override FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof) + { + VerifyContainingOneofType(oneof); + return oneofCase[oneof.Index]; + } + public override bool HasField(FieldDescriptor field) { VerifyContainingType(field); @@ -305,6 +327,17 @@ namespace Google.ProtocolBuffers } } + /// + /// Verifies that the oneof is an oneof of this message. + /// + private void VerifyContainingOneofType(OneofDescriptor oneof) + { + if (oneof.ContainingType != type) + { + throw new ArgumentException("OneofDescritpor does not match message type"); + } + } + /// /// Builder for dynamic messages. Instances are created with DynamicMessage.CreateBuilder. /// @@ -312,6 +345,7 @@ namespace Google.ProtocolBuffers { private readonly MessageDescriptor type; private FieldSet fields; + private FieldDescriptor[] oneofCase; private UnknownFieldSet unknownFields; internal Builder(MessageDescriptor type) @@ -319,6 +353,7 @@ namespace Google.ProtocolBuffers this.type = type; this.fields = FieldSet.CreateInstance(); this.unknownFields = UnknownFieldSet.DefaultInstance; + this.oneofCase = new FieldDescriptor[type.Proto.OneofDeclCount]; } protected override Builder ThisBuilder @@ -340,6 +375,23 @@ namespace Google.ProtocolBuffers } fields.MergeFrom(other); MergeUnknownFields(other.UnknownFields); + for (int i = 0; i < oneofCase.Length; i++) + { + if (other.HasOneof(type.Oneofs[i])) + { + if (oneofCase[i] == null) + { + oneofCase[i] = other.OneofFieldDescriptor(type.Oneofs[i]); + } else + { + if (oneofCase[i] != other.OneofFieldDescriptor(type.Oneofs[i])) + { + fields.ClearField(oneofCase[i]); + oneofCase[i] = other.OneofFieldDescriptor(type.Oneofs[i]); + } + } + } + } return this; } @@ -353,7 +405,7 @@ namespace Google.ProtocolBuffers { if (fields != null && !IsInitialized) { - throw new UninitializedMessageException(new DynamicMessage(type, fields, unknownFields)); + throw new UninitializedMessageException(new DynamicMessage(type, fields, oneofCase, unknownFields)); } return BuildPartial(); } @@ -367,7 +419,7 @@ namespace Google.ProtocolBuffers { if (!IsInitialized) { - throw new UninitializedMessageException(new DynamicMessage(type, fields, unknownFields)). + throw new UninitializedMessageException(new DynamicMessage(type, fields, oneofCase, unknownFields)). AsInvalidProtocolBufferException(); } return BuildPartial(); @@ -380,7 +432,7 @@ namespace Google.ProtocolBuffers throw new InvalidOperationException("Build() has already been called on this Builder."); } fields.MakeImmutable(); - DynamicMessage result = new DynamicMessage(type, fields, unknownFields); + DynamicMessage result = new DynamicMessage(type, fields, oneofCase, unknownFields); fields = null; unknownFields = null; return result; @@ -390,6 +442,7 @@ namespace Google.ProtocolBuffers { Builder result = new Builder(type); result.fields.MergeFrom(fields); + result.oneofCase = oneofCase; return result; } @@ -431,6 +484,23 @@ namespace Google.ProtocolBuffers return new Builder(field.MessageType); } + public override bool HasOneof(OneofDescriptor oneof) + { + VerifyContainingOneofType(oneof); + FieldDescriptor field = oneofCase[oneof.Index]; + if (field == null) + { + return false; + } + return true; + } + + public override FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof) + { + VerifyContainingOneofType(oneof); + return oneofCase[oneof.Index]; + } + public override bool HasField(FieldDescriptor field) { VerifyContainingType(field); @@ -466,6 +536,17 @@ namespace Google.ProtocolBuffers set { VerifyContainingType(field); + OneofDescriptor oneof = field.ContainingOneof; + if (oneof != null) + { + int index = oneof.Index; + FieldDescriptor oldField = oneofCase[index]; + if ((oldField != null) && (oldField != field)) + { + fields.ClearField(oldField); + } + oneofCase[index] = field; + } fields[field] = value; } } @@ -473,10 +554,30 @@ namespace Google.ProtocolBuffers public override Builder ClearField(FieldDescriptor field) { VerifyContainingType(field); + OneofDescriptor oneof = field.ContainingOneof; + if (oneof != null) + { + int index = oneof.Index; + if (oneofCase[index] == field) + { + oneofCase[index] = null; + } + } fields.ClearField(field); return this; } + public override Builder ClearOneof(OneofDescriptor oneof) + { + VerifyContainingOneofType(oneof); + FieldDescriptor field = oneofCase[oneof.Index]; + if (field != null) + { + ClearField(field); + } + return this; + } + public override int GetRepeatedFieldCount(FieldDescriptor field) { VerifyContainingType(field); @@ -507,6 +608,17 @@ namespace Google.ProtocolBuffers throw new ArgumentException("FieldDescriptor does not match message type."); } } + + /// + /// Verifies that the oneof is an oneof of this message. + /// + private void VerifyContainingOneofType(OneofDescriptor oneof) + { + if (oneof.ContainingType != type) + { + throw new ArgumentException("OneofDescriptor does not match message type"); + } + } } } } \ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs b/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs index 6e20b14e..ad1a4382 100644 --- a/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs +++ b/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs @@ -45,6 +45,7 @@ namespace Google.ProtocolBuffers.FieldAccess where TBuilder : IBuilder { private readonly IFieldAccessor[] accessors; + private readonly OneofAccessor[] oneofs; private readonly MessageDescriptor descriptor; @@ -68,17 +69,28 @@ namespace Google.ProtocolBuffers.FieldAccess { this.descriptor = descriptor; accessors = new IFieldAccessor[descriptor.Fields.Count]; + oneofs = new OneofAccessor[descriptor.Oneofs.Count]; bool supportFieldPresence = descriptor.File.Syntax == FileDescriptor.ProtoSyntax.Proto2; - for (int i = 0; i < accessors.Length; i++) + int fieldSize = accessors.Length; + for (int i = 0; i < fieldSize; i++) { - accessors[i] = CreateAccessor(descriptor.Fields[i], propertyNames[i], supportFieldPresence); + FieldDescriptor field = descriptor.Fields[i]; + string containingOneofName = (field.ContainingOneof != null) ? + propertyNames[fieldSize +field.ContainingOneof.Index] : null; + accessors[i] = CreateAccessor( + field, propertyNames[i], containingOneofName, supportFieldPresence); + } + for (int i = 0; i < oneofs.Length; i++) + { + oneofs[i] = new OneofAccessor(descriptor, propertyNames[i + accessors.Length]); } } /// /// Creates an accessor for a single field /// - private static IFieldAccessor CreateAccessor(FieldDescriptor field, string name, bool supportFieldPresence) + private static IFieldAccessor CreateAccessor( + FieldDescriptor field, string name, string containingOneofName, bool supportFieldPresence) { if (field.IsRepeated) { @@ -97,11 +109,24 @@ namespace Google.ProtocolBuffers.FieldAccess switch (field.MappedType) { case MappedType.Message: - return new SingleMessageAccessor(name); + { + if (field.ContainingOneof != null) + { + return new SingleMessageAccessor( + field, name, containingOneofName, supportFieldPresence); + } + else + { + return new SingleMessageAccessor( + field, name, containingOneofName, true); + } + } case MappedType.Enum: - return new SingleEnumAccessor(field, name, supportFieldPresence); + return new SingleEnumAccessor( + field, name, containingOneofName, supportFieldPresence); default: - return new SinglePrimitiveAccessor(field, name, supportFieldPresence); + return new SinglePrimitiveAccessor( + field, name, containingOneofName, supportFieldPresence); } } } @@ -123,5 +148,14 @@ namespace Google.ProtocolBuffers.FieldAccess return accessors[field.Index]; } } + + internal OneofAccessor Oneof(OneofDescriptor oneof) + { + 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 new file mode 100644 index 00000000..1a4bda76 --- /dev/null +++ b/csharp/src/ProtocolBuffers/FieldAccess/OneofAccessor.cs @@ -0,0 +1,91 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2015 Google Inc. All rights reserved. +// Author: jieluo@google.com (Jie Luo) +// +// 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. +using System; +using System.Reflection; +using Google.ProtocolBuffers.Descriptors; + +namespace Google.ProtocolBuffers.FieldAccess +{ + /// + /// Access for an oneof + /// + internal class OneofAccessor + where TMessage : IMessage + where TBuilder : IBuilder + { + private readonly Func caseDelegate; + private readonly Func clearDelegate; + private MessageDescriptor descriptor; + + internal OneofAccessor(MessageDescriptor descriptor, string name) + { + this.descriptor = descriptor; + MethodInfo clearMethod = typeof(TBuilder).GetMethod("Clear" + name); + PropertyInfo caseProperty = typeof(TMessage).GetProperty(name + "Case"); + if (clearMethod == null || caseProperty == null) + { + throw new ArgumentException("Not all required properties/methods available for oneof"); + } + + + clearDelegate = ReflectionUtil.CreateDelegateFunc(clearMethod); + caseDelegate = ReflectionUtil.CreateUpcastDelegate(caseProperty.GetGetMethod()); + } + + /// + /// Indicates whether the specified message has set any field in the oneof. + /// + public bool Has(TMessage message) + { + return ((int) caseDelegate(message) != 0); + } + + /// + /// Clears the oneof in the specified builder. + /// + public void Clear(TBuilder builder) + { + clearDelegate(builder); + } + + /// + /// Indicates which field in the oneof is set for specified message + /// + public virtual FieldDescriptor GetOneofFieldDescriptor(TMessage message) + { + int fieldNumber = (int) caseDelegate(message); + if (fieldNumber > 0) + { + return descriptor.FindFieldByNumber(fieldNumber); + } + return null; + } + } +} diff --git a/csharp/src/ProtocolBuffers/FieldAccess/SingleEnumAccessor.cs b/csharp/src/ProtocolBuffers/FieldAccess/SingleEnumAccessor.cs index e63f717c..89e10179 100644 --- a/csharp/src/ProtocolBuffers/FieldAccess/SingleEnumAccessor.cs +++ b/csharp/src/ProtocolBuffers/FieldAccess/SingleEnumAccessor.cs @@ -42,7 +42,8 @@ namespace Google.ProtocolBuffers.FieldAccess { private readonly EnumDescriptor enumDescriptor; - internal SingleEnumAccessor(FieldDescriptor field, string name, bool supportFieldPresence) : base(field, name, supportFieldPresence) + internal SingleEnumAccessor(FieldDescriptor field, string name, string containingOneofName, bool supportFieldPresence) + : base(field, name, containingOneofName, supportFieldPresence) { enumDescriptor = field.EnumType; } diff --git a/csharp/src/ProtocolBuffers/FieldAccess/SingleMessageAccessor.cs b/csharp/src/ProtocolBuffers/FieldAccess/SingleMessageAccessor.cs index 0ec2b0b7..9068d40a 100644 --- a/csharp/src/ProtocolBuffers/FieldAccess/SingleMessageAccessor.cs +++ b/csharp/src/ProtocolBuffers/FieldAccess/SingleMessageAccessor.cs @@ -31,6 +31,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System; using System.Reflection; +using Google.ProtocolBuffers.Descriptors; namespace Google.ProtocolBuffers.FieldAccess { @@ -48,7 +49,8 @@ namespace Google.ProtocolBuffers.FieldAccess /// private readonly Func createBuilderDelegate; - internal SingleMessageAccessor(string name) : base(null, name, true) + internal SingleMessageAccessor(FieldDescriptor field, string name, string containingOneofName, bool supportFieldPresence) + : base(field, name, containingOneofName, supportFieldPresence) { MethodInfo createBuilderMethod = ClrType.GetMethod("CreateBuilder", ReflectionUtil.EmptyTypes); if (createBuilderMethod == null) diff --git a/csharp/src/ProtocolBuffers/FieldAccess/SinglePrimitiveAccessor.cs b/csharp/src/ProtocolBuffers/FieldAccess/SinglePrimitiveAccessor.cs index b9ab7293..035fcf3c 100644 --- a/csharp/src/ProtocolBuffers/FieldAccess/SinglePrimitiveAccessor.cs +++ b/csharp/src/ProtocolBuffers/FieldAccess/SinglePrimitiveAccessor.cs @@ -47,6 +47,7 @@ namespace Google.ProtocolBuffers.FieldAccess private readonly Action setValueDelegate; private readonly Func hasDelegate; private readonly Func clearDelegate; + private readonly Func caseDelegate; /// /// The CLR type of the field (int, the enum type, ByteString, the message etc). @@ -57,7 +58,8 @@ namespace Google.ProtocolBuffers.FieldAccess get { return clrType; } } - internal SinglePrimitiveAccessor(FieldDescriptor fieldDescriptor, string name, bool supportFieldPresence) + internal SinglePrimitiveAccessor( + FieldDescriptor fieldDescriptor, string name, string containingOneofName, bool supportFieldPresence) { PropertyInfo messageProperty = typeof(TMessage).GetProperty(name, null, ReflectionUtil.EmptyTypes); PropertyInfo builderProperty = typeof(TBuilder).GetProperty(name, null, ReflectionUtil.EmptyTypes); @@ -77,7 +79,16 @@ namespace Google.ProtocolBuffers.FieldAccess hasDelegate = ReflectionUtil.CreateDelegateFunc(hasProperty.GetGetMethod()); } else { - hasDelegate = message => !GetValue(message).Equals(fieldDescriptor.DefaultValue); + if (fieldDescriptor.ContainingOneof != null) + { + PropertyInfo caseProperty = typeof(TMessage).GetProperty(containingOneofName + "Case"); + caseDelegate = ReflectionUtil.CreateUpcastDelegate(caseProperty.GetGetMethod()); + hasDelegate = message => OneofFieldNumber(message).Equals(fieldDescriptor.FieldNumber); + } + else + { + hasDelegate = message => !GetValue(message).Equals(fieldDescriptor.DefaultValue); + } } clrType = messageProperty.PropertyType; @@ -86,6 +97,11 @@ namespace Google.ProtocolBuffers.FieldAccess setValueDelegate = ReflectionUtil.CreateDowncastDelegate(builderProperty.GetSetMethod()); } + private int OneofFieldNumber(TMessage message) + { + return (int) caseDelegate(message); + } + public bool Has(TMessage message) { return hasDelegate(message); @@ -143,4 +159,4 @@ namespace Google.ProtocolBuffers.FieldAccess #endregion } -} \ No newline at end of file +} diff --git a/csharp/src/ProtocolBuffers/GeneratedBuilder.cs b/csharp/src/ProtocolBuffers/GeneratedBuilder.cs index e60a4201..0f121ae8 100644 --- a/csharp/src/ProtocolBuffers/GeneratedBuilder.cs +++ b/csharp/src/ProtocolBuffers/GeneratedBuilder.cs @@ -105,6 +105,16 @@ namespace Google.ProtocolBuffers set { InternalFieldAccessors[field].SetRepeated(ThisBuilder, index, value); } } + public override bool HasOneof(OneofDescriptor oneof) + { + return MessageBeingBuilt.HasOneof(oneof); + } + + public override FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof) + { + return MessageBeingBuilt.OneofFieldDescriptor(oneof); + } + public override bool HasField(FieldDescriptor field) { return MessageBeingBuilt.HasField(field); @@ -121,6 +131,12 @@ namespace Google.ProtocolBuffers return ThisBuilder; } + public override TBuilder ClearOneof(OneofDescriptor oneof) + { + InternalFieldAccessors.Oneof(oneof).Clear(ThisBuilder); + return ThisBuilder; + } + public override TBuilder MergeFrom(TMessage other) { if (other.DescriptorForType != InternalFieldAccessors.Descriptor) diff --git a/csharp/src/ProtocolBuffers/GeneratedMessage.cs b/csharp/src/ProtocolBuffers/GeneratedMessage.cs index c17c15cf..ff3a0c2a 100644 --- a/csharp/src/ProtocolBuffers/GeneratedMessage.cs +++ b/csharp/src/ProtocolBuffers/GeneratedMessage.cs @@ -142,6 +142,16 @@ namespace Google.ProtocolBuffers get { return Dictionaries.AsReadOnly(GetMutableFieldMap()); } } + public override bool HasOneof(OneofDescriptor oneof) + { + return InternalFieldAccessors.Oneof(oneof).Has(ThisMessage); + } + + public override FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof) + { + return InternalFieldAccessors.Oneof(oneof).GetOneofFieldDescriptor(ThisMessage); + } + public override bool HasField(FieldDescriptor field) { return InternalFieldAccessors[field].Has(ThisMessage); diff --git a/csharp/src/ProtocolBuffers/IBuilder.cs b/csharp/src/ProtocolBuffers/IBuilder.cs index f9c0df61..e765464a 100644 --- a/csharp/src/ProtocolBuffers/IBuilder.cs +++ b/csharp/src/ProtocolBuffers/IBuilder.cs @@ -103,6 +103,11 @@ namespace Google.ProtocolBuffers /// object this[FieldDescriptor field, int index] { get; set; } + + bool HasOneof(OneofDescriptor oneof); + + FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof); + /// /// /// @@ -125,6 +130,7 @@ namespace Google.ProtocolBuffers IBuilder WeakAddRepeatedField(FieldDescriptor field, object value); new IBuilder WeakClear(); IBuilder WeakClearField(FieldDescriptor field); + IBuilder WeakClearOneof(OneofDescriptor oneof); IBuilder WeakMergeFrom(IMessage message); new IBuilder WeakMergeFrom(ByteString data); new IBuilder WeakMergeFrom(ByteString data, ExtensionRegistry registry); @@ -227,6 +233,14 @@ namespace Google.ProtocolBuffers /// TBuilder ClearField(FieldDescriptor field); + /// + /// Clears the oneof. This is exactly equivalent to calling the generated + /// Clear method corresponding to the oneof. + /// + /// + /// + TBuilder ClearOneof(OneofDescriptor oneof); + /// /// Appends the given value as a new element for the specified repeated field. /// diff --git a/csharp/src/ProtocolBuffers/IMessage.cs b/csharp/src/ProtocolBuffers/IMessage.cs index c23bc3f7..dd309d4e 100644 --- a/csharp/src/ProtocolBuffers/IMessage.cs +++ b/csharp/src/ProtocolBuffers/IMessage.cs @@ -67,6 +67,10 @@ namespace Google.ProtocolBuffers /// IDictionary AllFields { get; } + bool HasOneof(OneofDescriptor oneof); + + FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof); + /// /// Returns true if the given field is set. This is exactly equivalent /// to calling the generated "Has" property corresponding to the field. diff --git a/csharp/src/ProtocolBuffers/ProtocolBuffers.csproj b/csharp/src/ProtocolBuffers/ProtocolBuffers.csproj index 32a343ad..7dc8f665 100644 --- a/csharp/src/ProtocolBuffers/ProtocolBuffers.csproj +++ b/csharp/src/ProtocolBuffers/ProtocolBuffers.csproj @@ -81,6 +81,7 @@ + @@ -106,6 +107,7 @@ + @@ -146,4 +148,4 @@ --> - \ No newline at end of file + -- cgit v1.2.3