2020-07-15 09:41:59 -07:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
|
2020-12-07 14:29:45 -08:00
|
|
|
|
using SabreTools.Logging;
|
2020-07-15 09:41:59 -07:00
|
|
|
|
|
2020-12-07 15:08:57 -08:00
|
|
|
|
namespace SabreTools.IO
|
2020-07-15 09:41:59 -07:00
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Extensions to Directory functionality
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static class DirectoryExtensions
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Cleans out the temporary directory
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="dir">Name of the directory to clean out</param>
|
|
|
|
|
|
public static void Clean(string dir)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (string file in Directory.EnumerateFiles(dir, "*", SearchOption.TopDirectoryOnly))
|
|
|
|
|
|
{
|
2020-12-07 15:08:57 -08:00
|
|
|
|
FileTryDelete(file);
|
2020-07-15 09:41:59 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach (string subdir in Directory.EnumerateDirectories(dir, "*", SearchOption.TopDirectoryOnly))
|
|
|
|
|
|
{
|
|
|
|
|
|
TryDelete(subdir);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Ensure the output directory is a proper format and can be created
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="dir">Directory to check</param>
|
|
|
|
|
|
/// <param name="create">True if the directory should be created, false otherwise (default)</param>
|
|
|
|
|
|
/// <param name="temp">True if this is a temp directory, false otherwise</param>
|
|
|
|
|
|
/// <returns>Full path to the directory</returns>
|
|
|
|
|
|
public static string Ensure(string dir, bool create = false, bool temp = false)
|
|
|
|
|
|
{
|
|
|
|
|
|
// If the output directory is invalid
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(dir))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (temp)
|
|
|
|
|
|
dir = Path.GetTempPath();
|
|
|
|
|
|
else
|
|
|
|
|
|
dir = Environment.CurrentDirectory;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Get the full path for the output directory
|
|
|
|
|
|
dir = Path.GetFullPath(dir);
|
|
|
|
|
|
|
|
|
|
|
|
// If we're creating the output folder, do so
|
|
|
|
|
|
if (create)
|
|
|
|
|
|
Directory.CreateDirectory(dir);
|
|
|
|
|
|
|
|
|
|
|
|
return dir;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Retrieve a list of just directories from inputs
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="inputs">List of strings representing directories and files</param>
|
2020-07-26 23:39:33 -07:00
|
|
|
|
/// <param name="appendparent">True if the parent name should be included in the ParentablePath, false otherwise (default)</param>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// <returns>List of strings representing just directories from the inputs</returns>
|
2020-07-26 23:39:33 -07:00
|
|
|
|
public static List<ParentablePath> GetDirectoriesOnly(List<string> inputs, bool appendparent = false)
|
2020-07-15 09:41:59 -07:00
|
|
|
|
{
|
2020-07-26 23:39:33 -07:00
|
|
|
|
List<ParentablePath> outputs = new List<ParentablePath>();
|
2020-08-08 21:53:34 -07:00
|
|
|
|
for (int i = 0; i < inputs.Count; i++)
|
2020-07-15 09:41:59 -07:00
|
|
|
|
{
|
2020-08-08 21:53:34 -07:00
|
|
|
|
string input = inputs[i];
|
|
|
|
|
|
|
2020-08-17 14:10:53 -07:00
|
|
|
|
// If we have a null or empty path
|
|
|
|
|
|
if (string.IsNullOrEmpty(input))
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
2020-08-08 21:53:34 -07:00
|
|
|
|
// If we have a wildcard
|
|
|
|
|
|
string pattern = "*";
|
|
|
|
|
|
if (input.Contains("*") || input.Contains("?"))
|
|
|
|
|
|
{
|
|
|
|
|
|
pattern = Path.GetFileName(input);
|
|
|
|
|
|
input = input.Substring(0, input.Length - pattern.Length);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-08-17 14:10:53 -07:00
|
|
|
|
// Get the parent path in case of appending
|
|
|
|
|
|
string parentPath;
|
|
|
|
|
|
try
|
2020-07-15 09:41:59 -07:00
|
|
|
|
{
|
2020-08-17 14:10:53 -07:00
|
|
|
|
parentPath = Path.GetFullPath(input);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2020-10-07 15:42:30 -07:00
|
|
|
|
LoggerImpl.Error(ex, $"An exception occurred getting the full path for '{input}'");
|
2020-08-17 14:10:53 -07:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2020-07-26 23:39:33 -07:00
|
|
|
|
|
2020-08-17 14:10:53 -07:00
|
|
|
|
if (Directory.Exists(input))
|
|
|
|
|
|
{
|
2020-08-08 21:53:34 -07:00
|
|
|
|
List<string> directories = GetDirectoriesOrdered(input, pattern);
|
2020-07-15 09:41:59 -07:00
|
|
|
|
foreach (string dir in directories)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2020-07-26 23:39:33 -07:00
|
|
|
|
outputs.Add(new ParentablePath(Path.GetFullPath(dir), appendparent ? parentPath : string.Empty));
|
2020-07-15 09:41:59 -07:00
|
|
|
|
}
|
2020-09-15 12:12:13 -07:00
|
|
|
|
catch (PathTooLongException ex)
|
2020-07-15 09:41:59 -07:00
|
|
|
|
{
|
2020-10-07 15:42:30 -07:00
|
|
|
|
LoggerImpl.Warning(ex, $"The path for '{dir}' was too long");
|
2020-07-15 09:41:59 -07:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2020-10-07 15:42:30 -07:00
|
|
|
|
LoggerImpl.Error(ex, $"An exception occurred processing '{dir}'");
|
2020-07-15 09:41:59 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return outputs;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Retrieve a list of directories from a directory recursively in proper order
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="dir">Directory to parse</param>
|
2020-08-08 21:53:34 -07:00
|
|
|
|
/// <param name="pattern">Optional pattern to search for directory names</param>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// <returns>List with all new files</returns>
|
2020-08-08 21:53:34 -07:00
|
|
|
|
private static List<string> GetDirectoriesOrdered(string dir, string pattern = "*")
|
2020-07-15 09:41:59 -07:00
|
|
|
|
{
|
2020-08-08 21:53:34 -07:00
|
|
|
|
return GetDirectoriesOrderedHelper(dir, new List<string>(), pattern);
|
2020-07-15 09:41:59 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Retrieve a list of directories from a directory recursively in proper order
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="dir">Directory to parse</param>
|
|
|
|
|
|
/// <param name="infiles">List representing existing files</param>
|
2020-08-08 21:53:34 -07:00
|
|
|
|
/// <param name="pattern">Optional pattern to search for directory names</param>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// <returns>List with all new files</returns>
|
2020-08-08 21:53:34 -07:00
|
|
|
|
private static List<string> GetDirectoriesOrderedHelper(string dir, List<string> infiles, string pattern)
|
2020-07-15 09:41:59 -07:00
|
|
|
|
{
|
|
|
|
|
|
// Take care of the files in the top directory
|
2020-08-08 21:53:34 -07:00
|
|
|
|
List<string> toadd = Directory.EnumerateDirectories(dir, pattern, SearchOption.TopDirectoryOnly).ToList();
|
2020-07-15 09:41:59 -07:00
|
|
|
|
toadd.Sort(new NaturalComparer());
|
|
|
|
|
|
infiles.AddRange(toadd);
|
|
|
|
|
|
|
|
|
|
|
|
// Then recurse through and add from the directories
|
|
|
|
|
|
foreach (string subDir in toadd)
|
|
|
|
|
|
{
|
2020-08-08 21:53:34 -07:00
|
|
|
|
infiles = GetDirectoriesOrderedHelper(subDir, infiles, pattern);
|
2020-07-15 09:41:59 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Return the new list
|
|
|
|
|
|
return infiles;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Retrieve a list of just files from inputs
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="inputs">List of strings representing directories and files</param>
|
2020-07-26 23:39:33 -07:00
|
|
|
|
/// <param name="appendparent">True if the parent name should be be included in the ParentablePath, false otherwise (default)</param>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// <returns>List of strings representing just files from the inputs</returns>
|
2020-07-26 23:39:33 -07:00
|
|
|
|
public static List<ParentablePath> GetFilesOnly(List<string> inputs, bool appendparent = false)
|
2020-07-15 09:41:59 -07:00
|
|
|
|
{
|
2020-07-26 23:39:33 -07:00
|
|
|
|
List<ParentablePath> outputs = new List<ParentablePath>();
|
2020-08-08 21:53:34 -07:00
|
|
|
|
for (int i = 0; i < inputs.Count; i++)
|
2020-07-15 09:41:59 -07:00
|
|
|
|
{
|
2020-08-26 17:11:24 -07:00
|
|
|
|
string input = inputs[i].Trim('"');
|
2020-08-08 21:53:34 -07:00
|
|
|
|
|
2020-08-17 14:10:53 -07:00
|
|
|
|
// If we have a null or empty path
|
|
|
|
|
|
if (string.IsNullOrEmpty(input))
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
2020-08-08 21:53:34 -07:00
|
|
|
|
// If we have a wildcard
|
|
|
|
|
|
string pattern = "*";
|
|
|
|
|
|
if (input.Contains("*") || input.Contains("?"))
|
|
|
|
|
|
{
|
|
|
|
|
|
pattern = Path.GetFileName(input);
|
|
|
|
|
|
input = input.Substring(0, input.Length - pattern.Length);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-26 23:39:33 -07:00
|
|
|
|
// Get the parent path in case of appending
|
2020-08-17 13:48:05 -07:00
|
|
|
|
string parentPath;
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
parentPath = Path.GetFullPath(input);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2020-10-07 15:42:30 -07:00
|
|
|
|
LoggerImpl.Error(ex, $"An exception occurred getting the full path for '{input}'");
|
2020-08-17 13:48:05 -07:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2020-07-26 23:39:33 -07:00
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
if (Directory.Exists(input))
|
|
|
|
|
|
{
|
2020-08-08 21:53:34 -07:00
|
|
|
|
List<string> files = GetFilesOrdered(input, pattern);
|
2020-07-15 09:41:59 -07:00
|
|
|
|
foreach (string file in files)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2020-07-26 23:39:33 -07:00
|
|
|
|
outputs.Add(new ParentablePath(Path.GetFullPath(file), appendparent ? parentPath : string.Empty));
|
2020-07-15 09:41:59 -07:00
|
|
|
|
}
|
2020-09-15 12:12:13 -07:00
|
|
|
|
catch (PathTooLongException ex)
|
2020-07-15 09:41:59 -07:00
|
|
|
|
{
|
2020-10-07 15:42:30 -07:00
|
|
|
|
LoggerImpl.Warning(ex, $"The path for '{file}' was too long");
|
2020-07-15 09:41:59 -07:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2020-10-07 15:42:30 -07:00
|
|
|
|
LoggerImpl.Error(ex, $"An exception occurred processing '{file}'");
|
2020-07-15 09:41:59 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (File.Exists(input))
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2020-07-26 23:39:33 -07:00
|
|
|
|
outputs.Add(new ParentablePath(Path.GetFullPath(input), appendparent ? parentPath : string.Empty));
|
2020-07-15 09:41:59 -07:00
|
|
|
|
}
|
2020-09-15 12:12:13 -07:00
|
|
|
|
catch (PathTooLongException ex)
|
2020-07-15 09:41:59 -07:00
|
|
|
|
{
|
2020-10-07 15:42:30 -07:00
|
|
|
|
LoggerImpl.Warning(ex, $"The path for '{input}' was too long");
|
2020-07-15 09:41:59 -07:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2020-10-07 15:42:30 -07:00
|
|
|
|
LoggerImpl.Error(ex, $"An exception occurred processing '{input}'");
|
2020-07-15 09:41:59 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return outputs;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Retrieve a list of files from a directory recursively in proper order
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="dir">Directory to parse</param>
|
2020-08-08 21:53:34 -07:00
|
|
|
|
/// <param name="pattern">Optional pattern to search for directory names</param>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// <returns>List with all new files</returns>
|
2020-08-08 21:53:34 -07:00
|
|
|
|
public static List<string> GetFilesOrdered(string dir, string pattern = "*")
|
2020-07-15 09:41:59 -07:00
|
|
|
|
{
|
2020-08-08 21:53:34 -07:00
|
|
|
|
return GetFilesOrderedHelper(dir, new List<string>(), pattern);
|
2020-07-15 09:41:59 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Retrieve a list of files from a directory recursively in proper order
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="dir">Directory to parse</param>
|
|
|
|
|
|
/// <param name="infiles">List representing existing files</param>
|
2020-08-08 21:53:34 -07:00
|
|
|
|
/// <param name="pattern">Optional pattern to search for directory names</param>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// <returns>List with all new files</returns>
|
2020-08-08 21:53:34 -07:00
|
|
|
|
private static List<string> GetFilesOrderedHelper(string dir, List<string> infiles, string pattern)
|
2020-07-15 09:41:59 -07:00
|
|
|
|
{
|
|
|
|
|
|
// Take care of the files in the top directory
|
2020-08-08 21:53:34 -07:00
|
|
|
|
List<string> toadd = Directory.EnumerateFiles(dir, pattern, SearchOption.TopDirectoryOnly).ToList();
|
2020-07-15 09:41:59 -07:00
|
|
|
|
toadd.Sort(new NaturalComparer());
|
|
|
|
|
|
infiles.AddRange(toadd);
|
|
|
|
|
|
|
|
|
|
|
|
// Then recurse through and add from the directories
|
2020-08-08 21:53:34 -07:00
|
|
|
|
List<string> subDirs = Directory.EnumerateDirectories(dir, pattern, SearchOption.TopDirectoryOnly).ToList();
|
2020-07-15 09:41:59 -07:00
|
|
|
|
subDirs.Sort(new NaturalComparer());
|
|
|
|
|
|
foreach (string subdir in subDirs)
|
|
|
|
|
|
{
|
2020-08-08 21:53:34 -07:00
|
|
|
|
infiles = GetFilesOrderedHelper(subdir, infiles, pattern);
|
2020-07-15 09:41:59 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Return the new list
|
|
|
|
|
|
return infiles;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Get all empty folders within a root folder
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="root">Root directory to parse</param>
|
|
|
|
|
|
/// <returns>IEumerable containing all directories that are empty, an empty enumerable if the root is empty, null otherwise</returns>
|
|
|
|
|
|
public static List<string> ListEmpty(string root)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Check if the root exists first
|
|
|
|
|
|
if (!Directory.Exists(root))
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
|
|
// If it does and it is empty, return a blank enumerable
|
|
|
|
|
|
if (Directory.EnumerateFileSystemEntries(root, "*", SearchOption.AllDirectories).Count() == 0)
|
|
|
|
|
|
return new List<string>();
|
|
|
|
|
|
|
|
|
|
|
|
// Otherwise, get the complete list
|
|
|
|
|
|
return Directory.EnumerateDirectories(root, "*", SearchOption.AllDirectories)
|
|
|
|
|
|
.Where(dir => Directory.EnumerateFileSystemEntries(dir, "*", SearchOption.AllDirectories).Count() == 0)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Try to safely delete a directory, optionally throwing the error
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="file">Name of the directory to delete</param>
|
|
|
|
|
|
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
|
|
|
|
|
/// <returns>True if the file didn't exist or could be deleted, false otherwise</returns>
|
|
|
|
|
|
public static bool TryCreateDirectory(string file, bool throwOnError = false)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Now wrap creating the directory
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
Directory.CreateDirectory(file);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (throwOnError)
|
|
|
|
|
|
throw ex;
|
|
|
|
|
|
else
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Try to safely delete a directory, optionally throwing the error
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="file">Name of the directory to delete</param>
|
|
|
|
|
|
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
|
|
|
|
|
/// <returns>True if the file didn't exist or could be deleted, false otherwise</returns>
|
|
|
|
|
|
public static bool TryDelete(string file, bool throwOnError = false)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Check if the directory exists first
|
|
|
|
|
|
if (!Directory.Exists(file))
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
// Now wrap deleting the directory
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
Directory.Delete(file, true);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (throwOnError)
|
|
|
|
|
|
throw ex;
|
|
|
|
|
|
else
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-12-07 15:08:57 -08:00
|
|
|
|
|
|
|
|
|
|
// TODO: Remove this entire section once External and the rest of IO is in its own library (or pulled in otherwise)
|
|
|
|
|
|
#region TEMPORARY - REMOVEME
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Try to safely delete a file, optionally throwing the error
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="file">Name of the file to delete</param>
|
|
|
|
|
|
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
|
|
|
|
|
/// <returns>True if the file didn't exist or could be deleted, false otherwise</returns>
|
|
|
|
|
|
private static bool FileTryDelete(string file, bool throwOnError = false)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Check if the file exists first
|
|
|
|
|
|
if (!File.Exists(file))
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
// Now wrap deleting the file
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
File.Delete(file);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (throwOnError)
|
|
|
|
|
|
throw ex;
|
|
|
|
|
|
else
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private class NaturalComparer : Comparer<string>, IDisposable
|
|
|
|
|
|
{
|
|
|
|
|
|
private Dictionary<string, string[]> table;
|
|
|
|
|
|
|
|
|
|
|
|
public NaturalComparer()
|
|
|
|
|
|
{
|
|
|
|
|
|
table = new Dictionary<string, string[]>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
|
{
|
|
|
|
|
|
table.Clear();
|
|
|
|
|
|
table = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override int Compare(string x, string y)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (x.ToLowerInvariant() == y.ToLowerInvariant())
|
|
|
|
|
|
{
|
|
|
|
|
|
return x.CompareTo(y);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!table.TryGetValue(x, out string[] x1))
|
|
|
|
|
|
{
|
|
|
|
|
|
//x1 = Regex.Split(x.Replace(" ", string.Empty), "([0-9]+)");
|
|
|
|
|
|
x1 = System.Text.RegularExpressions.Regex.Split(x.ToLowerInvariant(), "([0-9]+)").Where(s => !string.IsNullOrWhiteSpace(s)).ToArray();
|
|
|
|
|
|
table.Add(x, x1);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!table.TryGetValue(y, out string[] y1))
|
|
|
|
|
|
{
|
|
|
|
|
|
//y1 = Regex.Split(y.Replace(" ", string.Empty), "([0-9]+)");
|
|
|
|
|
|
y1 = System.Text.RegularExpressions.Regex.Split(y.ToLowerInvariant(), "([0-9]+)").Where(s => !string.IsNullOrWhiteSpace(s)).ToArray();
|
|
|
|
|
|
table.Add(y, y1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < x1.Length && i < y1.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (x1[i] != y1[i])
|
|
|
|
|
|
{
|
|
|
|
|
|
return PartCompare(x1[i], y1[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (y1.Length > x1.Length)
|
|
|
|
|
|
{
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (x1.Length > y1.Length)
|
|
|
|
|
|
{
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return x.CompareTo(y);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static int PartCompare(string left, string right)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!long.TryParse(left, out long x))
|
|
|
|
|
|
{
|
|
|
|
|
|
return CompareNumeric(left, right);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!long.TryParse(right, out long y))
|
|
|
|
|
|
{
|
|
|
|
|
|
return CompareNumeric(left, right);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If we have an equal part, then make sure that "longer" ones are taken into account
|
|
|
|
|
|
if (x.CompareTo(y) == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return left.Length - right.Length;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return x.CompareTo(y);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static int CompareNumeric(string s1, string s2)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Save the orginal strings, for later comparison
|
|
|
|
|
|
string s1orig = s1;
|
|
|
|
|
|
string s2orig = s2;
|
|
|
|
|
|
|
|
|
|
|
|
// We want to normalize the strings, so we set both to lower case
|
|
|
|
|
|
s1 = s1.ToLowerInvariant();
|
|
|
|
|
|
s2 = s2.ToLowerInvariant();
|
|
|
|
|
|
|
|
|
|
|
|
// If the strings are the same exactly, return
|
|
|
|
|
|
if (s1 == s2)
|
|
|
|
|
|
return s1orig.CompareTo(s2orig);
|
|
|
|
|
|
|
|
|
|
|
|
// If one is null, then say that's less than
|
|
|
|
|
|
if (s1 == null)
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
if (s2 == null)
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
|
|
// Now split into path parts after converting AltDirSeparator to DirSeparator
|
|
|
|
|
|
s1 = s1.Replace(System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar);
|
|
|
|
|
|
s2 = s2.Replace(System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar);
|
|
|
|
|
|
string[] s1parts = s1.Split(System.IO.Path.DirectorySeparatorChar);
|
|
|
|
|
|
string[] s2parts = s2.Split(System.IO.Path.DirectorySeparatorChar);
|
|
|
|
|
|
|
|
|
|
|
|
// Then compare each part in turn
|
|
|
|
|
|
for (int j = 0; j < s1parts.Length && j < s2parts.Length; j++)
|
|
|
|
|
|
{
|
|
|
|
|
|
int compared = CompareNumericPart(s1parts[j], s2parts[j]);
|
|
|
|
|
|
if (compared != 0)
|
|
|
|
|
|
return compared;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If we got out here, then it looped through at least one of the strings
|
|
|
|
|
|
if (s1parts.Length > s2parts.Length)
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
if (s1parts.Length < s2parts.Length)
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
|
|
return s1orig.CompareTo(s2orig);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static int CompareNumericPart(string s1, string s2)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Otherwise, loop through until we have an answer
|
|
|
|
|
|
for (int i = 0; i < s1.Length && i < s2.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
int s1c = s1[i];
|
|
|
|
|
|
int s2c = s2[i];
|
|
|
|
|
|
|
|
|
|
|
|
// If the characters are the same, continue
|
|
|
|
|
|
if (s1c == s2c)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
// If they're different, check which one was larger
|
|
|
|
|
|
if (s1c > s2c)
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
if (s1c < s2c)
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If we got out here, then it looped through at least one of the strings
|
|
|
|
|
|
if (s1.Length > s2.Length)
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
if (s1.Length < s2.Length)
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
2020-07-15 09:41:59 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|