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 ...@@ -555,14 +555,10 @@ namespace Google.Protobuf
/// <summary> /// <summary>
/// Creates a new instance of the message type for the given field. /// 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> /// </summary>
private static IMessage NewMessageForField(FieldDescriptor field) private static IMessage NewMessageForField(FieldDescriptor field)
{ {
// TODO: Create an instance in a better way ? return field.MessageType.Parser.CreateTemplate();
// (We could potentially add a Parser property to MessageDescriptor... see issue 806.)
return (IMessage) Activator.CreateInstance(field.MessageType.GeneratedType);
} }
private static T ParseNumericString<T>(string text, Func<string, NumberStyles, IFormatProvider, T> parser, bool floatingPoint) private static T ParseNumericString<T>(string text, Func<string, NumberStyles, IFormatProvider, T> parser, bool floatingPoint)
......
...@@ -35,6 +35,109 @@ using System.IO; ...@@ -35,6 +35,109 @@ using System.IO;
namespace Google.Protobuf 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> /// <summary>
/// A parser for a specific message type. /// A parser for a specific message type.
/// </summary> /// </summary>
...@@ -51,8 +154,10 @@ namespace Google.Protobuf ...@@ -51,8 +154,10 @@ namespace Google.Protobuf
/// </p> /// </p>
/// </remarks> /// </remarks>
/// <typeparam name="T">The type of message to be parsed.</typeparam> /// <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; private readonly Func<T> factory;
/// <summary> /// <summary>
...@@ -63,7 +168,7 @@ namespace Google.Protobuf ...@@ -63,7 +168,7 @@ namespace Google.Protobuf
/// to require a parameterless constructor: delegates are significantly faster to execute. /// to require a parameterless constructor: delegates are significantly faster to execute.
/// </remarks> /// </remarks>
/// <param name="factory">Function to invoke when a new, empty message is required.</param> /// <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; this.factory = factory;
} }
...@@ -72,7 +177,7 @@ namespace Google.Protobuf ...@@ -72,7 +177,7 @@ namespace Google.Protobuf
/// Creates a template instance ready for population. /// Creates a template instance ready for population.
/// </summary> /// </summary>
/// <returns>An empty message.</returns> /// <returns>An empty message.</returns>
internal T CreateTemplate() internal new T CreateTemplate()
{ {
return factory(); return factory();
} }
...@@ -82,7 +187,7 @@ namespace Google.Protobuf ...@@ -82,7 +187,7 @@ namespace Google.Protobuf
/// </summary> /// </summary>
/// <param name="data">The byte array containing the message. Must not be null.</param> /// <param name="data">The byte array containing the message. Must not be null.</param>
/// <returns>The newly parsed message.</returns> /// <returns>The newly parsed message.</returns>
public T ParseFrom(byte[] data) public new T ParseFrom(byte[] data)
{ {
Preconditions.CheckNotNull(data, "data"); Preconditions.CheckNotNull(data, "data");
T message = factory(); T message = factory();
...@@ -95,7 +200,7 @@ namespace Google.Protobuf ...@@ -95,7 +200,7 @@ namespace Google.Protobuf
/// </summary> /// </summary>
/// <param name="data">The data to parse.</param> /// <param name="data">The data to parse.</param>
/// <returns>The parsed message.</returns> /// <returns>The parsed message.</returns>
public T ParseFrom(ByteString data) public new T ParseFrom(ByteString data)
{ {
Preconditions.CheckNotNull(data, "data"); Preconditions.CheckNotNull(data, "data");
T message = factory(); T message = factory();
...@@ -108,7 +213,7 @@ namespace Google.Protobuf ...@@ -108,7 +213,7 @@ namespace Google.Protobuf
/// </summary> /// </summary>
/// <param name="input">The stream to parse.</param> /// <param name="input">The stream to parse.</param>
/// <returns>The parsed message.</returns> /// <returns>The parsed message.</returns>
public T ParseFrom(Stream input) public new T ParseFrom(Stream input)
{ {
T message = factory(); T message = factory();
message.MergeFrom(input); message.MergeFrom(input);
...@@ -124,7 +229,7 @@ namespace Google.Protobuf ...@@ -124,7 +229,7 @@ namespace Google.Protobuf
/// </remarks> /// </remarks>
/// <param name="input">The stream to parse.</param> /// <param name="input">The stream to parse.</param>
/// <returns>The parsed message.</returns> /// <returns>The parsed message.</returns>
public T ParseDelimitedFrom(Stream input) public new T ParseDelimitedFrom(Stream input)
{ {
T message = factory(); T message = factory();
message.MergeDelimitedFrom(input); message.MergeDelimitedFrom(input);
...@@ -136,7 +241,7 @@ namespace Google.Protobuf ...@@ -136,7 +241,7 @@ namespace Google.Protobuf
/// </summary> /// </summary>
/// <param name="input">The stream to parse.</param> /// <param name="input">The stream to parse.</param>
/// <returns>The parsed message.</returns> /// <returns>The parsed message.</returns>
public T ParseFrom(CodedInputStream input) public new T ParseFrom(CodedInputStream input)
{ {
T message = factory(); T message = factory();
message.MergeFrom(input); message.MergeFrom(input);
...@@ -150,7 +255,7 @@ namespace Google.Protobuf ...@@ -150,7 +255,7 @@ namespace Google.Protobuf
/// <returns>The parsed message.</returns> /// <returns>The parsed message.</returns>
/// <exception cref="InvalidJsonException">The JSON does not comply with RFC 7159</exception> /// <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> /// <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(); T message = factory();
JsonParser.Default.Merge(message, json); JsonParser.Default.Merge(message, json);
......
...@@ -17,6 +17,11 @@ namespace Google.Protobuf.Reflection ...@@ -17,6 +17,11 @@ namespace Google.Protobuf.Reflection
/// </summary> /// </summary>
public Type ClrType { get; private set; } public Type ClrType { get; private set; }
/// <summary>
/// Irrelevant for file descriptors; the parser for message descriptors.
/// </summary>
public MessageParser Parser { get; private set; }
/// <summary> /// <summary>
/// Irrelevant for file descriptors; the CLR property names (in message descriptor field order) /// Irrelevant for file descriptors; the CLR property names (in message descriptor field order)
/// for fields in the message for message descriptors. /// for fields in the message for message descriptors.
...@@ -46,11 +51,12 @@ namespace Google.Protobuf.Reflection ...@@ -46,11 +51,12 @@ namespace Google.Protobuf.Reflection
/// Each array parameter may be null, to indicate a lack of values. /// 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. /// The parameter order is designed to make it feasible to format the generated code readably.
/// </summary> /// </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; NestedTypes = nestedTypes ?? EmptyCodeInfo;
NestedEnums = nestedEnums ?? ReflectionUtil.EmptyTypes; NestedEnums = nestedEnums ?? ReflectionUtil.EmptyTypes;
ClrType = clrType; ClrType = clrType;
Parser = parser;
PropertyNames = propertyNames ?? EmptyNames; PropertyNames = propertyNames ?? EmptyNames;
OneofNames = oneofNames ?? EmptyNames; OneofNames = oneofNames ?? EmptyNames;
} }
...@@ -59,7 +65,7 @@ namespace Google.Protobuf.Reflection ...@@ -59,7 +65,7 @@ namespace Google.Protobuf.Reflection
/// Creates a GeneratedCodeInfo for a file descriptor, with only types and enums. /// Creates a GeneratedCodeInfo for a file descriptor, with only types and enums.
/// </summary> /// </summary>
public GeneratedCodeInfo(Type[] nestedEnums, GeneratedCodeInfo[] nestedTypes) 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 ...@@ -67,11 +67,13 @@ namespace Google.Protobuf.Reflection
private readonly IList<OneofDescriptor> oneofs; private readonly IList<OneofDescriptor> oneofs;
// CLR representation of the type described by this descriptor, if any. // CLR representation of the type described by this descriptor, if any.
private readonly Type generatedType; private readonly Type generatedType;
private readonly MessageParser parser;
internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex, GeneratedCodeInfo generatedCodeInfo) internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex, GeneratedCodeInfo generatedCodeInfo)
: base(file, file.ComputeFullName(parent, proto.Name), typeIndex) : base(file, file.ComputeFullName(parent, proto.Name), typeIndex)
{ {
this.proto = proto; this.proto = proto;
parser = generatedCodeInfo == null ? null : generatedCodeInfo.Parser;
generatedType = generatedCodeInfo == null ? null : generatedCodeInfo.ClrType; generatedType = generatedCodeInfo == null ? null : generatedCodeInfo.ClrType;
containingType = parent; containingType = parent;
...@@ -122,6 +124,15 @@ namespace Google.Protobuf.Reflection ...@@ -122,6 +124,15 @@ namespace Google.Protobuf.Reflection
/// </summary> /// </summary>
public Type GeneratedType { get { return generatedType; } } 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> /// <summary>
/// Returns whether this message is one of the "well known types" which may have runtime/protoc support. /// Returns whether this message is one of the "well known types" which may have runtime/protoc support.
/// </summary> /// </summary>
......
...@@ -232,7 +232,7 @@ void UmbrellaClassGenerator::WriteGeneratedCodeInfo(const Descriptor* descriptor ...@@ -232,7 +232,7 @@ void UmbrellaClassGenerator::WriteGeneratedCodeInfo(const Descriptor* descriptor
return; return;
} }
// Generated message type // 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 // Fields
if (descriptor->field_count() > 0) { 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