• 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 ts from 'typescript';
17import { EventConstant } from '../../../utils/Constant';
18import { EventMethodData, CollectParamStatus } from '../../../typedef/checker/event_method_check_interface';
19import { ApiCheckInfo, ErrorBaseInfo, ErrorID, ErrorLevel, ErrorMessage, ErrorType, LogType } from '../../../typedef/checker/result_type';
20import { ApiInfo, ApiType, BasicApiInfo, MethodInfo, ParamInfo } from '../../../typedef/parser/ApiInfoDefination';
21import { CommonFunctions } from '../../../utils/checkUtils';
22import { FilesMap, Parser } from '../../parser/parser';
23import { AddErrorLogs } from './compile_info';
24import { compositiveResult, compositiveLocalResult } from '../../../utils/checkUtils';
25import { CheckHump } from './check_hump';
26import { ApiCheckVersion } from '../config/api_check_version.json';
27import { Check, currentFilePath } from './api_check_plugin';
28
29export class EventMethodChecker {
30  private apiData: FilesMap;
31  constructor(apiData: FilesMap) {
32    this.apiData = apiData;
33  }
34
35  public getAllEventMethod(): Map<string, EventMethodData> {
36    const allNodeInfos: ApiInfo[] = Parser.getAllBasicApi(this.apiData) as ApiInfo[];
37    let allBasicApi: ApiInfo[] = [];
38    Check.getHasJsdocApiInfos(allNodeInfos, allBasicApi);
39    const eventMethodInfo: MethodInfo[] = [];
40    allBasicApi.forEach((basicApi: ApiInfo) => {
41      if (basicApi.apiType === ApiType.METHOD && basicApi.getIsJoinType()) {
42        eventMethodInfo.push(basicApi as MethodInfo);
43      }
44    });
45    const eventMethodDataMap: Map<string, EventMethodData> = this.getEventMethodDataMap(eventMethodInfo);
46    return eventMethodDataMap;
47  }
48
49  public checkEventMethod(eventMethodData: Map<string, EventMethodData>): void {
50    eventMethodData.forEach((eventMethod: EventMethodData) => {
51      const onEvent: MethodInfo[] = eventMethod.onEvents.length > 0 ? eventMethod.onEvents : [];
52      const onEventPublishVersionValue: string = onEvent.length > 0 ? onEvent[0].jsDocInfos[0].since : '-1';
53      const offEvent: MethodInfo[] = eventMethod.offEvents.length > 0 ? eventMethod.offEvents : [];
54      const offEventPublishVersionValue: string = offEvent.length > 0 ? offEvent[0].jsDocInfos[0].since : '-1';
55      const isLostOnEvent: boolean = eventMethod.onEvents.length === 0 && eventMethod.offEvents.length !== 0 &&
56        offEventPublishVersionValue === JSON.stringify(ApiCheckVersion);
57      const isLostOffEvent: boolean = eventMethod.onEvents.length !== 0 && eventMethod.offEvents.length === 0 &&
58        onEventPublishVersionValue === JSON.stringify(ApiCheckVersion);
59
60      // check on&off event pair
61      if (isLostOnEvent || isLostOffEvent) {
62        const firstEvent: BasicApiInfo = eventMethod.onEvents.concat(eventMethod.offEvents)[0];
63        const errorMessage: string = CommonFunctions.createErrorInfo(ErrorMessage.ERROR_EVENT_ON_AND_OFF_PAIR, []);
64        const errorBaseInfo: ErrorBaseInfo = new ErrorBaseInfo();
65        errorBaseInfo
66          .setErrorID(ErrorID.API_PAIR_ERRORS_ID)
67          .setErrorLevel(ErrorLevel.MIDDLE)
68          .setErrorType(ErrorType.API_PAIR_ERRORS)
69          .setLogType(LogType.LOG_JSDOC)
70          .setErrorInfo(errorMessage);
71        const apiInfoEvent: ApiCheckInfo = CommonFunctions.getErrorInfo(firstEvent, undefined, currentFilePath,
72          errorBaseInfo);
73        AddErrorLogs.addAPICheckErrorLogs(apiInfoEvent, compositiveResult, compositiveLocalResult);
74      }
75
76      // check off event
77      let offEvnetCallbackNumber: number = 0;
78      let offCallbackRequiredNumber: number = 0;
79      for (let i = 0; i < eventMethod.offEvents.length; i++) {
80        const offEvent: MethodInfo = eventMethod.offEvents[i] as MethodInfo;
81        if (offEvent.getParams().length < 2) {
82          continue;
83        }
84        const eventCallbackStatus: CollectParamStatus = this.collectEventCallback(offEvent, offEvnetCallbackNumber,
85          offCallbackRequiredNumber);
86        offEvnetCallbackNumber = eventCallbackStatus.callbackNumber;
87        offCallbackRequiredNumber = eventCallbackStatus.requiredCallbackNumber;
88      }
89      if (eventMethod.offEvents.length > 0 && offEventPublishVersionValue === JSON.stringify(ApiCheckVersion)) {
90        if ((offEvnetCallbackNumber !== 0 && offEvnetCallbackNumber === eventMethod.offEvents.length &&
91          offEvnetCallbackNumber === offCallbackRequiredNumber) ||
92          (offEvnetCallbackNumber === 0 && eventMethod.offEvents.length !== 0)) {
93          const firstEvent: BasicApiInfo = eventMethod.offEvents[0];
94          const errorMessage: string = CommonFunctions.createErrorInfo(ErrorMessage.ERROR_EVENT_CALLBACK_OPTIONAL, []);
95          const errorBaseInfo: ErrorBaseInfo = new ErrorBaseInfo();
96          errorBaseInfo
97            .setErrorID(ErrorID.PARAMETER_ERRORS_ID)
98            .setErrorLevel(ErrorLevel.MIDDLE)
99            .setErrorType(ErrorType.PARAMETER_ERRORS)
100            .setLogType(LogType.LOG_JSDOC)
101            .setErrorInfo(errorMessage);
102          const apiInfoEvent: ApiCheckInfo = CommonFunctions.getErrorInfo(firstEvent, undefined, currentFilePath,
103            errorBaseInfo);
104          AddErrorLogs.addAPICheckErrorLogs(apiInfoEvent, compositiveResult, compositiveLocalResult);
105        }
106      }
107
108      // check event first param
109      const allEvents: BasicApiInfo[] = eventMethod.onEvents.concat(eventMethod.offEvents)
110        .concat(eventMethod.emitEvents).concat(eventMethod.onceEvents);
111
112      for (let i = 0; i < allEvents.length; i++) {
113        const event: BasicApiInfo = allEvents[i];
114        if (!this.checkVersionNeedCheck(event)) {
115          continue;
116        }
117        const eventParams: ParamInfo[] = (event as MethodInfo).getParams();
118        const eventPublishVersion: string = (event as MethodInfo).jsDocInfos[0].since;
119        if (eventParams.length < 1 && eventPublishVersion === JSON.stringify(ApiCheckVersion)) {
120          const errorMessage: string = CommonFunctions.createErrorInfo(ErrorMessage.ERROR_EVENT_WITHOUT_PARAMETER, []);
121          const errorBaseInfo: ErrorBaseInfo = new ErrorBaseInfo();
122          errorBaseInfo
123            .setErrorID(ErrorID.PARAMETER_ERRORS_ID)
124            .setErrorLevel(ErrorLevel.MIDDLE)
125            .setErrorType(ErrorType.PARAMETER_ERRORS)
126            .setLogType(LogType.LOG_JSDOC)
127            .setErrorInfo(errorMessage);
128          const apiInfoEvent: ApiCheckInfo = CommonFunctions.getErrorInfo(event, undefined, currentFilePath,
129            errorBaseInfo);
130          AddErrorLogs.addAPICheckErrorLogs(apiInfoEvent, compositiveResult, compositiveLocalResult);
131          continue;
132        }
133        const firstParam: ParamInfo | undefined = eventParams.length ? eventParams[0] : undefined;
134        if (firstParam === undefined || eventPublishVersion !== JSON.stringify(ApiCheckVersion)) {
135          continue;
136        }
137        if (firstParam.getParamType() === ts.SyntaxKind.LiteralType) {
138          const paramTypeName: string = firstParam.getType()[0].replace(/\'/g, '');
139          if (paramTypeName === '') {
140            const errorMessage: string = CommonFunctions.createErrorInfo(ErrorMessage.ERROR_EVENT_NAME_NULL,
141              [firstParam.getApiName()]);
142            const errorBaseInfo: ErrorBaseInfo = new ErrorBaseInfo();
143            errorBaseInfo
144              .setErrorID(ErrorID.PARAMETER_ERRORS_ID)
145              .setErrorLevel(ErrorLevel.MIDDLE)
146              .setErrorType(ErrorType.PARAMETER_ERRORS)
147              .setLogType(LogType.LOG_JSDOC)
148              .setErrorInfo(errorMessage);
149            const apiInfoEvent: ApiCheckInfo = CommonFunctions.getErrorInfo(event, undefined, currentFilePath,
150              errorBaseInfo);
151            AddErrorLogs.addAPICheckErrorLogs(apiInfoEvent, compositiveResult, compositiveLocalResult);
152          } else if (!CheckHump.checkSmallHump(paramTypeName)) {
153            const errorMessage: string = CommonFunctions.createErrorInfo(ErrorMessage.ERROR_EVENT_NAME_SMALL_HUMP,
154              [paramTypeName]);
155            const errorBaseInfo: ErrorBaseInfo = new ErrorBaseInfo();
156            errorBaseInfo
157              .setErrorID(ErrorID.PARAMETER_ERRORS_ID)
158              .setErrorLevel(ErrorLevel.MIDDLE)
159              .setErrorType(ErrorType.PARAMETER_ERRORS)
160              .setLogType(LogType.LOG_JSDOC)
161              .setErrorInfo(errorMessage);
162            const apiInfoEvent: ApiCheckInfo = CommonFunctions.getErrorInfo(event, undefined, currentFilePath,
163              errorBaseInfo);
164            AddErrorLogs.addAPICheckErrorLogs(apiInfoEvent, compositiveResult, compositiveLocalResult);
165          }
166        } else if (firstParam.getParamType() !== ts.SyntaxKind.StringKeyword) {
167          const errorMessage: string = CommonFunctions.createErrorInfo(ErrorMessage.ERROR_EVENT_NAME_STRING,
168            [firstParam.getApiName()]);
169          const errorBaseInfo: ErrorBaseInfo = new ErrorBaseInfo();
170          errorBaseInfo
171            .setErrorID(ErrorID.PARAMETER_ERRORS_ID)
172            .setErrorLevel(ErrorLevel.MIDDLE)
173            .setErrorType(ErrorType.PARAMETER_ERRORS)
174            .setLogType(LogType.LOG_JSDOC)
175            .setErrorInfo(errorMessage);
176          const apiInfoEvent: ApiCheckInfo = CommonFunctions.getErrorInfo(event, undefined, currentFilePath,
177            errorBaseInfo);
178          AddErrorLogs.addAPICheckErrorLogs(apiInfoEvent, compositiveResult, compositiveLocalResult);
179        }
180      }
181    });
182  }
183
184  private checkVersionNeedCheck(eventInfo: BasicApiInfo): boolean {
185    const eventApiVersion: string = CommonFunctions.getSinceVersion(eventInfo.getCurrentVersion());
186    return parseInt(eventApiVersion) >= EventConstant.eventMethodCheckVersion;
187  }
188
189  private collectEventCallback(offEvent: MethodInfo,
190    callbackNumber: number, requiredCallbackNumber: number): CollectParamStatus {
191    const lastParam: ParamInfo = offEvent.getParams().slice(-1)[0];
192    if (lastParam.paramType) {
193      const basicTypes = new Set([ts.SyntaxKind.NumberKeyword, ts.SyntaxKind.StringKeyword,
194      ts.SyntaxKind.BooleanKeyword, ts.SyntaxKind.UndefinedKeyword, ts.SyntaxKind.LiteralType]);
195      if (!basicTypes.has(lastParam.paramType)) {
196        callbackNumber++;
197        if (lastParam.getIsRequired()) {
198          requiredCallbackNumber++;
199        }
200      }
201    }
202    return {
203      callbackNumber: callbackNumber,
204      requiredCallbackNumber: requiredCallbackNumber
205    };
206  }
207
208  private getEventMethodDataMap(eventInfos: MethodInfo[]): Map<string, EventMethodData> {
209    let eventMethodDataMap: Map<string, EventMethodData> = new Map();
210    eventInfos.forEach((eventInfo: MethodInfo) => {
211      const directorRelations: string[] = [...eventInfo.hierarchicalRelations];
212      directorRelations.pop();
213      const apiCompletePath: string = [...directorRelations, this.getEventName(eventInfo.apiName)].join('/');
214      let eventMethodData: EventMethodData = {
215        onEvents: [],
216        offEvents: [],
217        emitEvents: [],
218        onceEvents: []
219      };
220      if (eventMethodDataMap.get(apiCompletePath)) {
221        eventMethodData = eventMethodDataMap.get(apiCompletePath) as EventMethodData;
222      }
223      eventMethodDataMap.set(apiCompletePath, this.collectEventMethod(eventMethodData, eventInfo));
224    });
225    return eventMethodDataMap;
226  }
227
228  private collectEventMethod(eventMethodData: EventMethodData, eventInfo: MethodInfo): EventMethodData {
229    const eventType: string = this.getEventType(eventInfo.apiName);
230    switch (eventType) {
231      case 'on':
232        eventMethodData.onEvents.push(eventInfo);
233        break;
234      case 'off':
235        eventMethodData.offEvents.push(eventInfo);
236        break;
237      case 'emit':
238        eventMethodData.emitEvents.push(eventInfo);
239        break;
240      case 'once':
241        eventMethodData.onceEvents.push(eventInfo);
242        break;
243    }
244    return eventMethodData;
245  }
246
247  private getEventName(apiName: string): string {
248    return apiName.split(/\_/)[1];
249  }
250
251  private getEventType(apiName: string): string {
252    return apiName.split(/\_/)[0];
253  }
254}
255