feat(configmanager): add RegexTransformerViewModel

Implements ViewModel for Regex transformer editor with:
- Column, Pattern, Replacement, IgnoreCase, NonMatchBehavior properties
- Mode toggle between Find & Replace and Match & Extract
- Live test/preview functionality with error handling
This commit is contained in:
Joseph Doherty
2026-01-22 07:17:53 -05:00
parent 8734c57c9b
commit 5058cd6802
@@ -1,5 +1,6 @@
using JdeScoping.ConfigManager.Models;
using System.Collections.ObjectModel;
using System.Text.RegularExpressions;
using System.Windows.Input;
namespace JdeScoping.ConfigManager.ViewModels.PipelineSteps;
@@ -278,6 +279,278 @@ public class JdeDateTransformerViewModel : TransformerStepViewModelBase
};
}
/// <summary>
/// View model for Regex transformer.
/// </summary>
public class RegexTransformerViewModel : TransformerStepViewModelBase
{
private string _columnName = string.Empty;
private string _pattern = string.Empty;
private string? _replacement = string.Empty;
private bool _isFindReplaceMode = true;
private bool _ignoreCase;
private NonMatchBehavior _nonMatchBehavior = NonMatchBehavior.KeepOriginal;
// Test feature fields
private string _testInput = string.Empty;
private string _testResultValue = string.Empty;
private string _testResultLabel = string.Empty;
private string _testResultIcon = string.Empty;
private string _testResultBackground = string.Empty;
private bool _hasTestResult;
private bool _hasTestError;
private string _testErrorMessage = string.Empty;
public RegexTransformerViewModel(TransformerModel model, Action onChanged) : base(onChanged)
{
_columnName = model.ColumnName ?? string.Empty;
_pattern = model.Pattern ?? string.Empty;
_replacement = model.Replacement;
_isFindReplaceMode = model.Replacement != null;
_ignoreCase = model.IgnoreCase;
_nonMatchBehavior = model.NonMatchBehavior;
TestPatternCommand = new RelayCommand(ExecuteTestPattern);
}
public RegexTransformerViewModel(Action onChanged) : base(onChanged)
{
TestPatternCommand = new RelayCommand(ExecuteTestPattern);
}
public override string TransformerType => "Regex";
public override string DisplayName => "Regex Transform";
public override string Icon => "󰑑"; // mdi-regex
public override string Summary => !string.IsNullOrEmpty(_columnName)
? $"{_columnName}: {(_isFindReplaceMode ? "Replace" : "Extract")}"
: "Configure...";
/// <summary>Gets or sets the column name to transform.</summary>
public string ColumnName
{
get => _columnName;
set
{
if (SetProperty(ref _columnName, value ?? string.Empty))
{
OnPropertyChanged(nameof(Summary));
NotifyChanged();
}
}
}
/// <summary>Gets or sets the regex pattern.</summary>
public string Pattern
{
get => _pattern;
set
{
if (SetProperty(ref _pattern, value ?? string.Empty))
{
ClearTestResult();
NotifyChanged();
}
}
}
/// <summary>Gets or sets the replacement string (Find &amp; Replace mode).</summary>
public string? Replacement
{
get => _replacement;
set
{
if (SetProperty(ref _replacement, value))
{
ClearTestResult();
NotifyChanged();
}
}
}
/// <summary>Gets or sets whether Find &amp; Replace mode is active.</summary>
public bool IsFindReplaceMode
{
get => _isFindReplaceMode;
set
{
if (SetProperty(ref _isFindReplaceMode, value))
{
OnPropertyChanged(nameof(IsMatchExtractMode));
OnPropertyChanged(nameof(PatternHelpText));
OnPropertyChanged(nameof(Summary));
ClearTestResult();
NotifyChanged();
}
}
}
/// <summary>Gets or sets whether Match &amp; Extract mode is active.</summary>
public bool IsMatchExtractMode
{
get => !_isFindReplaceMode;
set => IsFindReplaceMode = !value;
}
/// <summary>Gets the help text for the pattern field based on current mode.</summary>
public string PatternHelpText => _isFindReplaceMode
? "Pattern to search for in the column value"
: "Pattern with capture group - first group (parentheses) will be extracted";
/// <summary>Gets or sets whether matching is case-insensitive.</summary>
public bool IgnoreCase
{
get => _ignoreCase;
set
{
if (SetProperty(ref _ignoreCase, value))
{
ClearTestResult();
NotifyChanged();
}
}
}
/// <summary>Gets or sets the behavior when pattern doesn't match.</summary>
public NonMatchBehavior NonMatchBehavior
{
get => _nonMatchBehavior;
set
{
if (SetProperty(ref _nonMatchBehavior, value))
{
ClearTestResult();
NotifyChanged();
}
}
}
// Test feature properties
public string TestInput
{
get => _testInput;
set => SetProperty(ref _testInput, value ?? string.Empty);
}
public string TestResultValue
{
get => _testResultValue;
set => SetProperty(ref _testResultValue, value);
}
public string TestResultLabel
{
get => _testResultLabel;
set => SetProperty(ref _testResultLabel, value);
}
public string TestResultIcon
{
get => _testResultIcon;
set => SetProperty(ref _testResultIcon, value);
}
public string TestResultBackground
{
get => _testResultBackground;
set => SetProperty(ref _testResultBackground, value);
}
public bool HasTestResult
{
get => _hasTestResult;
set => SetProperty(ref _hasTestResult, value);
}
public bool HasTestError
{
get => _hasTestError;
set => SetProperty(ref _hasTestError, value);
}
public string TestErrorMessage
{
get => _testErrorMessage;
set => SetProperty(ref _testErrorMessage, value);
}
public ICommand TestPatternCommand { get; }
private void ExecuteTestPattern()
{
ClearTestResult();
if (string.IsNullOrEmpty(_pattern))
{
HasTestError = true;
TestErrorMessage = "Pattern is required";
return;
}
try
{
var options = _ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None;
var regex = new Regex(_pattern, options);
string result;
bool matched;
if (_isFindReplaceMode)
{
result = regex.Replace(_testInput, _replacement ?? string.Empty);
matched = regex.IsMatch(_testInput);
}
else
{
var match = regex.Match(_testInput);
if (match.Success && match.Groups.Count > 1)
{
result = match.Groups[1].Value;
matched = true;
}
else
{
matched = false;
result = _nonMatchBehavior switch
{
NonMatchBehavior.ReturnNull => "(null)",
NonMatchBehavior.ReturnEmpty => "(empty)",
_ => _testInput
};
}
}
HasTestResult = true;
TestResultValue = result;
TestResultLabel = matched ? "Output" : "No Match";
TestResultIcon = matched ? "✓" : "—";
TestResultBackground = matched ? "#22C55E" : "#F59E0B";
}
catch (RegexParseException ex)
{
HasTestError = true;
TestErrorMessage = ex.Message;
}
}
private void ClearTestResult()
{
HasTestResult = false;
HasTestError = false;
TestResultValue = string.Empty;
TestResultLabel = string.Empty;
TestErrorMessage = string.Empty;
}
public override TransformerModel ToModel() => new()
{
Type = TransformerType,
ColumnName = _columnName,
Pattern = _pattern,
Replacement = _isFindReplaceMode ? _replacement : null,
IgnoreCase = _ignoreCase,
NonMatchBehavior = _nonMatchBehavior
};
}
/// <summary>
/// Factory methods for creating transformer view models.
/// </summary>