From 2a15051a1e101ea58d7d2e262aa79c7c21f23266 Mon Sep 17 00:00:00 2001
From: Jon Skeet
Date: Fri, 13 Nov 2015 20:04:03 +0000
Subject: 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.
---
csharp/src/Google.Protobuf/JsonParser.cs | 6 +-
csharp/src/Google.Protobuf/MessageParser.cs | 123 +++++++++++++++++++--
.../Reflection/GeneratedCodeInfo.cs | 10 +-
.../Reflection/MessageDescriptor.cs | 11 ++
.../compiler/csharp/csharp_umbrella_class.cc | 2 +-
5 files changed, 135 insertions(+), 17 deletions(-)
diff --git a/csharp/src/Google.Protobuf/JsonParser.cs b/csharp/src/Google.Protobuf/JsonParser.cs
index 9e1c83cc..b4a35d7c 100644
--- a/csharp/src/Google.Protobuf/JsonParser.cs
+++ b/csharp/src/Google.Protobuf/JsonParser.cs
@@ -555,14 +555,10 @@ namespace Google.Protobuf
///
/// 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.
///
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(string text, Func parser, bool floatingPoint)
diff --git a/csharp/src/Google.Protobuf/MessageParser.cs b/csharp/src/Google.Protobuf/MessageParser.cs
index 8f2717c5..f911c9dd 100644
--- a/csharp/src/Google.Protobuf/MessageParser.cs
+++ b/csharp/src/Google.Protobuf/MessageParser.cs
@@ -35,6 +35,109 @@ using System.IO;
namespace Google.Protobuf
{
+ ///
+ /// A general message parser, typically used by reflection-based code as all the methods
+ /// return simple .
+ ///
+ public class MessageParser
+ {
+ private Func factory;
+
+ internal MessageParser(Func factory)
+ {
+ this.factory = factory;
+ }
+
+ ///
+ /// Creates a template instance ready for population.
+ ///
+ /// An empty message.
+ internal IMessage CreateTemplate()
+ {
+ return factory();
+ }
+
+ ///
+ /// Parses a message from a byte array.
+ ///
+ /// The byte array containing the message. Must not be null.
+ /// The newly parsed message.
+ public IMessage ParseFrom(byte[] data)
+ {
+ Preconditions.CheckNotNull(data, "data");
+ IMessage message = factory();
+ message.MergeFrom(data);
+ return message;
+ }
+
+ ///
+ /// Parses a message from the given byte string.
+ ///
+ /// The data to parse.
+ /// The parsed message.
+ public IMessage ParseFrom(ByteString data)
+ {
+ Preconditions.CheckNotNull(data, "data");
+ IMessage message = factory();
+ message.MergeFrom(data);
+ return message;
+ }
+
+ ///
+ /// Parses a message from the given stream.
+ ///
+ /// The stream to parse.
+ /// The parsed message.
+ public IMessage ParseFrom(Stream input)
+ {
+ IMessage message = factory();
+ message.MergeFrom(input);
+ return message;
+ }
+
+ ///
+ /// Parses a length-delimited message from the given stream.
+ ///
+ ///
+ /// The stream is expected to contain a length and then the data. Only the amount of data
+ /// specified by the length will be consumed.
+ ///
+ /// The stream to parse.
+ /// The parsed message.
+ public IMessage ParseDelimitedFrom(Stream input)
+ {
+ IMessage message = factory();
+ message.MergeDelimitedFrom(input);
+ return message;
+ }
+
+ ///
+ /// Parses a message from the given coded input stream.
+ ///
+ /// The stream to parse.
+ /// The parsed message.
+ public IMessage ParseFrom(CodedInputStream input)
+ {
+ IMessage message = factory();
+ message.MergeFrom(input);
+ return message;
+ }
+
+ ///
+ /// Parses a message from the given JSON.
+ ///
+ /// The JSON to parse.
+ /// The parsed message.
+ /// The JSON does not comply with RFC 7159
+ /// The JSON does not represent a Protocol Buffers message correctly
+ public IMessage ParseJson(string json)
+ {
+ IMessage message = factory();
+ JsonParser.Default.Merge(message, json);
+ return message;
+ }
+ }
+
///
/// A parser for a specific message type.
///
@@ -51,8 +154,10 @@ namespace Google.Protobuf
///
///
/// The type of message to be parsed.
- public sealed class MessageParser where T : IMessage
+ public sealed class MessageParser : MessageParser where T : IMessage
{
+ // Implementation note: all the methods here *could* just delegate up to the base class and cast the result.
+ // The
private readonly Func factory;
///
@@ -63,7 +168,7 @@ namespace Google.Protobuf
/// to require a parameterless constructor: delegates are significantly faster to execute.
///
/// Function to invoke when a new, empty message is required.
- public MessageParser(Func factory)
+ public MessageParser(Func factory) : base(() => factory())
{
this.factory = factory;
}
@@ -72,7 +177,7 @@ namespace Google.Protobuf
/// Creates a template instance ready for population.
///
/// An empty message.
- internal T CreateTemplate()
+ internal new T CreateTemplate()
{
return factory();
}
@@ -82,7 +187,7 @@ namespace Google.Protobuf
///
/// The byte array containing the message. Must not be null.
/// The newly parsed message.
- 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
///
/// The data to parse.
/// The parsed message.
- 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
///
/// The stream to parse.
/// The parsed message.
- public T ParseFrom(Stream input)
+ public new T ParseFrom(Stream input)
{
T message = factory();
message.MergeFrom(input);
@@ -124,7 +229,7 @@ namespace Google.Protobuf
///
/// The stream to parse.
/// The parsed message.
- public T ParseDelimitedFrom(Stream input)
+ public new T ParseDelimitedFrom(Stream input)
{
T message = factory();
message.MergeDelimitedFrom(input);
@@ -136,7 +241,7 @@ namespace Google.Protobuf
///
/// The stream to parse.
/// The parsed message.
- public T ParseFrom(CodedInputStream input)
+ public new T ParseFrom(CodedInputStream input)
{
T message = factory();
message.MergeFrom(input);
@@ -150,7 +255,7 @@ namespace Google.Protobuf
/// The parsed message.
/// The JSON does not comply with RFC 7159
/// The JSON does not represent a Protocol Buffers message correctly
- public T ParseJson(string json)
+ public new T ParseJson(string json)
{
T message = factory();
JsonParser.Default.Merge(message, json);
diff --git a/csharp/src/Google.Protobuf/Reflection/GeneratedCodeInfo.cs b/csharp/src/Google.Protobuf/Reflection/GeneratedCodeInfo.cs
index 8c52cd12..ff4ad0aa 100644
--- a/csharp/src/Google.Protobuf/Reflection/GeneratedCodeInfo.cs
+++ b/csharp/src/Google.Protobuf/Reflection/GeneratedCodeInfo.cs
@@ -17,6 +17,11 @@ namespace Google.Protobuf.Reflection
///
public Type ClrType { get; private set; }
+ ///
+ /// Irrelevant for file descriptors; the parser for message descriptors.
+ ///
+ public MessageParser Parser { get; private set; }
+
///
/// 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.
///
- 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.
///
public GeneratedCodeInfo(Type[] nestedEnums, GeneratedCodeInfo[] nestedTypes)
- : this(null, null, null, nestedEnums, nestedTypes)
+ : this(null, null, null, null, nestedEnums, nestedTypes)
{
}
}
diff --git a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
index e599998e..65040e42 100644
--- a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
@@ -67,11 +67,13 @@ namespace Google.Protobuf.Reflection
private readonly IList 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
///
public Type GeneratedType { get { return generatedType; } }
+ ///
+ /// A parser for this message type.
+ ///
+ ///
+ /// As is not generic, this cannot be statically
+ /// typed to the relevant type, but if returns a non-null value, the parser returned
+ ///
+ public MessageParser Parser { get { return parser; } }
+
///
/// Returns whether this message is one of the "well known types" which may have runtime/protoc support.
///
diff --git a/src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc b/src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc
index 8059fd2a..812c6fc8 100644
--- a/src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc
@@ -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) {
--
cgit v1.2.3