Commit 2a15051a authored by Jon Skeet's avatar Jon Skeet

Introduce a Parser property into MessageDescriptor, and populate it from generated types.

Generated code coming in next commit - in a subsequent PR I want to do a bit of renaming and redocumenting around this, in anticipation of DynamicMessage.
parent c581acb5
......@@ -555,14 +555,10 @@ namespace Google.Protobuf
/// <summary>
/// Creates a new instance of the message type for the given field.
/// This method is mostly extracted so we can replace it in one go when we work out
/// what we want to do instead of Activator.CreateInstance.
/// </summary>
private static IMessage NewMessageForField(FieldDescriptor field)
{
// TODO: Create an instance in a better way ?
// (We could potentially add a Parser property to MessageDescriptor... see issue 806.)
return (IMessage) Activator.CreateInstance(field.MessageType.GeneratedType);
return field.MessageType.Parser.CreateTemplate();
}
private static T ParseNumericString<T>(string text, Func<string, NumberStyles, IFormatProvider, T> parser, bool floatingPoint)
......
......@@ -35,6 +35,109 @@ using System.IO;
namespace Google.Protobuf
{
/// <summary>
/// A general message parser, typically used by reflection-based code as all the methods
/// return simple <see cref="IMessage"/>.
/// </summary>
public class MessageParser
{
private Func<IMessage> factory;
internal MessageParser(Func<IMessage> factory)
{
this.factory = factory;
}
/// <summary>
/// Creates a template instance ready for population.
/// </summary>
/// <returns>An empty message.</returns>
internal IMessage CreateTemplate()
{
return factory();
}
/// <summary>
/// Parses a message from a byte array.
/// </summary>
/// <param name="data">The byte array containing the message. Must not be null.</param>
/// <returns>The newly parsed message.</returns>
public IMessage ParseFrom(byte[] data)
{
Preconditions.CheckNotNull(data, "data");
IMessage message = factory();
message.MergeFrom(data);
return message;
}
/// <summary>
/// Parses a message from the given byte string.
/// </summary>
/// <param name="data">The data to parse.</param>
/// <returns>The parsed message.</returns>
public IMessage ParseFrom(ByteString data)
{
Preconditions.CheckNotNull(data, "data");
IMessage message = factory();
message.MergeFrom(data);
return message;
}
/// <summary>
/// Parses a message from the given stream.
/// </summary>
/// <param name="input">The stream to parse.</param>
/// <returns>The parsed message.</returns>
public IMessage ParseFrom(Stream input)
{
IMessage message = factory();
message.MergeFrom(input);
return message;
}
/// <summary>
/// Parses a length-delimited message from the given stream.
/// </summary>
/// <remarks>
/// The stream is expected to contain a length and then the data. Only the amount of data
/// specified by the length will be consumed.
/// </remarks>
/// <param name="input">The stream to parse.</param>
/// <returns>The parsed message.</returns>
public IMessage ParseDelimitedFrom(Stream input)
{
IMessage message = factory();
message.MergeDelimitedFrom(input);
return message;
}
/// <summary>
/// Parses a message from the given coded input stream.
/// </summary>
/// <param name="input">The stream to parse.</param>
/// <returns>The parsed message.</returns>
public IMessage ParseFrom(CodedInputStream input)
{
IMessage message = factory();
message.MergeFrom(input);
return message;
}
/// <summary>
/// Parses a message from the given JSON.
/// </summary>
/// <param name="json">The JSON to parse.</param>
/// <returns>The parsed message.</returns>
/// <exception cref="InvalidJsonException">The JSON does not comply with RFC 7159</exception>
/// <exception cref="InvalidProtocolBufferException">The JSON does not represent a Protocol Buffers message correctly</exception>
public IMessage ParseJson(string json)
{
IMessage message = factory();
JsonParser.Default.Merge(message, json);
return message;
}
}
/// <summary>
/// A parser for a specific message type.
/// </summary>
......@@ -51,8 +154,10 @@ namespace Google.Protobuf
/// </p>
/// </remarks>
/// <typeparam name="T">The type of message to be parsed.</typeparam>
public sealed class MessageParser<T> where T : IMessage<T>
public sealed class MessageParser<T> : MessageParser where T : IMessage<T>
{
// Implementation note: all the methods here *could* just delegate up to the base class and cast the result.
// The
private readonly Func<T> factory;
/// <summary>
......@@ -63,7 +168,7 @@ namespace Google.Protobuf
/// to require a parameterless constructor: delegates are significantly faster to execute.
/// </remarks>
/// <param name="factory">Function to invoke when a new, empty message is required.</param>
public MessageParser(Func<T> factory)
public MessageParser(Func<T> factory) : base(() => factory())
{
this.factory = factory;
}
......@@ -72,7 +177,7 @@ namespace Google.Protobuf
/// Creates a template instance ready for population.
/// </summary>
/// <returns>An empty message.</returns>
internal T CreateTemplate()
internal new T CreateTemplate()
{
return factory();
}
......@@ -82,7 +187,7 @@ namespace Google.Protobuf
/// </summary>
/// <param name="data">The byte array containing the message. Must not be null.</param>
/// <returns>The newly parsed message.</returns>
public T ParseFrom(byte[] data)
public new T ParseFrom(byte[] data)
{
Preconditions.CheckNotNull(data, "data");
T message = factory();
......@@ -95,7 +200,7 @@ namespace Google.Protobuf
/// </summary>
/// <param name="data">The data to parse.</param>
/// <returns>The parsed message.</returns>
public T ParseFrom(ByteString data)
public new T ParseFrom(ByteString data)
{
Preconditions.CheckNotNull(data, "data");
T message = factory();
......@@ -108,7 +213,7 @@ namespace Google.Protobuf
/// </summary>
/// <param name="input">The stream to parse.</param>
/// <returns>The parsed message.</returns>
public T ParseFrom(Stream input)
public new T ParseFrom(Stream input)
{
T message = factory();
message.MergeFrom(input);
......@@ -124,7 +229,7 @@ namespace Google.Protobuf
/// </remarks>
/// <param name="input">The stream to parse.</param>
/// <returns>The parsed message.</returns>
public T ParseDelimitedFrom(Stream input)
public new T ParseDelimitedFrom(Stream input)
{
T message = factory();
message.MergeDelimitedFrom(input);
......@@ -136,7 +241,7 @@ namespace Google.Protobuf
/// </summary>
/// <param name="input">The stream to parse.</param>
/// <returns>The parsed message.</returns>
public T ParseFrom(CodedInputStream input)
public new T ParseFrom(CodedInputStream input)
{
T message = factory();
message.MergeFrom(input);
......@@ -150,7 +255,7 @@ namespace Google.Protobuf
/// <returns>The parsed message.</returns>
/// <exception cref="InvalidJsonException">The JSON does not comply with RFC 7159</exception>
/// <exception cref="InvalidProtocolBufferException">The JSON does not represent a Protocol Buffers message correctly</exception>
public T ParseJson(string json)
public new T ParseJson(string json)
{
T message = factory();
JsonParser.Default.Merge(message, json);
......
......@@ -17,6 +17,11 @@ namespace Google.Protobuf.Reflection
/// </summary>
public Type ClrType { get; private set; }
/// <summary>
/// Irrelevant for file descriptors; the parser for message descriptors.
/// </summary>
public MessageParser Parser { get; private set; }
/// <summary>
/// Irrelevant for file descriptors; the CLR property names (in message descriptor field order)
/// for fields in the message for message descriptors.
......@@ -46,11 +51,12 @@ namespace Google.Protobuf.Reflection
/// Each array parameter may be null, to indicate a lack of values.
/// The parameter order is designed to make it feasible to format the generated code readably.
/// </summary>
public GeneratedCodeInfo(Type clrType, string[] propertyNames, string[] oneofNames, Type[] nestedEnums, GeneratedCodeInfo[] nestedTypes)
public GeneratedCodeInfo(Type clrType, MessageParser parser, string[] propertyNames, string[] oneofNames, Type[] nestedEnums, GeneratedCodeInfo[] nestedTypes)
{
NestedTypes = nestedTypes ?? EmptyCodeInfo;
NestedEnums = nestedEnums ?? ReflectionUtil.EmptyTypes;
ClrType = clrType;
Parser = parser;
PropertyNames = propertyNames ?? EmptyNames;
OneofNames = oneofNames ?? EmptyNames;
}
......@@ -59,7 +65,7 @@ namespace Google.Protobuf.Reflection
/// Creates a GeneratedCodeInfo for a file descriptor, with only types and enums.
/// </summary>
public GeneratedCodeInfo(Type[] nestedEnums, GeneratedCodeInfo[] nestedTypes)
: this(null, null, null, nestedEnums, nestedTypes)
: this(null, null, null, null, nestedEnums, nestedTypes)
{
}
}
......
......@@ -67,11 +67,13 @@ namespace Google.Protobuf.Reflection
private readonly IList<OneofDescriptor> oneofs;
// CLR representation of the type described by this descriptor, if any.
private readonly Type generatedType;
private readonly MessageParser parser;
internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex, GeneratedCodeInfo generatedCodeInfo)
: base(file, file.ComputeFullName(parent, proto.Name), typeIndex)
{
this.proto = proto;
parser = generatedCodeInfo == null ? null : generatedCodeInfo.Parser;
generatedType = generatedCodeInfo == null ? null : generatedCodeInfo.ClrType;
containingType = parent;
......@@ -122,6 +124,15 @@ namespace Google.Protobuf.Reflection
/// </summary>
public Type GeneratedType { get { return generatedType; } }
/// <summary>
/// A parser for this message type.
/// </summary>
/// <remarks>
/// As <see cref="MessageDescriptor"/> is not generic, this cannot be statically
/// typed to the relevant type, but if <see cref="GeneratedType"/> returns a non-null value, the parser returned
/// </remarks>
public MessageParser Parser { get { return parser; } }
/// <summary>
/// Returns whether this message is one of the "well known types" which may have runtime/protoc support.
/// </summary>
......
......@@ -232,7 +232,7 @@ void UmbrellaClassGenerator::WriteGeneratedCodeInfo(const Descriptor* descriptor
return;
}
// Generated message type
printer->Print("new pbr::GeneratedCodeInfo(typeof($type_name$), ", "type_name", GetClassName(descriptor));
printer->Print("new pbr::GeneratedCodeInfo(typeof($type_name$), $type_name$.Parser, ", "type_name", GetClassName(descriptor));
// Fields
if (descriptor->field_count() > 0) {
......
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