c6a35807创建于 2025年5月26日历史提交
/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import * as path from 'path';
import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { FileReports } from '../../model/Defects';
import { FileUtils, WriteFileMode } from './FileUtils';

const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'GeneratingJsonFile');
const severitys: string[] = ['OFF', 'WARN', 'ERROR', 'SUGGESTION'];
const FILE_NAMING_RULE = '@hw-stylistic/file-naming-convention';

export class GeneratingJsonFile {
    public static generatingJsonFile(filePath: string, fileReports: FileReports[]): void {
        const fileDefectInfos = this.format(fileReports);
        let results: Map<string, FileDefectInfo> = new Map();
        for (let fileDefectInfo of fileDefectInfos) {
            this.addResult(fileDefectInfo, results);
        }
        const jsonString = this.format2(results);
        try {
            FileUtils.writeToFile(filePath, jsonString, WriteFileMode.OVERWRITE);
        } catch (error) {
            logger.error(`write file ${filePath} failed, error: ${error}`);
        }
    }

    /**
     * 过滤掉部分不需要的defect属性
     *
     * @param fileReports 原始文件缺陷信息数组
     * @returns 过滤后的文件缺陷信息数组
     */
    private static format(fileReports: FileReports[]): FileDefectInfo[] {
        const fileDefectInfos: FileDefectInfo[] = [];
        for (const fileReport of fileReports) {
            const fileDefectInfo: FileDefectInfo = {
                filePath: fileReport.filePath,
                defects: []
            };
            for (const defect of fileReport.defects) {
                const defectInfo: DefectInfo = {
                    reportLine: defect.reportLine,
                    reportColumn: defect.reportColumn,
                    ruleId: defect.ruleId,
                    severity: severitys[defect.severity],
                    mergeKey: defect.mergeKey,
                    description: defect.description,
                    ruleDocPath: defect.ruleDocPath
                };
                fileDefectInfo.defects.push(defectInfo);
            }
            fileDefectInfos.push(fileDefectInfo);
        }
        return fileDefectInfos;
    }

    private static addResult(defect: FileDefectInfo, results: Map<string, FileDefectInfo>): void {
        const normalizedPath = path.normalize(defect.filePath).toLocaleLowerCase();
        if (!results.has(normalizedPath)) {
            results.set(normalizedPath, defect);
        } else {
            results.get(normalizedPath)?.defects.push(...defect.defects);
        }
        const defectInfo = results.get(normalizedPath);
        defectInfo?.defects.sort((defectA, defectB) => {
            if (defectA.ruleId === FILE_NAMING_RULE) {
                return -1;
            }
            if (defectB.ruleId === FILE_NAMING_RULE) {
                return 1;
            }
            if (defectA.reportLine === defectB.reportLine) {
                if (defectA.reportColumn === defectB.reportColumn) {
                    return defectA.mergeKey.localeCompare(defectB.mergeKey);
                }
                return defectA.reportColumn - defectB.reportColumn;
            }
            return defectA.reportLine - defectB.reportLine;
        });
    }

    private static format2(results: Map<string, FileDefectInfo>): string {
        const jsonResults: JsonResult[] = [];
        for (let result of results) {
            const oneResult: JsonResult = {
                filePath: '',
                messages: []
            };
            oneResult.filePath = result[1].filePath;
            let defects = result[1].defects;
            for (let defect of defects) {
                const oneDefect: SimpleDefect = {
                    line: 0,
                    column: 0,
                    severity: '',
                    message: '',
                    rule: '',
                };
                oneDefect.line = defect.reportLine;
                oneDefect.column = defect.reportColumn;
                oneDefect.severity = defect.severity;
                oneDefect.message = defect.description;
                oneDefect.rule = defect.ruleId;
                oneResult.messages.push(oneDefect);
            }
            jsonResults.push(oneResult);
        }
        return JSON.stringify(jsonResults, null, 2);
    }
}

interface JsonResult {
    filePath: string;
    messages: SimpleDefect[];
}

interface SimpleDefect {
    line: number;
    column: number;
    severity: string;
    message: string;
    rule: string | null;
}

interface FileDefectInfo {
    filePath: string;
    defects: DefectInfo[];
}

interface DefectInfo {
    severity: string;
    description: string;
    mergeKey: string;
    reportLine: number;
    reportColumn: number;
    ruleId: string | null;
    ruleDocPath: string | null;
}