blob: c460523c24ebae106406cc47bdde105a417a5df9 [file] [log] [blame]
using System;
using System.Collections.Generic;
using System.Globalization;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.Serialization
{
/// <summary>
/// Allows reading messages from a name/value dictionary
/// </summary>
public class DictionaryReader : AbstractReader
{
private readonly IEnumerator<KeyValuePair<string, object>> _input;
private bool _ready;
/// <summary>
/// Creates a dictionary reader from an enumeration of KeyValuePair data, like an IDictionary
/// </summary>
public DictionaryReader(IEnumerable<KeyValuePair<string, object>> input)
{
_input = input.GetEnumerator();
_ready = _input.MoveNext();
}
/// <summary>
/// No-op
/// </summary>
public override void ReadMessageStart()
{ }
/// <summary>
/// No-op
/// </summary>
public override void ReadMessageEnd()
{ }
/// <summary>
/// Merges the contents of stream into the provided message builder
/// </summary>
public override TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry)
{
builder.WeakMergeFrom(this, registry);
return builder;
}
/// <summary>
/// Peeks at the next field in the input stream and returns what information is available.
/// </summary>
/// <remarks>
/// This may be called multiple times without actually reading the field. Only after the field
/// is either read, or skipped, should PeekNext return a different value.
/// </remarks>
protected override bool PeekNext(out string field)
{
field = _ready ? _input.Current.Key : null;
return _ready;
}
/// <summary>
/// Causes the reader to skip past this field
/// </summary>
protected override void Skip()
{
_ready = _input.MoveNext();
}
private bool GetValue<T>(ref T value)
{
if (!_ready)
{
return false;
}
object obj = _input.Current.Value;
if (obj is T)
{
value = (T) obj;
}
else
{
try
{
if (obj is IConvertible)
{
value = (T)Convert.ChangeType(obj, typeof(T), FrameworkPortability.InvariantCulture);
}
else
{
value = (T) obj;
}
}
catch
{
_ready = _input.MoveNext();
return false;
}
}
_ready = _input.MoveNext();
return true;
}
/// <summary>
/// Returns true if it was able to read a Boolean from the input
/// </summary>
protected override bool Read(ref bool value)
{
return GetValue(ref value);
}
/// <summary>
/// Returns true if it was able to read a Int32 from the input
/// </summary>
protected override bool Read(ref int value)
{
return GetValue(ref value);
}
/// <summary>
/// Returns true if it was able to read a UInt32 from the input
/// </summary>
[CLSCompliant(false)]
protected override bool Read(ref uint value)
{
return GetValue(ref value);
}
/// <summary>
/// Returns true if it was able to read a Int64 from the input
/// </summary>
protected override bool Read(ref long value)
{
return GetValue(ref value);
}
/// <summary>
/// Returns true if it was able to read a UInt64 from the input
/// </summary>
[CLSCompliant(false)]
protected override bool Read(ref ulong value)
{
return GetValue(ref value);
}
/// <summary>
/// Returns true if it was able to read a Single from the input
/// </summary>
protected override bool Read(ref float value)
{
return GetValue(ref value);
}
/// <summary>
/// Returns true if it was able to read a Double from the input
/// </summary>
protected override bool Read(ref double value)
{
return GetValue(ref value);
}
/// <summary>
/// Returns true if it was able to read a String from the input
/// </summary>
protected override bool Read(ref string value)
{
return GetValue(ref value);
}
/// <summary>
/// Returns true if it was able to read a ByteString from the input
/// </summary>
protected override bool Read(ref ByteString value)
{
byte[] rawbytes = null;
if (GetValue(ref rawbytes))
{
value = ByteString.CopyFrom(rawbytes);
return true;
}
return false;
}
/// <summary>
/// returns true if it was able to read a single value into the value reference. The value
/// stored may be of type System.String, System.Int32, or an IEnumLite from the IEnumLiteMap.
/// </summary>
protected override bool ReadEnum(ref object value)
{
return GetValue(ref value);
}
/// <summary>
/// Merges the input stream into the provided IBuilderLite
/// </summary>
protected override bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry)
{
IDictionary<string, object> values = null;
if (GetValue(ref values))
{
new DictionaryReader(values).Merge(builder, registry);
return true;
}
return false;
}
public override bool ReadArray<T>(FieldType type, string field, ICollection<T> items)
{
object[] array = null;
if (GetValue(ref array))
{
if (typeof(T) == typeof(ByteString))
{
ICollection<ByteString> output = (ICollection<ByteString>) items;
foreach (byte[] item in array)
{
output.Add(ByteString.CopyFrom(item));
}
}
else
{
foreach (T item in array)
{
items.Add(item);
}
}
return true;
}
return false;
}
public override bool ReadEnumArray(string field, ICollection<object> items)
{
object[] array = null;
if (GetValue(ref array))
{
foreach (object item in array)
{
items.Add(item);
}
return true;
}
return false;
}
public override bool ReadMessageArray<T>(string field, ICollection<T> items, IMessageLite messageType,
ExtensionRegistry registry)
{
object[] array = null;
if (GetValue(ref array))
{
foreach (IDictionary<string, object> item in array)
{
IBuilderLite builder = messageType.WeakCreateBuilderForType();
new DictionaryReader(item).Merge(builder);
items.Add((T) builder.WeakBuild());
}
return true;
}
return false;
}
public override bool ReadGroupArray<T>(string field, ICollection<T> items, IMessageLite messageType,
ExtensionRegistry registry)
{
return ReadMessageArray(field, items, messageType, registry);
}
}
}