BaseItemComparer.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. using MediaBrowser.Controller.Entities;
  2. using MediaBrowser.Model.Logging;
  3. using System;
  4. using System.Collections.Generic;
  5. namespace MediaBrowser.Controller.Sorting {
  6. /// <summary>
  7. /// Class BaseItemComparer
  8. /// </summary>
  9. public class BaseItemComparer : IComparer<BaseItem> {
  10. /// <summary>
  11. /// The _order
  12. /// </summary>
  13. private readonly SortOrder _order;
  14. /// <summary>
  15. /// The _property name
  16. /// </summary>
  17. private readonly string _propertyName;
  18. /// <summary>
  19. /// The _compare culture
  20. /// </summary>
  21. private readonly StringComparison _compareCulture = StringComparison.CurrentCultureIgnoreCase;
  22. /// <summary>
  23. /// Gets or sets the logger.
  24. /// </summary>
  25. /// <value>The logger.</value>
  26. private ILogger Logger { get; set; }
  27. /// <summary>
  28. /// Initializes a new instance of the <see cref="BaseItemComparer" /> class.
  29. /// </summary>
  30. /// <param name="order">The order.</param>
  31. /// <param name="logger">The logger.</param>
  32. public BaseItemComparer(SortOrder order, ILogger logger) {
  33. _order = order;
  34. Logger = logger;
  35. }
  36. /// <summary>
  37. /// Initializes a new instance of the <see cref="BaseItemComparer" /> class.
  38. /// </summary>
  39. /// <param name="order">The order.</param>
  40. /// <param name="compare">The compare.</param>
  41. /// <param name="logger">The logger.</param>
  42. public BaseItemComparer(SortOrder order, StringComparison compare, ILogger logger)
  43. {
  44. _order = order;
  45. _compareCulture = compare;
  46. Logger = logger;
  47. }
  48. /// <summary>
  49. /// Initializes a new instance of the <see cref="BaseItemComparer" /> class.
  50. /// </summary>
  51. /// <param name="property">The property.</param>
  52. /// <param name="logger">The logger.</param>
  53. public BaseItemComparer(string property, ILogger logger)
  54. {
  55. _order = SortOrder.Custom;
  56. _propertyName = property;
  57. Logger = logger;
  58. }
  59. /// <summary>
  60. /// Initializes a new instance of the <see cref="BaseItemComparer" /> class.
  61. /// </summary>
  62. /// <param name="property">The property.</param>
  63. /// <param name="compare">The compare.</param>
  64. /// <param name="logger">The logger.</param>
  65. public BaseItemComparer(string property, StringComparison compare, ILogger logger)
  66. {
  67. _order = SortOrder.Custom;
  68. _propertyName = property;
  69. _compareCulture = compare;
  70. Logger = logger;
  71. }
  72. #region IComparer<BaseItem> Members
  73. /// <summary>
  74. /// Compares the specified x.
  75. /// </summary>
  76. /// <param name="x">The x.</param>
  77. /// <param name="y">The y.</param>
  78. /// <returns>System.Int32.</returns>
  79. public int Compare(BaseItem x, BaseItem y) {
  80. int compare = 0;
  81. switch (_order) {
  82. case SortOrder.Date:
  83. compare = -x.DateCreated.CompareTo(y.DateCreated);
  84. break;
  85. case SortOrder.Year:
  86. var xProductionYear = x.ProductionYear ?? 0;
  87. var yProductionYear = y.ProductionYear ?? 0;
  88. compare = yProductionYear.CompareTo(xProductionYear);
  89. break;
  90. case SortOrder.Rating:
  91. var xRating = x.CommunityRating ?? 0;
  92. var yRating = y.CommunityRating ?? 0;
  93. compare = yRating.CompareTo(xRating);
  94. break;
  95. case SortOrder.Runtime:
  96. var xRuntime = x.RunTimeTicks ?? 0;
  97. var yRuntime = y.RunTimeTicks ?? 0;
  98. compare = xRuntime.CompareTo(yRuntime);
  99. break;
  100. case SortOrder.Custom:
  101. Logger.Debug("Sorting on custom field " + _propertyName);
  102. var yProp = y.GetType().GetProperty(_propertyName);
  103. var xProp = x.GetType().GetProperty(_propertyName);
  104. if (yProp == null || xProp == null) break;
  105. var yVal = yProp.GetValue(y, null);
  106. var xVal = xProp.GetValue(x,null);
  107. if (yVal == null && xVal == null) break;
  108. if (yVal == null) return 1;
  109. if (xVal == null) return -1;
  110. compare = String.Compare(xVal.ToString(), yVal.ToString(),_compareCulture);
  111. break;
  112. default:
  113. compare = 0;
  114. break;
  115. }
  116. if (compare == 0) {
  117. var name1 = x.SortName ?? x.Name ?? "";
  118. var name2 = y.SortName ?? y.Name ?? "";
  119. //if (Config.Instance.EnableAlphanumericSorting)
  120. compare = AlphaNumericCompare(name1, name2,_compareCulture);
  121. //else
  122. // compare = String.Compare(name1,name2,_compareCulture);
  123. }
  124. return compare;
  125. }
  126. #endregion
  127. /// <summary>
  128. /// Alphas the numeric compare.
  129. /// </summary>
  130. /// <param name="s1">The s1.</param>
  131. /// <param name="s2">The s2.</param>
  132. /// <param name="compareCulture">The compare culture.</param>
  133. /// <returns>System.Int32.</returns>
  134. private int AlphaNumericCompare(string s1, string s2, StringComparison compareCulture) {
  135. // http://dotnetperls.com/Content/Alphanumeric-Sorting.aspx
  136. int len1 = s1.Length;
  137. int len2 = s2.Length;
  138. int marker1 = 0;
  139. int marker2 = 0;
  140. // Walk through two the strings with two markers.
  141. while (marker1 < len1 && marker2 < len2) {
  142. char ch1 = s1[marker1];
  143. char ch2 = s2[marker2];
  144. // Some buffers we can build up characters in for each chunk.
  145. var space1 = new char[len1];
  146. var loc1 = 0;
  147. var space2 = new char[len2];
  148. var loc2 = 0;
  149. // Walk through all following characters that are digits or
  150. // characters in BOTH strings starting at the appropriate marker.
  151. // Collect char arrays.
  152. do {
  153. space1[loc1++] = ch1;
  154. marker1++;
  155. if (marker1 < len1) {
  156. ch1 = s1[marker1];
  157. } else {
  158. break;
  159. }
  160. } while (char.IsDigit(ch1) == char.IsDigit(space1[0]));
  161. do {
  162. space2[loc2++] = ch2;
  163. marker2++;
  164. if (marker2 < len2) {
  165. ch2 = s2[marker2];
  166. } else {
  167. break;
  168. }
  169. } while (char.IsDigit(ch2) == char.IsDigit(space2[0]));
  170. // If we have collected numbers, compare them numerically.
  171. // Otherwise, if we have strings, compare them alphabetically.
  172. var str1 = new string(space1);
  173. var str2 = new string(space2);
  174. var result = 0;
  175. //biggest int - 2147483647
  176. if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]) /*&& str1.Length < 10 && str2.Length < 10*/) //this assumed the entire string was a number...
  177. {
  178. int thisNumericChunk;
  179. var isValid = false;
  180. if (int.TryParse(str1.Substring(0, str1.Length > 9 ? 10 : str1.Length), out thisNumericChunk))
  181. {
  182. int thatNumericChunk;
  183. if (int.TryParse(str2.Substring(0, str2.Length > 9 ? 10 : str2.Length), out thatNumericChunk))
  184. {
  185. isValid = true;
  186. result = thisNumericChunk.CompareTo(thatNumericChunk);
  187. }
  188. }
  189. if (!isValid)
  190. {
  191. Logger.Error("Error comparing numeric strings: " + str1 + "/" + str2);
  192. result = String.Compare(str1, str2, compareCulture);
  193. }
  194. } else {
  195. result = String.Compare(str1,str2,compareCulture);
  196. }
  197. if (result != 0) {
  198. return result;
  199. }
  200. }
  201. return len1 - len2;
  202. }
  203. }
  204. }