Imports many of the useful extension methods from v5 that are also used in umbraMVCo to begin the
MVC integration. Imported the unit tests associated with each of these imported classes.
This commit is contained in:
60
src/Umbraco.Core/DelegateEqualityComparer.cs
Normal file
60
src/Umbraco.Core/DelegateEqualityComparer.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom equality comparer that excepts a delegate to do the comparison operation
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class DelegateEqualityComparer<T> : IEqualityComparer<T>
|
||||
{
|
||||
private readonly Func<T, T, bool> _equals;
|
||||
private readonly Func<T, int> _getHashcode;
|
||||
|
||||
#region Implementation of IEqualityComparer<in T>
|
||||
|
||||
public DelegateEqualityComparer(Func<T, T, bool> equals, Func<T, int> getHashcode)
|
||||
{
|
||||
_getHashcode = getHashcode;
|
||||
_equals = equals;
|
||||
}
|
||||
|
||||
public static DelegateEqualityComparer<T> CompareMember<TMember>(Func<T, TMember> memberExpression) where TMember : IEquatable<TMember>
|
||||
{
|
||||
return new DelegateEqualityComparer<T>(
|
||||
(x, y) => memberExpression.Invoke(x).Equals((TMember)memberExpression.Invoke(y)),
|
||||
x =>
|
||||
{
|
||||
var invoked = memberExpression.Invoke(x);
|
||||
return !ReferenceEquals(invoked, default(TMember)) ? invoked.GetHashCode() : 0;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified objects are equal.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true if the specified objects are equal; otherwise, false.
|
||||
/// </returns>
|
||||
/// <param name="x">The first object of type <paramref name="T"/> to compare.</param><param name="y">The second object of type <paramref name="T"/> to compare.</param>
|
||||
public bool Equals(T x, T y)
|
||||
{
|
||||
return _equals.Invoke(x, y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for the specified object.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for the specified object.
|
||||
/// </returns>
|
||||
/// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.</param><exception cref="T:System.ArgumentNullException">The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.</exception>
|
||||
public int GetHashCode(T obj)
|
||||
{
|
||||
return _getHashcode.Invoke(obj);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
269
src/Umbraco.Core/EnumerableExtensions.cs
Normal file
269
src/Umbraco.Core/EnumerableExtensions.cs
Normal file
@@ -0,0 +1,269 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
///<summary>
|
||||
/// Extensions for enumerable sources
|
||||
///</summary>
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
public static IEnumerable<IEnumerable<T>> InGroupsOf<T>(this IEnumerable<T> source, int groupSize)
|
||||
{
|
||||
var i = 0;
|
||||
var length = source.Count();
|
||||
|
||||
while ((i * groupSize) < length)
|
||||
{
|
||||
yield return source.Skip(i * groupSize).Take(groupSize);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>The distinct by.</summary>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="keySelector">The key selector.</param>
|
||||
/// <typeparam name="TSource">Source type</typeparam>
|
||||
/// <typeparam name="TKey">Key type</typeparam>
|
||||
/// <returns>the unique list</returns>
|
||||
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
return source.Distinct(DelegateEqualityComparer<TSource>.CompareMember(keySelector));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a sequence of length <paramref name="count"/> whose elements are the result of invoking <paramref name="factory"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="factory">The factory.</param>
|
||||
/// <param name="count">The count.</param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> Range<T>(Func<int, T> factory, int count)
|
||||
{
|
||||
for (int i = 1; i <= count; i++)
|
||||
{
|
||||
yield return factory.Invoke(i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The if not null.</summary>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="action">The action.</param>
|
||||
/// <typeparam name="TItem">The type</typeparam>
|
||||
public static void IfNotNull<TItem>(this IEnumerable<TItem> items, Action<TItem> action) where TItem : class
|
||||
{
|
||||
if (items != null)
|
||||
{
|
||||
foreach (TItem item in items)
|
||||
{
|
||||
item.IfNotNull(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The for each.</summary>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="func">The func.</param>
|
||||
/// <typeparam name="TItem">item type</typeparam>
|
||||
/// <typeparam name="TResult">Result type</typeparam>
|
||||
/// <returns>the Results</returns>
|
||||
public static TResult[] ForEach<TItem, TResult>(this IEnumerable<TItem> items, Func<TItem, TResult> func)
|
||||
{
|
||||
return items.Select(func).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>The for each.</summary>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="action">The action.</param>
|
||||
/// <typeparam name="TItem">Item type</typeparam>
|
||||
/// <returns>list of TItem</returns>
|
||||
public static IEnumerable<TItem> ForEach<TItem>(this IEnumerable<TItem> items, Action<TItem> action)
|
||||
{
|
||||
if (items != null)
|
||||
{
|
||||
foreach (TItem item in items)
|
||||
{
|
||||
action(item);
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <summary>The flatten list.</summary>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="selectChild">The select child.</param>
|
||||
/// <typeparam name="TItem">Item type</typeparam>
|
||||
/// <returns>list of TItem</returns>
|
||||
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")]
|
||||
public static IEnumerable<TItem> FlattenList<TItem>(this IEnumerable<TItem> items, Func<TItem, IEnumerable<TItem>> selectChild)
|
||||
{
|
||||
IEnumerable<TItem> children = items != null && items.Any()
|
||||
? items.SelectMany(selectChild).FlattenList(selectChild)
|
||||
: Enumerable.Empty<TItem>();
|
||||
|
||||
if (items != null)
|
||||
{
|
||||
return items.Concat(children);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if all items in the other collection exist in this collection
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
public static bool ContainsAll<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> other)
|
||||
{
|
||||
var matches = true;
|
||||
foreach (var i in other)
|
||||
{
|
||||
matches = source.Contains(i);
|
||||
if (!matches) break;
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the source contains any of the items in the other list
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
public static bool ContainsAny<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> other)
|
||||
{
|
||||
return other.Any(i => source.Contains(i));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all matching items from an <see cref="IList{T}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="list">The list.</param>
|
||||
/// <param name="predicate">The predicate.</param>
|
||||
/// <remarks></remarks>
|
||||
public static void RemoveAll<T>(this IList<T> list, Func<T, bool> predicate)
|
||||
{
|
||||
for (var i = 0; i < list.Count; i++)
|
||||
{
|
||||
if (predicate(list[i]))
|
||||
{
|
||||
list.RemoveAt(i--);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all matching items from an <see cref="ICollection{T}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="list">The list.</param>
|
||||
/// <param name="predicate">The predicate.</param>
|
||||
/// <remarks></remarks>
|
||||
public static void RemoveAll<T>(this ICollection<T> list, Func<T, bool> predicate)
|
||||
{
|
||||
var matches = list.Where(predicate).ToArray();
|
||||
foreach (var match in matches)
|
||||
{
|
||||
list.Remove(match);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TSource> SelectRecursive<TSource>(
|
||||
this IEnumerable<TSource> source,
|
||||
Func<TSource, IEnumerable<TSource>> recursiveSelector, int maxRecusionDepth = 100)
|
||||
{
|
||||
var stack = new Stack<IEnumerator<TSource>>();
|
||||
stack.Push(source.GetEnumerator());
|
||||
|
||||
try
|
||||
{
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
if (stack.Count > maxRecusionDepth)
|
||||
throw new InvalidOperationException("Maximum recursion depth reached of " + maxRecusionDepth);
|
||||
|
||||
if (stack.Peek().MoveNext())
|
||||
{
|
||||
var current = stack.Peek().Current;
|
||||
|
||||
yield return current;
|
||||
|
||||
stack.Push(recursiveSelector(current).GetEnumerator());
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.Pop().Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
stack.Pop().Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters a sequence of values to ignore those which are null.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="coll">The coll.</param>
|
||||
/// <returns></returns>
|
||||
/// <remarks></remarks>
|
||||
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T> coll) where T : class
|
||||
{
|
||||
return coll.Where(x => x != null);
|
||||
}
|
||||
|
||||
public static IEnumerable<TBase> ForAllThatAre<TBase, TActual>(this IEnumerable<TBase> sequence, Action<TActual> projection)
|
||||
where TActual : class
|
||||
{
|
||||
return sequence.Select(
|
||||
x =>
|
||||
{
|
||||
if (typeof(TActual).IsAssignableFrom(x.GetType()))
|
||||
{
|
||||
var casted = x as TActual;
|
||||
projection.Invoke(casted);
|
||||
}
|
||||
return x;
|
||||
});
|
||||
}
|
||||
|
||||
///<summary>Finds the index of the first item matching an expression in an enumerable.</summary>
|
||||
///<param name="items">The enumerable to search.</param>
|
||||
///<param name="predicate">The expression to test the items against.</param>
|
||||
///<returns>The index of the first matching item, or -1 if no items match.</returns>
|
||||
public static int FindIndex<T>(this IEnumerable<T> items, Func<T, bool> predicate)
|
||||
{
|
||||
if (items == null) throw new ArgumentNullException("items");
|
||||
if (predicate == null) throw new ArgumentNullException("predicate");
|
||||
|
||||
var retVal = 0;
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (predicate(item)) return retVal;
|
||||
retVal++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
///<summary>Finds the index of the first occurence of an item in an enumerable.</summary>
|
||||
///<param name="items">The enumerable to search.</param>
|
||||
///<param name="item">The item to find.</param>
|
||||
///<returns>The index of the first matching item, or -1 if the item was not found.</returns>
|
||||
public static int IndexOf<T>(this IEnumerable<T> items, T item) { return items.FindIndex(i => EqualityComparer<T>.Default.Equals(item, i)); }
|
||||
}
|
||||
}
|
||||
67
src/Umbraco.Core/IfExtensions.cs
Normal file
67
src/Umbraco.Core/IfExtensions.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for 'If' checking like checking If something is null or not null
|
||||
/// </summary>
|
||||
public static class IfExtensions
|
||||
{
|
||||
|
||||
/// <summary>The if not null.</summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="action">The action.</param>
|
||||
/// <typeparam name="TItem">The type</typeparam>
|
||||
public static void IfNotNull<TItem>(this TItem item, Action<TItem> action) where TItem : class
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
action(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The if true.</summary>
|
||||
/// <param name="predicate">The predicate.</param>
|
||||
/// <param name="action">The action.</param>
|
||||
public static void IfTrue(this bool predicate, Action action)
|
||||
{
|
||||
if (predicate)
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the item is not null, and if so returns an action on that item, or a default value
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">the result type</typeparam>
|
||||
/// <typeparam name="TItem">The type</typeparam>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="action">The action.</param>
|
||||
/// <param name="defaultValue">The default value.</param>
|
||||
/// <returns></returns>
|
||||
public static TResult IfNotNull<TResult, TItem>(this TItem item, Func<TItem, TResult> action, TResult defaultValue = default(TResult))
|
||||
where TItem : class
|
||||
{
|
||||
return item != null ? action(item) : defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the value is null, if it is it returns the value specified, otherwise returns the non-null value
|
||||
/// </summary>
|
||||
/// <typeparam name="TItem"></typeparam>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public static TItem IfNull<TItem>(this TItem item, Func<TItem, TItem> action)
|
||||
where TItem : class
|
||||
{
|
||||
return item ?? action(item);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
9
src/Umbraco.Core/StringAliasCaseType.cs
Normal file
9
src/Umbraco.Core/StringAliasCaseType.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public enum StringAliasCaseType
|
||||
{
|
||||
PascalCase,
|
||||
CamelCase,
|
||||
Unchanged
|
||||
}
|
||||
}
|
||||
606
src/Umbraco.Core/StringExtensions.cs
Normal file
606
src/Umbraco.Core/StringExtensions.cs
Normal file
@@ -0,0 +1,606 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
///<summary>
|
||||
/// String extension methods
|
||||
///</summary>
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Trims the specified value from a string; accepts a string input whereas the in-built implementation only accepts char or char[].
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="forRemoving">For removing.</param>
|
||||
/// <returns></returns>
|
||||
public static string Trim(this string value, string forRemoving)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) return value;
|
||||
return value.TrimEnd(forRemoving).TrimStart(forRemoving);
|
||||
}
|
||||
|
||||
public static string TrimEnd(this string value, string forRemoving)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) return value;
|
||||
while (value.EndsWith(forRemoving, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
value = value.Remove(value.LastIndexOf(forRemoving, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static string TrimStart(this string value, string forRemoving)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) return value;
|
||||
while (value.StartsWith(forRemoving, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
value = value.Substring(forRemoving.Length);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static string EnsureStartsWith(this string input, string toStartWith)
|
||||
{
|
||||
if (input.StartsWith(toStartWith)) return input;
|
||||
return toStartWith + input.TrimStart(toStartWith.ToArray()); // Ensure each char is removed first from input, e.g. ~/ plus /Path will equal ~/Path not ~//Path
|
||||
}
|
||||
|
||||
public static bool IsLowerCase(this char ch)
|
||||
{
|
||||
return ch.ToString(CultureInfo.InvariantCulture) == ch.ToString(CultureInfo.InvariantCulture).ToLower();
|
||||
}
|
||||
|
||||
/// <summary>Is null or white space.</summary>
|
||||
/// <param name="str">The str.</param>
|
||||
/// <returns>The is null or white space.</returns>
|
||||
public static bool IsNullOrWhiteSpace(this string str)
|
||||
{
|
||||
return (str == null) || (str.Trim().Length == 0);
|
||||
}
|
||||
|
||||
public static string IfNullOrWhiteSpace(this string str, string defaultValue)
|
||||
{
|
||||
return str.IsNullOrWhiteSpace() ? defaultValue : str;
|
||||
}
|
||||
|
||||
/// <summary>The to delimited list.</summary>
|
||||
/// <param name="list">The list.</param>
|
||||
/// <param name="delimiter">The delimiter.</param>
|
||||
/// <returns>the list</returns>
|
||||
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "By design")]
|
||||
public static IList<string> ToDelimitedList(this string list, string delimiter = ",")
|
||||
{
|
||||
var delimiters = new[] { delimiter };
|
||||
return !list.IsNullOrWhiteSpace()
|
||||
? list.Split(delimiters, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(i => i.Trim())
|
||||
.ToList()
|
||||
: new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>enum try parse.</summary>
|
||||
/// <param name="strType">The str type.</param>
|
||||
/// <param name="ignoreCase">The ignore case.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
/// <typeparam name="T">The type</typeparam>
|
||||
/// <returns>The enum try parse.</returns>
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "By Design")]
|
||||
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "By Design")]
|
||||
public static bool EnumTryParse<T>(this string strType, bool ignoreCase, out T result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = (T)Enum.Parse(typeof(T), strType, ignoreCase);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
result = default(T);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Strips all html from a string.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <returns>Returns the string without any html tags.</returns>
|
||||
public static string StripHtml(this string text)
|
||||
{
|
||||
const string pattern = @"<(.|\n)*?>";
|
||||
return Regex.Replace(text, pattern, String.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts string to a URL alias.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="charReplacements">The char replacements.</param>
|
||||
/// <param name="replaceDoubleDashes">if set to <c>true</c> replace double dashes.</param>
|
||||
/// <param name="stripNonAscii">if set to <c>true</c> strip non ASCII.</param>
|
||||
/// <param name="urlEncode">if set to <c>true</c> URL encode.</param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This ensures that ONLY ascii chars are allowed and of those ascii chars, only digits and lowercase chars, all
|
||||
/// punctuation, etc... are stripped out, however this method allows you to pass in string's to replace with the
|
||||
/// specified replacement character before the string is converted to ascii and it has invalid characters stripped out.
|
||||
/// This allows you to replace strings like & , etc.. with your replacement character before the automatic
|
||||
/// reduction.
|
||||
/// </remarks>
|
||||
public static string ToUrlAlias(this string value, IDictionary<string, string> charReplacements, bool replaceDoubleDashes, bool stripNonAscii, bool urlEncode)
|
||||
{
|
||||
//first to lower case
|
||||
value = value.ToLowerInvariant();
|
||||
|
||||
//then replacement chars
|
||||
value = charReplacements.Aggregate(value, (current, kvp) => current.Replace(kvp.Key, kvp.Value));
|
||||
|
||||
//then convert to only ascii, this will remove the rest of any invalid chars
|
||||
if (stripNonAscii)
|
||||
{
|
||||
value = Encoding.ASCII.GetString(
|
||||
Encoding.Convert(
|
||||
Encoding.UTF8,
|
||||
Encoding.GetEncoding(
|
||||
Encoding.ASCII.EncodingName,
|
||||
new EncoderReplacementFallback(String.Empty),
|
||||
new DecoderExceptionFallback()),
|
||||
Encoding.UTF8.GetBytes(value)));
|
||||
|
||||
//remove all characters that do not fall into the following categories (apart from the replacement val)
|
||||
var validCodeRanges =
|
||||
//digits
|
||||
Enumerable.Range(48, 10).Concat(
|
||||
//lowercase chars
|
||||
Enumerable.Range(97, 26));
|
||||
|
||||
var sb = new StringBuilder();
|
||||
foreach (var c in value.Where(c => charReplacements.Values.Contains(c.ToString()) || validCodeRanges.Contains(c)))
|
||||
{
|
||||
sb.Append(c);
|
||||
}
|
||||
|
||||
value = sb.ToString();
|
||||
}
|
||||
|
||||
//trim dashes from end
|
||||
value = value.Trim('-', '_');
|
||||
|
||||
//replace double occurances of - or _
|
||||
value = replaceDoubleDashes ? Regex.Replace(value, @"([-_]){2,}", "$1", RegexOptions.Compiled) : value;
|
||||
|
||||
//url encode result
|
||||
return urlEncode ? HttpUtility.UrlEncode(value) : value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string for use with an entity alias which is camel case and without invalid characters
|
||||
/// </summary>
|
||||
/// <param name="phrase">The phrase.</param>
|
||||
/// <param name="caseType">By default this is camel case</param>
|
||||
/// <param name="removeSpaces">if set to <c>true</c> [remove spaces].</param>
|
||||
/// <returns></returns>
|
||||
public static string ToUmbracoAlias(this string phrase, StringAliasCaseType caseType = StringAliasCaseType.CamelCase, bool removeSpaces = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(phrase)) return string.Empty;
|
||||
|
||||
//convert case first
|
||||
var tmp = phrase.ConvertCase(caseType);
|
||||
|
||||
//remove non-alphanumeric chars
|
||||
var result = Regex.Replace(tmp, @"[^a-zA-Z0-9\s\.-]+", "", RegexOptions.Compiled);
|
||||
|
||||
if (removeSpaces)
|
||||
result = result.Replace(" ", "");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the phrase to specified convention.
|
||||
/// </summary>
|
||||
/// <param name="phrase"></param>
|
||||
/// <param name="cases">The cases.</param>
|
||||
/// <returns>string</returns>
|
||||
public static string ConvertCase(this string phrase, StringAliasCaseType cases)
|
||||
{
|
||||
var splittedPhrase = Regex.Split(phrase, @"[^a-zA-Z0-9\']", RegexOptions.Compiled);
|
||||
|
||||
if (cases == StringAliasCaseType.Unchanged)
|
||||
return string.Join("", splittedPhrase);
|
||||
|
||||
//var splittedPhrase = phrase.Split(' ', '-', '.');
|
||||
var sb = new StringBuilder();
|
||||
|
||||
foreach (var splittedPhraseChars in splittedPhrase.Select(s => s.ToCharArray()))
|
||||
{
|
||||
if (splittedPhraseChars.Length > 0)
|
||||
{
|
||||
splittedPhraseChars[0] = ((new String(splittedPhraseChars[0], 1)).ToUpper().ToCharArray())[0];
|
||||
}
|
||||
sb.Append(new String(splittedPhraseChars));
|
||||
}
|
||||
|
||||
var result = sb.ToString();
|
||||
|
||||
if (cases == StringAliasCaseType.CamelCase)
|
||||
{
|
||||
if (result.Length > 1)
|
||||
{
|
||||
var pattern = new Regex("^([A-Z]*)([A-Z].*)$", RegexOptions.Singleline | RegexOptions.Compiled);
|
||||
var match = pattern.Match(result);
|
||||
if (match.Success)
|
||||
{
|
||||
result = match.Groups[1].Value.ToLower() + match.Groups[2].Value;
|
||||
|
||||
return result.Substring(0, 1).ToLower() + result.Substring(1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return result.ToLower();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes as GUID.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <returns></returns>
|
||||
public static Guid EncodeAsGuid(this string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input)) throw new ArgumentNullException("input");
|
||||
|
||||
var convertToHex = input.ConvertToHex();
|
||||
var hexLength = convertToHex.Length < 32 ? convertToHex.Length : 32;
|
||||
var hex = convertToHex.Substring(0, hexLength).PadLeft(32, '0');
|
||||
var output = Guid.Empty;
|
||||
return Guid.TryParse(hex, out output) ? output : Guid.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts to hex.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <returns></returns>
|
||||
public static string ConvertToHex(this string input)
|
||||
{
|
||||
if (String.IsNullOrEmpty(input)) return String.Empty;
|
||||
|
||||
var sb = new StringBuilder(input.Length);
|
||||
foreach (char c in input)
|
||||
{
|
||||
int tmp = c;
|
||||
sb.AppendFormat("{0:x2}", Convert.ToUInt32(c));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
///<summary>
|
||||
/// Encodes a string to a safe URL base64 string
|
||||
///</summary>
|
||||
///<param name="input"></param>
|
||||
///<returns></returns>
|
||||
public static string ToUrlBase64(this string input)
|
||||
{
|
||||
if (input == null) throw new ArgumentNullException("input");
|
||||
|
||||
if (String.IsNullOrEmpty(input)) return String.Empty;
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(input);
|
||||
return UrlTokenEncode(bytes);
|
||||
//return Convert.ToBase64String(bytes).Replace(".", "-").Replace("/", "_").Replace("=", ",");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a URL safe base64 string back
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public static string FromUrlBase64(this string input)
|
||||
{
|
||||
if (input == null) throw new ArgumentNullException("input");
|
||||
|
||||
//if (input.IsInvalidBase64()) return null;
|
||||
|
||||
try
|
||||
{
|
||||
//var decodedBytes = Convert.FromBase64String(input.Replace("-", ".").Replace("_", "/").Replace(",", "="));
|
||||
byte[] decodedBytes = UrlTokenDecode(input);
|
||||
return decodedBytes != null ? Encoding.UTF8.GetString(decodedBytes) : null;
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// formats the string with invariant culture
|
||||
/// </summary>
|
||||
/// <param name="format">The format.</param>
|
||||
/// <param name="args">The args.</param>
|
||||
/// <returns></returns>
|
||||
public static string InvariantFormat(this string format, params object[] args)
|
||||
{
|
||||
return String.Format(CultureInfo.InvariantCulture, format, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares 2 strings with invariant culture and case ignored
|
||||
/// </summary>
|
||||
/// <param name="compare">The compare.</param>
|
||||
/// <param name="compareTo">The compare to.</param>
|
||||
/// <returns></returns>
|
||||
public static bool InvariantEquals(this string compare, string compareTo)
|
||||
{
|
||||
return String.Equals(compare, compareTo, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool InvariantContains(this IEnumerable<string> compare, string compareTo)
|
||||
{
|
||||
return compare.Contains(compareTo, new DelegateEqualityComparer<string>((source, dest) => source.Equals(dest, StringComparison.InvariantCultureIgnoreCase), x => x.GetHashCode()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the string is a Guid
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <param name="withHyphens"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsGuid(this string str, bool withHyphens)
|
||||
{
|
||||
var isGuid = false;
|
||||
|
||||
if (!String.IsNullOrEmpty(str))
|
||||
{
|
||||
Regex guidRegEx;
|
||||
if (withHyphens)
|
||||
{
|
||||
guidRegEx = new Regex(@"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$");
|
||||
}
|
||||
else
|
||||
{
|
||||
guidRegEx = new Regex(@"^(\{{0,1}([0-9a-fA-F]){8}([0-9a-fA-F]){4}([0-9a-fA-F]){4}([0-9a-fA-F]){4}([0-9a-fA-F]){12}\}{0,1})$");
|
||||
}
|
||||
isGuid = guidRegEx.IsMatch(str);
|
||||
}
|
||||
|
||||
return isGuid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to parse a string into the supplied type by finding and using the Type's "Parse" method
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="val"></param>
|
||||
/// <returns></returns>
|
||||
public static T ParseInto<T>(this string val)
|
||||
{
|
||||
return (T)val.ParseInto(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to parse a string into the supplied type by finding and using the Type's "Parse" method
|
||||
/// </summary>
|
||||
/// <param name="val"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static object ParseInto(this string val, Type type)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(val))
|
||||
{
|
||||
TypeConverter tc = TypeDescriptor.GetConverter(type);
|
||||
return tc.ConvertFrom(val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the string to MD5
|
||||
/// </summary>
|
||||
/// <param name="stringToConvert">referrs to itself</param>
|
||||
/// <returns>the md5 hashed string</returns>
|
||||
public static string ToMd5(this string stringToConvert)
|
||||
{
|
||||
//create an instance of the MD5CryptoServiceProvider
|
||||
var md5Provider = new MD5CryptoServiceProvider();
|
||||
|
||||
//convert our string into byte array
|
||||
var byteArray = Encoding.UTF8.GetBytes(stringToConvert);
|
||||
|
||||
//get the hashed values created by our MD5CryptoServiceProvider
|
||||
var hashedByteArray = md5Provider.ComputeHash(byteArray);
|
||||
|
||||
//create a StringBuilder object
|
||||
var stringBuilder = new StringBuilder();
|
||||
|
||||
//loop to each each byte
|
||||
foreach (var b in hashedByteArray)
|
||||
{
|
||||
//append it to our StringBuilder
|
||||
stringBuilder.Append(b.ToString("x2").ToLower());
|
||||
}
|
||||
|
||||
//return the hashed value
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a string that was encoded with UrlTokenEncode
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
internal static byte[] UrlTokenDecode(string input)
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
throw new ArgumentNullException("input");
|
||||
}
|
||||
int length = input.Length;
|
||||
if (length < 1)
|
||||
{
|
||||
return new byte[0];
|
||||
}
|
||||
int num2 = input[length - 1] - '0';
|
||||
if ((num2 < 0) || (num2 > 10))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
char[] inArray = new char[(length - 1) + num2];
|
||||
for (int i = 0; i < (length - 1); i++)
|
||||
{
|
||||
char ch = input[i];
|
||||
switch (ch)
|
||||
{
|
||||
case '-':
|
||||
inArray[i] = '+';
|
||||
break;
|
||||
|
||||
case '_':
|
||||
inArray[i] = '/';
|
||||
break;
|
||||
|
||||
default:
|
||||
inArray[i] = ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int j = length - 1; j < inArray.Length; j++)
|
||||
{
|
||||
inArray[j] = '=';
|
||||
}
|
||||
return Convert.FromBase64CharArray(inArray, 0, inArray.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes a string so that it is 'safe' for URLs, files, etc..
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
internal static string UrlTokenEncode(byte[] input)
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
throw new ArgumentNullException("input");
|
||||
}
|
||||
if (input.Length < 1)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
string str = null;
|
||||
int index = 0;
|
||||
char[] chArray = null;
|
||||
str = Convert.ToBase64String(input);
|
||||
if (str == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
index = str.Length;
|
||||
while (index > 0)
|
||||
{
|
||||
if (str[index - 1] != '=')
|
||||
{
|
||||
break;
|
||||
}
|
||||
index--;
|
||||
}
|
||||
chArray = new char[index + 1];
|
||||
chArray[index] = (char)((0x30 + str.Length) - index);
|
||||
for (int i = 0; i < index; i++)
|
||||
{
|
||||
char ch = str[i];
|
||||
switch (ch)
|
||||
{
|
||||
case '+':
|
||||
chArray[i] = '-';
|
||||
break;
|
||||
|
||||
case '/':
|
||||
chArray[i] = '_';
|
||||
break;
|
||||
|
||||
case '=':
|
||||
chArray[i] = ch;
|
||||
break;
|
||||
|
||||
default:
|
||||
chArray[i] = ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new string(chArray);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the folder path endds with a DirectorySeperatorChar
|
||||
/// </summary>
|
||||
/// <param name="currentFolder"></param>
|
||||
/// <returns></returns>
|
||||
public static string NormaliseDirectoryPath(this string currentFolder)
|
||||
{
|
||||
currentFolder = currentFolder
|
||||
.IfNull(x => String.Empty)
|
||||
.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
|
||||
return currentFolder;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Truncates the specified text string.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="maxLength">Length of the max.</param>
|
||||
/// <param name="suffix">The suffix.</param>
|
||||
/// <returns></returns>
|
||||
public static string Truncate(this string text, int maxLength, string suffix = "...")
|
||||
{
|
||||
// replaces the truncated string to a ...
|
||||
var truncatedString = text;
|
||||
|
||||
if (maxLength <= 0) return truncatedString;
|
||||
var strLength = maxLength - suffix.Length;
|
||||
|
||||
if (strLength <= 0) return truncatedString;
|
||||
|
||||
if (text == null || text.Length <= maxLength) return truncatedString;
|
||||
|
||||
truncatedString = text.Substring(0, strLength);
|
||||
truncatedString = truncatedString.TrimEnd();
|
||||
truncatedString += suffix;
|
||||
|
||||
return truncatedString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Strips carrage returns and line feeds from the specified text.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <returns></returns>
|
||||
public static string StripNewLines(this string input)
|
||||
{
|
||||
return input.Replace("\r", "").Replace("\n", "");
|
||||
}
|
||||
|
||||
public static string OrIfNullOrWhiteSpace(this string input, string alternative)
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(input)
|
||||
? input
|
||||
: alternative;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
@@ -43,7 +44,13 @@
|
||||
<Compile Include="..\SolutionInfo.cs">
|
||||
<Link>Properties\SolutionInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="DelegateEqualityComparer.cs" />
|
||||
<Compile Include="EnumerableExtensions.cs" />
|
||||
<Compile Include="IfExtensions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="StringAliasCaseType.cs" />
|
||||
<Compile Include="StringExtensions.cs" />
|
||||
<Compile Include="WriteLock.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
||||
34
src/Umbraco.Core/WriteLock.cs
Normal file
34
src/Umbraco.Core/WriteLock.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a convenience methodology for implementing locked access to resources.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Intended as an infrastructure class.
|
||||
/// </remarks>
|
||||
public class WriteLock : IDisposable
|
||||
{
|
||||
private readonly ReaderWriterLockSlim _rwLock;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WriteLock"/> class.
|
||||
/// </summary>
|
||||
/// <param name="rwLock">The rw lock.</param>
|
||||
public WriteLock(ReaderWriterLockSlim rwLock)
|
||||
{
|
||||
_rwLock = rwLock;
|
||||
_rwLock.EnterWriteLock();
|
||||
}
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
_rwLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/Umbraco.Tests/EnumerableExtensionsTests.cs
Normal file
60
src/Umbraco.Tests/EnumerableExtensionsTests.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class EnumerableExtensionsTests
|
||||
{
|
||||
[Test]
|
||||
public void InGroupsOf_ReturnsAllElements()
|
||||
{
|
||||
var integers = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
|
||||
var groupsOfTwo = integers.InGroupsOf(2).ToArray();
|
||||
|
||||
var flattened = groupsOfTwo.SelectMany(x => x).ToArray();
|
||||
|
||||
Assert.That(groupsOfTwo.Length, Is.EqualTo(5));
|
||||
Assert.That(flattened.Length, Is.EqualTo(integers.Length));
|
||||
CollectionAssert.AreEquivalent(integers, flattened);
|
||||
|
||||
var groupsOfMassive = integers.InGroupsOf(100).ToArray();
|
||||
Assert.That(groupsOfMassive.Length, Is.EqualTo(1));
|
||||
flattened = groupsOfMassive.SelectMany(x => x).ToArray();
|
||||
Assert.That(flattened.Length, Is.EqualTo(integers.Length));
|
||||
CollectionAssert.AreEquivalent(integers, flattened);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void DistinctBy_ReturnsDistinctElements_AndResetsIteratorCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var tuple1 = new Tuple<string, string>("fruit", "apple");
|
||||
var tuple2 = new Tuple<string, string>("fruit", "orange");
|
||||
var tuple3 = new Tuple<string, string>("fruit", "banana");
|
||||
var tuple4 = new Tuple<string, string>("fruit", "banana"); // Should be filtered out
|
||||
var list = new List<Tuple<string, string>>()
|
||||
{
|
||||
tuple1,
|
||||
tuple2,
|
||||
tuple3,
|
||||
tuple4
|
||||
};
|
||||
|
||||
// Act
|
||||
var iteratorSource = list.DistinctBy(x => x.Item2);
|
||||
|
||||
// Assert
|
||||
// First check distinction
|
||||
Assert.AreEqual(3, iteratorSource.Count());
|
||||
|
||||
// Check for iterator block mistakes - reset to original query first
|
||||
iteratorSource = list.DistinctBy(x => x.Item2);
|
||||
Assert.AreEqual(iteratorSource.Count(), iteratorSource.ToList().Count());
|
||||
}
|
||||
}
|
||||
}
|
||||
133
src/Umbraco.Tests/StringExtensionsTests.cs
Normal file
133
src/Umbraco.Tests/StringExtensionsTests.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class StringExtensionsTests
|
||||
{
|
||||
[TestCase("Hello this is my string", " string", "Hello this is my")]
|
||||
[TestCase("Hello this is my string strung", " string", "Hello this is my string strung")]
|
||||
[TestCase("Hello this is my string string", " string", "Hello this is my")]
|
||||
[TestCase("Hello this is my string string", "g", "Hello this is my string strin")]
|
||||
[TestCase("Hello this is my string string", "ello this is my string string", "H")]
|
||||
[TestCase("Hello this is my string string", "Hello this is my string string", "")]
|
||||
public void TrimEnd(string input, string forTrimming, string shouldBe)
|
||||
{
|
||||
var trimmed = input.TrimEnd(forTrimming);
|
||||
Assert.AreEqual(shouldBe, trimmed);
|
||||
}
|
||||
|
||||
[TestCase("Hello this is my string", "hello", " this is my string")]
|
||||
[TestCase("Hello this is my string", "Hello this", " is my string")]
|
||||
[TestCase("Hello this is my string", "Hello this is my ", "string")]
|
||||
[TestCase("Hello this is my string", "Hello this is my string", "")]
|
||||
public void TrimStart(string input, string forTrimming, string shouldBe)
|
||||
{
|
||||
var trimmed = input.TrimStart(forTrimming);
|
||||
Assert.AreEqual(shouldBe, trimmed);
|
||||
}
|
||||
|
||||
|
||||
[TestCase]
|
||||
public void StringExtensions_To_Url_Alias()
|
||||
{
|
||||
var replacements = new Dictionary<string, string>
|
||||
{
|
||||
{" ", "-"},
|
||||
{"\"", ""},
|
||||
{""", ""},
|
||||
{"@", ""},
|
||||
{"%", ""},
|
||||
{".", ""},
|
||||
{";", ""},
|
||||
{"/", ""},
|
||||
{":", ""},
|
||||
{"#", ""},
|
||||
{"+", ""},
|
||||
{"*", ""},
|
||||
{"&", ""},
|
||||
{"?", ""}
|
||||
};
|
||||
|
||||
var name1 = "Home Page";
|
||||
var name2 = "Shannon's Home Page!";
|
||||
var name3 = "#Someones's Twitter $h1z%n";
|
||||
var name4 = "Räksmörgås";
|
||||
var name5 = "'em guys-over there, are#goin' a \"little\"bit crazy eh!! :)";
|
||||
var name6 = "汉#字*/漢?字";
|
||||
|
||||
var url1 = name1.ToUrlAlias(replacements, true, true, false);
|
||||
var url2 = name2.ToUrlAlias(replacements, true, true, false);
|
||||
var url3 = name3.ToUrlAlias(replacements, true, true, false);
|
||||
var url4 = name4.ToUrlAlias(replacements, true, true, false);
|
||||
var url5 = name5.ToUrlAlias(replacements, true, true, false);
|
||||
var url6 = name6.ToUrlAlias(replacements, true, true, false);
|
||||
var url7 = name6.ToUrlAlias(replacements, true, false, false);
|
||||
var url8 = name6.ToUrlAlias(replacements, true, false, true);
|
||||
|
||||
Assert.AreEqual("home-page", url1);
|
||||
Assert.AreEqual("shannons-home-page", url2);
|
||||
Assert.AreEqual("someoness-twitter-h1zn", url3);
|
||||
Assert.AreEqual("rksmrgs", url4);
|
||||
Assert.AreEqual("em-guys-over-there-aregoin-a-littlebit-crazy-eh", url5);
|
||||
Assert.AreEqual("", url6);
|
||||
Assert.AreEqual("汉字漢字", url7);
|
||||
Assert.AreEqual("%e6%b1%89%e5%ad%97%e6%bc%a2%e5%ad%97", url8);
|
||||
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void StringExtensions_To_Camel_Case()
|
||||
{
|
||||
//Arrange
|
||||
|
||||
var name1 = "Tab 1";
|
||||
var name2 = "Home - Page";
|
||||
var name3 = "Shannon's document type";
|
||||
|
||||
//Act
|
||||
|
||||
var camelCase1 = name1.ConvertCase(StringAliasCaseType.CamelCase);
|
||||
var camelCase2 = name2.ConvertCase(StringAliasCaseType.CamelCase);
|
||||
var camelCase3 = name3.ConvertCase(StringAliasCaseType.CamelCase);
|
||||
|
||||
//Assert
|
||||
|
||||
Assert.AreEqual("tab1", camelCase1);
|
||||
Assert.AreEqual("homePage", camelCase2);
|
||||
Assert.AreEqual("shannon'sDocumentType", camelCase3);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void StringExtensions_To_Entity_Alias()
|
||||
{
|
||||
//Arrange
|
||||
|
||||
var name1 = "Tab 1";
|
||||
var name2 = "Home - Page";
|
||||
var name3 = "Shannon's Document Type";
|
||||
var name4 = "!BADDLY nam-ed Document Type";
|
||||
var name5 = "i %Want!thisTo end up In Proper@case";
|
||||
|
||||
//Act
|
||||
|
||||
var alias1 = name1.ToUmbracoAlias();
|
||||
var alias2 = name2.ToUmbracoAlias();
|
||||
var alias3 = name3.ToUmbracoAlias();
|
||||
var alias4 = name4.ToUmbracoAlias();
|
||||
var alias5 = name5.ToUmbracoAlias(StringAliasCaseType.PascalCase);
|
||||
|
||||
//Assert
|
||||
|
||||
Assert.AreEqual("tab1", alias1);
|
||||
Assert.AreEqual("homePage", alias2);
|
||||
Assert.AreEqual("shannonsDocumentType", alias3);
|
||||
Assert.AreEqual("baddlyNamEdDocumentType", alias4);
|
||||
Assert.AreEqual("IWantThisToEndUpInProperCase", alias5);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,9 @@
|
||||
<Compile Include="BusinessLogic\ApplicationTest.cs" />
|
||||
<Compile Include="BusinessLogic\ApplicationTreeTest.cs" />
|
||||
<Compile Include="BusinessLogic\BaseTest.cs" />
|
||||
<Compile Include="EnumerableExtensionsTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="StringExtensionsTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
@@ -61,6 +63,10 @@
|
||||
<Project>{E469A9CE-1BEC-423F-AC44-713CD72457EA}</Project>
|
||||
<Name>umbraco.businesslogic</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Umbraco.Core\Umbraco.Core.csproj">
|
||||
<Project>{31785BC3-256C-4613-B2F5-A1B0BDDED8C1}</Project>
|
||||
<Name>Umbraco.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\umbraco.datalayer\umbraco.datalayer.csproj">
|
||||
<Project>{C7CB79F0-1C97-4B33-BFA7-00731B579AE2}</Project>
|
||||
<Name>umbraco.datalayer</Name>
|
||||
|
||||
31
src/Umbraco.Web/ControllerExtensions.cs
Normal file
31
src/Umbraco.Web/ControllerExtensions.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
public static class ControllerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Return the controller name from the controller type
|
||||
/// </summary>
|
||||
/// <param name="controllerType"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetControllerName(Type controllerType)
|
||||
{
|
||||
return controllerType.Name.Substring(0, controllerType.Name.LastIndexOf("Controller"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the controller name from the controller type
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
/// <remarks></remarks>
|
||||
public static string GetControllerName<T>()
|
||||
{
|
||||
return GetControllerName(typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@
|
||||
<Compile Include="..\SolutionInfo.cs">
|
||||
<Link>Properties\SolutionInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="ControllerExtensions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
||||
Reference in New Issue
Block a user