Files
ZelWiki/ZelWiki.Library/NamespaceNavigation.cs
2025-02-20 15:20:28 +08:00

145 lines
4.5 KiB
C#

using System.Text;
using System.Text.RegularExpressions;
namespace ZelWiki.Library
{
public class NamespaceNavigation
{
private string _namespace = string.Empty;
private string _page = string.Empty;
private readonly bool _lowerCase = false;
public string Namespace
{
get => _namespace;
set => _namespace = CleanAndValidate(value.Replace("::", "_")).Trim();
}
public string Page
{
get => _page;
set => _page = CleanAndValidate(value.Replace("::", "_")).Trim();
}
public string Canonical
{
get
{
if (string.IsNullOrWhiteSpace(Namespace))
{
return Page;
}
return $"{Namespace}::{Page}";
}
set
{
var cleanedAndValidatedValue = CleanAndValidate(value, _lowerCase);
var parts = cleanedAndValidatedValue.Split("::");
if (parts.Length < 2)
{
Page = parts[0].Trim();
}
else
{
Namespace = parts[0].Trim();
Page = string.Join("_", parts.Skip(1).Select(o => o.Trim())).Trim();
}
}
}
/// <summary>
/// Creates a new instance of NamespaceNavigation.
/// </summary>
/// <param name="givenCanonical">Page navigation with optional namespace.</param>
public NamespaceNavigation(string givenCanonical)
{
_lowerCase = true;
Canonical = givenCanonical;
}
/// <summary>
/// Creates a new instance of NamespaceNavigation.
/// </summary>
/// <param name="givenCanonical">Page navigation with optional namespace.</param>
/// <param name="lowerCase">If false, the namespace and page name will not be lowercased.</param>
public NamespaceNavigation(string givenCanonical, bool lowerCase)
{
_lowerCase = lowerCase;
Canonical = givenCanonical;
}
public override string ToString()
{
return Canonical;
}
/// <summary>
/// Takes a page name with optional namespace and returns the cleaned version that can be used for matching Navigations.
/// </summary>
/// <param name="givenCanonical">Page navigation with optional namespace.</param>
/// <param name="lowerCase">If false, the namespace and page name will not be lowercased.</param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static string CleanAndValidate(string? str, bool lowerCase = true)
{
if (str == null)
{
return string.Empty;
}
//Fix names like "::Page" or "Namespace::".
str = str.Trim().Trim([':']).Trim();
if (str.Contains("::"))
{
var parts = str.Split("::");
if (parts.Length != 2)
{
throw new Exception("Navigation can not contain more than one namespace.");
}
return $"{CleanAndValidate(parts[0].Trim())}::{CleanAndValidate(parts[1].Trim(), lowerCase)}";
}
// Decode common HTML entities
str = str.Replace("&quot;", "\"")
.Replace("&amp;", "&")
.Replace("&lt;", "<")
.Replace("&gt;", ">")
.Replace("&nbsp;", " ");
// Normalize backslashes to forward slashes
str = str.Replace('\\', '/');
var sb = new StringBuilder();
foreach (char c in str)
{
if (char.IsWhiteSpace(c) || c == '.')
{
sb.Append('_');
}
else if (char.IsLetterOrDigit(c) || c == '_' || c == '/' || c == '-')
{
sb.Append(c);
}
}
string result = sb.ToString();
// Remove multiple consecutive underscores or slashes
result = Regex.Replace(result, @"[_]{2,}", "_");
result = Regex.Replace(result, @"[/]{2,}", "/");
if (lowerCase)
{
return result.TrimEnd(['/', '\\']).ToLowerInvariant();
}
else
{
return result.TrimEnd(['/', '\\']);
}
}
}
}