Skip to content

Commit

Permalink
Added GMWebClient.
Browse files Browse the repository at this point in the history
Added OrderByMultipleKeys to IEnumerableUtility.
Fixed a bug in StringUtility.RemoveAll().
  • Loading branch information
GregaMohorko committed Feb 2, 2018
1 parent 2c9c7d7 commit e1d6a56
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 6 deletions.
12 changes: 8 additions & 4 deletions src/GM.Utility/GM.Utility/GM.Utility.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<SignAssembly>true</SignAssembly>
<DelaySign>false</DelaySign>
<AssemblyOriginatorKeyFile>GM.StrongNameKey.snk</AssemblyOriginatorKeyFile>
<Version>1.2.0.2</Version>
<Version>1.2.1.0</Version>
<Title>GM.Utility</Title>
<Authors>Grega Mohorko</Authors>
<Company>Grega Mohorko</Company>
Expand All @@ -16,9 +16,13 @@
<Description>Library with various static classes and tools that provide universally useful functions, extensions and utilities.</Description>
<Copyright>Copyright © Grega Mohorko 2018</Copyright>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageReleaseNotes>Fixed a bug in ReflectionUtility.GetAssemblyInformation().</PackageReleaseNotes>
<AssemblyVersion>1.2.0.2</AssemblyVersion>
<FileVersion>1.2.0.2</FileVersion>
<PackageReleaseNotes>Added GMWebClient.

Added OrderByMultipleKeys to IEnumerableUtility.

Fixed a bug in StringUtility.RemoveAll().</PackageReleaseNotes>
<AssemblyVersion>1.2.1.0</AssemblyVersion>
<FileVersion>1.2.1.0</FileVersion>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
41 changes: 41 additions & 0 deletions src/GM.Utility/GM.Utility/IEnumerableUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,5 +110,46 @@ public static bool SequenceEqualUnordered<T>(this IEnumerable<T> first, IEnumera
// check if the resultant counts are all zeros
return ok && counts.Values.All(count => count == 0);
}

