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