diff --git a/DD.Persistence.Test/TreeBuilderShould.cs b/DD.Persistence.Test/TreeBuilderShould.cs new file mode 100644 index 0000000..f96f85b --- /dev/null +++ b/DD.Persistence.Test/TreeBuilderShould.cs @@ -0,0 +1,107 @@ +using DD.Persistence.Filter.Interpreter; +using DD.Persistence.Filter.Models; +using DD.Persistence.Filter.Models.Enumerations; +using Newtonsoft.Json; + +namespace DD.Persistence.Test; +public class TreeBuilderShould +{ + [Fact] + public void TestTreeBuilding() + { + //arrange + var treeString = "(\"A\"==1)||(\"B\"==2)&&(\"C\"==3)||((\"D\"==4)||(\"E\"==5))&&(\"F\"==6)"; + + //act + var root = treeString.BuildTree(); + + //assert + Assert.NotNull(root); + + var expectedRoot = JsonConvert.SerializeObject(new TVertex( + OperationEnum.And, + new TVertex( + OperationEnum.And, + new TVertex( + OperationEnum.Or, + new TLeaf(OperationEnum.Equal, "A", 1), + new TLeaf(OperationEnum.Equal, "B", 2) + ), + new TVertex( + OperationEnum.Or, + new TLeaf(OperationEnum.Equal, "C", 3), + new TVertex( + OperationEnum.Or, + new TLeaf(OperationEnum.Equal, "D", 4), + new TLeaf(OperationEnum.Equal, "E", 5) + ) + ) + ), + new TLeaf(OperationEnum.Equal, "F", 6) + )); + var actualRoot = JsonConvert.SerializeObject(root); + Assert.Equal(expectedRoot, actualRoot); + } + + [Fact] + public void TestTreeOperations() + { + //arrange + var treeString = "(\"A\"==1)||(\"B\"!=1)||(\"C\">1)||(\"D\">=1)||(\"E\"<1)||(\"F\"<=1)"; + + //act + var root = treeString.BuildTree(); + + //assert + Assert.NotNull(root); + + var expectedRoot = JsonConvert.SerializeObject(new TVertex( + OperationEnum.Or, + new TVertex( + OperationEnum.Or, + new TVertex( + OperationEnum.Or, + new TLeaf(OperationEnum.Equal, "A", 1), + new TLeaf(OperationEnum.NotEqual, "B", 1) + ), + new TVertex( + OperationEnum.Or, + new TLeaf(OperationEnum.Greate, "C", 1), + new TLeaf(OperationEnum.GreateOrEqual, "D", 1) + ) + ), + new TVertex( + OperationEnum.Or, + new TLeaf(OperationEnum.Less, "E", 1), + new TLeaf(OperationEnum.LessOrEqual, "F", 1) + ) + )); + var actualRoot = JsonConvert.SerializeObject(root); + Assert.Equal(expectedRoot, actualRoot); + } + + [Fact] + public void TestLeafValues() + { + //arrange + var treeString = "(\"A\"==1.2345)||(\"B\"==12345)||(\"C\"==\"12345\")"; + + //act + var root = treeString.BuildTree(); + + //assert + Assert.NotNull(root); + + var expectedRoot = JsonConvert.SerializeObject(new TVertex( + OperationEnum.Or, + new TVertex( + OperationEnum.Or, + new TLeaf(OperationEnum.Equal, "A", 1.2345), + new TLeaf(OperationEnum.Equal, "B", 12345) + ), + new TLeaf(OperationEnum.Equal, "C", "12345") + )); + var actualRoot = JsonConvert.SerializeObject(root); + Assert.Equal(expectedRoot, actualRoot); + } +} diff --git a/DD.Persistence/Filter/Models/Abstractions/INodeVisitor.cs b/DD.Persistence/Filter/Models/Abstractions/INodeVisitor.cs new file mode 100644 index 0000000..f039ee1 --- /dev/null +++ b/DD.Persistence/Filter/Models/Abstractions/INodeVisitor.cs @@ -0,0 +1,22 @@ +namespace DD.Persistence.Filter.Models.Abstractions; + +/// +/// Посетитель бинарного дерева +/// +/// +public interface INodeVisitor +{ + /// + /// Посетить узел + /// + /// + /// + TVisitResult Visit(TVertex vertex); + + /// + /// Посетить лист + /// + /// + /// + TVisitResult Visit(TLeaf leaf); +} diff --git a/DD.Persistence/Filter/Models/Abstractions/TNode.cs b/DD.Persistence/Filter/Models/Abstractions/TNode.cs new file mode 100644 index 0000000..226a3ef --- /dev/null +++ b/DD.Persistence/Filter/Models/Abstractions/TNode.cs @@ -0,0 +1,28 @@ +using DD.Persistence.Filter.Models.Enumerations; + +namespace DD.Persistence.Filter.Models.Abstractions; + +/// +/// Абстрактная модель вершины +/// +public abstract class TNode +{ + /// + public TNode(OperationEnum operation) + { + Operation = operation; + } + + /// + /// Логическая операция + /// + public OperationEnum Operation { get; } + + /// + /// Принять посетителя + /// + /// + /// + /// + public abstract TVisitResult AcceptVisitor(INodeVisitor visitor); +} diff --git a/DD.Persistence/Filter/Models/Enumerations/OperationEnum.cs b/DD.Persistence/Filter/Models/Enumerations/OperationEnum.cs new file mode 100644 index 0000000..8f22b86 --- /dev/null +++ b/DD.Persistence/Filter/Models/Enumerations/OperationEnum.cs @@ -0,0 +1,47 @@ +namespace DD.Persistence.Filter.Models.Enumerations; + +/// +/// Логические операции +/// +public enum OperationEnum +{ + /// + /// И + /// + And = 1, + + /// + /// ИЛИ + /// + Or = 2, + + /// + /// РАВНО + /// + Equal = 3, + + /// + /// НЕ РАВНО + /// + NotEqual = 4, + + /// + /// БОЛЬШЕ + /// + Greate = 5, + + /// + /// БОЛЬШЕ ЛИБО РАВНО + /// + GreateOrEqual = 6, + + /// + /// МЕНЬШЕ + /// + Less = 7, + + /// + /// МЕНЬШЕ ЛИБО РАВНО + /// + LessOrEqual = 8 +} diff --git a/DD.Persistence/Filter/Models/TLeaf.cs b/DD.Persistence/Filter/Models/TLeaf.cs new file mode 100644 index 0000000..d974ced --- /dev/null +++ b/DD.Persistence/Filter/Models/TLeaf.cs @@ -0,0 +1,33 @@ +using DD.Persistence.Filter.Models.Abstractions; +using DD.Persistence.Filter.Models.Enumerations; + +namespace DD.Persistence.Filter.Models; + +/// +/// Модель листа +/// +public class TLeaf : TNode +{ + /// + /// Наименование поля + /// + public string PropName { get; } + + /// + /// Значение для фильтрации + /// + public object? Value { get; } + + /// + public TLeaf(OperationEnum operation, string fieldName, object? value) : base(operation) + { + PropName = fieldName; + Value = value; + } + + /// + public override TVisitResult AcceptVisitor(INodeVisitor visitor) + { + return visitor.Visit(this); + } +} diff --git a/DD.Persistence/Filter/Models/TVertex.cs b/DD.Persistence/Filter/Models/TVertex.cs new file mode 100644 index 0000000..3f6c200 --- /dev/null +++ b/DD.Persistence/Filter/Models/TVertex.cs @@ -0,0 +1,33 @@ +using DD.Persistence.Filter.Models.Abstractions; +using DD.Persistence.Filter.Models.Enumerations; + +namespace DD.Persistence.Filter.Models; + +/// +/// Модель узла +/// +public class TVertex : TNode +{ + /// + /// Левый потомок + /// + public TNode Left { get; } + + /// + /// Правый потомок + /// + public TNode Rigth { get; } + + /// + public TVertex(OperationEnum operation, TNode left, TNode rigth) : base(operation) + { + Left = left; + Rigth = rigth; + } + + /// + public override TVisitResult AcceptVisitor(INodeVisitor visitor) + { + return visitor.Visit(this); + } +} diff --git a/DD.Persistence/Filter/TreeBuilder/Abstractions/ITreeBuilder.cs b/DD.Persistence/Filter/TreeBuilder/Abstractions/ITreeBuilder.cs new file mode 100644 index 0000000..b512a53 --- /dev/null +++ b/DD.Persistence/Filter/TreeBuilder/Abstractions/ITreeBuilder.cs @@ -0,0 +1,16 @@ +using DD.Persistence.Filter.Models.Abstractions; + +namespace DD.Persistence.Filter.Interpreter.Abstractions; + +/// +/// Интерпретатор для построения дерева +/// +public interface ITreeBuilder +{ + /// + /// Построить дерево + /// + /// Дерево в виде строки + /// Корень дерева в виде вершины + TNode? Build(string treeString); +} \ No newline at end of file diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Abstractions/IExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Abstractions/IExpression.cs new file mode 100644 index 0000000..b28bde8 --- /dev/null +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Abstractions/IExpression.cs @@ -0,0 +1,27 @@ +using DD.Persistence.Filter.Models.Enumerations; + +namespace DD.Persistence.Filter.Interpreter.Expressions.Abstract; + +/// +/// Интерфейс для выражений +/// +public interface IExpression +{ + /// + /// Получить логическую операцию + /// + /// + public OperationEnum GetOperation(); + + /// + /// Получить логическую операцию в виде строки (для регулярных выражений) + /// + /// + public string GetOperationString(); + + /// + /// Реализация правила + /// + /// + public void Interpret(InterpreterContext context); +} diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/Abstractions/NonTerminalExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/Abstractions/NonTerminalExpression.cs new file mode 100644 index 0000000..137ec4b --- /dev/null +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/Abstractions/NonTerminalExpression.cs @@ -0,0 +1,83 @@ +using DD.Persistence.Extensions; +using DD.Persistence.Filter.Interpreter.Expressions.Abstract; +using DD.Persistence.Filter.Models; +using DD.Persistence.Filter.Models.Enumerations; +using System.Text.RegularExpressions; + +namespace DD.Persistence.Filter.Interpreter.Expressions.NonTerminal.Base; + +/// +/// Абстрактный класс для нетерминальных выражений +/// +public abstract class NonTerminalExpression : IExpression +{ + /// + /// Реализация правила для нетерминальных выражений + /// + /// + public void Interpret(InterpreterContext context) + { + var operation = GetOperation(); + var operationString = GetOperationString(); + + var matches = GetMatches(context, operation, operationString); + while (matches.Any()) + { + matches.ForEach(m => + { + var matchString = m.ToString(); + + var separator = operationString.Replace("\\", string.Empty); + var pair = matchString + .Trim(['(', ')']) + .Split(separator) + .Select(e => int.Parse(e)); + + var leftNode = context.treeNodes + .FirstOrDefault(e => e.Key == pair.First()) + .Value; + var rigthNode = context.treeNodes + .FirstOrDefault(e => e.Key == pair.Last()) + .Value; + var node = new TVertex(operation, leftNode, rigthNode); + + var key = context.treeNodes.Count(); + context.treeNodes.Add(key, node); + + var keyString = key.ToString(); + context.treeString = context.treeString.Replace(matchString, keyString); + }); + + matches = GetMatches(context, operation, operationString); + } + + var isRoot = int.TryParse(context.treeString, out _); + if (isRoot) + { + context.treeString = string.Empty; + context.root = context.treeNodes.Last().Value; + } + } + + /// + public abstract OperationEnum GetOperation(); + + /// + public abstract string GetOperationString(); + + + /// + /// Получить из акткуального состояния строки все совпадения для текущего выражения + /// + private IEnumerable GetMatches(InterpreterContext context, OperationEnum operation, string operationString) + { + string pattern = context.treeString.Contains('(') && context.treeString.Contains(')') + ? $@"\(\d+{operationString}\d+\)" : $@"\d+{operationString}\d+"; + Regex regex = new Regex(pattern); + var matches = regex + .Matches(context.treeString) + .ToArray(); + + return matches; + } +} diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/AndExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/AndExpression.cs new file mode 100644 index 0000000..4a3d363 --- /dev/null +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/AndExpression.cs @@ -0,0 +1,24 @@ +using DD.Persistence.Filter.Interpreter.Expressions.NonTerminal.Base; +using DD.Persistence.Filter.Models.Enumerations; + +namespace DD.Persistence.Filter.Interpreter.Expressions.NonTerminal; + +/// +/// Выражение для "И" +/// +public class AndExpression : NonTerminalExpression +{ + private const string AndString = "&&"; + + /// + public override OperationEnum GetOperation() + { + return OperationEnum.And; + } + + /// + public override string GetOperationString() + { + return AndString; + } +} diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/OrExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/OrExpression.cs new file mode 100644 index 0000000..e47b935 --- /dev/null +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/OrExpression.cs @@ -0,0 +1,24 @@ +using DD.Persistence.Filter.Interpreter.Expressions.NonTerminal.Base; +using DD.Persistence.Filter.Models.Enumerations; + +namespace DD.Persistence.Filter.Interpreter.Expressions.NonTerminal; + +/// +/// Выражение для "ИЛИ" +/// +public class OrExpression : NonTerminalExpression +{ + private const string OrString = @"\|\|"; + + /// + public override OperationEnum GetOperation() + { + return OperationEnum.Or; + } + + /// + public override string GetOperationString() + { + return OrString; + } +} diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs new file mode 100644 index 0000000..ada1f56 --- /dev/null +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs @@ -0,0 +1,79 @@ +using DD.Persistence.Extensions; +using DD.Persistence.Filter.Interpreter.Expressions.Abstract; +using DD.Persistence.Filter.Models; +using DD.Persistence.Filter.Models.Enumerations; +using System.Text.RegularExpressions; + +namespace DD.Persistence.Filter.Interpreter.Expressions.Terminal.Base; + +/// +/// Абстрактный класс для терминальных выражений +/// +public abstract class TerminalExpression : IExpression +{ + /// + /// Реализация правила для терминальных выражений + /// + /// + public void Interpret(InterpreterContext context) + { + var operation = GetOperation(); + var operationString = GetOperationString(); + + var matches = GetMatches(context, operation, operationString); + matches.ForEach(m => + { + var matchString = m.ToString(); + + var pair = matchString + .Trim(['(', ')']) + .Split(operationString); + var fieldName = pair + .First() + .Trim('\"'); + var value = ParseValue(pair.Last()); + var node = new TLeaf(operation, fieldName, value); + + var key = context.treeNodes.Count(); + context.treeNodes.Add(key, node); + + var keyString = key.ToString(); + context.treeString = context.treeString.Replace(matchString, keyString); + }); + } + + /// + public abstract OperationEnum GetOperation(); + + /// + public abstract string GetOperationString(); + + /// + /// Получить из акткуального состояния строки все совпадения для текущего выражения + /// + private IEnumerable GetMatches(InterpreterContext context, OperationEnum operation, string operationString) + { + string pattern = $@"\([^()]*{operationString}.*?\)"; + Regex regex = new Regex(pattern); + var matches = regex.Matches(context.treeString); + + return matches; + } + + private object? ParseValue(string value) + { + value = value.Replace('.', ','); + if (value.Contains(',') && double.TryParse(value, out _)) + { + return double.Parse(value); + } + + if (int.TryParse(value, out _)) + { + return int.Parse(value); + } + + value = value.Trim('\"'); + return value; + } +} diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/EqualExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/EqualExpression.cs new file mode 100644 index 0000000..4c78e36 --- /dev/null +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/EqualExpression.cs @@ -0,0 +1,24 @@ +using DD.Persistence.Filter.Interpreter.Expressions.Terminal.Base; +using DD.Persistence.Filter.Models.Enumerations; + +namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; + +/// +/// Выражение для "РАВНО" +/// +public class EqualExpression : TerminalExpression +{ + private const string EqualString = "=="; + + /// + public override OperationEnum GetOperation() + { + return OperationEnum.Equal; + } + + /// + public override string GetOperationString() + { + return EqualString; + } +} diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessExpression.cs new file mode 100644 index 0000000..a7e6902 --- /dev/null +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessExpression.cs @@ -0,0 +1,24 @@ +using DD.Persistence.Filter.Interpreter.Expressions.Terminal.Base; +using DD.Persistence.Filter.Models.Enumerations; + +namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; + +/// +/// Выражение для "МЕНЬШЕ" +/// +public class LessExpression : TerminalExpression +{ + private const string EqualString = "<"; + + /// + public override OperationEnum GetOperation() + { + return OperationEnum.Less; + } + + /// + public override string GetOperationString() + { + return EqualString; + } +} diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessOrEqualExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessOrEqualExpression.cs new file mode 100644 index 0000000..9a5c6be --- /dev/null +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessOrEqualExpression.cs @@ -0,0 +1,24 @@ +using DD.Persistence.Filter.Interpreter.Expressions.Terminal.Base; +using DD.Persistence.Filter.Models.Enumerations; + +namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; + +/// +/// Выражение для "МЕНЬШЕ ЛИБО РАВНО" +/// +public class LessOrEqualExpression : TerminalExpression +{ + private const string EqualString = "<="; + + /// + public override OperationEnum GetOperation() + { + return OperationEnum.LessOrEqual; + } + + /// + public override string GetOperationString() + { + return EqualString; + } +} diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreExpression.cs new file mode 100644 index 0000000..0d6c8b0 --- /dev/null +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreExpression.cs @@ -0,0 +1,24 @@ +using DD.Persistence.Filter.Interpreter.Expressions.Terminal.Base; +using DD.Persistence.Filter.Models.Enumerations; + +namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; + +/// +/// Выражение для "БОЛЬШЕ" +/// +public class MoreExpression : TerminalExpression +{ + private const string EqualString = ">"; + + /// + public override OperationEnum GetOperation() + { + return OperationEnum.Greate; + } + + /// + public override string GetOperationString() + { + return EqualString; + } +} diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreOrEqualExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreOrEqualExpression.cs new file mode 100644 index 0000000..1dd2db4 --- /dev/null +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreOrEqualExpression.cs @@ -0,0 +1,24 @@ +using DD.Persistence.Filter.Interpreter.Expressions.Terminal.Base; +using DD.Persistence.Filter.Models.Enumerations; + +namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; + +/// +/// Выражение для "БОЛЬШЕ ЛИБО РАВНО" +/// +public class MoreOrEqualExpression : TerminalExpression +{ + private const string EqualString = ">="; + + /// + public override OperationEnum GetOperation() + { + return OperationEnum.GreateOrEqual; + } + + /// + public override string GetOperationString() + { + return EqualString; + } +} diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/NotEqualExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/NotEqualExpression.cs new file mode 100644 index 0000000..b51817a --- /dev/null +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/NotEqualExpression.cs @@ -0,0 +1,24 @@ +using DD.Persistence.Filter.Interpreter.Expressions.Terminal.Base; +using DD.Persistence.Filter.Models.Enumerations; + +namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; + +/// +/// Выражение для "НЕРАВНО" +/// +public class NotEqualExpression : TerminalExpression +{ + private const string NotEqulString = "!="; + + /// + public override OperationEnum GetOperation() + { + return OperationEnum.NotEqual; + } + + /// + public override string GetOperationString() + { + return NotEqulString; + } +} diff --git a/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs b/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs new file mode 100644 index 0000000..72e0384 --- /dev/null +++ b/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs @@ -0,0 +1,30 @@ +using DD.Persistence.Filter.Models.Abstractions; + +namespace DD.Persistence.Filter.Interpreter; + +/// +/// Контекст интерпретатора +/// +public class InterpreterContext +{ + /// + /// Корень дерева (результат интерпретации) + /// + public TNode? root { get; set; } + + /// + /// Дерево в виде строки (входной параметр) + /// + public string treeString { get; set; } + + /// + /// Проиндексированные вершины дерева + /// + public Dictionary treeNodes { get; set; } = []; + + /// + public InterpreterContext(string theeString) + { + this.treeString = theeString; + } +} diff --git a/DD.Persistence/Filter/TreeBuilder/TreeBuilder.cs b/DD.Persistence/Filter/TreeBuilder/TreeBuilder.cs new file mode 100644 index 0000000..224c74c --- /dev/null +++ b/DD.Persistence/Filter/TreeBuilder/TreeBuilder.cs @@ -0,0 +1,52 @@ +using DD.Persistence.Filter.Interpreter.Expressions.Abstract; +using DD.Persistence.Filter.Interpreter.Expressions.NonTerminal; +using DD.Persistence.Filter.Models.Abstractions; +using DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; + +namespace DD.Persistence.Filter.Interpreter; + +/// +/// Строитель бинарных деревьев +/// +public static class TreeBuilder +{ + + /// + /// Построить бинарное дерево логических операций сравнения из строки + /// + /// + /// + public static TNode? BuildTree(this string treeString) + { + InterpreterContext context = new InterpreterContext(treeString); + + // Порядок важен + List terminalExpressions = new List + { + new EqualExpression(), + new NotEqualExpression(), + new MoreOrEqualExpression(), + new LessOrEqualExpression(), + new MoreExpression(), + new LessExpression() + }; + terminalExpressions.ForEach(e => { + e.Interpret(context); + }); + + // Порядок важен + List nonTerminalExpressions = new List + { + new OrExpression(), + new AndExpression() + }; + while (!string.IsNullOrEmpty(context.treeString)) + { + nonTerminalExpressions.ForEach(e => { + e.Interpret(context); + }); + } + + return context.root; + } +} diff --git a/DD.Persistence/Filter/Visitors/NodeVisitor.cs b/DD.Persistence/Filter/Visitors/NodeVisitor.cs new file mode 100644 index 0000000..07c7792 --- /dev/null +++ b/DD.Persistence/Filter/Visitors/NodeVisitor.cs @@ -0,0 +1,24 @@ +using DD.Persistence.Filter.Models; +using DD.Persistence.Filter.Models.Abstractions; + +namespace DD.Persistence.Filter.Visitors; + +/// +public class NodeVisitor : INodeVisitor +{ + private readonly Func _ifVertex; + private readonly Func _ifLeaf; + + /// + public NodeVisitor(Func ifVertex, Func ifLeaf) + { + _ifVertex = ifVertex; + _ifLeaf = ifLeaf; + } + + /// + public TVisitResult Visit(TVertex vertex) => _ifVertex(vertex); + + /// + public TVisitResult Visit(TLeaf leaf) => _ifLeaf(leaf); +}