/// <summary>
/// Orders this collection using the keys that will be extracted using the provided key selectors.
/// <para>Key selectors are <see cref="Tuple{T1, T2}"/> where the first item is a function that selects the key and the second item determines whether to order ascendingly (if true) or descendingly (if false).</para>
/// </summary>
/// <typeparam name="T">The type of elements.</typeparam>
/// <param name="collection">The collection to order.</param>
/// <param name="keySelectors">The key selectors that will be used to extract the keys on which to order from the elements.</param>
public static IOrderedEnumerable<T> OrderByMultipleKeys<T>(this IEnumerable<T> collection, IEnumerable<Tuple<Func<T, object>, bool>> keySelectors)
{
int sortKeySelectorsCount = keySelectors.Count();

if(sortKeySelectorsCount == 0) {
throw new NotImplementedException("Empty SortKeySelector list provided.");
}

IOrderedEnumerable<T> orderedList;

Tuple<Func<T, object>, bool> tuple = keySelectors.ElementAt(0);

Func<T, object> sortKeySelector = tuple.Item1;

if(tuple.Item2) {
orderedList = collection.OrderBy(sortKeySelector);
} else {
orderedList = collection.OrderByDescending(sortKeySelector);
}

for(int i = 1; i < sortKeySelectorsCount; ++i) {
tuple = keySelectors.ElementAt(i);
sortKeySelector = tuple.Item1;

if(tuple.Item2) {
orderedList = orderedList.ThenBy(sortKeySelector);
} else {
orderedList = orderedList.ThenByDescending(sortKeySelector);
}
}

return orderedList;
}
}
}
157 changes: 157 additions & 0 deletions src/GM.Utility/GM.Utility/Net/GMWebClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace GM.Utility.Net
{
/// <summary>
/// A version of <see cref="WebClient"/> with added functionalities.
/// <para>Added support for request timeout. Default timeout is 10000 ms.</para>
/// </summary>
public class GMWebClient:IDisposable
{
/// <summary>
/// Default timeout.
/// </summary>
public const int DEFAULT_TIMEOUT = 10000;

private readonly GMWebClientInternal webClient;

/// <summary>
/// Creates a new instance of <see cref="GMWebClient"/> with default timeout.
/// </summary>
public GMWebClient() : this(DEFAULT_TIMEOUT) { }

/// <summary>
/// Creates a new instance of <see cref="GMWebClient"/> with the specified timeout.
/// </summary>
/// <param name="timeout">The length of time, in milliseconds, before the request times out.</param>
public GMWebClient(int timeout)
{
webClient = new GMWebClientInternal();
Timeout = timeout;
}

/// <summary>
/// Releases all resources used by this web client.
/// </summary>
public void Dispose()
{
webClient?.Dispose();
}

/// <summary>
/// Gets or sets the length of time, in milliseconds, before the request times out.
/// </summary>
public int Timeout
{
get => webClient.Timeout;
set => webClient.Timeout = value;
}

/// <summary>
/// Uploads the specified name/value collection to the resource identified by the specified URI.
/// </summary>
/// <param name="address">The URI of the resource to receive the collection.</param>
/// <param name="data">The NameValueCollection to send to the resource.</param>
/// <param name="method">The http method used to send data to the resource. Allowed methods are POST and GET.</param>
public string UploadValues(string address, NameValueCollection data, HttpMethod method)
{
switch(method) {
case HttpMethod.GET:
return UploadValuesGet(address, data);
case HttpMethod.POST:
return UploadValuesPost(address, data);
}
throw new NotImplementedException("Unsupported http method.");
}

/// <summary>
/// Asynchronously uploads the specified name/value collection to the resource identified by the specified URI.
/// </summary>
/// <param name="address">The URI of the resource to receive the collection.</param>
/// <param name="data">The NameValueCollection to send to the resource.</param>
/// <param name="method">The http method used to send data to the resource. Allowed methods are POST and GET.</param>
public Task<string> UploadValuesAsyncTask(string address, NameValueCollection data, HttpMethod method)
{
switch(method) {
case HttpMethod.GET:
return UploadValuesGetAsyncTask(address, data);
case HttpMethod.POST:
return UploadValuesPostAsyncTask(address, data);
}
throw new NotImplementedException("Unsupported http method.");
}

private string UploadValuesGet(string address, NameValueCollection data)
{
AddGetParameters(ref address, data);
return webClient.DownloadString(address);
}

private Task<string> UploadValuesGetAsyncTask(string address, NameValueCollection data)
{
AddGetParameters(ref address, data);
return webClient.DownloadStringTaskAsync(address);
}

private void AddGetParameters(ref string address, NameValueCollection data)
{
if(data.Count == 0) {
return;
}

List<string> collection = new List<string>(data.Count);
foreach(string name in data.AllKeys) {
string value = data.Get(name);
collection.Add($"{name}={value}");
}
address += "?" + string.Join("&", collection);
}

private string UploadValuesPost(string address, NameValueCollection data)
{
byte[] byteArray = webClient.UploadValues(address, data);
return BytesToString(byteArray);
}

private async Task<string> UploadValuesPostAsyncTask(string address, NameValueCollection data)
{
byte[] byteArray = await webClient.UploadValuesTaskAsync(address, data);
return BytesToString(byteArray);
}

private string BytesToString(byte[] bytes)
{
return Encoding.UTF8.GetString(bytes);
}

/// <summary>
/// Solution for timeout came from here: http://w3ka.blogspot.si/2009/12/how-to-fix-webclient-timeout-issue.html
/// </summary>
private class GMWebClientInternal : WebClient
{
/// <summary>
/// Gets or sets the length of time, in milliseconds, before the request times out.
/// </summary>
public int Timeout { get; set; }

/// <summary>
/// Overriden just for added functionality of timeout.
/// </summary>
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);

if(request != null) {
request.Timeout = Timeout;
}

return request;
}
}
}
}
25 changes: 25 additions & 0 deletions src/GM.Utility/GM.Utility/Net/HttpMethod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace GM.Utility.Net
{
/// <summary>
/// A HTTP method.
/// </summary>
public enum HttpMethod
{
/// <summary>
/// Unknown.
/// </summary>
UNKNOWN = 0,
/// <summary>
/// Get method.
/// </summary>
GET = 1,
/// <summary>
/// Post method.
/// </summary>
POST = 2
}
}
3 changes: 1 addition & 2 deletions src/GM.Utility/GM.Utility/StringUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,10 @@ public static string RemoveAllOf(this string text, string value)
// TEST performance
//return text.Replace(value, string.Empty);

int index = text.LastIndexOf(value);
int index = text.LastIndexOf(value,text.Length);
while(index >= 0) {
text = text.Remove(index, value.Length);
--index;
index = text.LastIndexOf(value, index);
}
return text;
}
Expand Down

0 comments on commit e1d6a56

Please sign in to comment.