From 287d6e7111e4fbf3f13fc88f97ccbcf8e792aa26 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Wed, 5 Feb 2025 08:46:08 +0500 Subject: [PATCH 1/5] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D1=8C=20=D0=BF=D0=BE=D1=81=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B4=D0=B5=D1=80=D0=B5=D0=B2=D0=B0?= =?UTF-8?q?=20=D0=B8=D0=B7=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DD.Persistence.Test/TreeBuilderShould.cs | 107 ++++++++++++++++++ .../Models/Abstractions/INodeVisitor.cs | 22 ++++ .../Filter/Models/Abstractions/TNode.cs | 28 +++++ .../Models/Enumerations/OperationEnum.cs | 47 ++++++++ DD.Persistence/Filter/Models/TLeaf.cs | 33 ++++++ DD.Persistence/Filter/Models/TVertex.cs | 33 ++++++ .../TreeBuilder/Abstractions/ITreeBuilder.cs | 16 +++ .../Expressions/Abstractions/IExpression.cs | 27 +++++ .../Abstractions/NonTerminalExpression.cs | 83 ++++++++++++++ .../Expressions/NonTerminal/AndExpression.cs | 24 ++++ .../Expressions/NonTerminal/OrExpression.cs | 24 ++++ .../Terminal/Abstract/TerminalExpression.cs | 79 +++++++++++++ .../Expressions/Terminal/EqualExpression.cs | 24 ++++ .../Expressions/Terminal/LessExpression.cs | 24 ++++ .../Terminal/LessOrEqualExpression.cs | 24 ++++ .../Expressions/Terminal/MoreExpression.cs | 24 ++++ .../Terminal/MoreOrEqualExpression.cs | 24 ++++ .../Terminal/NotEqualExpression.cs | 24 ++++ .../Filter/TreeBuilder/InterpreterContext.cs | 31 +++++ .../Filter/TreeBuilder/TreeBuilder.cs | 52 +++++++++ DD.Persistence/Filter/Visitors/NodeVisitor.cs | 24 ++++ 21 files changed, 774 insertions(+) create mode 100644 DD.Persistence.Test/TreeBuilderShould.cs create mode 100644 DD.Persistence/Filter/Models/Abstractions/INodeVisitor.cs create mode 100644 DD.Persistence/Filter/Models/Abstractions/TNode.cs create mode 100644 DD.Persistence/Filter/Models/Enumerations/OperationEnum.cs create mode 100644 DD.Persistence/Filter/Models/TLeaf.cs create mode 100644 DD.Persistence/Filter/Models/TVertex.cs create mode 100644 DD.Persistence/Filter/TreeBuilder/Abstractions/ITreeBuilder.cs create mode 100644 DD.Persistence/Filter/TreeBuilder/Expressions/Abstractions/IExpression.cs create mode 100644 DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/Abstractions/NonTerminalExpression.cs create mode 100644 DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/AndExpression.cs create mode 100644 DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/OrExpression.cs create mode 100644 DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs create mode 100644 DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/EqualExpression.cs create mode 100644 DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessExpression.cs create mode 100644 DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessOrEqualExpression.cs create mode 100644 DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreExpression.cs create mode 100644 DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreOrEqualExpression.cs create mode 100644 DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/NotEqualExpression.cs create mode 100644 DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs create mode 100644 DD.Persistence/Filter/TreeBuilder/TreeBuilder.cs create mode 100644 DD.Persistence/Filter/Visitors/NodeVisitor.cs 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..f07cd10 --- /dev/null +++ b/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs @@ -0,0 +1,31 @@ +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 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); +} -- 2.45.2 From cb0508afe9ea713be7d7a9abddafe026d50d1591 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Wed, 5 Feb 2025 08:48:20 +0500 Subject: [PATCH 2/5] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D1=82=D1=8C=20?= =?UTF-8?q?=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D0=B9=20=D0=BA=D0=BE=D0=BC=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D0=B0=D1=80=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs b/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs index f07cd10..72e0384 100644 --- a/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs +++ b/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs @@ -20,7 +20,6 @@ public class InterpreterContext /// /// Проиндексированные вершины дерева /// - //public Dictionary treeNodes { get; set; } = []; public Dictionary treeNodes { get; set; } = []; /// -- 2.45.2 From e3c1d02650371eedf299ba3f71b79432f0b18932 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Wed, 5 Feb 2025 10:58:26 +0500 Subject: [PATCH 3/5] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E=20#1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TimestampedValuesServiceShould.cs | 5 ++--- .../TreeBuilder/Abstractions/ITreeBuilder.cs | 16 ---------------- 2 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 DD.Persistence/Filter/TreeBuilder/Abstractions/ITreeBuilder.cs diff --git a/DD.Persistence.Test/TimestampedValuesServiceShould.cs b/DD.Persistence.Test/TimestampedValuesServiceShould.cs index 0acb6de..165d4e0 100644 --- a/DD.Persistence.Test/TimestampedValuesServiceShould.cs +++ b/DD.Persistence.Test/TimestampedValuesServiceShould.cs @@ -3,12 +3,12 @@ using DD.Persistence.Repositories; using DD.Persistence.Services; using NSubstitute; -namespace DD.Persistence.Repository.Test; +namespace DD.Persistence.Test; public class TimestampedValuesServiceShould { private readonly ITimestampedValuesRepository timestampedValuesRepository = Substitute.For(); private readonly IDataSchemeRepository dataSchemeRepository = Substitute.For(); - private TimestampedValuesService timestampedValuesService; + private readonly TimestampedValuesService timestampedValuesService; public TimestampedValuesServiceShould() { @@ -40,7 +40,6 @@ public class TimestampedValuesServiceShould private static IEnumerable Generate(int countToCreate, DateTimeOffset from) { - var result = new List(); for (int i = 0; i < countToCreate; i++) { var values = new Dictionary() diff --git a/DD.Persistence/Filter/TreeBuilder/Abstractions/ITreeBuilder.cs b/DD.Persistence/Filter/TreeBuilder/Abstractions/ITreeBuilder.cs deleted file mode 100644 index b512a53..0000000 --- a/DD.Persistence/Filter/TreeBuilder/Abstractions/ITreeBuilder.cs +++ /dev/null @@ -1,16 +0,0 @@ -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 -- 2.45.2 From 560073ed07cfd172bfd4941dd89e0c46d6e80e2b Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Wed, 5 Feb 2025 11:45:47 +0500 Subject: [PATCH 4/5] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E=20#2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TreeBuilder/Expressions/Abstractions/IExpression.cs | 8 ++++---- .../NonTerminal/Abstractions/NonTerminalExpression.cs | 2 +- .../TreeBuilder/Expressions/NonTerminal/AndExpression.cs | 2 +- .../TreeBuilder/Expressions/NonTerminal/OrExpression.cs | 2 +- .../Expressions/Terminal/Abstract/TerminalExpression.cs | 2 +- .../TreeBuilder/Expressions/Terminal/EqualExpression.cs | 2 +- .../TreeBuilder/Expressions/Terminal/LessExpression.cs | 2 +- .../Expressions/Terminal/LessOrEqualExpression.cs | 2 +- .../TreeBuilder/Expressions/Terminal/MoreExpression.cs | 2 +- .../Expressions/Terminal/MoreOrEqualExpression.cs | 2 +- .../Expressions/Terminal/NotEqualExpression.cs | 2 +- DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs | 2 +- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Abstractions/IExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Abstractions/IExpression.cs index b28bde8..c1418bf 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Abstractions/IExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Abstractions/IExpression.cs @@ -5,23 +5,23 @@ namespace DD.Persistence.Filter.Interpreter.Expressions.Abstract; /// /// Интерфейс для выражений /// -public interface IExpression +interface IExpression { /// /// Получить логическую операцию /// /// - public OperationEnum GetOperation(); + OperationEnum GetOperation(); /// /// Получить логическую операцию в виде строки (для регулярных выражений) /// /// - public string GetOperationString(); + string GetOperationString(); /// /// Реализация правила /// /// - public void Interpret(InterpreterContext context); + 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 index 137ec4b..d543b8b 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/Abstractions/NonTerminalExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/Abstractions/NonTerminalExpression.cs @@ -9,7 +9,7 @@ namespace DD.Persistence.Filter.Interpreter.Expressions.NonTerminal.Base; /// /// Абстрактный класс для нетерминальных выражений /// -public abstract class NonTerminalExpression : IExpression +abstract class NonTerminalExpression : IExpression { /// /// Реализация правила для нетерминальных выражений diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/AndExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/AndExpression.cs index 4a3d363..599315c 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/AndExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/AndExpression.cs @@ -6,7 +6,7 @@ namespace DD.Persistence.Filter.Interpreter.Expressions.NonTerminal; /// /// Выражение для "И" /// -public class AndExpression : NonTerminalExpression +class AndExpression : NonTerminalExpression { private const string AndString = "&&"; diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/OrExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/OrExpression.cs index e47b935..6aa6c69 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/OrExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/OrExpression.cs @@ -6,7 +6,7 @@ namespace DD.Persistence.Filter.Interpreter.Expressions.NonTerminal; /// /// Выражение для "ИЛИ" /// -public class OrExpression : NonTerminalExpression +class OrExpression : NonTerminalExpression { private const string OrString = @"\|\|"; diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs index ada1f56..5d2caed 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs @@ -9,7 +9,7 @@ namespace DD.Persistence.Filter.Interpreter.Expressions.Terminal.Base; /// /// Абстрактный класс для терминальных выражений /// -public abstract class TerminalExpression : IExpression +abstract class TerminalExpression : IExpression { /// /// Реализация правила для терминальных выражений diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/EqualExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/EqualExpression.cs index 4c78e36..aec5a6b 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/EqualExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/EqualExpression.cs @@ -6,7 +6,7 @@ namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; /// /// Выражение для "РАВНО" /// -public class EqualExpression : TerminalExpression +class EqualExpression : TerminalExpression { private const string EqualString = "=="; diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessExpression.cs index a7e6902..7382e87 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessExpression.cs @@ -6,7 +6,7 @@ namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; /// /// Выражение для "МЕНЬШЕ" /// -public class LessExpression : TerminalExpression +class LessExpression : TerminalExpression { private const string EqualString = "<"; diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessOrEqualExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessOrEqualExpression.cs index 9a5c6be..c706843 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessOrEqualExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessOrEqualExpression.cs @@ -6,7 +6,7 @@ namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; /// /// Выражение для "МЕНЬШЕ ЛИБО РАВНО" /// -public class LessOrEqualExpression : TerminalExpression +class LessOrEqualExpression : TerminalExpression { private const string EqualString = "<="; diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreExpression.cs index 0d6c8b0..cbc3d82 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreExpression.cs @@ -6,7 +6,7 @@ namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; /// /// Выражение для "БОЛЬШЕ" /// -public class MoreExpression : TerminalExpression +class MoreExpression : TerminalExpression { private const string EqualString = ">"; diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreOrEqualExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreOrEqualExpression.cs index 1dd2db4..a6a8345 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreOrEqualExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreOrEqualExpression.cs @@ -6,7 +6,7 @@ namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; /// /// Выражение для "БОЛЬШЕ ЛИБО РАВНО" /// -public class MoreOrEqualExpression : TerminalExpression +class MoreOrEqualExpression : TerminalExpression { private const string EqualString = ">="; diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/NotEqualExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/NotEqualExpression.cs index b51817a..49031f1 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/NotEqualExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/NotEqualExpression.cs @@ -6,7 +6,7 @@ namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; /// /// Выражение для "НЕРАВНО" /// -public class NotEqualExpression : TerminalExpression +class NotEqualExpression : TerminalExpression { private const string NotEqulString = "!="; diff --git a/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs b/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs index 72e0384..86e0ef8 100644 --- a/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs +++ b/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs @@ -5,7 +5,7 @@ namespace DD.Persistence.Filter.Interpreter; /// /// Контекст интерпретатора /// -public class InterpreterContext +class InterpreterContext { /// /// Корень дерева (результат интерпретации) -- 2.45.2 From 63e6816e350c49b50859e3fed31771c490089f1a Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Wed, 5 Feb 2025 12:10:01 +0500 Subject: [PATCH 5/5] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E=20#3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SetpointRepositoryShould.cs | 1 - ...reeBuilderShould.cs => TreeBuilderTest.cs} | 12 +++---- .../Expressions/Abstractions/IExpression.cs | 2 +- .../Abstractions/NonTerminalExpression.cs | 34 +++++++++---------- .../Expressions/NonTerminal/AndExpression.cs | 6 ++-- .../Expressions/NonTerminal/OrExpression.cs | 6 ++-- .../Terminal/Abstract/TerminalExpression.cs | 20 +++++------ .../Expressions/Terminal/EqualExpression.cs | 4 +-- .../Expressions/Terminal/LessExpression.cs | 4 +-- .../Terminal/LessOrEqualExpression.cs | 4 +-- .../Expressions/Terminal/MoreExpression.cs | 4 +-- .../Terminal/MoreOrEqualExpression.cs | 4 +-- .../Terminal/NotEqualExpression.cs | 4 +-- .../{TreeBuilder.cs => FilterTreeBuilder.cs} | 34 ++++++++++--------- .../Filter/TreeBuilder/InterpreterContext.cs | 10 +++--- 15 files changed, 75 insertions(+), 74 deletions(-) rename DD.Persistence.Test/{TreeBuilderShould.cs => TreeBuilderTest.cs} (93%) rename DD.Persistence/Filter/TreeBuilder/{TreeBuilder.cs => FilterTreeBuilder.cs} (58%) diff --git a/DD.Persistence.Repository.Test/SetpointRepositoryShould.cs b/DD.Persistence.Repository.Test/SetpointRepositoryShould.cs index 6b05ff3..c9f6088 100644 --- a/DD.Persistence.Repository.Test/SetpointRepositoryShould.cs +++ b/DD.Persistence.Repository.Test/SetpointRepositoryShould.cs @@ -29,7 +29,6 @@ public class SetpointRepositoryShould : IClassFixture var value = GetJsonFromObject(22); await sut.Add(id, value, Guid.NewGuid(), CancellationToken.None); - var t = fixture.dbContainer.GetConnectionString(); //act var result = await sut.GetCurrent([id], CancellationToken.None); diff --git a/DD.Persistence.Test/TreeBuilderShould.cs b/DD.Persistence.Test/TreeBuilderTest.cs similarity index 93% rename from DD.Persistence.Test/TreeBuilderShould.cs rename to DD.Persistence.Test/TreeBuilderTest.cs index f96f85b..1259ca7 100644 --- a/DD.Persistence.Test/TreeBuilderShould.cs +++ b/DD.Persistence.Test/TreeBuilderTest.cs @@ -1,13 +1,13 @@ -using DD.Persistence.Filter.Interpreter; -using DD.Persistence.Filter.Models; +using DD.Persistence.Filter.Models; using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.TreeBuilder; using Newtonsoft.Json; namespace DD.Persistence.Test; -public class TreeBuilderShould +public class TreeBuilderTest { [Fact] - public void TestTreeBuilding() + public void TreeBuildingShouldBuilt() { //arrange var treeString = "(\"A\"==1)||(\"B\"==2)&&(\"C\"==3)||((\"D\"==4)||(\"E\"==5))&&(\"F\"==6)"; @@ -44,7 +44,7 @@ public class TreeBuilderShould } [Fact] - public void TestTreeOperations() + public void TreeOperationsShouldBuilt() { //arrange var treeString = "(\"A\"==1)||(\"B\"!=1)||(\"C\">1)||(\"D\">=1)||(\"E\"<1)||(\"F\"<=1)"; @@ -81,7 +81,7 @@ public class TreeBuilderShould } [Fact] - public void TestLeafValues() + public void LeafValuesShouldBuilt() { //arrange var treeString = "(\"A\"==1.2345)||(\"B\"==12345)||(\"C\"==\"12345\")"; diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Abstractions/IExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Abstractions/IExpression.cs index c1418bf..8a5a526 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Abstractions/IExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Abstractions/IExpression.cs @@ -1,6 +1,6 @@ using DD.Persistence.Filter.Models.Enumerations; -namespace DD.Persistence.Filter.Interpreter.Expressions.Abstract; +namespace DD.Persistence.Filter.TreeBuilder.Expressions.Abstractions; /// /// Интерфейс для выражений diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/Abstractions/NonTerminalExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/Abstractions/NonTerminalExpression.cs index d543b8b..5ce524b 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/Abstractions/NonTerminalExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/Abstractions/NonTerminalExpression.cs @@ -1,10 +1,10 @@ using DD.Persistence.Extensions; -using DD.Persistence.Filter.Interpreter.Expressions.Abstract; using DD.Persistence.Filter.Models; using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.TreeBuilder.Expressions.Abstractions; using System.Text.RegularExpressions; -namespace DD.Persistence.Filter.Interpreter.Expressions.NonTerminal.Base; +namespace DD.Persistence.Filter.TreeBuilder.Expressions.NonTerminal.Abstractions; /// /// Абстрактный класс для нетерминальных выражений @@ -20,8 +20,8 @@ abstract class NonTerminalExpression : IExpression var operation = GetOperation(); var operationString = GetOperationString(); - var matches = GetMatches(context, operation, operationString); - while (matches.Any()) + var matches = GetMatches(context, operationString); + while (matches.Length != 0) { matches.ForEach(m => { @@ -33,29 +33,29 @@ abstract class NonTerminalExpression : IExpression .Split(separator) .Select(e => int.Parse(e)); - var leftNode = context.treeNodes + var leftNode = context.TreeNodes .FirstOrDefault(e => e.Key == pair.First()) .Value; - var rigthNode = context.treeNodes + 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 key = context.TreeNodes.Count; + context.TreeNodes.Add(key, node); var keyString = key.ToString(); - context.treeString = context.treeString.Replace(matchString, keyString); + context.TreeString = context.TreeString.Replace(matchString, keyString); }); - matches = GetMatches(context, operation, operationString); + matches = GetMatches(context, operationString); } - var isRoot = int.TryParse(context.treeString, out _); + var isRoot = int.TryParse(context.TreeString, out _); if (isRoot) { - context.treeString = string.Empty; - context.root = context.treeNodes.Last().Value; + context.TreeString = string.Empty; + context.Root = context.TreeNodes.Last().Value; } } @@ -69,13 +69,13 @@ abstract class NonTerminalExpression : IExpression /// /// Получить из акткуального состояния строки все совпадения для текущего выражения /// - private IEnumerable GetMatches(InterpreterContext context, OperationEnum operation, string operationString) + private static Match[] GetMatches(InterpreterContext context, string operationString) { - string pattern = context.treeString.Contains('(') && context.treeString.Contains(')') + string pattern = context.TreeString.Contains('(') && context.TreeString.Contains(')') ? $@"\(\d+{operationString}\d+\)" : $@"\d+{operationString}\d+"; - Regex regex = new Regex(pattern); + Regex regex = new(pattern); var matches = regex - .Matches(context.treeString) + .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 index 599315c..9af8dbe 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/AndExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/AndExpression.cs @@ -1,7 +1,7 @@ -using DD.Persistence.Filter.Interpreter.Expressions.NonTerminal.Base; -using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.TreeBuilder.Expressions.NonTerminal.Abstractions; -namespace DD.Persistence.Filter.Interpreter.Expressions.NonTerminal; +namespace DD.Persistence.Filter.TreeBuilder.Expressions.NonTerminal; /// /// Выражение для "И" diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/OrExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/OrExpression.cs index 6aa6c69..4be21bf 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/OrExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/NonTerminal/OrExpression.cs @@ -1,7 +1,7 @@ -using DD.Persistence.Filter.Interpreter.Expressions.NonTerminal.Base; -using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.TreeBuilder.Expressions.NonTerminal.Abstractions; -namespace DD.Persistence.Filter.Interpreter.Expressions.NonTerminal; +namespace DD.Persistence.Filter.TreeBuilder.Expressions.NonTerminal; /// /// Выражение для "ИЛИ" diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs index 5d2caed..1d13d6b 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs @@ -1,10 +1,10 @@ using DD.Persistence.Extensions; -using DD.Persistence.Filter.Interpreter.Expressions.Abstract; using DD.Persistence.Filter.Models; using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.TreeBuilder.Expressions.Abstractions; using System.Text.RegularExpressions; -namespace DD.Persistence.Filter.Interpreter.Expressions.Terminal.Base; +namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal.Abstract; /// /// Абстрактный класс для терминальных выражений @@ -20,7 +20,7 @@ abstract class TerminalExpression : IExpression var operation = GetOperation(); var operationString = GetOperationString(); - var matches = GetMatches(context, operation, operationString); + var matches = GetMatches(context, operationString); matches.ForEach(m => { var matchString = m.ToString(); @@ -34,11 +34,11 @@ abstract class TerminalExpression : IExpression var value = ParseValue(pair.Last()); var node = new TLeaf(operation, fieldName, value); - var key = context.treeNodes.Count(); - context.treeNodes.Add(key, node); + var key = context.TreeNodes.Count; + context.TreeNodes.Add(key, node); var keyString = key.ToString(); - context.treeString = context.treeString.Replace(matchString, keyString); + context.TreeString = context.TreeString.Replace(matchString, keyString); }); } @@ -51,16 +51,16 @@ abstract class TerminalExpression : IExpression /// /// Получить из акткуального состояния строки все совпадения для текущего выражения /// - private IEnumerable GetMatches(InterpreterContext context, OperationEnum operation, string operationString) + private static Match[] GetMatches(InterpreterContext context, string operationString) { string pattern = $@"\([^()]*{operationString}.*?\)"; - Regex regex = new Regex(pattern); - var matches = regex.Matches(context.treeString); + Regex regex = new(pattern); + var matches = regex.Matches(context.TreeString).ToArray(); return matches; } - private object? ParseValue(string value) + private static object? ParseValue(string value) { value = value.Replace('.', ','); if (value.Contains(',') && double.TryParse(value, out _)) diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/EqualExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/EqualExpression.cs index aec5a6b..3c79045 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/EqualExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/EqualExpression.cs @@ -1,5 +1,5 @@ -using DD.Persistence.Filter.Interpreter.Expressions.Terminal.Base; -using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.TreeBuilder.Expressions.Terminal.Abstract; namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessExpression.cs index 7382e87..dbbdf3c 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessExpression.cs @@ -1,5 +1,5 @@ -using DD.Persistence.Filter.Interpreter.Expressions.Terminal.Base; -using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.TreeBuilder.Expressions.Terminal.Abstract; namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessOrEqualExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessOrEqualExpression.cs index c706843..c9514e5 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessOrEqualExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/LessOrEqualExpression.cs @@ -1,5 +1,5 @@ -using DD.Persistence.Filter.Interpreter.Expressions.Terminal.Base; -using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.TreeBuilder.Expressions.Terminal.Abstract; namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreExpression.cs index cbc3d82..6cdca36 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreExpression.cs @@ -1,5 +1,5 @@ -using DD.Persistence.Filter.Interpreter.Expressions.Terminal.Base; -using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.TreeBuilder.Expressions.Terminal.Abstract; namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreOrEqualExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreOrEqualExpression.cs index a6a8345..d0e13e3 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreOrEqualExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/MoreOrEqualExpression.cs @@ -1,5 +1,5 @@ -using DD.Persistence.Filter.Interpreter.Expressions.Terminal.Base; -using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.TreeBuilder.Expressions.Terminal.Abstract; namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/NotEqualExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/NotEqualExpression.cs index 49031f1..66662c6 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/NotEqualExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/NotEqualExpression.cs @@ -1,5 +1,5 @@ -using DD.Persistence.Filter.Interpreter.Expressions.Terminal.Base; -using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.TreeBuilder.Expressions.Terminal.Abstract; namespace DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; diff --git a/DD.Persistence/Filter/TreeBuilder/TreeBuilder.cs b/DD.Persistence/Filter/TreeBuilder/FilterTreeBuilder.cs similarity index 58% rename from DD.Persistence/Filter/TreeBuilder/TreeBuilder.cs rename to DD.Persistence/Filter/TreeBuilder/FilterTreeBuilder.cs index 224c74c..4112e21 100644 --- a/DD.Persistence/Filter/TreeBuilder/TreeBuilder.cs +++ b/DD.Persistence/Filter/TreeBuilder/FilterTreeBuilder.cs @@ -1,14 +1,14 @@ -using DD.Persistence.Filter.Interpreter.Expressions.Abstract; -using DD.Persistence.Filter.Interpreter.Expressions.NonTerminal; -using DD.Persistence.Filter.Models.Abstractions; +using DD.Persistence.Filter.Models.Abstractions; +using DD.Persistence.Filter.TreeBuilder.Expressions.Abstractions; +using DD.Persistence.Filter.TreeBuilder.Expressions.NonTerminal; using DD.Persistence.Filter.TreeBuilder.Expressions.Terminal; -namespace DD.Persistence.Filter.Interpreter; +namespace DD.Persistence.Filter.TreeBuilder; /// /// Строитель бинарных деревьев /// -public static class TreeBuilder +public static class FilterTreeBuilder { /// @@ -18,35 +18,37 @@ public static class TreeBuilder /// public static TNode? BuildTree(this string treeString) { - InterpreterContext context = new InterpreterContext(treeString); + InterpreterContext context = new(treeString); // Порядок важен - List terminalExpressions = new List - { + List terminalExpressions = + [ new EqualExpression(), new NotEqualExpression(), new MoreOrEqualExpression(), new LessOrEqualExpression(), new MoreExpression(), new LessExpression() - }; - terminalExpressions.ForEach(e => { + ]; + terminalExpressions.ForEach(e => + { e.Interpret(context); }); // Порядок важен - List nonTerminalExpressions = new List - { + List nonTerminalExpressions = + [ new OrExpression(), new AndExpression() - }; - while (!string.IsNullOrEmpty(context.treeString)) + ]; + while (!string.IsNullOrEmpty(context.TreeString)) { - nonTerminalExpressions.ForEach(e => { + nonTerminalExpressions.ForEach(e => + { e.Interpret(context); }); } - return context.root; + return context.Root; } } diff --git a/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs b/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs index 86e0ef8..3471f8d 100644 --- a/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs +++ b/DD.Persistence/Filter/TreeBuilder/InterpreterContext.cs @@ -1,6 +1,6 @@ using DD.Persistence.Filter.Models.Abstractions; -namespace DD.Persistence.Filter.Interpreter; +namespace DD.Persistence.Filter.TreeBuilder; /// /// Контекст интерпретатора @@ -10,21 +10,21 @@ class InterpreterContext /// /// Корень дерева (результат интерпретации) /// - public TNode? root { get; set; } + public TNode? Root { get; set; } /// /// Дерево в виде строки (входной параметр) /// - public string treeString { get; set; } + public string TreeString { get; set; } /// /// Проиндексированные вершины дерева /// - public Dictionary treeNodes { get; set; } = []; + public Dictionary TreeNodes { get; set; } = []; /// public InterpreterContext(string theeString) { - this.treeString = theeString; + TreeString = theeString; } } -- 2.45.2