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