Make filename character replacement more xplat and allow replacing any char, not just illegal.

This commit is contained in:
Michael Bucari-Tovo 2022-12-15 15:50:48 -07:00
parent 80bcf60b5b
commit a0dd2ccad6
9 changed files with 1141 additions and 1133 deletions

View File

@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AudibleApi" Version="7.0.0.5" />
<PackageReference Include="AudibleApi" Version="7.1.0.1" />
</ItemGroup>
<ItemGroup>

View File

@ -105,7 +105,7 @@ namespace FileManager
// Other illegal characters will be taken care of later. Must take care of slashes now so params can't introduce new folders.
// Esp important for file templates.
return replacements.ReplaceInvalidFilenameChars(value.ToString());
return replacements.ReplaceFilenameChars(value.ToString());
}
}
}

View File

@ -84,7 +84,7 @@ namespace FileManager
var pathNoPrefix = path.PathWithoutPrefix;
pathNoPrefix = replacements.ReplaceInvalidPathChars(pathNoPrefix);
pathNoPrefix = replacements.ReplacePathChars(pathNoPrefix);
pathNoPrefix = removeDoubleSlashes(pathNoPrefix);
return pathNoPrefix;

View File

@ -69,7 +69,7 @@ namespace FileManager
= IsWindows
? new()
{
Replacements = new List<Replacement>()
Replacements = new Replacement[]
{
Replacement.OtherInvalid("_"),
Replacement.FilenameForwardSlash(""),
@ -87,7 +87,7 @@ namespace FileManager
}
: new()
{
Replacements = new List<Replacement>()
Replacements = new Replacement[]
{
Replacement.OtherInvalid("_"),
Replacement.FilenameForwardSlash(""),
@ -102,7 +102,7 @@ namespace FileManager
= IsWindows
? new()
{
Replacements = new List<Replacement>()
Replacements = new Replacement[]
{
Replacement.OtherInvalid("_"),
Replacement.FilenameForwardSlash("_"),
@ -121,7 +121,7 @@ namespace FileManager
= IsWindows
? new ()
{
Replacements = new List<Replacement>()
Replacements = new Replacement[]
{
Replacement.OtherInvalid("_"),
Replacement.FilenameForwardSlash("_"),
@ -133,7 +133,7 @@ namespace FileManager
}
: new ()
{
Replacements = new List<Replacement>()
Replacements = new Replacement[]
{
Replacement.OtherInvalid("_"),
Replacement.FilenameForwardSlash("_"),
@ -147,11 +147,11 @@ namespace FileManager
private static bool IsWindows => Environment.OSVersion.Platform is PlatformID.Win32NT;
private static readonly char[] invalidPathChars = Path.GetInvalidFileNameChars().Except(new[] {
'\\', '/'
Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar
}).ToArray();
private static readonly char[] invalidSlashes = Path.GetInvalidFileNameChars().Intersect(new[] {
'\\', '/'
Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar
}).ToArray();
public IReadOnlyList<Replacement> Replacements { get; init; }
@ -214,7 +214,7 @@ namespace FileManager
public static bool ContainsInvalidFilenameChar(string path)
=> ContainsInvalidPathChar(path) || path.Any(c => invalidSlashes.Contains(c));
public string ReplaceInvalidFilenameChars(string fileName)
public string ReplaceFilenameChars(string fileName)
{
if (string.IsNullOrEmpty(fileName)) return string.Empty;
var builder = new System.Text.StringBuilder();
@ -222,7 +222,9 @@ namespace FileManager
{
var c = fileName[i];
if (invalidPathChars.Contains(c) || invalidSlashes.Contains(c))
if (invalidPathChars.Contains(c)
|| invalidSlashes.Contains(c)
|| Replacements.Any(r => r.CharacterToReplace == c) /* Replace any other legal characters that they user wants. */ )
{
char preceding = i > 0 ? fileName[i - 1] : default;
char succeeding = i < fileName.Length - 1 ? fileName[i + 1] : default;
@ -230,29 +232,42 @@ namespace FileManager
}
else
builder.Append(c);
}
return builder.ToString();
}
public string ReplaceInvalidPathChars(string pathStr)
public string ReplacePathChars(string pathStr)
{
if (string.IsNullOrEmpty(pathStr)) return string.Empty;
// replace all colons except within the first 2 chars
var builder = new System.Text.StringBuilder();
for (var i = 0; i < pathStr.Length; i++)
{
var c = pathStr[i];
if (!invalidPathChars.Contains(c) || (c == ':' && i == 1 && Path.IsPathRooted(pathStr)))
builder.Append(c);
else
if (
(
invalidPathChars.Contains(c)
|| ( // Replace any other legal characters that they user wants.
c != Path.DirectorySeparatorChar
&& c != Path.AltDirectorySeparatorChar
&& Replacements.Any(r => r.CharacterToReplace == c)
)
)
&& !( // replace all colons except drive letter designator on Windows
c == ':'
&& i == 1
&& Path.IsPathRooted(pathStr)
&& IsWindows
)
)
{
char preceding = i > 0 ? pathStr[i - 1] : default;
char succeeding = i < pathStr.Length - 1 ? pathStr[i + 1] : default;
builder.Append(GetPathCharReplacement(c, preceding, succeeding));
char preceding = i > 0 ? pathStr[i - 1] : default;
char succeeding = i < pathStr.Length - 1 ? pathStr[i + 1] : default;
builder.Append(GetPathCharReplacement(c, preceding, succeeding));
}
else
builder.Append(c);
}
return builder.ToString();
}
@ -274,28 +289,19 @@ namespace FileManager
//Ensure that the first 6 replacements are for the expected chars and that all replacement strings are valid.
//If not, reset to default.
var default0 = Replacement.OtherInvalid("");
var default1 = Replacement.FilenameForwardSlash("");
var default2 = Replacement.FilenameBackSlash("");
var default3 = Replacement.OpenQuote("");
var default4 = Replacement.CloseQuote("");
var default5 = Replacement.OtherQuote("");
if (dict.Count < Replacement.FIXED_COUNT ||
dict[0].CharacterToReplace != default0.CharacterToReplace || dict[0].Description != default0.Description ||
dict[1].CharacterToReplace != default1.CharacterToReplace || dict[1].Description != default1.Description ||
dict[2].CharacterToReplace != default2.CharacterToReplace || dict[2].Description != default2.Description ||
dict[3].CharacterToReplace != default3.CharacterToReplace || dict[3].Description != default3.Description ||
dict[4].CharacterToReplace != default4.CharacterToReplace || dict[4].Description != default4.Description ||
dict[5].CharacterToReplace != default5.CharacterToReplace || dict[5].Description != default5.Description ||
dict.Any(r => ReplacementCharacters.ContainsInvalidFilenameChar(r.ReplacementString))
)
{
dict = ReplacementCharacters.Default.Replacements;
}
//First FIXED_COUNT are mandatory
for (int i = 0; i < Replacement.FIXED_COUNT; i++)
{
if (dict.Count < Replacement.FIXED_COUNT
|| dict[i].CharacterToReplace != ReplacementCharacters.Barebones.Replacements[i].CharacterToReplace
|| dict[i].Description != ReplacementCharacters.Barebones.Replacements[i].Description)
{
dict = ReplacementCharacters.Default.Replacements;
break;
}
//First FIXED_COUNT are mandatory
dict[i].Mandatory = true;
}
return new ReplacementCharacters { Replacements = dict };
}
@ -305,7 +311,7 @@ namespace FileManager
ReplacementCharacters replacements = (ReplacementCharacters)value;
var propertyNames = replacements.Replacements
.Select(c => JObject.FromObject(c)).ToList();
.Select(JObject.FromObject).ToList();
var prop = new JProperty(nameof(Replacement), new JArray(propertyNames));

View File

@ -158,7 +158,7 @@ namespace LibationAvalonia.Dialogs
set
{
if (value?.Length != 1 || !ReplacementCharacters.ContainsInvalidFilenameChar(value))
if (value?.Length != 1)
this.RaisePropertyChanged(nameof(CharacterToReplace));
else
{

View File

@ -4,6 +4,7 @@ using System.ComponentModel;
using System.Linq;
using Dinah.Core;
using Dinah.Core.Logging;
using FileManager;
using Microsoft.Extensions.Configuration;
using Serilog;
using Serilog.Events;
@ -21,6 +22,7 @@ namespace LibationFileManager
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.Destructure.ByTransforming<LongPath>(lp => lp.Path)
.CreateLogger();
}

View File

@ -276,7 +276,7 @@ namespace LibationFileManager
#region templates: custom file naming
[Description("Edit how illegal filename characters are replaced")]
[Description("Edit how filename characters are replaced")]
public ReplacementCharacters ReplacementCharacters
{
get => persistentDictionary.GetNonString<ReplacementCharacters>(nameof(ReplacementCharacters));

File diff suppressed because it is too large Load Diff

View File

@ -83,17 +83,17 @@ namespace FileUtilityTests
[TestMethod]
// empty replacement
[DataRow("http://test.com/a/b/c", "httptest.comabc")]
public void DefaultReplacementTest(string inStr, string outStr) => Default.ReplaceInvalidFilenameChars(inStr).Should().Be(outStr);
public void DefaultReplacementTest(string inStr, string outStr) => Default.ReplaceFilenameChars(inStr).Should().Be(outStr);
[TestMethod]
// empty replacement
[DataRow("http://test.com/a/b/c", "http-__test.com_a_b_c")]
public void LoFiDefaultReplacementTest(string inStr, string outStr) => LoFiDefault.ReplaceInvalidFilenameChars(inStr).Should().Be(outStr);
public void LoFiDefaultReplacementTest(string inStr, string outStr) => LoFiDefault.ReplaceFilenameChars(inStr).Should().Be(outStr);
[TestMethod]
// empty replacement
[DataRow("http://test.com/a/b/c", "http___test.com_a_b_c")]
public void BarebonesDefaultReplacementTest(string inStr, string outStr) => Barebones.ReplaceInvalidFilenameChars(inStr).Should().Be(outStr);
public void BarebonesDefaultReplacementTest(string inStr, string outStr) => Barebones.ReplaceFilenameChars(inStr).Should().Be(outStr);
}
[TestClass]