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 { Code } from '../utils/constant'; 18import { FileUtils } from '../utils/fileUtils'; 19import { LogUtil } from '../utils/logUtil'; 20import { StringUtils } from '../utils/stringUtils'; 21import { CommentHelper } from './coreImpls'; 22import type { Context, ISourceCodeProcessor, ProcessResult, sourceParser } from './typedef'; 23import type { comment} from './typedef'; 24 25export class OutputProcessor implements ISourceCodeProcessor { 26 async process(context: Context, content: string): Promise<ProcessResult> { 27 try { 28 let outputContent = content; 29 const formater = new Formatter(content); 30 outputContent = formater.format(context); 31 32 const prettier = require('prettier'); 33 outputContent = prettier.format(outputContent, context.getOptions().formaterOption); 34 35 const numberLiteralCaseRule = new NumberLiteralCaseRule(true, context.getSourceParser(outputContent)); 36 outputContent = numberLiteralCaseRule.apply(outputContent); 37 38 FileUtils.writeStringToFile(outputContent, context.getOutputFile()); 39 return { code: Code.OK, content: outputContent }; 40 } catch (error) { 41 LogUtil.e('OutputProcessor', `error: ${context.getInputFile()}, ${error}`); 42 return { code: Code.OK, content: content }; 43 } 44 } 45} 46 47class Formatter implements sourceParser.INodeVisitorCallback { 48 source: string; 49 50 constructor(source: string) { 51 this.source = source; 52 } 53 54 onVisitNode(node: comment.CommentNode): void { 55 if (node.astNode && node.commentInfos && node.commentInfos.length > 0) { 56 CommentHelper.addComment(node.astNode, node.commentInfos); 57 } 58 } 59 60 format(context: Context): string { 61 const sourceParser = context.getSourceParser(this.source); 62 const sourceFile = sourceParser.visitEachNodeComment(this); 63 return sourceFile ? sourceParser.printSourceFile(sourceFile) : this.source; 64 } 65} 66 67/** 68 * 转换16进制字符大小写 69 */ 70class NumberLiteralCaseRule { 71 upperCase: boolean; 72 sourceParser: sourceParser.SourceCodeParser; 73 content: string | undefined; 74 75 constructor(upperCase: boolean, sourceParser: sourceParser.SourceCodeParser) { 76 this.upperCase = upperCase; 77 this.sourceParser = sourceParser; 78 } 79 80 apply(content: string): string { 81 const sourceFile = this.sourceParser.createSourceFile(content, 'numberLiteral'); 82 if (!sourceFile) { 83 return content; 84 } 85 this.content = content; 86 sourceFile.forEachChild((child) => { 87 this.nodeVisitor(child); 88 }); 89 return this.content; 90 } 91 92 private nodeVisitor(node: ts.Node): void { 93 if (!ts.isNumericLiteral(node)) { 94 node.forEachChild((child) => { 95 this.nodeVisitor(child); 96 }); 97 } 98 const value = node.getText().trim(); 99 if (!this.isHexString(value)) { 100 return; 101 } 102 const START_POSITION_INDEX = 2; 103 let literalStr = value.substring(START_POSITION_INDEX, value.length); 104 let replacement = undefined; 105 if (this.upperCase && !this.isHexUpperCase(literalStr)) { 106 replacement = `0x${literalStr.toUpperCase()}`; 107 } 108 if (!this.upperCase && !this.isHexLowerCase(literalStr)) { 109 replacement = `0x${literalStr.toLowerCase()}`; 110 } 111 if (!replacement) { 112 return; 113 } 114 this.content = StringUtils.replaceAt(this.content!, node.getStart(), replacement); 115 } 116 117 private isHexString(str: string): boolean { 118 return /^0x([abcdefABCDEF\d]*)$/g.test(str); 119 } 120 121 private isHexUpperCase(str: string): boolean { 122 for (let c of str) { 123 if (c >= '0' && c <= '9') { 124 continue; 125 } 126 if (c >= 'a' && c <= 'f') { 127 return false; 128 } 129 } 130 return true; 131 } 132 133 private isHexLowerCase(str: string): boolean { 134 for (let c of str) { 135 if (c >= '0' && c <= '9') { 136 continue; 137 } 138 if (c >= 'A' && c <= 'F') { 139 return false; 140 } 141 } 142 return true; 143 } 144}