diff options
Diffstat (limited to 'src/ProtocolBuffers/FieldSet.cs')
-rw-r--r-- | src/ProtocolBuffers/FieldSet.cs | 199 |
1 files changed, 118 insertions, 81 deletions
diff --git a/src/ProtocolBuffers/FieldSet.cs b/src/ProtocolBuffers/FieldSet.cs index 7c373b95..c3e3d740 100644 --- a/src/ProtocolBuffers/FieldSet.cs +++ b/src/ProtocolBuffers/FieldSet.cs @@ -39,6 +39,21 @@ using Google.ProtocolBuffers.Collections; using Google.ProtocolBuffers.Descriptors; namespace Google.ProtocolBuffers { + + public interface IFieldDescriptorLite : IComparable<IFieldDescriptorLite> { + bool IsRepeated { get; } + bool IsRequired { get; } + bool IsPacked { get; } + bool IsExtension { get; } + bool MessageSetWireFormat { get; } //field.ContainingType.Options.MessageSetWireFormat + int FieldNumber { get; } + string FullName { get; } + IEnumLiteMap EnumType { get; } + FieldType FieldType { get; } + MappedType MappedType { get; } + object DefaultValue { get; } + } + /// <summary> /// A class which represents an arbitrary set of fields of some message type. /// This is used to implement DynamicMessage, and also to represent extensions @@ -56,17 +71,17 @@ namespace Google.ProtocolBuffers { /// </summary> internal sealed class FieldSet { - private static readonly FieldSet defaultInstance = new FieldSet(new Dictionary<FieldDescriptor, object>()).MakeImmutable(); + private static readonly FieldSet defaultInstance = new FieldSet(new Dictionary<IFieldDescriptorLite, object>()).MakeImmutable(); - private IDictionary<FieldDescriptor, object> fields; + private IDictionary<IFieldDescriptorLite, object> fields; - private FieldSet(IDictionary<FieldDescriptor, object> fields) { + private FieldSet(IDictionary<IFieldDescriptorLite, object> fields) { this.fields = fields; } public static FieldSet CreateInstance() { // Use SortedList to keep fields in the canonical order - return new FieldSet(new SortedList<FieldDescriptor, object>()); + return new FieldSet(new SortedList<IFieldDescriptorLite, object>()); } /// <summary> @@ -85,8 +100,8 @@ namespace Google.ProtocolBuffers { } if (hasRepeats) { - var tmp = new SortedList<FieldDescriptor, object>(); - foreach (KeyValuePair<FieldDescriptor, object> entry in fields) { + var tmp = new SortedList<IFieldDescriptorLite, object>(); + foreach (KeyValuePair<IFieldDescriptorLite, object> entry in fields) { IList<object> list = entry.Value as IList<object>; tmp[entry.Key] = list == null ? entry.Value : Lists.AsReadOnly(list); } @@ -110,14 +125,26 @@ namespace Google.ProtocolBuffers { /// is immutable, the entries may not be (i.e. any repeated values are represented by /// mutable lists). The behaviour is not specified if the contents are mutated. /// </summary> - internal IDictionary<FieldDescriptor, object> AllFields { + internal IDictionary<IFieldDescriptorLite, object> AllFields { get { return Dictionaries.AsReadOnly(fields); } } - +#if !LITE + /// <summary> + /// Force coercion to full descriptor dictionary. + /// </summary> + internal IDictionary<Descriptors.FieldDescriptor, object> AllFieldDescriptors { + get { + SortedList<Descriptors.FieldDescriptor, object> copy = new SortedList<Google.ProtocolBuffers.Descriptors.FieldDescriptor, object>(); + foreach (KeyValuePair<IFieldDescriptorLite, object> fd in fields) + copy.Add((Descriptors.FieldDescriptor)fd.Key, fd.Value); + return Dictionaries.AsReadOnly(copy); + } + } +#endif /// <summary> - /// See <see cref="IMessage.HasField"/>. + /// See <see cref="IMessageLite.HasField"/>. /// </summary> - public bool HasField(FieldDescriptor field) { + public bool HasField(IFieldDescriptorLite field) { if (field.IsRepeated) { throw new ArgumentException("HasField() can only be called on non-repeated fields."); } @@ -133,7 +160,7 @@ namespace Google.ProtocolBuffers { } /// <summary> - /// See <see cref="IMessage.Item(FieldDescriptor)"/> + /// See <see cref="IMessageLite.Item(IFieldDescriptorLite)"/> /// </summary> /// <remarks> /// If the field is not set, the behaviour when fetching this property varies by field type: @@ -153,7 +180,7 @@ namespace Google.ProtocolBuffers { /// to ensure it is of an appropriate type. /// </remarks> /// - internal object this[FieldDescriptor field] { + internal object this[IFieldDescriptorLite field] { get { object result; if (fields.TryGetValue(field, out result)) { @@ -191,9 +218,9 @@ namespace Google.ProtocolBuffers { } /// <summary> - /// See <see cref="IMessage.Item(FieldDescriptor,int)" /> + /// See <see cref="IMessageLite.Item(IFieldDescriptorLite,int)" /> /// </summary> - internal object this[FieldDescriptor field, int index] { + internal object this[IFieldDescriptorLite field, int index] { get { if (!field.IsRepeated) { throw new ArgumentException("Indexer specifying field and index can only be called on repeated fields."); @@ -217,7 +244,7 @@ namespace Google.ProtocolBuffers { /// <summary> /// See <see cref="IBuilder{TMessage, TBuilder}.AddRepeatedField" /> /// </summary> - internal void AddRepeatedField(FieldDescriptor field, object value) { + internal void AddRepeatedField(IFieldDescriptorLite field, object value) { if (!field.IsRepeated) { throw new ArgumentException("AddRepeatedField can only be called on repeated fields."); } @@ -233,12 +260,12 @@ namespace Google.ProtocolBuffers { /// <summary> /// Returns an enumerator for the field map. Used to write the fields out. /// </summary> - internal IEnumerator<KeyValuePair<FieldDescriptor, object>> GetEnumerator() { + internal IEnumerator<KeyValuePair<IFieldDescriptorLite, object>> GetEnumerator() { return fields.GetEnumerator(); } /// <summary> - /// See <see cref="IMessage.IsInitialized" /> + /// See <see cref="IMessageLite.IsInitialized" /> /// </summary> /// <remarks> /// Since FieldSet itself does not have any way of knowing about @@ -248,17 +275,17 @@ namespace Google.ProtocolBuffers { /// </remarks> internal bool IsInitialized { get { - foreach (KeyValuePair<FieldDescriptor, object> entry in fields) { - FieldDescriptor field = entry.Key; + foreach (KeyValuePair<IFieldDescriptorLite, object> entry in fields) { + IFieldDescriptorLite field = entry.Key; if (field.MappedType == MappedType.Message) { if (field.IsRepeated) { - foreach(IMessage message in (IEnumerable) entry.Value) { + foreach(IMessageLite message in (IEnumerable) entry.Value) { if (!message.IsInitialized) { return false; } } } else { - if (!((IMessage) entry.Value).IsInitialized) { + if (!((IMessageLite)entry.Value).IsInitialized) { return false; } } @@ -273,8 +300,8 @@ namespace Google.ProtocolBuffers { /// descriptor are present in this field set, as well as whether /// all the embedded messages are themselves initialized. /// </summary> - internal bool IsInitializedWithRespectTo(MessageDescriptor type) { - foreach (FieldDescriptor field in type.Fields) { + internal bool IsInitializedWithRespectTo(IEnumerable typeFields) { + foreach (IFieldDescriptorLite field in typeFields) { if (field.IsRequired && !HasField(field)) { return false; } @@ -285,14 +312,14 @@ namespace Google.ProtocolBuffers { /// <summary> /// See <see cref="IBuilder{TMessage, TBuilder}.ClearField" /> /// </summary> - public void ClearField(FieldDescriptor field) { + public void ClearField(IFieldDescriptorLite field) { fields.Remove(field); } /// <summary> - /// See <see cref="IMessage.GetRepeatedFieldCount" /> + /// See <see cref="IMessageLite.GetRepeatedFieldCount" /> /// </summary> - public int GetRepeatedFieldCount(FieldDescriptor field) { + public int GetRepeatedFieldCount(IFieldDescriptorLite field) { if (!field.IsRepeated) { throw new ArgumentException("GetRepeatedFieldCount() can only be called on repeated fields."); } @@ -300,64 +327,63 @@ namespace Google.ProtocolBuffers { return ((IList<object>) this[field]).Count; } +#if !LITE + /// <summary> + /// See <see cref="IBuilder{TMessage, TBuilder}.MergeFrom(IMessageLite)" /> + /// </summary> + public void MergeFrom(IMessage other) { + foreach (KeyValuePair<Descriptors.FieldDescriptor, object> fd in other.AllFields) + MergeField(fd.Key, fd.Value); + } +#endif + /// <summary> /// Implementation of both <c>MergeFrom</c> methods. /// </summary> /// <param name="otherFields"></param> - private void MergeFields(IEnumerable<KeyValuePair<FieldDescriptor, object>> otherFields) { + public void MergeFrom(FieldSet other) { // Note: We don't attempt to verify that other's fields have valid // types. Doing so would be a losing battle. We'd have to verify // all sub-messages as well, and we'd have to make copies of all of // them to insure that they don't change after verification (since - // the IMessage interface itself cannot enforce immutability of + // the IMessageLite interface itself cannot enforce immutability of // implementations). // TODO(jonskeet): Provide a function somewhere called MakeDeepCopy() // which allows people to make secure deep copies of messages. - foreach (KeyValuePair<FieldDescriptor, object> entry in otherFields) { - FieldDescriptor field = entry.Key; - object existingValue; - fields.TryGetValue(field, out existingValue); - if (field.IsRepeated) { - if (existingValue == null) { - existingValue = new List<object>(); - fields[field] = existingValue; - } - IList<object> list = (IList<object>) existingValue; - foreach (object otherValue in (IEnumerable) entry.Value) { - list.Add(otherValue); - } - } else if (field.MappedType == MappedType.Message && existingValue != null) { - IMessage existingMessage = (IMessage)existingValue; - IMessage merged = existingMessage.WeakToBuilder() - .WeakMergeFrom((IMessage) entry.Value) - .WeakBuild(); - this[field] = merged; - } else { - this[field] = entry.Value; - } + foreach (KeyValuePair<IFieldDescriptorLite, object> entry in other.fields) { + MergeField(entry.Key, entry.Value); } } - /// <summary> - /// See <see cref="IBuilder{TMessage, TBuilder}.MergeFrom(IMessage)" /> - /// </summary> - public void MergeFrom(IMessage other) { - MergeFields(other.AllFields); - } - - /// <summary> - /// Like <see cref="MergeFrom(IMessage)"/>, but merges from another <c>FieldSet</c>. - /// </summary> - public void MergeFrom(FieldSet other) { - MergeFields(other.fields); + private void MergeField(IFieldDescriptorLite field, object mergeValue) { + object existingValue; + fields.TryGetValue(field, out existingValue); + if (field.IsRepeated) { + if (existingValue == null) { + existingValue = new List<object>(); + fields[field] = existingValue; + } + IList<object> list = (IList<object>) existingValue; + foreach (object otherValue in (IEnumerable)mergeValue) { + list.Add(otherValue); + } + } else if (field.MappedType == MappedType.Message && existingValue != null) { + IMessageLite existingMessage = (IMessageLite)existingValue; + IMessageLite merged = existingMessage.WeakToBuilder() + .WeakMergeFrom((IMessageLite)mergeValue) + .WeakBuild(); + this[field] = merged; + } else { + this[field] = mergeValue; + } } /// <summary> - /// See <see cref="IMessage.WriteTo(CodedOutputStream)" />. + /// See <see cref="IMessageLite.WriteTo(CodedOutputStream)" />. /// </summary> public void WriteTo(CodedOutputStream output) { - foreach (KeyValuePair<FieldDescriptor, object> entry in fields) { + foreach (KeyValuePair<IFieldDescriptorLite, object> entry in fields) { WriteField(entry.Key, entry.Value, output); } } @@ -365,9 +391,9 @@ namespace Google.ProtocolBuffers { /// <summary> /// Writes a single field to a CodedOutputStream. /// </summary> - public void WriteField(FieldDescriptor field, Object value, CodedOutputStream output) { - if (field.IsExtension && field.ContainingType.Options.MessageSetWireFormat) { - output.WriteMessageSetExtension(field.FieldNumber, (IMessage) value); + public void WriteField(IFieldDescriptorLite field, Object value, CodedOutputStream output) { + if (field.IsExtension && field.MessageSetWireFormat) { + output.WriteMessageSetExtension(field.FieldNumber, (IMessageLite) value); } else { if (field.IsRepeated) { IEnumerable valueList = (IEnumerable) value; @@ -395,18 +421,18 @@ namespace Google.ProtocolBuffers { } /// <summary> - /// See <see cref="IMessage.SerializedSize" />. It's up to the caller to + /// See <see cref="IMessageLite.SerializedSize" />. It's up to the caller to /// cache the resulting size if desired. /// </summary> public int SerializedSize { get { int size = 0; - foreach (KeyValuePair<FieldDescriptor, object> entry in fields) { - FieldDescriptor field = entry.Key; + foreach (KeyValuePair<IFieldDescriptorLite, object> entry in fields) { + IFieldDescriptorLite field = entry.Key; object value = entry.Value; - if (field.IsExtension && field.ContainingType.Options.MessageSetWireFormat) { - size += CodedOutputStream.ComputeMessageSetExtensionSize(field.FieldNumber, (IMessage)value); + if (field.IsExtension && field.MessageSetWireFormat) { + size += CodedOutputStream.ComputeMessageSetExtensionSize(field.FieldNumber, (IMessageLite)value); } else { if (field.IsRepeated) { IEnumerable valueList = (IEnumerable)value; @@ -440,7 +466,7 @@ namespace Google.ProtocolBuffers { /// </remarks> /// <exception cref="ArgumentException">The value is not of the right type.</exception> /// <exception cref="ArgumentNullException">The value is null.</exception> - private static void VerifyType(FieldDescriptor field, object value) { + private static void VerifyType(IFieldDescriptorLite field, object value) { ThrowHelper.ThrowIfNull(value, "value"); bool isValid = false; switch (field.MappedType) { @@ -454,12 +480,17 @@ namespace Google.ProtocolBuffers { case MappedType.String: isValid = value is string; break; case MappedType.ByteString: isValid = value is ByteString; break; case MappedType.Enum: - EnumValueDescriptor enumValue = value as EnumValueDescriptor; - isValid = enumValue != null && enumValue.EnumDescriptor == field.EnumType; + IEnumLite enumValue = value as IEnumLite; + isValid = enumValue != null && field.EnumType.IsValidValue(enumValue); break; case MappedType.Message: - IMessage messageValue = value as IMessage; - isValid = messageValue != null && messageValue.DescriptorForType == field.MessageType; + IMessageLite messageValue = value as IMessageLite; + isValid = messageValue != null; +#if !LITE + if (isValid && messageValue is IMessage && field is FieldDescriptor) { + isValid = ((IMessage) messageValue).DescriptorForType == ((FieldDescriptor) field).MessageType; + } +#endif break; } @@ -468,10 +499,16 @@ namespace Google.ProtocolBuffers { // the stack trace which exact call failed, since the whole chain is // considered one line of code. So, let's make sure to include the // field name and other useful info in the exception. - throw new ArgumentException("Wrong object type used with protocol message reflection. " - + "Message type \"" + field.ContainingType.FullName - + "\", field \"" + (field.IsExtension ? field.FullName : field.Name) - + "\", value was type \"" + value.GetType().Name + "\"."); + string message = "Wrong object type used with protocol message reflection."; +#if !LITE + Google.ProtocolBuffers.Descriptors.FieldDescriptor fieldinfo = field as Google.ProtocolBuffers.Descriptors.FieldDescriptor; + if (fieldinfo != null) { + message += "Message type \"" + fieldinfo.ContainingType.FullName; + message += "\", field \"" + (fieldinfo.IsExtension ? fieldinfo.FullName : fieldinfo.Name); + message += "\", value was type \"" + value.GetType().Name + "\"."; + } +#endif + throw new ArgumentException(message); } } } |