• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { LogUtil } from '../../utils/logUtil';
17import { NumberConstant } from '../../utils/Constant';
18const PATT: { [key: string]: RegExp | string } = {
19  GET_NOT_TRANSFERCHAR: /(?<!\\)([\*|\.|\?|\+|\^|\$|\||\/|\[|\]|\(|\)|\{|\}])/g, //获取需要转义的字符
20  VARIABLE_START: '(?<=\\b|\\s|\\*|\\.|\\?|\\+|\\^|\\$|\\||\\/|\\[|\\]|\\(|\\)|\\{|\\})', //变量需要是一个完整单词开头,或者以运算相关字符开头
21  VARIABLE_END: '(?=\\b|\\s|\\*|\\.|\\?|\\+|\\^|\\$|\\||\\/|\\[|\\]|\\(|\\)|\\{|\\})', //变量需要是一个完整单词结尾,或者以运算相关字符结尾
22  SPILT_CHAR_START: '(\\b|\\s)', //替换字符需要是完整字符开头
23  SPILT_CHAR_END: '(\\b|\\s)', //替换字符需要是完整字符结尾
24};
25export class PermissionsProcessorHelper {
26  private static NEED_TRANSFER_CHAR: Array<string> = [
27    '*',
28    '.',
29    '?',
30    '+',
31    '^',
32    '$',
33    '|',
34    '/',
35    '[',
36    ']',
37    '(',
38    ')',
39    '{',
40    '}',
41  ]; //需要转义的字符
42
43  /**
44   * 转义的规则,可以在new的时候传入相应格式进行修改
45   *
46   * @type {CharMapType} charMap
47   */
48  private charMap: CharMapType = new Map([
49    ['and', { splitchar: '&', transferchar: '&' }],
50    ['or', { splitchar: '\\|', transferchar: '|' }],
51    ['eq', { splitchar: '=', transferchar: '==' }],
52    ['LE', { splitchar: '->', transferchar: '<=' }],
53    ['RE', { splitchar: '<-', transferchar: '>=' }],
54    ['not', { splitchar: '-', transferchar: '!' }],
55    ['lcurve', { splitchar: '\\(', transferchar: '(' }],
56    ['rcurve', { splitchar: '\\)', transferchar: ')' }],
57  ]);
58
59  /**
60   * 用来记录分割的条件,需要满足可以放入正则,不能存在特殊字符,例如 {@link NEED_TRANSFER_CHAR}
61   *
62   * @type {Array<string|RegExp>}
63   */
64  private splitchar: Array<string | RegExp> = ['&', '\\|', '->', '-', '=', '\\(', '\\)'];
65
66  /**
67   * 用来记录分割的条件最终需要替换的运算符
68   *
69   * @type {Array<string>}
70   */
71  private transferchar: Array<string> = ['&', '|', '<=', '!', '==', '(', ')'];
72
73  private variables: Array<string> = []; //命题集合
74
75  /**
76   * 构造方法
77   *
78   * @param {CharMapType} charMap 如果存在相关字符,需要进行转义 * . ? + ^ $ | / [ ] ( ) { }
79   */
80  constructor(charMap?: CharMapType) {
81    let map: CharMapType = this.charMap;
82    if (charMap) {
83      map = new Map([...map, ...charMap]);
84      this.splitchar = [];
85      this.transferchar = [];
86      map.forEach((item, key) => {
87        let splitchar: string | RegExp = item.splitchar;
88        if (!(splitchar instanceof RegExp)) {
89          splitchar = splitchar.replace(PATT.GET_NOT_TRANSFERCHAR, '\\$1');
90          if (splitchar !== item.splitchar) {
91            LogUtil.i(
92              'PermissionsProcessorHelper',
93              `传入的表达式有问题,已经自行修改! ${key}中的splitchar为${item.splitchar},修改为${splitchar}`
94            );
95          }
96        }
97        this.splitchar.push(splitchar);
98        this.transferchar.push(item.transferchar);
99      });
100    }
101    return this;
102  }
103
104  getvariables(): Array<string> {
105    return this.variables;
106  }
107
108  /**
109   * 比较两个版本Permission的变化
110   *
111   * @param {string} oldStr
112   * @param {string} newStr
113   * @returns {CompareReturnObjType}
114   */
115  comparePermissions(oldStr: string, newStr: string): CompareReturnObjType {
116    const calculateResult: CompareReturnObjType = {
117      range: RangeChange.NO,
118      situationDown: [],
119      situationUp: [],
120      variables: [],
121    };
122    const downVal: CompareReturnObjType = this.calculateParadigmDown(oldStr, newStr);
123    const upVal: CompareReturnObjType = this.calculateParadigmUp(oldStr, newStr);
124    calculateResult.variables = this.variables;
125    if (downVal.range !== RangeChange.NO) {
126      calculateResult.situationDown = downVal.situationDown;
127      calculateResult.range = RangeChange.DOWN;
128    }
129    if (upVal.range !== RangeChange.NO) {
130      calculateResult.situationUp = upVal.situationUp;
131      calculateResult.range = calculateResult.range === RangeChange.NO ? RangeChange.UP : RangeChange.CHANGE;
132    }
133    return calculateResult;
134  }
135
136  /**
137   * 比较两个版本逻辑关系的变化,返回变小的情况
138   *
139   * @param {string} oldStr
140   * @param {string} newStr
141   * @returns {CompareReturnObjType}
142   */
143  calculateParadigmDown(oldStr: string, newStr: string): CompareReturnObjType {
144    const calculateResult: CompareReturnObjType = {
145      range: RangeChange.NO,
146      situationDown: [],
147      situationUp: [],
148      variables: [],
149    };
150    const char: CharMapTypeValue | undefined = this.charMap.get('LE');
151    if (!char) {
152      return calculateResult;
153    }
154    const mergeStr: string = `(${oldStr}) ${char.splitchar} (${newStr})`;
155    const permissionsDiff: CalculateReturnType = this.calculateParadigm(mergeStr);
156    calculateResult.variables = this.variables;
157    if (permissionsDiff.fail.length > 0) {
158      calculateResult.range = RangeChange.DOWN;
159      calculateResult.situationDown = permissionsDiff.fail.map((item: number) => {
160        let itemNums: string[] = Number(item)
161          .toString(NumberConstant.BINARY_SYSTEM)
162          .padStart(this.variables.length, '0')
163          .split('');
164        return itemNums.map((num: string) => {
165          return Boolean(Number(num));
166        });
167      });
168    }
169    return calculateResult;
170  }
171
172  /**
173   * 比较两个版本逻辑关系的变化,返回变大的情况
174   *
175   * @param {string} oldStr
176   * @param {string} newStr
177   * @returns {CompareReturnObjType}
178   */
179  calculateParadigmUp(oldStr: string, newStr: string): CompareReturnObjType {
180    const calculateResult: CompareReturnObjType = {
181      range: RangeChange.NO,
182      situationDown: [],
183      situationUp: [],
184      variables: [],
185    };
186    const char = this.charMap.get('RE');
187    if (!char) {
188      return calculateResult;
189    }
190    const mergeStr: string = `(${oldStr}) ${char.splitchar} (${newStr})`;
191    const permissionsDiff: CalculateReturnType = this.calculateParadigm(mergeStr);
192
193    calculateResult.variables = this.variables;
194    if (permissionsDiff.fail.length > 0) {
195      calculateResult.range = RangeChange.UP;
196      calculateResult.situationUp = permissionsDiff.fail.map((item) => {
197        let itemNums: string[] = Number(item)
198          .toString(NumberConstant.BINARY_SYSTEM)
199          .padStart(this.variables.length, '0')
200          .split('');
201        return itemNums.map((num: string) => {
202          return Boolean(Number(num));
203        });
204      });
205    }
206    return calculateResult;
207  }
208
209  /**
210   * 计算逻辑表达式的析取范式和合取范式
211   *
212   * @param {string} str
213   * @returns {CalculateReturnType}
214   */
215  calculateParadigm(str: string): CalculateReturnType {
216    const variable = this.findVariables(str);
217    this.variables = variable;
218    str = this.formatten(str);
219    const variablesLength: number = this.variables.length;
220    const states: string[][] = PermissionsProcessorHelper.getAllState(variablesLength);
221    const values: number[] = this.calculate(str, variablesLength, states);
222    return this.processValues(values);
223  }
224
225  /**
226   * 根据传入数据,提取出所有条件,以splitchar中的数据进行分割
227   *
228   * @param {string} str
229   * @returns {Array<string>}
230   */
231  private findVariables(str: string): Array<string> {
232    this.splitchar.forEach((char: string | RegExp) => {
233      const reg = new RegExp(char, 'g');
234      str = str.replace(reg, ' ');
235    });
236    const set: Set<string> = new Set(str.split(' '));
237    set.delete('');
238    const sortSet: string[] = [...set].sort((a, b) => {
239      if (a.length === b.length) {
240        return a < b ? -1 : 1;
241      } else {
242        return a.length < b.length ? 1 : -1;
243      }
244    });
245    return sortSet;
246  }
247
248  /**
249   * 格式化字符串的逻辑条件
250   *
251   * @param {string} str
252   * @returns {string}
253   */
254  private formatten(str: string): string {
255    this.splitchar.forEach((char: string | RegExp, index: number) => {
256      if (!(char instanceof RegExp)) {
257        char = new RegExp(PATT.SPILT_CHAR_START + char + PATT.SPILT_CHAR_END, 'g');
258      }
259      str = str.replace(char, this.transferchar[index]);
260    });
261    return str;
262  }
263
264  /**
265   * 计算n个长度对应的真值表
266   *
267   * @param {number} len
268   * @returns {Array<Array<string>>}
269   */
270  private static getAllState(len: number): Array<Array<string>> {
271    const states: string[][] = [];
272    for (let index = 0; index < NumberConstant.BINARY_SYSTEM ** len; index++) {
273      states.push(Number(index).toString(NumberConstant.BINARY_SYSTEM).padStart(len, '0').split(''));
274    }
275    return states;
276  }
277
278  /**
279   * 通过真值表计算最终结果
280   *
281   * @param {string} str
282   * @param {number} variablesLen
283   * @param {Array<Array<string>>} states
284   * @returns {Array<number>}
285   */
286  private calculate(str: string, variablesLen: number, states: Array<Array<string>>): Array<number> {
287    const statesLen = states.length;
288    const values: Array<number> = [];
289    let outError: boolean = true;
290    for (let i = 0; i < statesLen; i++) {
291      const state = states[i];
292      let modifyStr = str;
293      for (let j = 0; j < variablesLen; j++) {
294        let variable = this.variables[j];
295        variable = variable.replace(PATT.GET_NOT_TRANSFERCHAR, '\\$1');
296        const reg = new RegExp(PATT.VARIABLE_START + variable + PATT.VARIABLE_END, 'g');
297        modifyStr = modifyStr.replace(reg, state[j]);
298      }
299      try {
300        values.push(eval(modifyStr));
301      } catch (error) {
302        if (outError) {
303          outError = !outError;
304          LogUtil.e('PermissionsProcessor.calculate', `error logical expression: ${str}`);
305          values.push(0);
306        }
307      }
308    }
309    return values;
310  }
311
312  /**
313   * 最终结果分类处理
314   *
315   * @param {Array<number>} values
316   * @returns {CalculateReturnType}
317   */
318  private processValues(values: Array<number>): CalculateReturnType {
319    const calculate: CalculateReturnType = { pass: [], fail: [] };
320    for (let index = 0; index < values.length; index++) {
321      const element = values[index];
322      if (element) {
323        calculate.pass.push(index);
324      } else {
325        calculate.fail.push(index);
326      }
327    }
328    return calculate;
329  }
330}
331
332export enum RangeChange {
333  DOWN = 'down',
334  UP = 'up',
335  NO = 'no',
336  CHANGE = 'change',
337}
338
339/**
340 * 转义的规则集合类型
341 */
342export type CharMapType = Map<string, CharMapTypeValue>;
343
344/**
345 * 转义的规则对象
346 * @type {object}
347 * @property {string | RegExp} splitchar - 逻辑条件的区分字符.
348 * @property {string} transferchar - 区分字符转为的逻辑运算符和位运算符
349 */
350export type CharMapTypeValue = {
351  /**
352   *
353   * 逻辑条件的区分字符
354   * @type {(string | RegExp)}
355   */
356  splitchar: string | RegExp;
357  /**
358   * 区分字符转为的逻辑运算符和位运算符
359   *
360   * @type {string}
361   */
362  transferchar: string;
363};
364
365/**
366 * 逻辑运算的初步结果
367 *
368 */
369export type CalculateReturnType = {
370  /**
371   * 逻辑运算通过的真值表对应十进制项 ——析取范式
372   *
373   * @type {Array<number>}
374   */
375  pass: Array<number>;
376  /**
377   * 逻辑运算不通过的真值表对应十进制项 ——合取范式
378   *
379   * @type {Array<number>}
380   */
381  fail: Array<number>;
382};
383
384/**
385 * 比较两个运算的返回值
386 *
387 */
388export type CompareReturnObjType = {
389  /**
390   * 范围变化
391   *
392   * @type {RangeChange}
393   */
394  range: RangeChange;
395  /**
396   * 权限变小的情况,在对应逻辑条件为当前状态时会有次情况
397   *
398   * @type {Boolean[][]}
399   */
400  situationDown: Boolean[][];
401  /**
402   * 权限变大的情况,在对应逻辑条件为当前状态时会有次情况
403   *
404   * @type {Boolean[][]}
405   */
406  situationUp: Boolean[][];
407  /**
408   * 逻辑条件,提取两个逻辑运算的变量
409   *
410   * @type {Array<string>}
411   */
412  variables: Array<string>;
413};
414