/
Rule.cs
340 lines (320 loc) · 19.3 KB
/
Rule.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/*----------------------------------------------------------------------------------------------
* Rule.cs -- Rule, which describes how to recognyze Components used in Model in Supplier's price-list
*
* 7.06.2017 П.Храпкин
*
*--- History ---
* 17.10.2016 code file created from module Matcher
* 2.05.2017 FingerPrint references removed
* 19.05.2017 public DateTime Rule.Date
* 24.05.2017 Rule.getList
* 6.06.2017 remove readonly attribute for CompSet foe test
* 7.06.2017 Rule.SynParse bug fix, Rule module audit
* ---- Unit Test ----
* 2017.06.07 UT_Rules_Constructor OK; UT_Rule_SynParse OK
* ---- Constructors & methods ----
* Rule(Docs doc, int n) - load Rule from line n of Document doc
* Rule(int n) - by default of doc load Rule from TSmatchINFO.xlsx/Rules
* RuleSynParse(str) - parse string - Rule.Text into Syninims Dictionary
*/
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using log4net;
using Lib = match.Lib.MatchLib;
using Log = match.Lib.Log;
using Msg = TSmatch.Message.Message;
using Decl = TSmatch.Declaration.Declaration;
using Docs = TSmatch.Document.Document;
using Comp = TSmatch.Component.Component;
using CmpSet = TSmatch.CompSet.CompSet;
using Supl = TSmatch.Suppliers.Supplier;
using DP = TSmatch.DPar.DPar;
using Sec = TSmatch.Section.Section;
using SType = TSmatch.Section.Section.SType;
using TSmatch.Section;
namespace TSmatch.Rule
{
public class Rule
{
public static readonly ILog log = LogManager.GetLogger("Rule");
public DateTime date; //Date and Time, when the Rule is defined for the Model
public string text; //internal Rule text - with ToLat & ToLower
public string Text; //external Rule text - as in Excel for UI display
public CmpSet CompSet; //Componet Set dedicated to the Rule
public Supl Supplier; //Supplier dedicated to this Rule
public DP ruleDP; //identifiers of Materials, Profile, and others
public Dictionary<SType, List<string>> synonyms = new Dictionary<SType, List<string>>();
public double RuleRedundencyPerCent = 0.0; //коэффициент избыточности, требуемый запас по данному материалу/профилю/Правилу
public string sSupl;
public string sCS;
public Rule() { }
public Rule(Docs doc, int i)
{
date = Lib.getDateTime(doc.Body.Strng(i, Decl.RULE_DATE));
Text = doc.Body.Strng(i, Decl.RULE_RULETEXT);
text = Lib.ToLat(Text.ToLower());
synonyms = RuleSynParse(text);
ruleDP = new DP(text);
sSupl = doc.Body.Strng(i, Decl.RULE_SUPPLIERNAME);
sCS = doc.Body.Strng(i, Decl.RULE_COMPSETNAME);
if (date > DateTime.Now || date < Decl.OLD
|| string.IsNullOrWhiteSpace(Text)
|| string.IsNullOrWhiteSpace(sSupl)
|| string.IsNullOrWhiteSpace(sCS)) throw new InvalidCastException("Rule line " + i);
}
public Rule(int n) : this(Docs.getDoc(Decl.TSMATCHINFO_RULES), n) { }
public Dictionary<SType, List<string>> RuleSynParse(string str)
{
var Syns = new Dictionary<SType, List<string>>();
string[] sections = str.Split(';');
foreach (var s in sections)
{
List<string> synLst = new List<string>();
Section.Section sect = new Section.Section(s);
string[] strs = sect.body.Split('=');
foreach (string tx in strs)
{
int indxPar = tx.IndexOf('*');
string stx;
if (indxPar < 0) stx = tx;
else stx = tx.Substring(0, indxPar);
synLst.Add(stx);
}
if (synLst.Count > 1) Syns.Add(sect.type, synLst);
}
return Syns;
}
public void Init()
{
Supplier = new Supl(sSupl);
CompSet = new CmpSet(sCS, Supplier);
CompSet.doc.Close();
}
internal static List<string> getList(string name)
{
List<string> result = new List<string>();
Docs doc = Docs.getDoc(Decl.TSMATCHINFO_RULES);
for (int i = doc.i0; i <= doc.il; i++)
{
string suplName = doc.Body.Strng(i, Decl.RULE_SUPPLIERNAME);
if (suplName != name) continue;
result.Add(suplName);
}
return result;
}
#if OLD
/// <summary>
/// isRuleApplicable(mat, prf) - return true when parsed Rule is applicable to mat and prf values
/// </summary>
/// <param name="mat">Material in Model</param>
/// <param name="prf">Profile in Model</param>
/// <returns>true, if Rule after Parcing mentioned mat and prf</returns>
/// <history>9.4.2016
/// 29.12.2016 use FingerPrint
/// </history>
/// <TODO>29.12.16 implement with FP</TODO>
internal bool isRuleApplicable(string mat, string prf)
{
bool ok= false;
//29.12.16 ok = Lib.IContains(RuleMatList, mat) && Lib.IContains(RulePrfList, prf);
return ok;
}
///<summary> /// private RuleParser(string) - Parse string text of Rule to fill Rule parameters.
/// Regular Expression methodisa are in use.
///</summary>
/// <описание>
/// -------------------------- СИНТАКСИС ПРАВИЛ -----------------------
/// RuleParser разбирает текстовую строку - правило, выделяя и возвращая в списках или полях,
/// входящих в состав класса Rule значения и списки, полученные из текста Правила.
/// ...... Части Правила, иначе - Разделы или Секции ......
/// Разделы начинаются признаком раздела, например, "Материал:" и разделяются между собой знаком ';'.
/// Заголовок раздела распознается по первым буквам 'M' (Материал), "Пр" ("Pr" для английского текста),
/// 'C' (Cost или Стоимость) или 'Ц' (Цена) и завершается ':'. Поэтому
/// "Профиль:" = "П:" = "Прф:" = "п:" = "Prof:"
/// Заглавные и строчные буквы эквивалентны, национальные символы переводятся в эквивалентные по начертанию
/// знаки латинского алфавита, чтобы избежать путаницы между латинским Х и русским Х.
/// Разделы Правила можно менять местами и пропускать; тогда они работают "по умолчанию".
/// В теле раздела могут быть синонимы - альтернативные обозначения Материала или Профиля. Их синтаксис:
/// '=' означает эквивалентность. Допустимы выражения "C255=Ст3=сталь3".
/// ',' позволяет перечислять элементы и подразделы Правила.
/// ' ' пробелы, табы и знаки конца строки игнорируются, однако они могут служить
/// признаком перечисления, так же, как пробелы. Таким образом, названия материалов
/// или профилей можно просто перечислить - это эквивалентно '='
/// В результате работы метода RuleParser списки альтернативных наименований для Материала, Профили и Прочего
/// RuleMatList - список допустимых альтернативных наименований материалов, с которыми работает Правило
/// RulePrfList - список альтернативных наименований Профиля
/// RuleCstList - список альтернативных наименований других разделов Правила
/// ...... Параметры ......
/// Текст Раздела Правила может иметь т.н. Параметры - символические обозначения каких-либо значений.
/// Например,"$p1" или просто "p1" или "Параметр235". Параметр начинается со знака'$' или 'p' и кончается цифрой,
/// причем знак '$' можно опускать. Значение Параметра подставляется из атрибутов модели в САПР в порядке следования
/// Параметров в Правиле.
///
/// #параметры - величины в Правиле, которые соответствуют "значению" в тексте атрибута или прайс-листа,
/// например, номер колонки с весом или с ценой
/// *параметры - Wild параметр комплектующих; соответствующий элемент модели может иметь
/// любое значение, например, ширина листа, из которого нарезают полосы
/// Redunduncy% параметр - коэффициент запаса в процентах. Перед десятичным числом -
/// коэффициентом запаса - могут быть ключевые слова, возможно, сокращенные
/// (избыток, запас, отходы, Redundency, Excess, Waste) по русски или
/// по английски заглавными или строчными буквами.
/// </описание>
/// <history> декабрь 2015 - январь 2016 предистория
/// 19.2.16 - #параметры
/// 29.2.16 - *параметры
/// 17.10.16 - перенос в Rule class
/// 28.12.16 - введены Параметры вида {..} правила. Остальные типы правил пока не разбираем
/// 25.03.17 - переписал с использованием Section
/// </history>
public Dictionary<SType, FP> Parser(FP.type _type, string text)
{
Log.set("Rule.Parser(" + _type + ", " + text);
Dictionary<SType, FP> result = new Dictionary<SType, FP>();
string[] sections = Lib.ToLat(text).ToLower().Split(';');
foreach (string sec in sections)
{
Sec txSec = new Sec(sec);
if (txSec.type == SType.NOT_DEFINED) continue;
FP fp = new FP(_type, txSec.body);
result.Add(txSec.type, fp);
}
Log.exit();
return result;
}
private void RuleLogInfo()
{
log.Info("----- Parse Rule (\"" + text + "\") -----");
// this.ruleFPs(x => x.);
//if (matFP != null) log.Info("MatFP:" + matFP.strINFO());
//if (prfFP != null) log.Info("PrfFP:" + prfFP.strINFO());
}
///////////////////////////// <summary>
///////////////////////////// SectionParse(string) - parse Section - part of the Rule text. Result filled in Rule field
///////////////////////////// Lists Synonims and Parameters. Regular Expressions used to parse input string.
///////////////////////////// </summary>
///////////////////////////// <param name="str">Part of the Rule string to be parsed</param>
///////////////////////////// <history> 12.2.2016 PKh
///////////////////////////// 19.10.2016 re-done from previous static attParse
///////////////////////////// 23.10.2016 bug fix, Section header is parsing now in this module
///////////////////////////// 23.11.2016 Cost Section parse for the Volume dependent calcalation
///////////////////////////// 2.12.2016 RecognizeSection
///////////////////////////// <\history>
//////////////////////////private void SectionParse(string str)
//////////////////////////{
////////////////////////// Section section = RecognyseSection(ref str);
////////////////////////// switch (section)
////////////////////////// {
////////////////////////// case Section.Material: matFP = new FP(FP.type.Rule, str);
////////////////////////// break;
////////////////////////// case Section.Profile: prfFP = new FP(FP.type.Rule, str);
////////////////////////// break;
/////// 2.1.17 /////////// //case Section.Cost: Parser(ref str, RuleCstList, RuleCstPar);
////////////////////////// //RuleCstPar = Parameter(ref str);
////////////////////////// //RuleCstList = Synonym(ref str);
////////////////////////// //break;
////////////////////////// ////default: Msg.F("Rule.SectionParse-- wrong Section recognition", section);
////////////////////////// //// break;
////////////////////////// }
////////////////////////// ////////if (str != "") Log.FATAL("строка \"" + str + "\" разобрана не полностью");
//////////////////////////}
private void Parser(ref string str, List<string> synonym, Dictionary<string, string> parametr)
{
//--выделение параметров из строки str, как регулярных выражений PARAM
const string PARAM = @"(?<param>(\$|p|р|п|P|Р|П)\w*\d)"; //параметры в Правилах
// const string PAR_DELIM = @"(x|#)";
if (str.Length == 0) return;
//12/12 string[] parametrs = Regex.Split(str, PARAM);
foreach (var par in Regex.Split(str, PARAM))
{
if (string.IsNullOrEmpty(par)
|| string.IsNullOrWhiteSpace(par)
|| !IsMtch(par, PARAM)) continue;
parametr.Add(par.ToUpper(), "");
}
//-- оформление списка синонимов по параметрам в Dictionary<str,str>parametr
const string DELIMETR = @"(${must}|,|=| |\t|\n|\*|x)";
string[] subStr = Regex.Split(str, DELIMETR);
foreach (var s in subStr)
{
if (string.IsNullOrEmpty(s)) continue;
if (!string.IsNullOrWhiteSpace(s) && !Regex.IsMatch(s, DELIMETR))
{
string tmp = s;
foreach (var par in parametr)
{
if (!tmp.Contains(par.Key)) continue;
int fr = tmp.IndexOf(par.Key);
tmp = tmp.Remove(fr, par.Key.Length);
}
synonym.Add(tmp);
}
str = str.Replace(s, "");
}
}
/// <summary>
/// Parse Section for the sub-strings - synonims, and return them as List<string> separated by Delimenters
/// </summary>
/// <param name="str">string to be parsed</param>
/// <returns>List of the synonyms</returns>
/// <ToDo>
/// 17.11 перенести определение ATT_DELIM сюда. Для этого надо убедиться Shft/F12, что ATT_DELIM используется только тут
/// 9.12.16 в список синонимов помещать текст без $*1. Для этого Parametr() надо вызывать отсюда!
/// </ToDo>
private List<string> Synonym(ref string str)
{
const string DELIMETR = @"(${must}|,|=| |\t|\n|\*|x)";
List<string> result = new List<string>();
string[] subStr = Regex.Split(str, DELIMETR);
foreach (var s in subStr)
{
if (string.IsNullOrEmpty(s)) continue;
if (!string.IsNullOrWhiteSpace(s) && !Regex.IsMatch(s, DELIMETR)) result.Add(s);
str = str.Replace(s, "");
}
return result;
}
private Dictionary<string, string> Parameter(ref string str)
{
const string PARAM = @"(?<param>(\$|p|р|п|P|Р|П)\w*\d)"; //параметры в Правилах
const string PAR_DELIM = @"(x|#)";
Dictionary<string, string> results = new Dictionary<string, string>();
if (str.Length == 0) return results;
string[] parametrs = Regex.Split(str, PARAM);
foreach (var par in parametrs)
{
if (string.IsNullOrEmpty(par)
|| string.IsNullOrWhiteSpace(par)
|| !IsMtch(par, PARAM)) continue;
results.Add(par.ToUpper(), "");
}
return results;
}
/// <summary>
/// IsMtch(string str, string reg) - return TRUE, if str contains regular expression reg.
/// </summary>
/// <param name="str">string to check</param>
/// <param name="reg">regular expression</param>
/// <returns>true is str is in match with reg, ignoring letter case and with ToLat(str)</returns>
private bool IsMtch(string str, string reg)
{
//str = Lib.ToLat(str).ToLower();
//reg = Lib.ToLat(reg).ToLower();
return Regex.IsMatch(str, reg, RegexOptions.IgnoreCase);
}
/// <summary>
/// RemMtch(str, reg) - remove substring matching regular expression reg from str
/// </summary>
/// <param name="str">string to be modified</param>
/// <param name="reg">regular expression to seek in str</param>
/// <returns>modified str</returns>
private string RemMtch(string str, string reg)
{
////str = Lib.ToLat(str).ToLower();
////reg = Lib.ToLat(reg).ToLower();
Regex regHdr = new Regex(reg, RegexOptions.IgnoreCase);
return regHdr.Replace(str, ""); // remove Section header
}
#endif //OLD
} // end class Rule
} // end namespace Rule