using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Sorting {
///
/// Class BaseItemComparer
///
public class BaseItemComparer : IComparer {
///
/// The _order
///
private readonly SortOrder _order;
///
/// The _property name
///
private readonly string _propertyName;
///
/// The _compare culture
///
private readonly StringComparison _compareCulture = StringComparison.CurrentCultureIgnoreCase;
///
/// Gets or sets the logger.
///
/// The logger.
private ILogger Logger { get; set; }
///
/// Initializes a new instance of the class.
///
/// The order.
/// The logger.
public BaseItemComparer(SortOrder order, ILogger logger) {
_order = order;
Logger = logger;
}
///
/// Initializes a new instance of the class.
///
/// The order.
/// The compare.
/// The logger.
public BaseItemComparer(SortOrder order, StringComparison compare, ILogger logger)
{
_order = order;
_compareCulture = compare;
Logger = logger;
}
///
/// Initializes a new instance of the class.
///
/// The property.
/// The logger.
public BaseItemComparer(string property, ILogger logger)
{
_order = SortOrder.Custom;
_propertyName = property;
Logger = logger;
}
///
/// Initializes a new instance of the class.
///
/// The property.
/// The compare.
/// The logger.
public BaseItemComparer(string property, StringComparison compare, ILogger logger)
{
_order = SortOrder.Custom;
_propertyName = property;
_compareCulture = compare;
Logger = logger;
}
#region IComparer Members
///
/// Compares the specified x.
///
/// The x.
/// The y.
/// System.Int32.
public int Compare(BaseItem x, BaseItem y) {
int compare = 0;
switch (_order) {
case SortOrder.Date:
compare = -x.DateCreated.CompareTo(y.DateCreated);
break;
case SortOrder.Year:
var xProductionYear = x.ProductionYear ?? 0;
var yProductionYear = y.ProductionYear ?? 0;
compare = yProductionYear.CompareTo(xProductionYear);
break;
case SortOrder.Rating:
var xRating = x.CommunityRating ?? 0;
var yRating = y.CommunityRating ?? 0;
compare = yRating.CompareTo(xRating);
break;
case SortOrder.Runtime:
var xRuntime = x.RunTimeTicks ?? 0;
var yRuntime = y.RunTimeTicks ?? 0;
compare = xRuntime.CompareTo(yRuntime);
break;
case SortOrder.Custom:
Logger.Debug("Sorting on custom field " + _propertyName);
var yProp = y.GetType().GetProperty(_propertyName);
var xProp = x.GetType().GetProperty(_propertyName);
if (yProp == null || xProp == null) break;
var yVal = yProp.GetValue(y, null);
var xVal = xProp.GetValue(x,null);
if (yVal == null && xVal == null) break;
if (yVal == null) return 1;
if (xVal == null) return -1;
compare = String.Compare(xVal.ToString(), yVal.ToString(),_compareCulture);
break;
default:
compare = 0;
break;
}
if (compare == 0) {
var name1 = x.SortName ?? x.Name ?? "";
var name2 = y.SortName ?? y.Name ?? "";
//if (Config.Instance.EnableAlphanumericSorting)
compare = AlphaNumericCompare(name1, name2,_compareCulture);
//else
// compare = String.Compare(name1,name2,_compareCulture);
}
return compare;
}
#endregion
///
/// Alphas the numeric compare.
///
/// The s1.
/// The s2.
/// The compare culture.
/// System.Int32.
private int AlphaNumericCompare(string s1, string s2, StringComparison compareCulture) {
// http://dotnetperls.com/Content/Alphanumeric-Sorting.aspx
int len1 = s1.Length;
int len2 = s2.Length;
int marker1 = 0;
int marker2 = 0;
// Walk through two the strings with two markers.
while (marker1 < len1 && marker2 < len2) {
char ch1 = s1[marker1];
char ch2 = s2[marker2];
// Some buffers we can build up characters in for each chunk.
var space1 = new char[len1];
var loc1 = 0;
var space2 = new char[len2];
var loc2 = 0;
// Walk through all following characters that are digits or
// characters in BOTH strings starting at the appropriate marker.
// Collect char arrays.
do {
space1[loc1++] = ch1;
marker1++;
if (marker1 < len1) {
ch1 = s1[marker1];
} else {
break;
}
} while (char.IsDigit(ch1) == char.IsDigit(space1[0]));
do {
space2[loc2++] = ch2;
marker2++;
if (marker2 < len2) {
ch2 = s2[marker2];
} else {
break;
}
} while (char.IsDigit(ch2) == char.IsDigit(space2[0]));
// If we have collected numbers, compare them numerically.
// Otherwise, if we have strings, compare them alphabetically.
var str1 = new string(space1);
var str2 = new string(space2);
var result = 0;
//biggest int - 2147483647
if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]) /*&& str1.Length < 10 && str2.Length < 10*/) //this assumed the entire string was a number...
{
int thisNumericChunk;
var isValid = false;
if (int.TryParse(str1.Substring(0, str1.Length > 9 ? 10 : str1.Length), out thisNumericChunk))
{
int thatNumericChunk;
if (int.TryParse(str2.Substring(0, str2.Length > 9 ? 10 : str2.Length), out thatNumericChunk))
{
isValid = true;
result = thisNumericChunk.CompareTo(thatNumericChunk);
}
}
if (!isValid)
{
Logger.Error("Error comparing numeric strings: " + str1 + "/" + str2);
result = String.Compare(str1, str2, compareCulture);
}
} else {
result = String.Compare(str1,str2,compareCulture);
}
if (result != 0) {
return result;
}
}
return len1 - len2;
}
}
}