• 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 _ from 'lodash';
17import ts from 'typescript';
18import crypto from 'crypto';
19import { StringConstant } from '../../utils/Constant';
20import {
21  ApiInfo,
22  ApiType,
23  BasicApiInfo,
24  ContainerApiInfo,
25  MethodInfo,
26  ParamInfo,
27  containerApiTypes,
28  TypeAliasInfo,
29  PropertyInfo,
30} from '../../typedef/parser/ApiInfoDefination';
31import {
32  ApiDiffType,
33  ApiStatusCode,
34  BasicDiffInfo,
35  DiffTypeInfo,
36  TagInfoDiffProcessor,
37} from '../../typedef/diff/ApiInfoDiff';
38import { ApiInfosMap, FileInfoMap, FilesMap, Parser } from '../parser/parser';
39import { apiStatisticsType } from '../../typedef/statistics/ApiStatistics';
40import { DiffProcessorHelper } from './DiffProcessor';
41import { FunctionUtils } from '../../utils/FunctionUtils';
42import { Comment } from '../../typedef/parser/Comment';
43import { notJsDocApiTypes } from '../../typedef/parser/ApiInfoDefination';
44import { StringUtils } from '../../utils/StringUtils';
45import { CommentHelper } from '../parser/JsDocProcessor';
46import { ResultsProcessHelper } from '../parser/ResultsProcess';
47
48export class DiffHelper {
49  /**
50   * 根据解析后的新旧版本SDK结果,得到对比差异
51   *
52   * @param { FilesMap } oldSDKApiMap 旧版本SDK解析后的结果
53   * @param { FilesMap } newSDKApiMap 新版本SDK解析后的结果
54   * @returns { BasicDiffInfo[] } 差异结果集
55   */
56  static diffSDK(
57    clonedOldSDKApiMap: FilesMap,
58    clonedNewSDKApiMap: FilesMap,
59    isAllSheet: boolean,
60    isCheck?: boolean
61  ): BasicDiffInfo[] {
62    const diffInfos: BasicDiffInfo[] = [];
63    const oldSDKApiLocations: Map<string, string[]> = DiffHelper.getApiLocations(clonedOldSDKApiMap, isCheck);
64    const newSDKApiLocations: Map<string, string[]> = DiffHelper.getApiLocations(clonedNewSDKApiMap, isCheck);
65    DiffHelper.diffKit(clonedOldSDKApiMap, clonedNewSDKApiMap, diffInfos);
66    const oldFilePathSet: Set<string> = new Set(Array.from(clonedOldSDKApiMap.keys()));
67    // 先以旧版本为基础进行对比
68    for (const key of oldSDKApiLocations.keys()) {
69      const apiLocation: string[] = oldSDKApiLocations.get(key) as string[];
70      const oldApiInfos: ApiInfo[] = Parser.getApiInfo(apiLocation, clonedOldSDKApiMap, isAllSheet) as ApiInfo[];
71      // 如果旧版本中的API在新版本中不存在,则为删除
72      if (!newSDKApiLocations.has(key)) {
73        oldApiInfos.forEach((oldApiInfo: ApiInfo) => {
74          diffInfos.push(
75            DiffProcessorHelper.wrapDiffInfo(
76              oldApiInfo,
77              undefined,
78              new DiffTypeInfo(ApiStatusCode.DELETE, ApiDiffType.REDUCE, oldApiInfo.getDefinedText())
79            )
80          );
81        });
82        continue;
83      }
84      // 新旧版本均存在,则进行对比
85      const newApiInfos: ApiInfo[] = Parser.getApiInfo(apiLocation, clonedNewSDKApiMap, isAllSheet) as ApiInfo[];
86      DiffHelper.diffApis(oldApiInfos, newApiInfos, diffInfos, isAllSheet, isCheck);
87      // 对比完则将新版本中的对应API进行删除
88      newSDKApiLocations.delete(key);
89    }
90    // 对比完还剩下的新版本中的API即为新增API
91    for (const key of newSDKApiLocations.keys()) {
92      const locations: string[] = newSDKApiLocations.get(key) as string[];
93      const newApiInfos: ApiInfo[] = Parser.getApiInfo(locations, clonedNewSDKApiMap, isAllSheet) as ApiInfo[];
94      const clonedLocations: string[] = locations;
95      const parentLocations = clonedLocations.slice(0, -1);
96      newApiInfos.forEach((newApiInfo: ApiInfo) => {
97        let isNewFile: boolean = false;
98        if (!oldFilePathSet.has(newApiInfo.getFilePath()) || !oldSDKApiLocations.get(parentLocations.join())) {
99          isNewFile = true;
100        }
101        diffInfos.push(
102          DiffProcessorHelper.wrapDiffInfo(
103            undefined,
104            newApiInfo,
105            new DiffTypeInfo(ApiStatusCode.NEW_API, ApiDiffType.ADD, undefined, newApiInfo.getDefinedText()),
106            isNewFile
107          )
108        );
109      });
110    }
111    return diffInfos;
112  }
113
114  static diffKit(clonedOldSDKApiMap: FilesMap, clonedNewSDKApiMap: FilesMap, diffInfos: BasicDiffInfo[]): void {
115    for (const key of clonedOldSDKApiMap.keys()) {
116      const oldSourceFileInfo: ApiInfo | undefined = DiffHelper.getSourceFileInfo(clonedOldSDKApiMap.get(key));
117      oldSourceFileInfo?.setSyscap(DiffHelper.getSyscapField(oldSourceFileInfo));
118      const oldKitInfo: string | undefined = oldSourceFileInfo?.getLastJsDocInfo()?.getKit();
119      //文件在新版本中被删除
120      if (!clonedNewSDKApiMap.get(key) && oldKitInfo !== 'NA') {
121        diffInfos.push(
122          DiffProcessorHelper.wrapDiffInfo(
123            oldSourceFileInfo,
124            undefined,
125            new DiffTypeInfo(ApiStatusCode.KIT_CHANGE, ApiDiffType.KIT_HAVE_TO_NA, oldKitInfo, 'NA')
126          )
127        );
128      } else if (clonedNewSDKApiMap.get(key)) {
129        const newSourceFileInfo: ApiInfo | undefined = DiffHelper.getSourceFileInfo(clonedNewSDKApiMap.get(key));
130        const newKitInfo: string | undefined = newSourceFileInfo?.getLastJsDocInfo()?.getKit();
131        if (oldKitInfo !== newKitInfo) {
132          diffInfos.push(
133            DiffProcessorHelper.wrapDiffInfo(
134              oldSourceFileInfo,
135              newSourceFileInfo,
136              new DiffTypeInfo(
137                ApiStatusCode.KIT_CHANGE,
138                DiffHelper.getKitDiffType(oldKitInfo, newKitInfo),
139                oldKitInfo,
140                newKitInfo
141              )
142            )
143          );
144        }
145      }
146    }
147
148    for (const key of clonedNewSDKApiMap.keys()) {
149      const newSourceFileInfo: ApiInfo | undefined = DiffHelper.getSourceFileInfo(clonedNewSDKApiMap.get(key));
150      const newKitInfo: string | undefined = newSourceFileInfo?.getLastJsDocInfo()?.getKit();
151      if (!clonedOldSDKApiMap.get(key) && newKitInfo !== 'NA') {
152        diffInfos.push(
153          DiffProcessorHelper.wrapDiffInfo(
154            undefined,
155            newSourceFileInfo,
156            new DiffTypeInfo(ApiStatusCode.KIT_CHANGE, ApiDiffType.KIT_NA_TO_HAVE, 'NA', newKitInfo)
157          )
158        );
159      }
160    }
161  }
162
163  static getKitDiffType(oldKitInfo: string | undefined, newKitInfo: string | undefined): ApiDiffType {
164    if (oldKitInfo === 'NA' && newKitInfo === '') {
165      return ApiDiffType.KIT_NA_TO_HAVE;
166    } else if (oldKitInfo === '' && newKitInfo === 'NA') {
167      return ApiDiffType.KIT_HAVE_TO_NA;
168    } else if (oldKitInfo === 'NA' || oldKitInfo === '') {
169      return ApiDiffType.KIT_NA_TO_HAVE;
170    } else if (newKitInfo === 'NA' || newKitInfo === '') {
171      return ApiDiffType.KIT_HAVE_TO_NA;
172    }
173    return ApiDiffType.KIT_CHANGE;
174  }
175
176  static getSourceFileInfo(fileMap: FileInfoMap | undefined): ApiInfo | undefined {
177    if (!fileMap) {
178      return undefined;
179    }
180    let sourceFileInfos: ApiInfo[] = [];
181    for (const apiKey of fileMap.keys()) {
182      if (apiKey === StringConstant.SELF) {
183        sourceFileInfos = fileMap.get(apiKey) as ApiInfo[];
184      }
185    }
186    return sourceFileInfos[0];
187  }
188
189  /**
190   * 通过数组长度判断是否为同名函数
191   *
192   * @param apiInfos
193   */
194  static judgeIsSameNameFunction(apiInfos: BasicApiInfo[]): boolean {
195    let isSameNameFunction: boolean = false;
196    if (apiInfos.length > 1 && apiInfos[0].getApiType() === ApiType.METHOD) {
197      isSameNameFunction = true;
198    }
199    return isSameNameFunction;
200  }
201
202  /**
203   * 对比新旧版本API差异,类型为数组是由于同名函数的存在,因此统一为数组方便处理
204   *
205   * @param { ApiInfo[] } oldApiInfos 老版本API信息
206   * @param { ApiInfo[] } newApiInfos 新版本API信息
207   * @param { BasicDiffInfo[] } diffInfos api差异结果集
208   */
209  static diffApis(
210    oldApiInfos: ApiInfo[],
211    newApiInfos: ApiInfo[],
212    diffInfos: BasicDiffInfo[],
213    isAllSheet: boolean,
214    isCheck?: boolean
215  ): void {
216    const diffSets: Map<string, ApiInfo>[] = DiffHelper.getDiffSet(oldApiInfos, newApiInfos);
217    const oldReduceNewMap: Map<string, ApiInfo> = diffSets[0];
218    const newReduceOldMap: Map<string, ApiInfo> = diffSets[1];
219    if (oldReduceNewMap.size === 0) {
220      newReduceOldMap.forEach((newApiInfo: ApiInfo) => {
221        diffInfos.push(
222          DiffProcessorHelper.wrapDiffInfo(
223            undefined,
224            newApiInfo,
225            new DiffTypeInfo(ApiStatusCode.NEW_API, ApiDiffType.ADD, undefined, newApiInfo.getDefinedText())
226          )
227        );
228      });
229      return;
230    }
231    if (newReduceOldMap.size === 0) {
232      oldReduceNewMap.forEach((oldApiInfo: ApiInfo) => {
233        diffInfos.push(
234          DiffProcessorHelper.wrapDiffInfo(
235            oldApiInfo,
236            undefined,
237            new DiffTypeInfo(ApiStatusCode.DELETE, ApiDiffType.REDUCE, oldApiInfo.getDefinedText(), undefined)
238          )
239        );
240      });
241      return;
242    }
243
244    DiffHelper.diffChangeApi(oldApiInfos, newApiInfos, diffInfos, isCheck);
245  }
246
247  /**
248   * 删除完全一样的API后,进行对比
249   * @param { ApiInfo[] } oldApiInfos
250   * @param { ApiInfo[] } newApiInfos
251   * @param diffInfos
252   * @param { boolean } isCheck 是否是api_check工具进行调用
253   */
254  static diffChangeApi(
255    oldApiInfos: ApiInfo[],
256    newApiInfos: ApiInfo[],
257    diffInfos: BasicDiffInfo[],
258    isCheck?: boolean
259  ): void {
260    //长度为1说明新旧版本有变更的只有1个,可直接进行对比
261    if (oldApiInfos.length === 1 && oldApiInfos.length === newApiInfos.length) {
262      DiffProcessorHelper.JsDocDiffHelper.diffJsDocInfo(oldApiInfos[0], newApiInfos[0], diffInfos);
263      DiffProcessorHelper.ApiDecoratorsDiffHelper.diffDecorator(oldApiInfos[0], newApiInfos[0], diffInfos);
264      DiffProcessorHelper.ApiNodeDiffHelper.diffNodeInfo(oldApiInfos[0], newApiInfos[0], diffInfos, isCheck);
265    } else {
266      const newMethodInfoMap: Map<string, ApiInfo> = DiffHelper.setmethodInfoMap(newApiInfos);
267      const oldMethodInfoMap: Map<string, ApiInfo> = DiffHelper.setmethodInfoMap(oldApiInfos);
268      oldApiInfos.forEach((oldApiInfo: ApiInfo) => {
269        const newApiInfo: ApiInfo | undefined = newMethodInfoMap.get(oldApiInfo.getDefinedText().replace(/\r|\n|\s+|,|;/g, ''));
270        if (!newApiInfo) {
271          return;
272        }
273        DiffProcessorHelper.JsDocDiffHelper.diffJsDocInfo(oldApiInfo, newApiInfo, diffInfos);
274        DiffProcessorHelper.ApiDecoratorsDiffHelper.diffDecorator(oldApiInfo, newApiInfo, diffInfos);
275        newMethodInfoMap.delete(oldApiInfo.getDefinedText().replace(/\r|\n|\s+|,|;/g, ''));
276        oldMethodInfoMap.delete(oldApiInfo.getDefinedText().replace(/\r|\n|\s+|,|;/g, ''));
277      });
278
279      for (const apiInfo of newMethodInfoMap.values()) {
280        const jsDocLength: number = apiInfo.getJsDocInfos().length;
281        if (jsDocLength === 1) {
282          diffInfos.push(
283            DiffProcessorHelper.wrapDiffInfo(
284              undefined,
285              apiInfo,
286              new DiffTypeInfo(
287                ApiStatusCode.NEW_API,
288                ApiDiffType.NEW_SAME_NAME_FUNCTION,
289                undefined,
290                apiInfo.getDefinedText()
291              )
292            )
293          );
294          newMethodInfoMap.delete(apiInfo.getDefinedText().replace(/\r|\n|\s+|,|;/g, ''));
295        }
296      }
297
298      if (oldMethodInfoMap.size === 1 && newMethodInfoMap.size === 1) {
299        const oldMethodInfo: ApiInfo = oldMethodInfoMap.entries().next().value[1];
300        const newMethodInfo: ApiInfo = newMethodInfoMap.entries().next().value[1];
301        DiffProcessorHelper.JsDocDiffHelper.diffJsDocInfo(oldMethodInfo, newMethodInfo, diffInfos);
302        DiffProcessorHelper.ApiDecoratorsDiffHelper.diffDecorator(oldMethodInfo, newMethodInfo, diffInfos);
303        DiffProcessorHelper.ApiNodeDiffHelper.diffNodeInfo(oldMethodInfo, newMethodInfo, diffInfos, isCheck);
304      } else {
305        DiffHelper.diffSameNameFunction(oldMethodInfoMap, newMethodInfoMap, diffInfos, isCheck);
306      }
307    }
308  }
309
310  static diffSameNameFunction(
311    oldMethodInfoMap: Map<string, ApiInfo>,
312    newMethodInfoMap: Map<string, ApiInfo>,
313    diffInfos: BasicDiffInfo[],
314    isCheck?: boolean
315  ): void {
316    for (const newApiInfo of newMethodInfoMap.values()) {
317      const newJsDocInfo: Comment.JsDocInfo | undefined = newApiInfo.getPenultimateJsDocInfo();
318      for (const oldApiInfo of oldMethodInfoMap.values()) {
319        const oldJsDocInfo: Comment.JsDocInfo | undefined = oldApiInfo.getLastJsDocInfo();
320        if (!DiffHelper.diffJsDoc(newJsDocInfo, oldJsDocInfo)) {
321          continue;
322        }
323        DiffProcessorHelper.ApiNodeDiffHelper.diffNodeInfo(oldApiInfo, newApiInfo, diffInfos, isCheck);
324        oldMethodInfoMap.delete(oldApiInfo.getDefinedText().replace(/\r|\n|\s+|,|;/g, ''));
325        newMethodInfoMap.delete(newApiInfo.getDefinedText().replace(/\r|\n|\s+|,|;/g, ''));
326      }
327    }
328
329    for (const apiInfo of newMethodInfoMap.values()) {
330      diffInfos.push(
331        DiffProcessorHelper.wrapDiffInfo(
332          undefined,
333          apiInfo,
334          new DiffTypeInfo(
335            ApiStatusCode.NEW_API,
336            ApiDiffType.NEW_SAME_NAME_FUNCTION,
337            undefined,
338            apiInfo.getDefinedText()
339          )
340        )
341      );
342    }
343
344    for (const apiInfo of oldMethodInfoMap.values()) {
345      diffInfos.push(
346        DiffProcessorHelper.wrapDiffInfo(
347          apiInfo,
348          undefined,
349          new DiffTypeInfo(
350            ApiStatusCode.DELETE,
351            ApiDiffType.REDUCE_SAME_NAME_FUNCTION,
352            apiInfo.getDefinedText(),
353            undefined
354          )
355        )
356      );
357    }
358  }
359
360  static removeApiInfo(basicApiInfo: BasicApiInfo): void {
361    DiffHelper.cleanApiInfo(basicApiInfo);
362    if (!containerApiTypes.has(basicApiInfo.getApiType())) {
363      return;
364    }
365    const containerApiInfo: ContainerApiInfo = basicApiInfo as ContainerApiInfo;
366    containerApiInfo.getChildApis().forEach((childApiInfo: BasicApiInfo) => {
367      DiffHelper.removeApiInfo(childApiInfo);
368    });
369  }
370
371  static cleanApiInfo(basicApiInfo: BasicApiInfo | undefined): void {
372    if (!basicApiInfo) {
373      return;
374    }
375    basicApiInfo.setParentApi(undefined);
376    basicApiInfo.removeNode();
377    if (basicApiInfo instanceof MethodInfo || basicApiInfo instanceof PropertyInfo) {
378      DiffHelper.cleanChildrenApiInfo(basicApiInfo.getObjLocations());
379      DiffHelper.cleanChildrenApiInfo(basicApiInfo.getTypeLocations());
380      if (basicApiInfo instanceof MethodInfo) {
381        basicApiInfo.getParams().forEach((param: ParamInfo) => {
382          DiffHelper.cleanChildrenApiInfo(param.getObjLocations());
383          DiffHelper.cleanChildrenApiInfo(param.getTypeLocations());
384          DiffHelper.cleanApiInfo(param.getMethodApiInfo());
385        });
386      }
387    }
388    if (basicApiInfo instanceof TypeAliasInfo) {
389      DiffHelper.cleanChildrenApiInfo(basicApiInfo.getTypeLiteralApiInfos());
390      basicApiInfo.getParamInfos().forEach((param: ParamInfo) => {
391        DiffHelper.cleanChildrenApiInfo(param.getObjLocations());
392        DiffHelper.cleanChildrenApiInfo(param.getTypeLocations());
393        DiffHelper.cleanApiInfo(param.getMethodApiInfo());
394      });
395    }
396  }
397
398  static cleanChildrenApiInfo(basicApiInfos: BasicApiInfo[] | undefined): void {
399    if (!basicApiInfos) {
400      return;
401    }
402    basicApiInfos.forEach((basicApiInfos: BasicApiInfo) => {
403      DiffHelper.processApiInfos(basicApiInfos);
404    });
405  }
406
407  /**
408   * 将每一个节点解析后的对象的parentApi属性置为undefined,防止循环引用
409   *
410   * @param { BasicApiInfo } basicApiInfo 解析后的api对象
411   */
412  static processApiInfos(basicApiInfo: BasicApiInfo | undefined): void {
413    if (!basicApiInfo) {
414      return;
415    }
416    DiffHelper.cleanApiInfo(basicApiInfo);
417    if (!containerApiTypes.has(basicApiInfo.getApiType())) {
418      return;
419    }
420    const containerApiInfo: ContainerApiInfo = basicApiInfo as ContainerApiInfo;
421    containerApiInfo.getChildApis().forEach((childApiInfo: BasicApiInfo) => {
422      DiffHelper.processApiInfos(childApiInfo);
423    });
424  }
425
426  static diffJsDoc(newJsDocInfo: Comment.JsDocInfo | undefined, oldJsDocInfo: Comment.JsDocInfo | undefined): boolean {
427    const tagInfoIsSame: Set<boolean> = new Set();
428    const paramAndReturnsIsSame: boolean =
429      DiffTagInfoHelper.diffParamTagInfo(oldJsDocInfo, newJsDocInfo) &&
430      DiffTagInfoHelper.diffReturnsTagInfo(oldJsDocInfo, newJsDocInfo);
431    newJsDocInfo?.getTags()?.forEach((tagInfo: Comment.CommentTag) => {
432      const method: TagInfoDiffProcessor | undefined = diffJsdocMethod.get(tagInfo.tag);
433      if (!method) {
434        return;
435      }
436      tagInfoIsSame.add(method(oldJsDocInfo, newJsDocInfo));
437    });
438
439    if (tagInfoIsSame.size === 1 && tagInfoIsSame.has(true)) {
440      return true;
441    } else if (tagInfoIsSame.size > 1 && paramAndReturnsIsSame) {
442      return true;
443    }
444    return false;
445  }
446
447  static judgeIsAllDeprecated(apiInfos: ApiInfo[]): boolean {
448    let isAllDeprecated: boolean = true;
449    apiInfos.forEach((apiInfo: ApiInfo) => {
450      const deprecatedVersion = apiInfo.getLastJsDocInfo()?.getDeprecatedVersion();
451      if (deprecatedVersion === '-1' || !deprecatedVersion) {
452        isAllDeprecated = false;
453      }
454    });
455    return isAllDeprecated;
456  }
457
458  static handleDeprecatedVersion(apiInfos: ApiInfo[]): void {
459    let isAllDeprecated: boolean = true;
460    apiInfos.forEach((apiInfo: ApiInfo) => {
461      const deprecatedVersion = apiInfo.getLastJsDocInfo()?.getDeprecatedVersion();
462      if (deprecatedVersion === '-1' || !deprecatedVersion) {
463        isAllDeprecated = false;
464      }
465    });
466    if (isAllDeprecated) {
467      return;
468    }
469    apiInfos.forEach((apiInfo: ApiInfo) => {
470      apiInfo.getLastJsDocInfo()?.setDeprecatedVersion('-1');
471    });
472  }
473
474  /**
475   * 拼接同名函数的API声明
476   *
477   * @param methodInfoMap
478   * @returns
479   */
480  static joinApiText(methodInfoMap: Map<string, ApiInfo>): string {
481    const apiTextArr: string[] = [];
482    for (const apiText of methodInfoMap.keys()) {
483      apiTextArr.push(apiText);
484    }
485    return apiTextArr.join(' #&# ');
486  }
487
488  /**
489   * 生成map,key为API声明,value为API信息
490   *
491   * @param apiInfos
492   * @returns
493   */
494  static setmethodInfoMap(apiInfos: ApiInfo[]): Map<string, ApiInfo> {
495    const methodInfoMap: Map<string, ApiInfo> = new Map();
496    apiInfos.forEach((apiInfo: ApiInfo) => {
497      methodInfoMap.set(apiInfo.getDefinedText().replace(/\r|\n|\s+|,|;/g, ''), apiInfo);
498    });
499    return methodInfoMap;
500  }
501
502  /**
503   * 删除新旧版本里所有信息一样的API
504   *
505   * @param oldApiInfos
506   * @param newApiInfos
507   * @returns
508   */
509  static getDiffSet(oldApiInfos: ApiInfo[], newApiInfos: ApiInfo[]): Map<string, ApiInfo>[] {
510    const oldApiInfoMap: Map<string, ApiInfo> = new Map();
511    const newApiInfoMap: Map<string, ApiInfo> = new Map();
512    DiffHelper.setApiInfoMap(oldApiInfoMap, oldApiInfos);
513    DiffHelper.setApiInfoMap(newApiInfoMap, newApiInfos);
514    const oldReduceNewMap: Map<string, ApiInfo> = new Map();
515    oldApiInfoMap.forEach((apiInfo: ApiInfo, key: string) => {
516      if (!newApiInfoMap.has(key)) {
517        oldReduceNewMap.set(key, apiInfo);
518      }
519    });
520    const newReduceOldMap: Map<string, ApiInfo> = new Map();
521    newApiInfoMap.forEach((apiInfo: ApiInfo, key: string) => {
522      if (!oldApiInfoMap.has(key)) {
523        newReduceOldMap.set(key, apiInfo);
524      }
525    });
526    return [oldReduceNewMap, newReduceOldMap];
527  }
528
529  static setApiInfoMap(apiInfoMap: Map<string, ApiInfo>, apiInfos: ApiInfo[]): void {
530    apiInfos.forEach((apiInfo: ApiInfo) => {
531      const key: string = `${apiInfo.getDefinedText()}#${apiInfo.getJsDocText()}#${JSON.stringify(apiInfo.getDecorators())}`;
532      apiInfoMap.set(key, apiInfo);
533    });
534  }
535
536  static getApiLocations(apiMap: FilesMap, isCheck?: boolean): Map<string, string[]> {
537    const apiLocations: Map<string, string[]> = new Map();
538    for (const filePath of apiMap.keys()) {
539      const index: number = 0;
540      const fileMap: FileInfoMap = apiMap.get(filePath) as FileInfoMap;
541      DiffHelper.processFileApiMap(fileMap, apiLocations, index, isCheck);
542    }
543    return apiLocations;
544  }
545
546  static processFileApiMap(
547    fileMap: FileInfoMap,
548    apiLocations: Map<string, string[]>,
549    index: number,
550    isCheck?: boolean
551  ): void {
552    for (const apiKey of fileMap.keys()) {
553      if (apiKey === StringConstant.SELF) {
554        continue;
555      }
556      const apiInfoMap: ApiInfosMap = fileMap.get(apiKey) as ApiInfosMap;
557      const apiInfos: BasicApiInfo[] = apiInfoMap.get(StringConstant.SELF) as BasicApiInfo[];
558      apiInfos.forEach((apiInfo: BasicApiInfo) => {
559        DiffHelper.processApiInfo(apiInfo, apiLocations, index, isCheck);
560        index++;
561      });
562    }
563  }
564
565  /**
566   * 删除最外层第一个API的jsdocText中,版权头和kit相关jsdoc
567   *
568   * @param apiInfo
569   * @returns
570   */
571  static deleteUselessJsdoc(apiInfo: BasicApiInfo): string {
572    const jsdocTextArr: string[] = apiInfo.getJsDocText().split('*/');
573    const clonedJsdocTextArr: string[] = jsdocTextArr;
574    if (clonedJsdocTextArr[1] && StringUtils.hasSubstring(clonedJsdocTextArr[1], CommentHelper.fileTag)) {
575      jsdocTextArr.splice(1, 1);
576    }
577
578    if (clonedJsdocTextArr[0] && StringUtils.hasSubstring(clonedJsdocTextArr[0], CommentHelper.fileTag)) {
579      jsdocTextArr.splice(0, 1);
580    }
581
582    if (clonedJsdocTextArr[0] && StringUtils.hasSubstring(clonedJsdocTextArr[0], CommentHelper.licenseKeyword)) {
583      jsdocTextArr.splice(0, 1);
584    }
585
586    return jsdocTextArr.join('*/');
587  }
588
589  static processApiInfo(
590    basicApiInfo: BasicApiInfo,
591    apiLocations: Map<string, string[]>,
592    index: number,
593    isCheck?: boolean
594  ): void {
595    const apiNode: ts.Node | undefined = basicApiInfo.getNode();
596    if (isCheck) {
597      const jsDocText: string | undefined = apiNode?.getFullText().replace(apiNode.getText(), '');
598      if (jsDocText) {
599        basicApiInfo.setJsDocText(jsDocText);
600      }
601    }
602
603    if (index === 0 && basicApiInfo.getParentApiType() === ApiType.SOURCE_FILE) {
604      const jsDocText: string = DiffHelper.deleteUselessJsdoc(basicApiInfo);
605      basicApiInfo.setJsDocText(jsDocText);
606    }
607
608    basicApiInfo.setSyscap(DiffHelper.getSyscapField(basicApiInfo));
609    if (!apiStatisticsType.has(basicApiInfo.getApiType())) {
610      return;
611    }
612    if (basicApiInfo.getApiName() === 'constructor') {
613      return;
614    }
615    const apiInfo: ApiInfo = basicApiInfo as ApiInfo;
616    const locations: string[] = apiInfo.getHierarchicalRelations();
617    apiLocations.set(locations.toString(), locations);
618    if (!containerApiTypes.has(apiInfo.getApiType())) {
619      return;
620    }
621    const containerApiInfo: ContainerApiInfo = apiInfo as ContainerApiInfo;
622    containerApiInfo.getChildApis().forEach((childApiInfo: BasicApiInfo) => {
623      DiffHelper.processApiInfo(childApiInfo, apiLocations, 1, isCheck);
624    });
625  }
626
627  static getSyscapField(apiInfo: BasicApiInfo): string {
628    if (apiInfo.getApiType() === ApiType.SOURCE_FILE) {
629      const sourceFileContent: string = apiInfo.getNode()?.getFullText() as string;
630      let syscap = '';
631      if (/\@[S|s][Y|y][S|s][C|c][A|a][P|p]\s*((\w|\.|\/|\{|\@|\}|\s)+)/g.test(sourceFileContent)) {
632        sourceFileContent.replace(
633          /\@[S|s][Y|y][S|s][C|c][A|a][P|p]\s*((\w|\.|\/|\{|\@|\}|\s)+)/g,
634          (sysCapInfo: string, args: []) => {
635            syscap = sysCapInfo.replace(/\@[S|s][Y|y][S|s][C|c][A|a][P|p]/g, '').trim();
636            return syscap;
637          }
638        );
639      }
640      return FunctionUtils.handleSyscap(syscap);
641    }
642    if (notJsDocApiTypes.has(apiInfo.getApiType())) {
643      return '';
644    }
645    const clonedApiInfo: ApiInfo = apiInfo as ApiInfo;
646    const latestJsDocInfo: Comment.JsDocInfo | undefined = clonedApiInfo.getLastJsDocInfo();
647    if (!latestJsDocInfo) {
648      return DiffHelper.searchSyscapFieldInParent(clonedApiInfo);
649    }
650    let syscap: string | undefined = latestJsDocInfo?.getSyscap();
651    if (!syscap) {
652      return DiffHelper.searchSyscapFieldInParent(clonedApiInfo);
653    }
654    if (syscap) {
655      return FunctionUtils.handleSyscap(syscap);
656    }
657    return '';
658  }
659
660  static searchSyscapFieldInParent(apiInfo: ApiInfo): string {
661    let curApiInfo: ApiInfo = apiInfo;
662    let syscap: string = '';
663    const node: ts.Node | undefined = curApiInfo.getNode();
664    while (node && curApiInfo && !ts.isSourceFile(node)) {
665      syscap = curApiInfo.getSyscap();
666      if (syscap) {
667        return syscap;
668      }
669      curApiInfo = curApiInfo.getParentApi() as ApiInfo;
670    }
671    return syscap;
672  }
673}
674
675/**
676 * 比较每个标签的值
677 */
678export class DiffTagInfoHelper {
679  static diffSyscapTagInfo(
680    oldJsDocInfo: Comment.JsDocInfo | undefined,
681    newJsDocInfo: Comment.JsDocInfo | undefined
682  ): boolean {
683    if (oldJsDocInfo?.getSyscap() === newJsDocInfo?.getSyscap()) {
684      return true;
685    }
686    return false;
687  }
688
689  static diffSinceTagInfo(
690    oldJsDocInfo: Comment.JsDocInfo | undefined,
691    newJsDocInfo: Comment.JsDocInfo | undefined
692  ): boolean {
693    if (oldJsDocInfo?.getSince() === newJsDocInfo?.getSince()) {
694      return true;
695    }
696    return false;
697  }
698
699  static diffFormTagInfo(
700    oldJsDocInfo: Comment.JsDocInfo | undefined,
701    newJsDocInfo: Comment.JsDocInfo | undefined
702  ): boolean {
703    if (oldJsDocInfo?.getIsForm() === newJsDocInfo?.getIsForm()) {
704      return true;
705    }
706    return false;
707  }
708
709  static diffCrossPlatFromTagInfo(
710    oldJsDocInfo: Comment.JsDocInfo | undefined,
711    newJsDocInfo: Comment.JsDocInfo | undefined
712  ): boolean {
713    if (oldJsDocInfo?.getIsCrossPlatForm() === newJsDocInfo?.getIsCrossPlatForm()) {
714      return true;
715    }
716    return false;
717  }
718
719  static diffSystemApiTagInfo(
720    oldJsDocInfo: Comment.JsDocInfo | undefined,
721    newJsDocInfo: Comment.JsDocInfo | undefined
722  ): boolean {
723    if (oldJsDocInfo?.getIsSystemApi() === newJsDocInfo?.getIsSystemApi()) {
724      return true;
725    }
726    return false;
727  }
728
729  static diffModelTagInfo(
730    oldJsDocInfo: Comment.JsDocInfo | undefined,
731    newJsDocInfo: Comment.JsDocInfo | undefined
732  ): boolean {
733    if (oldJsDocInfo?.getModelLimitation() === newJsDocInfo?.getModelLimitation()) {
734      return true;
735    }
736    return false;
737  }
738
739  static diffDeprecatedTagInfo(
740    oldJsDocInfo: Comment.JsDocInfo | undefined,
741    newJsDocInfo: Comment.JsDocInfo | undefined
742  ): boolean {
743    if (oldJsDocInfo?.getDeprecatedVersion() === newJsDocInfo?.getDeprecatedVersion()) {
744      return true;
745    }
746    return false;
747  }
748
749  static diffUseinsteadTagInfo(
750    oldJsDocInfo: Comment.JsDocInfo | undefined,
751    newJsDocInfo: Comment.JsDocInfo | undefined
752  ): boolean {
753    if (oldJsDocInfo?.getUseinstead() === newJsDocInfo?.getUseinstead()) {
754      return true;
755    }
756    return false;
757  }
758
759  static diffPermissionTagInfo(
760    oldJsDocInfo: Comment.JsDocInfo | undefined,
761    newJsDocInfo: Comment.JsDocInfo | undefined
762  ): boolean {
763    if (oldJsDocInfo?.getPermission() === newJsDocInfo?.getPermission()) {
764      return true;
765    }
766    return false;
767  }
768
769  static diffThrowsTagInfo(
770    oldJsDocInfo: Comment.JsDocInfo | undefined,
771    newJsDocInfo: Comment.JsDocInfo | undefined
772  ): boolean {
773    if (oldJsDocInfo?.getErrorCode().sort().join() === newJsDocInfo?.getErrorCode().sort().join()) {
774      return true;
775    }
776    return false;
777  }
778
779  static diffAtomicServiceTagInfo(
780    oldJsDocInfo: Comment.JsDocInfo | undefined,
781    newJsDocInfo: Comment.JsDocInfo | undefined
782  ): boolean {
783    if (oldJsDocInfo?.getIsAtomicService() === newJsDocInfo?.getIsAtomicService()) {
784      return true;
785    }
786    return false;
787  }
788
789  static diffParamTagInfo(
790    oldJsDocInfo: Comment.JsDocInfo | undefined,
791    newJsDocInfo: Comment.JsDocInfo | undefined
792  ): boolean {
793    const oldParamArr: string[] = [];
794    const newParamArr: string[] = [];
795    oldJsDocInfo?.getTags()?.forEach((tagInfo: Comment.CommentTag) => {
796      if (tagInfo.tag === Comment.JsDocTag.PARAM) {
797        oldParamArr.push(`${tagInfo.name}#${tagInfo.type}`);
798      }
799    });
800
801    newJsDocInfo?.getTags()?.forEach((tagInfo: Comment.CommentTag) => {
802      if (tagInfo.tag === Comment.JsDocTag.PARAM) {
803        newParamArr.push(`${tagInfo.name}#${tagInfo.type}`);
804      }
805    });
806    if (oldParamArr.join() === newParamArr.join()) {
807      return true;
808    }
809    return false;
810  }
811
812  static diffReturnsTagInfo(
813    oldJsDocInfo: Comment.JsDocInfo | undefined,
814    newJsDocInfo: Comment.JsDocInfo | undefined
815  ): boolean {
816    let oldReturnValue: string = '';
817    let newReturnValue: string = '';
818
819    oldJsDocInfo?.getTags()?.forEach((tagInfo: Comment.CommentTag) => {
820      if (tagInfo.tag === Comment.JsDocTag.RETURNS) {
821        oldReturnValue = tagInfo.type;
822      }
823    });
824
825    newJsDocInfo?.getTags()?.forEach((tagInfo: Comment.CommentTag) => {
826      if (tagInfo.tag === Comment.JsDocTag.PARAM) {
827        oldReturnValue = tagInfo.type;
828      }
829    });
830    if (oldReturnValue === oldReturnValue) {
831      return true;
832    }
833    return false;
834  }
835}
836
837export const diffJsdocMethod: Map<string, TagInfoDiffProcessor> = new Map([
838  [Comment.JsDocTag.SYSCAP, DiffTagInfoHelper.diffSyscapTagInfo],
839  [Comment.JsDocTag.SINCE, DiffTagInfoHelper.diffSinceTagInfo],
840  [Comment.JsDocTag.FORM, DiffTagInfoHelper.diffFormTagInfo],
841  [Comment.JsDocTag.CROSS_PLAT_FORM, DiffTagInfoHelper.diffCrossPlatFromTagInfo],
842  [Comment.JsDocTag.SYSTEM_API, DiffTagInfoHelper.diffSystemApiTagInfo],
843  [Comment.JsDocTag.STAGE_MODEL_ONLY, DiffTagInfoHelper.diffModelTagInfo],
844  [Comment.JsDocTag.FA_MODEL_ONLY, DiffTagInfoHelper.diffModelTagInfo],
845  [Comment.JsDocTag.DEPRECATED, DiffTagInfoHelper.diffDeprecatedTagInfo],
846  [Comment.JsDocTag.USEINSTEAD, DiffTagInfoHelper.diffUseinsteadTagInfo],
847  [Comment.JsDocTag.PERMISSION, DiffTagInfoHelper.diffPermissionTagInfo],
848  [Comment.JsDocTag.THROWS, DiffTagInfoHelper.diffThrowsTagInfo],
849  [Comment.JsDocTag.ATOMIC_SERVICE, DiffTagInfoHelper.diffAtomicServiceTagInfo],
850  [Comment.JsDocTag.PARAM, DiffTagInfoHelper.diffParamTagInfo],
851  [Comment.JsDocTag.RETURNS, DiffTagInfoHelper.diffReturnsTagInfo],
852]);
853