Make filename character replacement more xplat and allow replacing any char, not just illegal.
This commit is contained in:
parent
80bcf60b5b
commit
a0dd2ccad6
@ -5,7 +5,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AudibleApi" Version="7.0.0.5" />
|
<PackageReference Include="AudibleApi" Version="7.1.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -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.
|
// 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.
|
// Esp important for file templates.
|
||||||
return replacements.ReplaceInvalidFilenameChars(value.ToString());
|
return replacements.ReplaceFilenameChars(value.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,7 +84,7 @@ namespace FileManager
|
|||||||
|
|
||||||
var pathNoPrefix = path.PathWithoutPrefix;
|
var pathNoPrefix = path.PathWithoutPrefix;
|
||||||
|
|
||||||
pathNoPrefix = replacements.ReplaceInvalidPathChars(pathNoPrefix);
|
pathNoPrefix = replacements.ReplacePathChars(pathNoPrefix);
|
||||||
pathNoPrefix = removeDoubleSlashes(pathNoPrefix);
|
pathNoPrefix = removeDoubleSlashes(pathNoPrefix);
|
||||||
|
|
||||||
return pathNoPrefix;
|
return pathNoPrefix;
|
||||||
|
|||||||
@ -69,7 +69,7 @@ namespace FileManager
|
|||||||
= IsWindows
|
= IsWindows
|
||||||
? new()
|
? new()
|
||||||
{
|
{
|
||||||
Replacements = new List<Replacement>()
|
Replacements = new Replacement[]
|
||||||
{
|
{
|
||||||
Replacement.OtherInvalid("_"),
|
Replacement.OtherInvalid("_"),
|
||||||
Replacement.FilenameForwardSlash("∕"),
|
Replacement.FilenameForwardSlash("∕"),
|
||||||
@ -87,7 +87,7 @@ namespace FileManager
|
|||||||
}
|
}
|
||||||
: new()
|
: new()
|
||||||
{
|
{
|
||||||
Replacements = new List<Replacement>()
|
Replacements = new Replacement[]
|
||||||
{
|
{
|
||||||
Replacement.OtherInvalid("_"),
|
Replacement.OtherInvalid("_"),
|
||||||
Replacement.FilenameForwardSlash("∕"),
|
Replacement.FilenameForwardSlash("∕"),
|
||||||
@ -102,7 +102,7 @@ namespace FileManager
|
|||||||
= IsWindows
|
= IsWindows
|
||||||
? new()
|
? new()
|
||||||
{
|
{
|
||||||
Replacements = new List<Replacement>()
|
Replacements = new Replacement[]
|
||||||
{
|
{
|
||||||
Replacement.OtherInvalid("_"),
|
Replacement.OtherInvalid("_"),
|
||||||
Replacement.FilenameForwardSlash("_"),
|
Replacement.FilenameForwardSlash("_"),
|
||||||
@ -121,7 +121,7 @@ namespace FileManager
|
|||||||
= IsWindows
|
= IsWindows
|
||||||
? new ()
|
? new ()
|
||||||
{
|
{
|
||||||
Replacements = new List<Replacement>()
|
Replacements = new Replacement[]
|
||||||
{
|
{
|
||||||
Replacement.OtherInvalid("_"),
|
Replacement.OtherInvalid("_"),
|
||||||
Replacement.FilenameForwardSlash("_"),
|
Replacement.FilenameForwardSlash("_"),
|
||||||
@ -133,7 +133,7 @@ namespace FileManager
|
|||||||
}
|
}
|
||||||
: new ()
|
: new ()
|
||||||
{
|
{
|
||||||
Replacements = new List<Replacement>()
|
Replacements = new Replacement[]
|
||||||
{
|
{
|
||||||
Replacement.OtherInvalid("_"),
|
Replacement.OtherInvalid("_"),
|
||||||
Replacement.FilenameForwardSlash("_"),
|
Replacement.FilenameForwardSlash("_"),
|
||||||
@ -147,11 +147,11 @@ namespace FileManager
|
|||||||
private static bool IsWindows => Environment.OSVersion.Platform is PlatformID.Win32NT;
|
private static bool IsWindows => Environment.OSVersion.Platform is PlatformID.Win32NT;
|
||||||
|
|
||||||
private static readonly char[] invalidPathChars = Path.GetInvalidFileNameChars().Except(new[] {
|
private static readonly char[] invalidPathChars = Path.GetInvalidFileNameChars().Except(new[] {
|
||||||
'\\', '/'
|
Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
|
|
||||||
private static readonly char[] invalidSlashes = Path.GetInvalidFileNameChars().Intersect(new[] {
|
private static readonly char[] invalidSlashes = Path.GetInvalidFileNameChars().Intersect(new[] {
|
||||||
'\\', '/'
|
Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
|
|
||||||
public IReadOnlyList<Replacement> Replacements { get; init; }
|
public IReadOnlyList<Replacement> Replacements { get; init; }
|
||||||
@ -214,7 +214,7 @@ namespace FileManager
|
|||||||
public static bool ContainsInvalidFilenameChar(string path)
|
public static bool ContainsInvalidFilenameChar(string path)
|
||||||
=> ContainsInvalidPathChar(path) || path.Any(c => invalidSlashes.Contains(c));
|
=> 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;
|
if (string.IsNullOrEmpty(fileName)) return string.Empty;
|
||||||
var builder = new System.Text.StringBuilder();
|
var builder = new System.Text.StringBuilder();
|
||||||
@ -222,7 +222,9 @@ namespace FileManager
|
|||||||
{
|
{
|
||||||
var c = fileName[i];
|
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 preceding = i > 0 ? fileName[i - 1] : default;
|
||||||
char succeeding = i < fileName.Length - 1 ? fileName[i + 1] : default;
|
char succeeding = i < fileName.Length - 1 ? fileName[i + 1] : default;
|
||||||
@ -230,29 +232,42 @@ namespace FileManager
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
builder.Append(c);
|
builder.Append(c);
|
||||||
|
|
||||||
}
|
}
|
||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ReplaceInvalidPathChars(string pathStr)
|
public string ReplacePathChars(string pathStr)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(pathStr)) return string.Empty;
|
if (string.IsNullOrEmpty(pathStr)) return string.Empty;
|
||||||
|
|
||||||
// replace all colons except within the first 2 chars
|
|
||||||
var builder = new System.Text.StringBuilder();
|
var builder = new System.Text.StringBuilder();
|
||||||
for (var i = 0; i < pathStr.Length; i++)
|
for (var i = 0; i < pathStr.Length; i++)
|
||||||
{
|
{
|
||||||
var c = pathStr[i];
|
var c = pathStr[i];
|
||||||
|
|
||||||
if (!invalidPathChars.Contains(c) || (c == ':' && i == 1 && Path.IsPathRooted(pathStr)))
|
if (
|
||||||
builder.Append(c);
|
(
|
||||||
else
|
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 preceding = i > 0 ? pathStr[i - 1] : default;
|
||||||
char succeeding = i < pathStr.Length - 1 ? pathStr[i + 1] : default;
|
char succeeding = i < pathStr.Length - 1 ? pathStr[i + 1] : default;
|
||||||
builder.Append(GetPathCharReplacement(c, preceding, succeeding));
|
builder.Append(GetPathCharReplacement(c, preceding, succeeding));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
builder.Append(c);
|
||||||
}
|
}
|
||||||
return builder.ToString();
|
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.
|
//Ensure that the first 6 replacements are for the expected chars and that all replacement strings are valid.
|
||||||
//If not, reset to default.
|
//If not, reset to default.
|
||||||
|
|
||||||
var default0 = Replacement.OtherInvalid("");
|
for (int i = 0; i < Replacement.FIXED_COUNT; i++)
|
||||||
var default1 = Replacement.FilenameForwardSlash("");
|
{
|
||||||
var default2 = Replacement.FilenameBackSlash("");
|
if (dict.Count < Replacement.FIXED_COUNT
|
||||||
var default3 = Replacement.OpenQuote("");
|
|| dict[i].CharacterToReplace != ReplacementCharacters.Barebones.Replacements[i].CharacterToReplace
|
||||||
var default4 = Replacement.CloseQuote("");
|
|| dict[i].Description != ReplacementCharacters.Barebones.Replacements[i].Description)
|
||||||
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;
|
dict = ReplacementCharacters.Default.Replacements;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//First FIXED_COUNT are mandatory
|
//First FIXED_COUNT are mandatory
|
||||||
for (int i = 0; i < Replacement.FIXED_COUNT; i++)
|
|
||||||
dict[i].Mandatory = true;
|
dict[i].Mandatory = true;
|
||||||
|
}
|
||||||
|
|
||||||
return new ReplacementCharacters { Replacements = dict };
|
return new ReplacementCharacters { Replacements = dict };
|
||||||
}
|
}
|
||||||
@ -305,7 +311,7 @@ namespace FileManager
|
|||||||
ReplacementCharacters replacements = (ReplacementCharacters)value;
|
ReplacementCharacters replacements = (ReplacementCharacters)value;
|
||||||
|
|
||||||
var propertyNames = replacements.Replacements
|
var propertyNames = replacements.Replacements
|
||||||
.Select(c => JObject.FromObject(c)).ToList();
|
.Select(JObject.FromObject).ToList();
|
||||||
|
|
||||||
var prop = new JProperty(nameof(Replacement), new JArray(propertyNames));
|
var prop = new JProperty(nameof(Replacement), new JArray(propertyNames));
|
||||||
|
|
||||||
|
|||||||
@ -158,7 +158,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value?.Length != 1 || !ReplacementCharacters.ContainsInvalidFilenameChar(value))
|
if (value?.Length != 1)
|
||||||
this.RaisePropertyChanged(nameof(CharacterToReplace));
|
this.RaisePropertyChanged(nameof(CharacterToReplace));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@ -4,6 +4,7 @@ using System.ComponentModel;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using Dinah.Core.Logging;
|
using Dinah.Core.Logging;
|
||||||
|
using FileManager;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
@ -21,6 +22,7 @@ namespace LibationFileManager
|
|||||||
.Build();
|
.Build();
|
||||||
Log.Logger = new LoggerConfiguration()
|
Log.Logger = new LoggerConfiguration()
|
||||||
.ReadFrom.Configuration(configuration)
|
.ReadFrom.Configuration(configuration)
|
||||||
|
.Destructure.ByTransforming<LongPath>(lp => lp.Path)
|
||||||
.CreateLogger();
|
.CreateLogger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -276,7 +276,7 @@ namespace LibationFileManager
|
|||||||
|
|
||||||
#region templates: custom file naming
|
#region templates: custom file naming
|
||||||
|
|
||||||
[Description("Edit how illegal filename characters are replaced")]
|
[Description("Edit how filename characters are replaced")]
|
||||||
public ReplacementCharacters ReplacementCharacters
|
public ReplacementCharacters ReplacementCharacters
|
||||||
{
|
{
|
||||||
get => persistentDictionary.GetNonString<ReplacementCharacters>(nameof(ReplacementCharacters));
|
get => persistentDictionary.GetNonString<ReplacementCharacters>(nameof(ReplacementCharacters));
|
||||||
|
|||||||
@ -535,7 +535,7 @@
|
|||||||
this.editCharreplacementBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
this.editCharreplacementBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
this.editCharreplacementBtn.Location = new System.Drawing.Point(5, 158);
|
this.editCharreplacementBtn.Location = new System.Drawing.Point(5, 158);
|
||||||
this.editCharreplacementBtn.Name = "editCharreplacementBtn";
|
this.editCharreplacementBtn.Name = "editCharreplacementBtn";
|
||||||
this.editCharreplacementBtn.Size = new System.Drawing.Size(387, 23);
|
this.editCharreplacementBtn.Size = new System.Drawing.Size(281, 23);
|
||||||
this.editCharreplacementBtn.TabIndex = 8;
|
this.editCharreplacementBtn.TabIndex = 8;
|
||||||
this.editCharreplacementBtn.Text = "[edit char replacement desc]";
|
this.editCharreplacementBtn.Text = "[edit char replacement desc]";
|
||||||
this.editCharreplacementBtn.UseVisualStyleBackColor = true;
|
this.editCharreplacementBtn.UseVisualStyleBackColor = true;
|
||||||
|
|||||||
@ -83,17 +83,17 @@ namespace FileUtilityTests
|
|||||||
[TestMethod]
|
[TestMethod]
|
||||||
// empty replacement
|
// empty replacement
|
||||||
[DataRow("http://test.com/a/b/c", "http꞉∕∕test.com∕a∕b∕c")]
|
[DataRow("http://test.com/a/b/c", "http꞉∕∕test.com∕a∕b∕c")]
|
||||||
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]
|
[TestMethod]
|
||||||
// empty replacement
|
// empty replacement
|
||||||
[DataRow("http://test.com/a/b/c", "http-__test.com_a_b_c")]
|
[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]
|
[TestMethod]
|
||||||
// empty replacement
|
// empty replacement
|
||||||
[DataRow("http://test.com/a/b/c", "http___test.com_a_b_c")]
|
[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]
|
[TestClass]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user