bfr/BFR/Operations/Sort.cs

132 lines
5.9 KiB
C#
Raw Normal View History

2019-11-23 17:32:10 +00:00
using System;
using System.Linq;
using System.Globalization;
using System.Collections.Generic;
using BFR.Helpers;
using BFR.DataModels;
using Avalonia;
namespace BFR.Operations
{
public class Sort : Operation
{
public override string Name => nameof(Sort);
public override string Description => "Sorts the file names according to how they are when they arrive at this operation.";
public static readonly AvaloniaProperty<SortMode> ModeProperty =
AvaloniaProperty.Register<MainWindow, SortMode>(nameof(Mode), SortMode.Normal);
public SortMode Mode { get => GetValue(ModeProperty); set => SetValue(ModeProperty, value); }
public SortDirection Direction { get; set; }
public SortOperationMode[] Modes => SortOperationMode.Modes;
public string[] Directions => Enum.GetNames(typeof(SortDirection));
public bool FullName { get; set; } = false;
private Func<FileModel, string> GetName => FullName
? (Func<FileModel, string>)(x => x.FullName)
: (Func<FileModel, string>)(x => x.Name);
protected override void ApplyToInternal(IList<FileModel> files)
{
// Fail conditions: No fail conditions.
// Apply operation
files.ReplaceAll(new List<FileModel> (Mode switch
{
SortMode.Normal => files.OrderBy(GetName, StringComparer.CurrentCulture),
SortMode.Natural => files.OrderBy(GetName, Comparer<string>.Create(CompareNatural)),
SortMode.Ordinal => files.OrderBy(GetName, StringComparer.Ordinal),
SortMode.Length => files.OrderBy(x => GetName(x).Length),
SortMode.Reverse => files.Reverse(),
_ => files
}));
if (Mode != SortMode.Reverse && Direction == SortDirection.Descending)
files.ReplaceAll(new List<FileModel>(files.Reverse()));
}
// Using code from stackoverflow
// https://stackoverflow.com/questions/248603/natural-sort-order-in-c-sharp
2019-11-23 17:32:10 +00:00
public static int CompareNatural(string strA, string strB) =>
CompareNatural(strA, strB, CultureInfo.CurrentCulture, CompareOptions.IgnoreCase);
public static int CompareNatural(string strA, string strB, CultureInfo culture, CompareOptions options)
{
var cmp = culture.CompareInfo;
var (iA, iB, softResult, softResultWeight) = (0, 0, 0, 0);
2019-11-23 17:32:10 +00:00
while (iA < strA.Length && iB < strB.Length)
{
var isDigitA = char.IsDigit(strA[iA]);
var isDigitB = char.IsDigit(strB[iB]);
if (isDigitA != isDigitB)
return cmp.Compare(strA, iA, strB, iB, options);
else if (!isDigitA && !isDigitB)
{
var (jA, jB) = (iA + 1, iB + 1);
2019-11-23 17:32:10 +00:00
while (jA < strA.Length && !char.IsDigit(strA[jA])) jA++;
while (jB < strB.Length && !char.IsDigit(strB[jB])) jB++;
var cmpResult = cmp.Compare(strA, iA, jA - iA, strB, iB, jB - iB, options);
if (cmpResult != 0)
{
var (secA, secB) = (strA[iA..jA], strB[iB..jB]);
if (cmp.Compare(secA + "1", secB + "2", options) ==
cmp.Compare(secA + "2", secB + "1", options))
return cmp.Compare(strA, iA, strB, iB, options);
else if (softResultWeight < 1)
{
softResult = cmpResult;
softResultWeight = 1;
}
}
iA = jA;
iB = jB;
2019-11-23 17:32:10 +00:00
}
else
{
var zeroA = (char)(strA[iA] - (int)char.GetNumericValue(strA[iA]));
var zeroB = (char)(strB[iB] - (int)char.GetNumericValue(strB[iB]));
var (jA, jB) = (iA, iB);
while (jA < strA.Length && strA[jA] == zeroA) jA++;
while (jB < strB.Length && strB[jB] == zeroB) jB++;
var resultIfSameLength = 0;
2019-11-23 17:32:10 +00:00
do
{
isDigitA = jA < strA.Length && char.IsDigit(strA[jA]);
isDigitB = jB < strB.Length && char.IsDigit(strB[jB]);
var numA = isDigitA ? (int)char.GetNumericValue(strA[jA]) : 0;
var numB = isDigitB ? (int)char.GetNumericValue(strB[jB]) : 0;
if (isDigitA && (char)(strA[jA] - numA) != zeroA) isDigitA = false;
if (isDigitB && (char)(strB[jB] - numB) != zeroB) isDigitB = false;
if (isDigitA && isDigitB)
{
if (numA != numB && resultIfSameLength == 0)
resultIfSameLength = numA < numB ? -1 : 1;
jA++;
jB++;
}
} while (isDigitA && isDigitB);
2019-11-23 17:32:10 +00:00
if (isDigitA != isDigitB)
return isDigitA ? 1 : -1;
else if (resultIfSameLength != 0)
return resultIfSameLength;
var (lA, lB) = (jA - iA, jB - iB);
if (lA != lB)
return lA > lB ? -1 : 1;
else if (zeroA != zeroB && softResultWeight < 2)
{
softResult = cmp.Compare(strA, iA, 1, strB, iB, 1, options);
softResultWeight = 2;
}
(iA, iB) = (jA, jB);
}
}
if (iA < strA.Length || iB < strB.Length)
return iA < strA.Length ? 1 : -1;
else if (softResult != 0)
return softResult;
return 0;
}
}
}