using System; using System.IO; using System.Text; namespace Google.ProtocolBuffers.Serialization.Http { /// /// Allows reading messages from a name/value dictionary /// public class FormUrlEncodedReader : AbstractTextReader { private readonly TextReader _input; private string _fieldName, _fieldValue; private bool _ready; /// /// Creates a dictionary reader from an enumeration of KeyValuePair data, like an IDictionary /// FormUrlEncodedReader(TextReader input) { _input = input; int ch = input.Peek(); if (ch == '?') { input.Read(); } _ready = ReadNext(); } #region CreateInstance overloads /// /// Constructs a FormUrlEncodedReader to parse form data, or url query text into a message. /// public static FormUrlEncodedReader CreateInstance(Stream stream) { return new FormUrlEncodedReader(new StreamReader(stream, Encoding.UTF8, false)); } /// /// Constructs a FormUrlEncodedReader to parse form data, or url query text into a message. /// public static FormUrlEncodedReader CreateInstance(byte[] bytes) { return new FormUrlEncodedReader(new StreamReader(new MemoryStream(bytes, false), Encoding.UTF8, false)); } /// /// Constructs a FormUrlEncodedReader to parse form data, or url query text into a message. /// public static FormUrlEncodedReader CreateInstance(string text) { return new FormUrlEncodedReader(new StringReader(text)); } /// /// Constructs a FormUrlEncodedReader to parse form data, or url query text into a message. /// public static FormUrlEncodedReader CreateInstance(TextReader input) { return new FormUrlEncodedReader(input); } #endregion private bool ReadNext() { StringBuilder field = new StringBuilder(32); StringBuilder value = new StringBuilder(64); int ch; while (-1 != (ch = _input.Read()) && ch != '=' && ch != '&') { field.Append((char)ch); } if (ch != -1 && ch != '&') { while (-1 != (ch = _input.Read()) && ch != '&') { value.Append((char)ch); } } _fieldName = field.ToString(); _fieldValue = Uri.UnescapeDataString(value.Replace('+', ' ').ToString()); return !String.IsNullOrEmpty(_fieldName); } /// /// No-op /// public override void ReadMessageStart() { } /// /// No-op /// public override void ReadMessageEnd() { } /// /// Merges the contents of stream into the provided message builder /// public override TBuilder Merge(TBuilder builder, ExtensionRegistry registry) { builder.WeakMergeFrom(this, registry); return builder; } /// /// Causes the reader to skip past this field /// protected override void Skip() { _ready = ReadNext(); } /// /// Peeks at the next field in the input stream and returns what information is available. /// /// /// 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. /// protected override bool PeekNext(out string field) { field = _ready ? _fieldName : null; return field != null; } /// /// Returns true if it was able to read a String from the input /// protected override bool ReadAsText(ref string value, Type typeInfo) { if (_ready) { value = _fieldValue; _ready = ReadNext(); return true; } return false; } /// /// It's unlikely this will work for anything but text data as bytes UTF8 are transformed to text and back to bytes /// protected override ByteString DecodeBytes(string bytes) { return ByteString.CopyFromUtf8(bytes); } /// /// Not Supported /// public override bool ReadGroup(IBuilderLite value, ExtensionRegistry registry) { throw new NotSupportedException(); } /// /// Not Supported /// protected override bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry) { throw new NotSupportedException(); } } }