// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if NETFX_CORE
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Web.Http;
#else
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Net.Http.Internal;
using System.Runtime.Serialization;
using System.Text;
using System.Web.Http;
#endif
#if NETFX_CORE
namespace System.Net.Http.Formatting
#else
namespace System.Net.Http.Formatting.Internal
#endif
{
///
/// NameValueCollection to represent form data and to generate form data output.
///
#if NETFX_CORE
public class HttpValueCollection : IEnumerable>
#else
[Serializable]
internal class HttpValueCollection : NameValueCollection
#endif
{
#if NETFX_CORE
internal readonly HashSet Names = new HashSet(StringComparer.OrdinalIgnoreCase);
internal readonly List> List = new List>();
///
/// Creates a new instance
///
public HttpValueCollection()
{
}
#else
protected HttpValueCollection(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
private HttpValueCollection()
: base(StringComparer.OrdinalIgnoreCase) // case-insensitive keys
{
}
#endif
// Use a builder function instead of a ctor to avoid virtual calls from the ctor.
// The above condition is only important in the Full .NET fx implementation.
internal static HttpValueCollection Create()
{
return new HttpValueCollection();
}
internal static HttpValueCollection Create(IEnumerable> pairs)
{
Contract.Assert(pairs != null);
var hvc = new HttpValueCollection();
// Ordering example:
// k=A&j=B&k=C --> k:[A,C];j=[B].
foreach (KeyValuePair kvp in pairs)
{
hvc.Add(kvp.Key, kvp.Value);
}
#if !NETFX_CORE
hvc.IsReadOnly = false;
#endif
return hvc;
}
///
/// Adds a name-value pair to the collection.
///
/// The name to be added as a case insensitive string.
/// The value to be added.
public
#if !NETFX_CORE
override
#endif
void Add(string name, string value)
{
ThrowIfMaxHttpCollectionKeysExceeded(Count);
name = name ?? String.Empty;
value = value ?? String.Empty;
#if NETFX_CORE
Names.Add(name);
List.Add(new KeyValuePair(name, value));
#else
base.Add(name, value);
#endif
}
///
/// Converts the content of this instance to its equivalent string representation.
///
/// The string representation of the value of this instance, multiple values with a single key are comma separated.
public override string ToString()
{
return ToString(true);
}
private static void ThrowIfMaxHttpCollectionKeysExceeded(int count)
{
if (count >= MediaTypeFormatter.MaxHttpCollectionKeys)
{
throw Error.InvalidOperation(System.Net.Http.Properties.Resources.MaxHttpCollectionKeyLimitReached, MediaTypeFormatter.MaxHttpCollectionKeys, typeof(MediaTypeFormatter));
}
}
private string ToString(bool urlEncode)
{
if (Count == 0)
{
return String.Empty;
}
StringBuilder builder = new StringBuilder();
bool first = true;
#if NETFX_CORE
foreach (string name in Names)
#else
foreach (string name in this)
#endif
{
string[] values = GetValues(name);
if (values == null || values.Length == 0)
{
first = AppendNameValuePair(builder, first, urlEncode, name, String.Empty);
}
else
{
foreach (string value in values)
{
first = AppendNameValuePair(builder, first, urlEncode, name, value);
}
}
}
return builder.ToString();
}
private static bool AppendNameValuePair(StringBuilder builder, bool first, bool urlEncode, string name, string value)
{
string effectiveName = name ?? String.Empty;
string encodedName = urlEncode ? UriQueryUtility.UrlEncode(effectiveName) : effectiveName;
string effectiveValue = value ?? String.Empty;
string encodedValue = urlEncode ? UriQueryUtility.UrlEncode(effectiveValue) : effectiveValue;
if (first)
{
first = false;
}
else
{
builder.Append("&");
}
builder.Append(encodedName);
if (!String.IsNullOrEmpty(encodedValue))
{
builder.Append("=");
builder.Append(encodedValue);
}
return first;
}
#if NETFX_CORE
///
/// Gets the values associated with the specified name
/// combined into one comma-separated list.
///
/// The name of the entry that contains the values to get. The name can be null.
/// A that contains a comma-separated list of url encoded values associated
/// with the specified name if found; otherwise, null. The values are Url encoded.
public string this[string name]
{
get
{
return Get(name);
}
}
///
/// Gets the number of names in the collection.
///
public int Count
{
get
{
return Names.Count;
}
}
///
/// Gets the values associated with the specified name
/// combined into one comma-separated list.
///
/// The name of the entry that contains the values to get. The name can be null.
///
/// A that contains a comma-separated list of url encoded values associated
/// with the specified name if found; otherwise, null. The values are Url encoded.
///
public string Get(string name)
{
name = name ?? String.Empty;
if (!Names.Contains(name))
{
return null;
}
List values = GetValuesInternal(name);
Contract.Assert(values != null && values.Count > 0);
return String.Join(",", values);
}
///
/// Gets the values associated with the specified name.
///
/// The
/// A that contains url encoded values associated with the name, or null if the name does not exist.
public string[] GetValues(string name)
{
name = name ?? String.Empty;
if (!Names.Contains(name))
{
return null;
}
return GetValuesInternal(name).ToArray();
}
// call this when only when there are values available.
private List GetValuesInternal(string name)
{
List values = new List();
for (int i = 0; i < List.Count; i++)
{
KeyValuePair kvp = List[i];
if (String.Equals(kvp.Key, name, StringComparison.OrdinalIgnoreCase))
{
values.Add(kvp.Value);
}
}
return values;
}
///
public IEnumerator> GetEnumerator()
{
return List.GetEnumerator();
}
///
IEnumerator IEnumerable.GetEnumerator()
{
return List.GetEnumerator();
}
#endif
}
}