Files
LIAM/LiamNtfs/C4IT_IAM_SET/Helper.cs
2026-03-29 23:46:20 +02:00

320 lines
14 KiB
C#

using C4IT.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace C4IT_IAM_Engine
{
public static class Helper
{
public const int MaxAdGroupNameLength = 64;
public const int MaxAdGroupDescriptionLength = 1024;
public const int MaxAdGroupLoopDigits = 3;
private const int MinLeadingRelativePathSegmentLength = 3;
private const int MinSingleLeadingRelativePathSegmentLength = 2;
private const int MinLastRelativePathSegmentLength = 12;
public sealed class BoundedTemplateContext
{
public string[] SanitizedSegments { get; set; } = Array.Empty<string>();
public string FolderName { get; set; } = string.Empty;
public bool WasShortened { get; set; }
public string OriginalValue { get; set; } = string.Empty;
public string FinalValue { get; set; } = string.Empty;
public string Strategy { get; set; } = string.Empty;
}
public static string ReplaceLoopTag(this string str, int loop)
{
return Regex.Replace(str, @"(?<loopTag>{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}})", loop <= 0 ? "" : "${prefix}" + loop + "${postfix}");
}
public static string ReplaceTags(this string str, IDictionary<string, string> dict)
{
if (str.Equals(string.Empty) || str == null || dict == null || dict.Count == 0)
return str;
return dict.Aggregate(str, (current, value) =>
current.Replace("{{" + value.Key + "}}", value.Value));
}
public static string ApplyTemplatePlaceholders(string templateValue, bool allowRelativePath, string defaultRelativePath, string[] sanitizedSegments, string folderName)
{
if (templateValue == null)
return string.Empty;
var result = Regex.Replace(templateValue, @"{{\s*NAME\s*}}", folderName ?? string.Empty, RegexOptions.IgnoreCase);
if (allowRelativePath)
{
result = Regex.Replace(result, @"{{\s*RELATIVEPATH(?:\s*\(\s*(\d+)\s*\))?\s*}}", match =>
{
if (sanitizedSegments == null || sanitizedSegments.Length == 0)
return string.Empty;
if (!match.Groups[1].Success)
return defaultRelativePath;
if (!int.TryParse(match.Groups[1].Value, out var segmentIndex) || segmentIndex < 0)
return defaultRelativePath;
var segmentCount = Math.Min(sanitizedSegments.Length, segmentIndex + 1);
var skip = sanitizedSegments.Length - segmentCount;
return string.Join("_", sanitizedSegments.Skip(skip));
}, RegexOptions.IgnoreCase);
}
return result;
}
public static BoundedTemplateContext GetBoundedAdGroupTemplateContext(
string templateValue,
bool allowRelativePath,
string defaultRelativePath,
string[] sanitizedSegments,
string folderName,
IDictionary<string, string> replacementTags,
int maxLength,
string logContext,
string valueLabel = "AD-Gruppenname")
{
var effectiveSegments = (sanitizedSegments ?? Array.Empty<string>()).Where(i => i != null).ToArray();
var effectiveFolderName = folderName ?? string.Empty;
var currentRelativePath = GetCurrentRelativePath(effectiveSegments, defaultRelativePath);
var originalValue = MaterializeTemplateValue(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags);
var measuredValue = MaterializeTemplateValueForLength(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags);
var usesRelativePath = allowRelativePath && Regex.IsMatch(templateValue ?? string.Empty, @"{{\s*RELATIVEPATH", RegexOptions.IgnoreCase);
var usesName = Regex.IsMatch(templateValue ?? string.Empty, @"{{\s*NAME\s*}}", RegexOptions.IgnoreCase);
var strategy = string.Empty;
while (measuredValue.Length > maxLength)
{
var changed = false;
if (usesRelativePath && TryShortenRelativePath(ref effectiveSegments))
{
effectiveFolderName = effectiveSegments.Length > 0 ? effectiveSegments[effectiveSegments.Length - 1] : string.Empty;
if (string.IsNullOrWhiteSpace(strategy))
strategy = "truncate-relativepath";
changed = true;
}
else if (usesName && !usesRelativePath && TryShortenName(ref effectiveFolderName))
{
if (string.IsNullOrWhiteSpace(strategy))
strategy = "truncate-name";
changed = true;
}
if (!changed)
break;
currentRelativePath = GetCurrentRelativePath(effectiveSegments, defaultRelativePath);
originalValue = MaterializeTemplateValue(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags);
measuredValue = MaterializeTemplateValueForLength(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags);
}
var initialValue = MaterializeTemplateValue(
templateValue,
allowRelativePath,
GetCurrentRelativePath(sanitizedSegments, defaultRelativePath),
sanitizedSegments,
folderName,
replacementTags);
var result = new BoundedTemplateContext
{
SanitizedSegments = effectiveSegments,
FolderName = effectiveSegments.Length > 0 ? effectiveSegments[effectiveSegments.Length - 1] : effectiveFolderName,
OriginalValue = initialValue,
FinalValue = originalValue,
WasShortened = !string.Equals(initialValue, originalValue, StringComparison.Ordinal),
Strategy = strategy
};
if (result.WasShortened)
{
cLogManager.DefaultLogger.LogEntry(
LogLevels.Warning,
$"{valueLabel} gekuerzt ({logContext}): '{result.OriginalValue}' ({GetMeasuredTemplateLength(result.OriginalValue)}) -> '{result.FinalValue}' ({GetMeasuredTemplateLength(result.FinalValue)}), Strategie: {result.Strategy}, Limit: {maxLength}.");
}
if (measuredValue.Length > maxLength)
{
cLogManager.DefaultLogger.LogEntry(
LogLevels.Warning,
$"{valueLabel} ueberschreitet weiterhin das sichere Limit ({logContext}): '{result.FinalValue}' ({measuredValue.Length}), Limit: {maxLength}.");
}
return result;
}
public static string SanitizePathSegment(string segment)
{
if (string.IsNullOrEmpty(segment))
return string.Empty;
return Regex.Replace(segment, @"[\s\-]", "_");
}
public static void CreatePathWithWriteAccess(string FilePath)
{
try
{
var PF = Environment.ExpandEnvironmentVariables(FilePath);
Directory.CreateDirectory(PF);
}
catch { }
}
public static string MaskAllButLastAndFirst(this string input, char maskingChar = '*')
{
if (input.Length > 3)
{
var pattern = @"^(.{1})(.+)(.{1})$";
var match = Regex.Match(input, pattern);
var mask = new string(maskingChar, match.Groups[2].Length);
return $"{match.Groups[1]}{mask}{match.Groups[3]}";
}
else
return new string(maskingChar, input.Length);
}
private static string MaterializeTemplateValue(
string templateValue,
bool allowRelativePath,
string defaultRelativePath,
string[] sanitizedSegments,
string folderName,
IDictionary<string, string> replacementTags)
{
return ApplyTemplatePlaceholders(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName)
.ReplaceTags(replacementTags)
.ToUpper();
}
private static string MaterializeTemplateValueForLength(
string templateValue,
bool allowRelativePath,
string defaultRelativePath,
string[] sanitizedSegments,
string folderName,
IDictionary<string, string> replacementTags)
{
return NormalizeLoopPlaceholderLength(
MaterializeTemplateValue(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName, replacementTags));
}
private static string NormalizeLoopPlaceholderLength(string templateValue)
{
if (string.IsNullOrWhiteSpace(templateValue))
return templateValue ?? string.Empty;
return Regex.Replace(
templateValue,
@"{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}}",
match => match.Groups["prefix"].Value + new string('9', MaxAdGroupLoopDigits) + match.Groups["postfix"].Value,
RegexOptions.IgnoreCase);
}
private static int GetMeasuredTemplateLength(string templateValue)
{
return NormalizeLoopPlaceholderLength(templateValue).Length;
}
private static string GetCurrentRelativePath(string[] sanitizedSegments, string fallbackRelativePath)
{
if (sanitizedSegments != null && sanitizedSegments.Length > 0)
return string.Join("_", sanitizedSegments);
return fallbackRelativePath ?? string.Empty;
}
private static int GetLoopReservationLength(string templateValue)
{
if (string.IsNullOrWhiteSpace(templateValue))
return 0;
var reservation = 0;
foreach (Match match in Regex.Matches(templateValue, @"{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}}", RegexOptions.IgnoreCase))
{
reservation += match.Groups["prefix"].Value.Length + MaxAdGroupLoopDigits + match.Groups["postfix"].Value.Length;
}
return reservation;
}
private static bool TryShortenRelativePath(ref string[] segments)
{
if (segments == null || segments.Length == 0)
return false;
var lastIndex = segments.Length - 1;
if (segments.Length > 2)
{
var candidateIndex = -1;
var candidateLength = MinLeadingRelativePathSegmentLength;
for (var i = 0; i < lastIndex; i++)
{
if (string.IsNullOrWhiteSpace(segments[i]) || segments[i].Length <= candidateLength)
continue;
candidateIndex = i;
candidateLength = segments[i].Length;
}
if (candidateIndex >= 0)
{
segments[candidateIndex] = segments[candidateIndex].Substring(0, segments[candidateIndex].Length - 1);
return true;
}
segments = segments.Skip(1).ToArray();
return true;
}
if (segments.Length == 2)
{
if (segments[0].Length > MinSingleLeadingRelativePathSegmentLength)
{
segments[0] = segments[0].Substring(0, segments[0].Length - 1);
return true;
}
if (segments[lastIndex].Length > MinLastRelativePathSegmentLength)
{
segments[lastIndex] = segments[lastIndex].Substring(0, segments[lastIndex].Length - 1);
return true;
}
if (segments[0].Length > 1)
{
segments[0] = segments[0].Substring(0, segments[0].Length - 1);
return true;
}
if (segments[lastIndex].Length > 1)
{
segments[lastIndex] = segments[lastIndex].Substring(0, segments[lastIndex].Length - 1);
return true;
}
return false;
}
if (segments[lastIndex].Length > 1)
{
segments[lastIndex] = segments[lastIndex].Substring(0, segments[lastIndex].Length - 1);
return true;
}
return false;
}
private static bool TryShortenName(ref string folderName)
{
if (string.IsNullOrWhiteSpace(folderName) || folderName.Length <= 1)
return false;
folderName = folderName.Substring(0, folderName.Length - 1);
return true;
}
}
}