Commit ec8c3955 authored by Jon Skeet's avatar Jon Skeet

Gradually implementing FieldSet

parent b83aee75
...@@ -25,8 +25,8 @@ namespace Google.ProtocolBuffers { ...@@ -25,8 +25,8 @@ namespace Google.ProtocolBuffers {
protected abstract IMessage BuildPartialImpl(); protected abstract IMessage BuildPartialImpl();
protected abstract IBuilder CloneImpl(); protected abstract IBuilder CloneImpl();
protected abstract IMessage DefaultInstanceForTypeImpl { get; } protected abstract IMessage DefaultInstanceForTypeImpl { get; }
protected abstract IBuilder NewBuilderForFieldImpl<TField>(FieldDescriptor field); protected abstract IBuilder NewBuilderForFieldImpl(FieldDescriptor field);
protected abstract IBuilder ClearFieldImpl(); protected abstract IBuilder ClearFieldImpl(FieldDescriptor field);
protected abstract IBuilder AddRepeatedFieldImpl(FieldDescriptor field, object value); protected abstract IBuilder AddRepeatedFieldImpl(FieldDescriptor field, object value);
#endregion #endregion
...@@ -39,30 +39,30 @@ namespace Google.ProtocolBuffers { ...@@ -39,30 +39,30 @@ namespace Google.ProtocolBuffers {
return BuildPartialImpl(); return BuildPartialImpl();
} }
public IBuilder Clone() { IBuilder IBuilder.Clone() {
return CloneImpl(); return CloneImpl();
} }
public IMessage DefaultInstanceForType { IMessage IBuilder.DefaultInstanceForType {
get { return DefaultInstanceForTypeImpl; } get { return DefaultInstanceForTypeImpl; }
} }
public IBuilder NewBuilderForField<TField>(FieldDescriptor field) { IBuilder IBuilder.NewBuilderForField(FieldDescriptor field) {
return NewBuilderForFieldImpl<TField>(field); return NewBuilderForFieldImpl(field);
} }
public IBuilder ClearField(FieldDescriptor field) { IBuilder IBuilder.ClearField(FieldDescriptor field) {
return ClearFieldImpl(); return ClearFieldImpl(field);
} }
public IBuilder AddRepeatedField(FieldDescriptor field, object value) { IBuilder IBuilder.AddRepeatedField(FieldDescriptor field, object value) {
return AddRepeatedFieldImpl(field, value); return AddRepeatedFieldImpl(field, value);
} }
#endregion #endregion
public IBuilder Clear() { public IBuilder Clear() {
foreach(FieldDescriptor field in AllFields.Keys) { foreach(FieldDescriptor field in AllFields.Keys) {
ClearField(field); ClearFieldImpl(field);
} }
return this; return this;
} }
...@@ -85,7 +85,7 @@ namespace Google.ProtocolBuffers { ...@@ -85,7 +85,7 @@ namespace Google.ProtocolBuffers {
if (field.IsRepeated) { if (field.IsRepeated) {
// Concatenate repeated fields // Concatenate repeated fields
foreach (object element in (IEnumerable) entry.Value) { foreach (object element in (IEnumerable) entry.Value) {
AddRepeatedField(field, element); AddRepeatedFieldImpl(field, element);
} }
} else if (field.MappedType == MappedType.Message) { } else if (field.MappedType == MappedType.Message) {
// Merge singular messages // Merge singular messages
......
using System;
using System.Collections.Generic;
using System.Text;
namespace Google.ProtocolBuffers.Collections {
/// <summary>
/// Non-generic class with generic methods which proxy to the non-generic methods
/// in the generic class.
/// </summary>
public static class Dictionaries {
public static IDictionary<TKey, TValue> AsReadOnly<TKey, TValue> (IDictionary<TKey, TValue> dictionary) {
return dictionary.IsReadOnly ? dictionary : new ReadOnlyDictionary<TKey, TValue>(dictionary);
}
}
}
...@@ -4,18 +4,34 @@ using System.Collections.ObjectModel; ...@@ -4,18 +4,34 @@ using System.Collections.ObjectModel;
using System.Text; using System.Text;
namespace Google.ProtocolBuffers.Collections { namespace Google.ProtocolBuffers.Collections {
public static class Lists {
public static IList<T> AsReadOnly<T>(IList<T> list) {
return Lists<T>.AsReadOnly(list);
}
}
/// <summary> /// <summary>
/// Utilities class for dealing with lists. /// Utilities class for dealing with lists.
/// </summary> /// </summary>
static class Lists<T> { public static class Lists<T> {
static readonly ReadOnlyCollection<T> empty = new ReadOnlyCollection<T>(new T[0]); static readonly ReadOnlyCollection<T> empty = new ReadOnlyCollection<T>(new T[0]);
/// <summary> /// <summary>
/// Returns an immutable empty list. /// Returns an immutable empty list.
/// </summary> /// </summary>
internal static ReadOnlyCollection<T> Empty { public static ReadOnlyCollection<T> Empty {
get { return empty; } get { return empty; }
} }
/// <summary>
/// Returns either the original reference if it's already read-only,
/// or a new ReadOnlyCollection wrapping the original list.
/// </summary>
public static IList<T> AsReadOnly(IList<T> list) {
return list.IsReadOnly ? list : new ReadOnlyCollection<T>(list);
}
} }
} }
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using IEnumerable=System.Collections.IEnumerable;
namespace Google.ProtocolBuffers.Collections {
/// <summary>
/// Read-only wrapper around another dictionary.
/// </summary>
public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue> {
readonly IDictionary<TKey, TValue> wrapped;
public ReadOnlyDictionary(IDictionary<TKey, TValue> wrapped) {
this.wrapped = wrapped;
}
public void Add(TKey key, TValue value) {
throw new InvalidOperationException();
}
public bool ContainsKey(TKey key) {
return wrapped.ContainsKey(key);
}
public ICollection<TKey> Keys {
get { return wrapped.Keys; }
}
public bool Remove(TKey key) {
throw new InvalidOperationException();
}
public bool TryGetValue(TKey key, out TValue value) {
return wrapped.TryGetValue(key, out value);
}
public ICollection<TValue> Values {
get { return wrapped.Values; }
}
public TValue this[TKey key] {
get {
return wrapped[key];
}
set {
throw new InvalidOperationException();
}
}
public void Add(KeyValuePair<TKey, TValue> item) {
throw new InvalidOperationException();
}
public void Clear() {
throw new InvalidOperationException();
}
public bool Contains(KeyValuePair<TKey, TValue> item) {
return wrapped.Contains(item);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
wrapped.CopyTo(array, arrayIndex);
}
public int Count {
get { return wrapped.Count; }
}
public bool IsReadOnly {
get { return true; }
}
public bool Remove(KeyValuePair<TKey, TValue> item) {
throw new InvalidOperationException();
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
return wrapped.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return ((IEnumerable) wrapped).GetEnumerator();
}
public override bool Equals(object obj) {
return wrapped.Equals(obj);
}
public override int GetHashCode() {
return wrapped.GetHashCode();
}
public override string ToString() {
return wrapped.ToString();
}
}
}

namespace Google.ProtocolBuffers.Descriptors {
public class EnumDescriptor {
}
}
...@@ -2,8 +2,14 @@ ...@@ -2,8 +2,14 @@
namespace Google.ProtocolBuffers.Descriptors { namespace Google.ProtocolBuffers.Descriptors {
public class EnumValueDescriptor { public class EnumValueDescriptor {
private EnumDescriptor enumDescriptor;
public int Number { public int Number {
get { throw new NotImplementedException(); } get { throw new NotImplementedException(); }
} }
public EnumDescriptor EnumDescriptor {
get { return enumDescriptor; }
}
} }
} }
 
using System;
namespace Google.ProtocolBuffers.Descriptors { namespace Google.ProtocolBuffers.Descriptors {
public class FieldDescriptor { public class FieldDescriptor {
private EnumDescriptor enumType;
public bool IsRequired { public bool IsRequired {
get; get;
set; set;
...@@ -25,5 +29,32 @@ namespace Google.ProtocolBuffers.Descriptors { ...@@ -25,5 +29,32 @@ namespace Google.ProtocolBuffers.Descriptors {
public MessageDescriptor MessageType { get; set; } public MessageDescriptor MessageType { get; set; }
public MessageDescriptor ExtensionScope { get; set; } public MessageDescriptor ExtensionScope { get; set; }
/// <summary>
/// For enum fields, returns the field's type.
/// </summary>
public EnumDescriptor EnumType {
get {
if (MappedType != MappedType.Enum) {
throw new InvalidOperationException("EnumType is only valid for enum fields.");
}
return enumType;
}
}
/// <summary>
/// The default value for this field. For repeated fields
/// this will always be an empty list. For message fields it will
/// always be null. For singular values, it will depend on the descriptor.
/// </summary>
public object DefaultValue
{
get { throw new NotImplementedException(); }
}
public string Name
{
get { throw new NotImplementedException(); }
}
} }
} }
...@@ -9,6 +9,8 @@ namespace Google.ProtocolBuffers.Descriptors { ...@@ -9,6 +9,8 @@ namespace Google.ProtocolBuffers.Descriptors {
public enum MappedType { public enum MappedType {
Int32, Int32,
Int64, Int64,
UInt32,
UInt64,
Single, Single,
Double, Double,
Boolean, Boolean,
......
using System; namespace Google.ProtocolBuffers.FieldAccess {
using System.Collections.Generic;
using System.Text;
namespace Google.ProtocolBuffers.FieldAccess { /// <summary>
/// Allows fields to be reflectively accessed in a smart manner.
/// The property descriptors for each field are created once and then cached.
/// In addition, this interface holds knowledge of repeated fields, builders etc.
/// </summary>
internal interface IFieldAccessor<TMessage, TBuilder> internal interface IFieldAccessor<TMessage, TBuilder>
where TMessage : IMessage<TMessage> where TMessage : IMessage<TMessage>
where TBuilder : IBuilder<TMessage> { where TBuilder : IBuilder<TMessage> {
void AddRepeated(IBuilder<TMessage> builder, object value); /// <summary>
/// Indicates whether the specified message contains the field.
/// </summary>
bool Has(IMessage<TMessage> message); bool Has(IMessage<TMessage> message);
/// <summary>
/// Gets the count of the repeated field in the specified message.
/// </summary>
int GetRepeatedCount(IMessage<TMessage> message); int GetRepeatedCount(IMessage<TMessage> message);
void Clear(TBuilder builder);
TBuilder CreateBuilder(); /// <summary>
/// Clears the field in the specified builder.
/// </summary>
/// <param name="builder"></param>
void Clear(IBuilder<TMessage> builder);
/// <summary>
/// Creates a builder for the type of this field (which must be a message field).
/// </summary>
IBuilder CreateBuilder();
/// <summary> /// <summary>
/// Accessor for single fields /// Accessor for single fields
/// </summary> /// </summary>
object this[IMessage<TMessage> message] { get; } object GetValue(IMessage<TMessage> message);
/// <summary> /// <summary>
/// Mutator for single fields /// Mutator for single fields
/// </summary> /// </summary>
object this[IBuilder<TMessage> builder] { set; } void SetValue(IBuilder<TMessage> builder, object value);
/// <summary> /// <summary>
/// Accessor for repeated fields /// Accessor for repeated fields
/// </summary> /// </summary>
object this[IMessage<TMessage> message, int index] { get; } object GetRepeatedValue(IMessage<TMessage> message, int index);
/// <summary> /// <summary>
/// Mutator for repeated fields /// Mutator for repeated fields
/// </summary> /// </summary>
object this[IBuilder<TMessage> builder, int index] { set; } void SetRepeated(IBuilder<TMessage> builder, int index, object value);
/// <summary>
/// Adds the specified value to the field in the given builder.
/// </summary>
void AddRepeated(IBuilder<TMessage> builder, object value);
/// <summary>
/// Returns a read-only wrapper around the value of a repeated field.
/// </summary>
object GetRepeatedWrapper(IBuilder<TMessage> builder);
} }
} }
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.FieldAccess {
internal class SingularFieldAccessor<TMessage, TBuilder> : IFieldAccessor<TMessage, TBuilder>
where TMessage : IMessage<TMessage>
where TBuilder : IBuilder<TMessage> {
readonly HasFunction<TMessage> hasProxy;
readonly Action<TBuilder> clearProxy;
internal SingularFieldAccessor(FieldDescriptor descriptor, String pascalCaseName) {
/* Class<? extends GeneratedMessage> messageClass,
Class<? extends GeneratedMessage.Builder> builderClass) {
getMethod = getMethodOrDie(messageClass, "get" + camelCaseName);
type = getMethod.getReturnType();
setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type);
hasMethod =
getMethodOrDie(messageClass, "has" + camelCaseName);
clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); */
}
public bool Has(IMessage<TMessage> message) {
return false;// hasProxy(message);
}
public void Clear(TBuilder builder) {
// clearProxy(builder);
}
public TBuilder CreateBuilder() {
// return createBuilderProxy(builder);
return default(TBuilder);
}
public object this[IMessage<TMessage> message] {
get { return null;/* getProxy(message);*/ }
}
public object this[IBuilder<TMessage> builder] {
set { /*setProxy(builder, value);*/ }
}
#region Repeated operations (which just throw an exception)
public object this[IMessage<TMessage> message, int index] {
get { throw new InvalidOperationException("Repeated operation called on singular field"); }
}
public object this[IBuilder<TMessage> builder, int index] {
set { throw new InvalidOperationException("Repeated operation called on singular field"); }
}
public int GetRepeatedCount(IMessage<TMessage> message) {
throw new InvalidOperationException("Repeated operation called on singular field");
}
public void AddRepeated(IBuilder<TMessage> builder, object value) {
throw new InvalidOperationException("Repeated operation called on singular field");
}
#endregion
}
}
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Google.ProtocolBuffers.Descriptors;
using Google.ProtocolBuffers.Collections;
namespace Google.ProtocolBuffers { namespace Google.ProtocolBuffers {
public class FieldSet { /// <summary>
public static void MergeFrom(CodedInputStream input, /// A class which represents an arbitrary set of fields of some message type.
/// This is used to implement DynamicMessage, and also to represent extensions
/// in GeneratedMessage. This class is internal, since outside users should probably
/// be using DynamicMessage.
///
/// As in the Java implementation, this class goes against the rest of the framework
/// in terms of mutability. Instead of having a mutable Builder class and an immutable
/// FieldSet class, FieldSet just has a MakeImmutable() method. This is safe so long as
/// all callers are careful not to let a mutable FieldSet escape into the open. This would
/// be impossible to guarantee if this were a public class, of course.
/// </summary>
internal class FieldSet {
private static readonly FieldSet defaultInstance = new FieldSet(new Dictionary<FieldDescriptor, object>()).MakeImmutable();
private IDictionary<FieldDescriptor, object> fields;
private FieldSet(IDictionary<FieldDescriptor, object> fields) {
this.fields = fields;
}
/// <summary>
/// Makes this FieldSet immutable, and returns it for convenience. Any
/// mutable repeated fields are made immutable, as well as the map itself.
/// </summary>
internal FieldSet MakeImmutable() {
// First check if we have any repeated values
bool hasRepeats = false;
foreach (object value in fields.Values) {
IList<object> list = value as IList<object>;
if (list != null && !list.IsReadOnly) {
hasRepeats = true;
break;
}
}
if (hasRepeats) {
var tmp = new SortedList<FieldDescriptor, object>();
foreach (KeyValuePair<FieldDescriptor, object> entry in fields) {
IList<object> list = entry.Value as IList<object>;
tmp[entry.Key] = list == null ? entry.Value : Lists.AsReadOnly(list);
}
fields = tmp;
}
fields = Dictionaries.AsReadOnly(fields);
return this;
}
/// <summary>
/// Returns the default, immutable instance with no fields defined.
/// </summary>
internal static FieldSet DefaultInstance {
get { return defaultInstance; }
}
/// <summary>
/// Returns an immutable mapping of fields. Note that although the mapping itself
/// 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 {
get { return Dictionaries.AsReadOnly(fields); }
}
/// <summary>
/// See <see cref="IMessage.HasField"/>.
/// </summary>
public bool HasField(FieldDescriptor field) {
if (field.IsRepeated) {
throw new ArgumentException("HasField() can only be called on non-repeated fields.");
}
return fields.ContainsKey(field);
}
// TODO(jonskeet): Should this be in UnknownFieldSet.Builder really?
internal static void MergeFrom(CodedInputStream input,
UnknownFieldSet.Builder unknownFields, UnknownFieldSet.Builder unknownFields,
ExtensionRegistry extensionRegistry, ExtensionRegistry extensionRegistry,
IBuilder builder) { IBuilder builder) {
...@@ -22,12 +103,220 @@ namespace Google.ProtocolBuffers { ...@@ -22,12 +103,220 @@ namespace Google.ProtocolBuffers {
} }
} }
public static bool MergeFieldFrom(CodedInputStream input, // TODO(jonskeet): Should this be in UnknownFieldSet.Builder really?
internal static bool MergeFieldFrom(CodedInputStream input,
UnknownFieldSet.Builder unknownFields, UnknownFieldSet.Builder unknownFields,
ExtensionRegistry extensionRegistry, ExtensionRegistry extensionRegistry,
IBuilder builder, IBuilder builder,
uint tag) { uint tag) {
throw new NotImplementedException(); throw new NotImplementedException();
} }
/// <summary>
/// Clears all fields.
/// </summary>
internal void Clear() {
fields.Clear();
}
/// <summary>
/// <see cref="IMessage.Item(FieldDescriptor)"/>
/// </summary>
/// <remarks>
/// If the field is not set, the behaviour when fetching this property varies by field type:
/// <list>
/// <item>For singular message values, null is returned.</item>
/// <item>For singular non-message values, the default value of the field is returned.</item>
/// <item>For repeated values, an empty immutable list is returned.</item>
/// </list>
/// This method returns null if the field is a singular message type
/// and is not set; in this case it is up to the caller to fetch the
/// message's default instance. For repeated fields of message types,
/// an empty collection is returned. For repeated fields of non-message
/// types, null is returned.
/// <para />
/// When setting this property, any list values are copied, and each element is checked
/// to ensure it is of an appropriate type.
/// </remarks>
///
internal object this[FieldDescriptor field] {
get {
object result;
if (fields.TryGetValue(field, out result)) {
return result;
}
// This will just do the right thing
return field.DefaultValue;
}
set {
if (field.IsRepeated) {
List<object> list = value as List<object>;
if (list == null) {
throw new ArgumentException("Wrong object type used with protocol message reflection.");
}
// Wrap the contents in a new list so that the caller cannot change
// the list's contents after setting it.
List<object> newList = new List<object>(list);
foreach (object element in newList) {
VerifyType(field, element);
}
value = newList;
}
else {
VerifyType(field, value);
}
fields[field] = value;
}
}
/// <summary>
/// <see cref="IMessage.Item(FieldDescriptor,int)" />
/// </summary>
internal object this[FieldDescriptor field, int index] {
get {
if (!field.IsRepeated) {
throw new ArgumentException("Indexer specifying field and index can only be called on repeated fields.");
}
return ((List<object>)this[field])[index];
}
set {
if (!field.IsRepeated) {
throw new ArgumentException("Indexer specifying field and index can only be called on repeated fields.");
}
VerifyType(field, value);
object list;
if (!fields.TryGetValue(field, out list)) {
throw new ArgumentOutOfRangeException();
}
((List<object>) list)[index] = value;
}
}
/// <summary>
/// <see cref="IBuilder.AddRepeatedField" />
/// </summary>
/// <param name="field"></param>
/// <param name="value"></param>
internal void AddRepeatedField(FieldDescriptor field, object value) {
if (!field.IsRepeated) {
throw new ArgumentException("AddRepeatedField can only be called on repeated fields.");
}
VerifyType(field, value);
object list;
if (!fields.TryGetValue(field, out list)) {
list = new List<object>();
fields[field] = list;
}
((List<object>) list).Add(value);
}
/// <summary>
/// <see cref="IMessage.IsInitialized" />
/// </summary>
/// <remarks>
/// Since FieldSet itself does not have any way of knowing about
/// required fields that aren't actually present in the set, it is up
/// to the caller to check for genuinely required fields. This property
/// merely checks that any messages present are themselves initialized.
/// </remarks>
internal bool IsInitialized {
get {
foreach (KeyValuePair<FieldDescriptor, object> entry in fields) {
FieldDescriptor field = entry.Key;
if (field.MappedType == MappedType.Message) {
if (field.IsRepeated) {
foreach(IMessage message in (IEnumerable) entry.Value) {
if (!message.IsInitialized) {
return false;
}
}
} else {
if (!((IMessage) entry.Value).IsInitialized) {
return false;
}
}
}
}
return true;
}
}
/// <summary>
/// Verifies whether all the required fields in the specified message
/// 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) {
if (field.IsRequired && !HasField(field)) {
return false;
}
}
return IsInitialized;
}
/// <summary>
/// <see cref="IBuilder.ClearField" />
/// </summary>
public void ClearField(FieldDescriptor field) {
fields.Remove(field);
}
/// <summary>
/// <see cref="IMessage.GetRepeatedFieldCount" />
/// </summary>
public int GetRepeatedFieldCount(FieldDescriptor field) {
if (!field.IsRepeated) {
throw new ArgumentException("GetRepeatedFieldCount() can only be called on repeated fields.");
}
return ((List<object>) this[field]).Count;
}
/// <summary>
/// Verifies that the given object is of the correct type to be a valid
/// value for the given field.
/// </summary>
/// <remarks>
/// For repeated fields, this checks if the object is of the right
/// element type, not whether it's a list.
/// </remarks>
/// <exception cref="ArgumentException">The value is not of the right type.</exception>
private static void VerifyType(FieldDescriptor field, object value) {
bool isValid = false;
switch (field.MappedType) {
case MappedType.Int32: isValid = value is int; break;
case MappedType.Int64: isValid = value is long; break;
case MappedType.UInt32: isValid = value is uint; break;
case MappedType.UInt64: isValid = value is ulong; break;
case MappedType.Single: isValid = value is float; break;
case MappedType.Double: isValid = value is double; break;
case MappedType.Boolean: isValid = value is bool; break;
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;
break;
case MappedType.Message:
IMessage messageValue = value as IMessage;
isValid = messageValue != null && messageValue.DescriptorForType == field.MessageType;
break;
}
if (!isValid) {
// When chaining calls to SetField(), it can be hard to tell from
// 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 + "\".");
}
}
} }
} }
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Collections;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers {
/// <summary>
/// All generated protocol message builder classes extend this class. It implements
/// most of the IBuilder interface using reflection. Users can ignore this class
/// as an implementation detail.
/// </summary>
public abstract class GeneratedBuilder<TMessage, TBuilder> : AbstractBuilder, IBuilder<TMessage>
where TMessage : GeneratedMessage <TMessage, TBuilder>
where TBuilder : GeneratedBuilder<TMessage, TBuilder>, IBuilder<TMessage> {
/// <summary>
/// Returns the message being built at the moment.
/// </summary>
protected abstract GeneratedMessage<TMessage,TBuilder> MessageBeingBuilt { get; }
public override bool Initialized {
get { return MessageBeingBuilt.IsInitialized; }
}
public override IDictionary<FieldDescriptor, object> AllFields {
get { return MessageBeingBuilt.AllFields; }
}
public override object this[FieldDescriptor field] {
get {
// For repeated fields, the underlying list object is still modifiable at this point.
// Make sure not to expose the modifiable list to the caller.
return field.IsRepeated
? MessageBeingBuilt.InternalFieldAccessors[field].GetRepeatedWrapper(this)
: MessageBeingBuilt[field];
}
set {
MessageBeingBuilt.InternalFieldAccessors[field].SetValue(this, value);
}
}
public override MessageDescriptor DescriptorForType {
get { return MessageBeingBuilt.DescriptorForType; }
}
public override int GetRepeatedFieldCount(FieldDescriptor field) {
return MessageBeingBuilt.GetRepeatedFieldCount(field);
}
public override object this[FieldDescriptor field, int index] {
get { return MessageBeingBuilt[field, index]; }
set { MessageBeingBuilt.InternalFieldAccessors[field].SetRepeated(this, index, value); }
}
public override bool HasField(FieldDescriptor field) {
return MessageBeingBuilt.HasField(field);
}
protected override IMessage BuildImpl() {
return Build();
}
protected override IMessage BuildPartialImpl() {
return BuildPartial();
}
protected override IBuilder CloneImpl() {
return Clone();
}
protected override IMessage DefaultInstanceForTypeImpl {
get { return DefaultInstanceForType; }
}
protected override IBuilder NewBuilderForFieldImpl(FieldDescriptor field) {
return NewBuilderForField(field);
}
protected override IBuilder ClearFieldImpl(FieldDescriptor field) {
return ClearField(field);
}
protected override IBuilder AddRepeatedFieldImpl(FieldDescriptor field, object value) {
return AddRepeatedField(field, value);
}
public new abstract IBuilder<TMessage> Clear();
public IBuilder<TMessage> MergeFrom(IMessage<TMessage> other) {
throw new NotImplementedException();
}
public abstract IMessage<TMessage> Build();
public abstract IMessage<TMessage> BuildPartial();
public abstract IBuilder<TMessage> Clone();
IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(CodedInputStream input) {
throw new NotImplementedException();
}
IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry) {
throw new NotImplementedException();
}
public IMessage<TMessage> DefaultInstanceForType {
get { throw new NotImplementedException(); }
}
public IBuilder NewBuilderForField(FieldDescriptor field) {
throw new NotImplementedException();
}
public IBuilder<TMessage> ClearField(FieldDescriptor field) {
MessageBeingBuilt.InternalFieldAccessors[field].Clear(this);
return this;
}
public IBuilder<TMessage> AddRepeatedField(FieldDescriptor field, object value) {
return this;
}
public new IBuilder<TMessage> MergeUnknownFields(UnknownFieldSet unknownFields) {
throw new NotImplementedException();
}
IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(ByteString data) {
throw new NotImplementedException();
}
IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(ByteString data, ExtensionRegistry extensionRegistry) {
throw new NotImplementedException();
}
IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(byte[] data) {
throw new NotImplementedException();
}
IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(byte[] data, ExtensionRegistry extensionRegistry) {
throw new NotImplementedException();
}
IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(System.IO.Stream input) {
throw new NotImplementedException();
}
IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(System.IO.Stream input, ExtensionRegistry extensionRegistry) {
throw new NotImplementedException();
}
}
}
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Google.ProtocolBuffers.Collections;
using Google.ProtocolBuffers.Descriptors; using Google.ProtocolBuffers.Descriptors;
using Google.ProtocolBuffers.FieldAccess; using Google.ProtocolBuffers.FieldAccess;
...@@ -7,7 +8,7 @@ namespace Google.ProtocolBuffers { ...@@ -7,7 +8,7 @@ namespace Google.ProtocolBuffers {
/// <summary> /// <summary>
/// All generated protocol message classes extend this class. It implements /// All generated protocol message classes extend this class. It implements
/// most of the IMessage and IBuilder interfaces using reflection. Users /// most of the IMessage interface using reflection. Users
/// can ignore this class as an implementation detail. /// can ignore this class as an implementation detail.
/// </summary> /// </summary>
public abstract class GeneratedMessage<TMessage, TBuilder> : AbstractMessage, IMessage<TMessage> public abstract class GeneratedMessage<TMessage, TBuilder> : AbstractMessage, IMessage<TMessage>
...@@ -15,7 +16,7 @@ namespace Google.ProtocolBuffers { ...@@ -15,7 +16,7 @@ namespace Google.ProtocolBuffers {
private readonly UnknownFieldSet unknownFields = UnknownFieldSet.DefaultInstance; private readonly UnknownFieldSet unknownFields = UnknownFieldSet.DefaultInstance;
protected abstract FieldAccessorTable<TMessage, TBuilder> InternalFieldAccessors { get; } protected internal abstract FieldAccessorTable<TMessage, TBuilder> InternalFieldAccessors { get; }
public override MessageDescriptor DescriptorForType { public override MessageDescriptor DescriptorForType {
get { return InternalFieldAccessors.Descriptor; } get { return InternalFieldAccessors.Descriptor; }
...@@ -38,21 +39,22 @@ namespace Google.ProtocolBuffers { ...@@ -38,21 +39,22 @@ namespace Google.ProtocolBuffers {
} }
private IDictionary<FieldDescriptor, Object> GetMutableFieldMap() { private IDictionary<FieldDescriptor, Object> GetMutableFieldMap() {
var ret = new Dictionary<FieldDescriptor, object>();
// Use a SortedList so we'll end up serializing fields in order
var ret = new SortedList<FieldDescriptor, object>();
MessageDescriptor descriptor = DescriptorForType; MessageDescriptor descriptor = DescriptorForType;
foreach (FieldDescriptor field in descriptor.Fields) { foreach (FieldDescriptor field in descriptor.Fields) {
IFieldAccessor<TMessage, TBuilder> accessor = InternalFieldAccessors[field]; IFieldAccessor<TMessage, TBuilder> accessor = InternalFieldAccessors[field];
if ((field.IsRepeated && accessor.GetRepeatedCount(this) != 0) if ((field.IsRepeated && accessor.GetRepeatedCount(this) != 0)
|| accessor.Has(this)) { || accessor.Has(this)) {
ret[field] = accessor[this]; ret[field] = accessor.GetValue(this);
} }
} }
return ret; return ret;
} }
public override IDictionary<FieldDescriptor, object> AllFields { public override IDictionary<FieldDescriptor, object> AllFields {
// FIXME: Make it immutable get { return Dictionaries.AsReadOnly(GetMutableFieldMap()); }
get { return GetMutableFieldMap(); }
} }
public override bool HasField(FieldDescriptor field) { public override bool HasField(FieldDescriptor field) {
...@@ -64,11 +66,11 @@ namespace Google.ProtocolBuffers { ...@@ -64,11 +66,11 @@ namespace Google.ProtocolBuffers {
} }
public override object this[FieldDescriptor field, int index] { public override object this[FieldDescriptor field, int index] {
get { return InternalFieldAccessors[field][this, index]; } get { return InternalFieldAccessors[field].GetRepeatedValue(this, index); }
} }
public override object this[FieldDescriptor field] { public override object this[FieldDescriptor field] {
get { return InternalFieldAccessors[field][this]; } get { return InternalFieldAccessors[field].GetValue(this); }
} }
public override UnknownFieldSet UnknownFields { public override UnknownFieldSet UnknownFields {
......
...@@ -88,7 +88,7 @@ namespace Google.ProtocolBuffers { ...@@ -88,7 +88,7 @@ namespace Google.ProtocolBuffers {
IBuilder MergeFrom(CodedInputStream input); IBuilder MergeFrom(CodedInputStream input);
IBuilder MergeFrom(CodedInputStream codedInputStream, ExtensionRegistry extensionRegistry); IBuilder MergeFrom(CodedInputStream codedInputStream, ExtensionRegistry extensionRegistry);
IMessage DefaultInstanceForType { get; } IMessage DefaultInstanceForType { get; }
IBuilder NewBuilderForField<TField>(FieldDescriptor field); IBuilder NewBuilderForField(FieldDescriptor field);
IBuilder ClearField(FieldDescriptor field); IBuilder ClearField(FieldDescriptor field);
IBuilder AddRepeatedField(FieldDescriptor field, object value); IBuilder AddRepeatedField(FieldDescriptor field, object value);
IBuilder MergeUnknownFields(UnknownFieldSet unknownFields); IBuilder MergeUnknownFields(UnknownFieldSet unknownFields);
...@@ -191,7 +191,7 @@ namespace Google.ProtocolBuffers { ...@@ -191,7 +191,7 @@ namespace Google.ProtocolBuffers {
/// Messages built with this can then be passed to the various mutation properties /// Messages built with this can then be passed to the various mutation properties
/// and methods. /// and methods.
/// </summary> /// </summary>
new IBuilder<TField> NewBuilderForField<TField>(FieldDescriptor field) where TField : IMessage<TField>; //new IBuilder<TField> NewBuilderForField<TField>(FieldDescriptor field) where TField : IMessage<TField>;
/// <summary> /// <summary>
/// Clears the field. This is exactly equivalent to calling the generated /// Clears the field. This is exactly equivalent to calling the generated
......
...@@ -42,7 +42,10 @@ ...@@ -42,7 +42,10 @@
<Compile Include="ByteString.cs" /> <Compile Include="ByteString.cs" />
<Compile Include="CodedInputStream.cs" /> <Compile Include="CodedInputStream.cs" />
<Compile Include="CodedOutputStream.cs" /> <Compile Include="CodedOutputStream.cs" />
<Compile Include="Collections\Dictionaries.cs" />
<Compile Include="Collections\Lists.cs" /> <Compile Include="Collections\Lists.cs" />
<Compile Include="Collections\ReadOnlyDictionary.cs" />
<Compile Include="Descriptors\EnumDescriptor.cs" />
<Compile Include="Descriptors\EnumValueDescriptor.cs" /> <Compile Include="Descriptors\EnumValueDescriptor.cs" />
<Compile Include="Descriptors\FieldDescriptor.cs" /> <Compile Include="Descriptors\FieldDescriptor.cs" />
<Compile Include="Descriptors\FieldType.cs" /> <Compile Include="Descriptors\FieldType.cs" />
...@@ -53,8 +56,8 @@ ...@@ -53,8 +56,8 @@
<Compile Include="FieldAccess\Delegates.cs" /> <Compile Include="FieldAccess\Delegates.cs" />
<Compile Include="FieldAccess\IFieldAccessor.cs" /> <Compile Include="FieldAccess\IFieldAccessor.cs" />
<Compile Include="FieldAccess\FieldAccessorTable.cs" /> <Compile Include="FieldAccess\FieldAccessorTable.cs" />
<Compile Include="FieldAccess\SingularFieldAccessor.cs" />
<Compile Include="FieldSet.cs" /> <Compile Include="FieldSet.cs" />
<Compile Include="GeneratedBuilder.cs" />
<Compile Include="GeneratedExtension.cs" /> <Compile Include="GeneratedExtension.cs" />
<Compile Include="GeneratedMessage.cs" /> <Compile Include="GeneratedMessage.cs" />
<Compile Include="IBuilder.cs" /> <Compile Include="IBuilder.cs" />
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Google.ProtocolBuffers.Collections;
namespace Google.ProtocolBuffers { namespace Google.ProtocolBuffers {
public class UnknownFieldSet { public class UnknownFieldSet {
...@@ -48,10 +49,10 @@ namespace Google.ProtocolBuffers { ...@@ -48,10 +49,10 @@ namespace Google.ProtocolBuffers {
} }
/// <summary> /// <summary>
/// Creates and returns a copy of the mapping from field numbers to values. /// Returns a read-only view of the mapping from field numbers to values.
/// </summary> /// </summary>
public IDictionary<int, UnknownField> FieldDictionary { public IDictionary<int, UnknownField> FieldDictionary {
get { return new Dictionary<int, UnknownField>(fields); } get { return Dictionaries.AsReadOnly(fields); }
} }
/// <summary> /// <summary>
...@@ -195,7 +196,11 @@ namespace Google.ProtocolBuffers { ...@@ -195,7 +196,11 @@ namespace Google.ProtocolBuffers {
public class Builder public class Builder
{ {
private Dictionary<int, UnknownField> fields = new Dictionary<int, UnknownField>(); /// <summary>
/// Mapping from number to field. Note that by using a SortedList we ensure
/// that the fields will be serialized in ascending order.
/// </summary>
private IDictionary<int, UnknownField> fields = new SortedList<int, UnknownField>();
// Optimization: We keep around a builder for the last field that was // Optimization: We keep around a builder for the last field that was
// modified so that we can efficiently add to it multiple times in a // modified so that we can efficiently add to it multiple times in a
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment