• 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 {
17  ApiInfo,
18  ApiType,
19  BasicApiInfo,
20  ContainerApiInfo,
21  containerApiTypes,
22  notJsDocApiTypes,
23} from '../../typedef/parser/ApiInfoDefination';
24import {
25  ApiStatisticsInfo,
26  apiNotStatisticsType,
27  apiStatisticsType,
28  mergeDefinedTextType,
29  notMergeDefinedText,
30  StatisticsInfoValueType,
31} from '../../typedef/statistics/ApiStatistics';
32import { Comment } from '../../typedef/parser/Comment';
33import { StringConstant, NumberConstant } from '../../utils/Constant';
34import { ApiInfosMap, FileInfoMap, FilesMap, BasicApiInfoMap } from '../parser/parser';
35import { LogUtil } from '../../utils/logUtil';
36import ts from 'typescript';
37
38export class ApiStatisticsHelper {
39  /**
40   * 获取需要收集的api信息
41   *
42   * @param { FilesMap } apiMap 根据接口定义文件处理得到的map结果
43   * @returns { ApiStatisticsInfo[] } 返回需要统计的api列表
44   */
45  static getApiStatisticsInfos(apiMap: FilesMap): StatisticsInfoValueType {
46    const apiStatisticsInfos: ApiStatisticsInfo[] = [];
47    const allApiStatisticsInfos: ApiStatisticsInfo[] = [];
48    // map的第一层key均为文件路径名
49    for (const filePath of apiMap.keys()) {
50      const fileMap: FileInfoMap | undefined = apiMap.get(filePath);
51      if (!fileMap) {
52        continue;
53      }
54      ApiStatisticsHelper.processFileApiMap(fileMap, apiStatisticsInfos, allApiStatisticsInfos);
55    }
56    return { apiStatisticsInfos: apiStatisticsInfos, allApiStatisticsInfos: allApiStatisticsInfos };
57  }
58
59  /**
60   * 得到SourceFile节点下的第一层节点解析后的对象,方便后续以此为基础,得到整个文件所有的节点信息
61   *
62   * @param { FileInfoMap } fileMap 获取到的一个文件解析到的map对象
63   * @param { ApiStatisticsInfo[] } apiStatisticsInfos SourceFile节点下的第一层节点解析后的对象
64   */
65  static processFileApiMap(
66    fileMap: FileInfoMap,
67    apiStatisticsInfos: ApiStatisticsInfo[],
68    allApiStatisticsInfos: ApiStatisticsInfo[]
69  ): void {
70    for (const apiKey of fileMap.keys()) {
71      if (apiKey === StringConstant.SELF) {
72        continue;
73      }
74      const apiInfoMap: ApiInfosMap | BasicApiInfo[] | undefined = fileMap.get(apiKey);
75      if (!apiInfoMap || Array.isArray(apiInfoMap)) {
76        continue;
77      }
78      const apiInfos: BasicApiInfo[] | BasicApiInfoMap | undefined = apiInfoMap.get(StringConstant.SELF);
79      const sameNameRawTextMap: Map<string, string> = new Map();
80      if (!apiInfos || !Array.isArray(apiInfos)) {
81        continue;
82      }
83      ApiStatisticsHelper.connectDefinedText(apiInfos, sameNameRawTextMap);
84      const apiRelations: Set<string> = new Set();
85      apiInfos.forEach((apiInfo: BasicApiInfo) => {
86        ApiStatisticsHelper.processApiInfo(
87          apiInfo,
88          apiStatisticsInfos,
89          sameNameRawTextMap,
90          allApiStatisticsInfos,
91          apiRelations
92        );
93      });
94    }
95  }
96
97  /**
98   * 遍历一个文件内的所有API,将同名函数的API声明合并
99   *
100   * @param { BasicApiInfo[] } apiInfos 存放API信息的数组
101   * @param { Map<string, string> } sameNameRawTextMap 存放处理后的同名函数声明
102   */
103  static connectDefinedText(apiInfos: BasicApiInfo[], sameNameRawTextMap: Map<string, string>): void {
104    apiInfos.forEach((apiInfo: BasicApiInfo) => {
105      if (apiInfo.getApiType() === ApiType.METHOD) {
106        if (notMergeDefinedText.has(apiInfo.getApiName())) {
107          return;
108        }
109        const currentApiText: string | undefined = sameNameRawTextMap.get(ApiStatisticsHelper.joinRelations(apiInfo));
110        if (currentApiText) {
111          const definedText: string = `${currentApiText}\n${apiInfo.getDefinedText()}`;
112          sameNameRawTextMap.set(ApiStatisticsHelper.joinRelations(apiInfo), definedText);
113          return;
114        } else {
115          sameNameRawTextMap.set(ApiStatisticsHelper.joinRelations(apiInfo), apiInfo.getDefinedText());
116        }
117      }
118
119      if (containerApiTypes.has(apiInfo.getApiType())) {
120        const containerApiInfo: ContainerApiInfo = apiInfo as ContainerApiInfo;
121        ApiStatisticsHelper.connectDefinedText(containerApiInfo.getChildApis(), sameNameRawTextMap);
122      }
123    });
124  }
125
126  /**
127   * 处理API层级关系
128   *
129   * @param { BasicApiInfo } childApiInfo API信息
130   * @returns
131   */
132  static joinRelations(childApiInfo: BasicApiInfo): string {
133    const relations = childApiInfo.getHierarchicalRelations().join();
134    return relations;
135  }
136
137  /**
138   * 根据传入的api信息,转化为需要统计的api信息对象并统计
139   *
140   * @param { BasicApiInfo } basicApiInfo 解析后的api对象
141   * @param apiStatisticsInfos api统计列表对象
142   */
143  static processApiInfo(
144    basicApiInfo: BasicApiInfo,
145    apiStatisticsInfos: ApiStatisticsInfo[],
146    sameNameRawTextMap: Map<string, string>,
147    allApiStatisticsInfos: ApiStatisticsInfo[],
148    apiRelations: Set<string>
149  ): void {
150    const apiInfo: ApiInfo = basicApiInfo as ApiInfo;
151    allApiStatisticsInfos.push(ApiStatisticsHelper.initApiStatisticsInfo(apiInfo, sameNameRawTextMap, true));
152
153    if (!apiStatisticsType.has(basicApiInfo.getApiType())) {
154      return;
155    }
156    if (basicApiInfo.getApiName() === StringConstant.CONSTRUCTOR_API_NAME) {
157      return;
158    }
159    const apiStatisticsInfo: ApiStatisticsInfo = ApiStatisticsHelper.initApiStatisticsInfo(apiInfo, sameNameRawTextMap);
160    if (
161      !apiNotStatisticsType.has(apiStatisticsInfo.getApiType()) &&
162      !apiRelations.has(ApiStatisticsHelper.joinRelations(basicApiInfo))
163    ) {
164      apiStatisticsInfos.push(apiStatisticsInfo);
165    }
166    apiRelations.add(ApiStatisticsHelper.joinRelations(basicApiInfo));
167    if (!containerApiTypes.has(apiInfo.getApiType())) {
168      return;
169    }
170    const containerApiInfo: ContainerApiInfo = apiInfo as ContainerApiInfo;
171    containerApiInfo.getChildApis().forEach((childApiInfo: BasicApiInfo) => {
172      ApiStatisticsHelper.processApiInfo(
173        childApiInfo,
174        apiStatisticsInfos,
175        sameNameRawTextMap,
176        allApiStatisticsInfos,
177        apiRelations
178      );
179    });
180  }
181
182  static initApiStatisticsInfo(
183    apiInfo: ApiInfo,
184    sameNameRawTextMap: Map<string, string>,
185    isAll?: boolean
186  ): ApiStatisticsInfo {
187    const apiStatisticsInfo: ApiStatisticsInfo = new ApiStatisticsInfo();
188    const relations = apiInfo.getHierarchicalRelations();
189    if (relations.length > NumberConstant.RELATION_LENGTH) {
190      apiStatisticsInfo.setParentModuleName(relations[relations.length - NumberConstant.RELATION_LENGTH]);
191    }
192    const apiRelations: string = ApiStatisticsHelper.joinRelations(apiInfo);
193    const sameNameDefinedText: string | undefined = sameNameRawTextMap.get(apiRelations);
194
195    if (isAll) {
196      apiStatisticsInfo.setDefinedText(apiInfo.getDefinedText());
197    } else {
198      apiStatisticsInfo.setDefinedText(sameNameDefinedText ? sameNameDefinedText : apiInfo.getDefinedText());
199    }
200    apiStatisticsInfo
201      .setFilePath(apiInfo.getFilePath())
202      .setApiType(apiInfo.getApiType())
203      .setApiName(apiInfo.getApiName())
204      .setPos(apiInfo.getPos())
205      .setHierarchicalRelations(relations.join('/'))
206      .setDecorators(apiInfo.getDecorators());
207    if (notJsDocApiTypes.has(apiInfo.getApiType())) {
208      return apiStatisticsInfo;
209    }
210    const firstJsDocInfo: Comment.JsDocInfo | undefined = apiInfo.getJsDocInfos()[0];
211    if (firstJsDocInfo) {
212      apiStatisticsInfo.setSince(firstJsDocInfo.getSince());
213    }
214    const jsDocInfo: Comment.JsDocInfo | undefined = apiInfo.getLastJsDocInfo();
215    if (jsDocInfo) {
216      apiStatisticsInfo
217        .setSyscap(jsDocInfo.getSyscap() ? jsDocInfo.getSyscap() : ApiStatisticsHelper.extendSyscap(apiInfo))
218        .setPermission(jsDocInfo.getPermission())
219        .setIsForm(jsDocInfo.getIsForm())
220        .setIsCrossPlatForm(jsDocInfo.getIsCrossPlatForm())
221        .setDeprecatedVersion(
222          jsDocInfo.getDeprecatedVersion() === NumberConstant.DEFAULT_DEPRECATED_VERSION ?
223            '' :
224            jsDocInfo.getDeprecatedVersion()
225        )
226        .setUseInstead(jsDocInfo.getUseinstead())
227        .setApiLevel(jsDocInfo.getIsSystemApi())
228        .setModelLimitation(jsDocInfo.getModelLimitation())
229        .setIsAutomicService(jsDocInfo.getIsAtomicService())
230        .setErrorCodes(jsDocInfo.getErrorCode());
231    } else {
232      apiStatisticsInfo.setSyscap(ApiStatisticsHelper.extendSyscap(apiInfo));
233    }
234    return apiStatisticsInfo;
235  }
236
237  /**
238   * 未标注@syscap的API,从父级API寻找syscap
239   *
240   * @param {ApiInfo} apiInfo API信息
241   * @returns syscap
242   */
243  static extendSyscap(apiInfo: ApiInfo): string {
244    let curApiInfo: ApiInfo = apiInfo;
245    let syscap: string = '';
246    const node: ts.Node | undefined = curApiInfo.getNode();
247    try {
248      while (node && curApiInfo && !ts.isSourceFile(node)) {
249        const jsdoc: Comment.JsDocInfo | undefined = curApiInfo.getLastJsDocInfo();
250        if (jsdoc) {
251          syscap = jsdoc.getSyscap();
252        }
253        if (syscap) {
254          return syscap;
255        }
256        curApiInfo = curApiInfo.getParentApi() as ApiInfo;
257      }
258    } catch (error) {
259      LogUtil.e('SYSCAP ERROR', error);
260    }
261    return syscap;
262  }
263
264  /**
265   * 获取api的总数
266   *
267   * @param { ApiStatisticsInfo[] } apiStatisticsInfos api统计列表对象
268   * @returns { number } api的数量
269   */
270  static getApiNumber(apiStatisticsInfos: ApiStatisticsInfo[]): number {
271    const apis: Set<string> = new Set();
272    apiStatisticsInfos.forEach((apiStatisticsInfo: ApiStatisticsInfo) => {
273      apis.add(apiStatisticsInfo.getHierarchicalRelations());
274    });
275    return apis.size;
276  }
277}
278