1#region Copyright notice and license 2// Protocol Buffers - Google's data interchange format 3// Copyright 2015 Google Inc. All rights reserved. 4// https://developers.google.com/protocol-buffers/ 5// 6// Redistribution and use in source and binary forms, with or without 7// modification, are permitted provided that the following conditions are 8// met: 9// 10// * Redistributions of source code must retain the above copyright 11// notice, this list of conditions and the following disclaimer. 12// * Redistributions in binary form must reproduce the above 13// copyright notice, this list of conditions and the following disclaimer 14// in the documentation and/or other materials provided with the 15// distribution. 16// * Neither the name of Google Inc. nor the names of its 17// contributors may be used to endorse or promote products derived from 18// this software without specific prior written permission. 19// 20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31#endregion 32 33using System; 34using System.IO; 35 36namespace Google.Protobuf 37{ 38 /// <summary> 39 /// A general message parser, typically used by reflection-based code as all the methods 40 /// return simple <see cref="IMessage"/>. 41 /// </summary> 42 public class MessageParser 43 { 44 private Func<IMessage> factory; 45 46 internal MessageParser(Func<IMessage> factory) 47 { 48 this.factory = factory; 49 } 50 51 /// <summary> 52 /// Creates a template instance ready for population. 53 /// </summary> 54 /// <returns>An empty message.</returns> 55 internal IMessage CreateTemplate() 56 { 57 return factory(); 58 } 59 60 /// <summary> 61 /// Parses a message from a byte array. 62 /// </summary> 63 /// <param name="data">The byte array containing the message. Must not be null.</param> 64 /// <returns>The newly parsed message.</returns> 65 public IMessage ParseFrom(byte[] data) 66 { 67 ProtoPreconditions.CheckNotNull(data, "data"); 68 IMessage message = factory(); 69 message.MergeFrom(data); 70 return message; 71 } 72 73 /// <summary> 74 /// Parses a message from the given byte string. 75 /// </summary> 76 /// <param name="data">The data to parse.</param> 77 /// <returns>The parsed message.</returns> 78 public IMessage ParseFrom(ByteString data) 79 { 80 ProtoPreconditions.CheckNotNull(data, "data"); 81 IMessage message = factory(); 82 message.MergeFrom(data); 83 return message; 84 } 85 86 /// <summary> 87 /// Parses a message from the given stream. 88 /// </summary> 89 /// <param name="input">The stream to parse.</param> 90 /// <returns>The parsed message.</returns> 91 public IMessage ParseFrom(Stream input) 92 { 93 IMessage message = factory(); 94 message.MergeFrom(input); 95 return message; 96 } 97 98 /// <summary> 99 /// Parses a length-delimited message from the given stream. 100 /// </summary> 101 /// <remarks> 102 /// The stream is expected to contain a length and then the data. Only the amount of data 103 /// specified by the length will be consumed. 104 /// </remarks> 105 /// <param name="input">The stream to parse.</param> 106 /// <returns>The parsed message.</returns> 107 public IMessage ParseDelimitedFrom(Stream input) 108 { 109 IMessage message = factory(); 110 message.MergeDelimitedFrom(input); 111 return message; 112 } 113 114 /// <summary> 115 /// Parses a message from the given coded input stream. 116 /// </summary> 117 /// <param name="input">The stream to parse.</param> 118 /// <returns>The parsed message.</returns> 119 public IMessage ParseFrom(CodedInputStream input) 120 { 121 IMessage message = factory(); 122 message.MergeFrom(input); 123 return message; 124 } 125 126 /// <summary> 127 /// Parses a message from the given JSON. 128 /// </summary> 129 /// <param name="json">The JSON to parse.</param> 130 /// <returns>The parsed message.</returns> 131 /// <exception cref="InvalidJsonException">The JSON does not comply with RFC 7159</exception> 132 /// <exception cref="InvalidProtocolBufferException">The JSON does not represent a Protocol Buffers message correctly</exception> 133 public IMessage ParseJson(string json) 134 { 135 IMessage message = factory(); 136 JsonParser.Default.Merge(message, json); 137 return message; 138 } 139 } 140 141 /// <summary> 142 /// A parser for a specific message type. 143 /// </summary> 144 /// <remarks> 145 /// <p> 146 /// This delegates most behavior to the 147 /// <see cref="IMessage.MergeFrom"/> implementation within the original type, but 148 /// provides convenient overloads to parse from a variety of sources. 149 /// </p> 150 /// <p> 151 /// Most applications will never need to create their own instances of this type; 152 /// instead, use the static <c>Parser</c> property of a generated message type to obtain a 153 /// parser for that type. 154 /// </p> 155 /// </remarks> 156 /// <typeparam name="T">The type of message to be parsed.</typeparam> 157 public sealed class MessageParser<T> : MessageParser where T : IMessage<T> 158 { 159 // Implementation note: all the methods here *could* just delegate up to the base class and cast the result. 160 // The current implementation avoids a virtual method call and a cast, which *may* be significant in some cases. 161 // Benchmarking work is required to measure the significance - but it's only a few lines of code in any case. 162 // The API wouldn't change anyway - just the implementation - so this work can be deferred. 163 private readonly Func<T> factory; 164 165 /// <summary> 166 /// Creates a new parser. 167 /// </summary> 168 /// <remarks> 169 /// The factory method is effectively an optimization over using a generic constraint 170 /// to require a parameterless constructor: delegates are significantly faster to execute. 171 /// </remarks> 172 /// <param name="factory">Function to invoke when a new, empty message is required.</param> 173 public MessageParser(Func<T> factory) : base(() => factory()) 174 { 175 this.factory = factory; 176 } 177 178 /// <summary> 179 /// Creates a template instance ready for population. 180 /// </summary> 181 /// <returns>An empty message.</returns> 182 internal new T CreateTemplate() 183 { 184 return factory(); 185 } 186 187 /// <summary> 188 /// Parses a message from a byte array. 189 /// </summary> 190 /// <param name="data">The byte array containing the message. Must not be null.</param> 191 /// <returns>The newly parsed message.</returns> 192 public new T ParseFrom(byte[] data) 193 { 194 ProtoPreconditions.CheckNotNull(data, "data"); 195 T message = factory(); 196 message.MergeFrom(data); 197 return message; 198 } 199 200 /// <summary> 201 /// Parses a message from the given byte string. 202 /// </summary> 203 /// <param name="data">The data to parse.</param> 204 /// <returns>The parsed message.</returns> 205 public new T ParseFrom(ByteString data) 206 { 207 ProtoPreconditions.CheckNotNull(data, "data"); 208 T message = factory(); 209 message.MergeFrom(data); 210 return message; 211 } 212 213 /// <summary> 214 /// Parses a message from the given stream. 215 /// </summary> 216 /// <param name="input">The stream to parse.</param> 217 /// <returns>The parsed message.</returns> 218 public new T ParseFrom(Stream input) 219 { 220 T message = factory(); 221 message.MergeFrom(input); 222 return message; 223 } 224 225 /// <summary> 226 /// Parses a length-delimited message from the given stream. 227 /// </summary> 228 /// <remarks> 229 /// The stream is expected to contain a length and then the data. Only the amount of data 230 /// specified by the length will be consumed. 231 /// </remarks> 232 /// <param name="input">The stream to parse.</param> 233 /// <returns>The parsed message.</returns> 234 public new T ParseDelimitedFrom(Stream input) 235 { 236 T message = factory(); 237 message.MergeDelimitedFrom(input); 238 return message; 239 } 240 241 /// <summary> 242 /// Parses a message from the given coded input stream. 243 /// </summary> 244 /// <param name="input">The stream to parse.</param> 245 /// <returns>The parsed message.</returns> 246 public new T ParseFrom(CodedInputStream input) 247 { 248 T message = factory(); 249 message.MergeFrom(input); 250 return message; 251 } 252 253 /// <summary> 254 /// Parses a message from the given JSON. 255 /// </summary> 256 /// <param name="json">The JSON to parse.</param> 257 /// <returns>The parsed message.</returns> 258 /// <exception cref="InvalidJsonException">The JSON does not comply with RFC 7159</exception> 259 /// <exception cref="InvalidProtocolBufferException">The JSON does not represent a Protocol Buffers message correctly</exception> 260 public new T ParseJson(string json) 261 { 262 T message = factory(); 263 JsonParser.Default.Merge(message, json); 264 return message; 265 } 266 } 267} 268