1/* 2 * Copyright (c) 2024-2025 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 { ArkClass, ClassCategory } from '../../core/model/ArkClass'; 17import { SourceBase } from './SourceBase'; 18import { SourceBody } from './SourceBody'; 19import { SourceField } from './SourceField'; 20import { SourceMethod } from './SourceMethod'; 21import { SourceTransformer } from './SourceTransformer'; 22import { PrinterUtils } from '../base/PrinterUtils'; 23import { INSTANCE_INIT_METHOD_NAME, STATIC_INIT_METHOD_NAME } from '../../core/common/Const'; 24import { ArkNamespace } from '../../core/model/ArkNamespace'; 25import { FieldCategory } from '../../core/model/ArkField'; 26import { ArkMetadataKind, CommentsMetadata } from '../../core/model/ArkMetadata'; 27import { Dump } from '../base/BasePrinter'; 28 29/** 30 * @category save 31 */ 32export class SourceClass extends SourceBase { 33 protected cls: ArkClass; 34 private transformer: SourceTransformer; 35 36 public constructor(cls: ArkClass, indent: string = '') { 37 super(cls.getDeclaringArkFile(), indent); 38 this.cls = cls; 39 this.transformer = new SourceTransformer(this); 40 } 41 42 public getDeclaringArkNamespace(): ArkNamespace | undefined { 43 return this.cls.getDeclaringArkNamespace(); 44 } 45 46 public getLine(): number { 47 return this.cls.getLine(); 48 } 49 50 public dump(): string { 51 this.printer.clear(); 52 53 if (this.cls.getCategory() === ClassCategory.OBJECT) { 54 return this.dumpObject(); 55 } 56 57 if (this.cls.getCategory() === ClassCategory.TYPE_LITERAL) { 58 return this.dumpTypeLiteral(); 59 } 60 61 const commentsMetadata = this.cls.getMetadata(ArkMetadataKind.LEADING_COMMENTS); 62 if (commentsMetadata instanceof CommentsMetadata) { 63 const comments = commentsMetadata.getComments(); 64 comments.forEach(comment => { 65 this.printer.writeIndent().writeLine(comment.content); 66 }); 67 } 68 69 this.printDecorator(this.cls.getDecorators()); 70 // print export class name<> + extends c0 implements x1, x2 { 71 this.printer 72 .writeIndent() 73 .writeSpace(this.modifiersToString(this.cls.getModifiers())) 74 .write(`${this.classOriginTypeToString(this.cls.getCategory())} `); 75 76 if (!PrinterUtils.isAnonymousClass(this.cls.getName())) { 77 this.printer.write(this.cls.getName()); 78 } 79 const genericsTypes = this.cls.getGenericsTypes(); 80 if (genericsTypes) { 81 this.printer.write(`<${this.transformer.typeArrayToString(genericsTypes)}>`); 82 } 83 if (this.cls.getSuperClassName() && !this.cls.hasComponentDecorator()) { 84 this.printer.write(` extends ${this.cls.getSuperClassName()}`); 85 } 86 if (this.cls.getImplementedInterfaceNames().length > 0) { 87 this.printer.write(` implements ${this.cls.getImplementedInterfaceNames().join(', ')}`); 88 } 89 90 this.printer.writeLine(' {'); 91 this.printer.incIndent(); 92 let items: Dump[] = []; 93 94 items.push(...this.printFields()); 95 items.push(...this.printMethods()); 96 97 items.sort((a, b) => a.getLine() - b.getLine()); 98 items.forEach((v): void => { 99 this.printer.write(v.dump()); 100 }); 101 102 this.printer.decIndent(); 103 this.printer.writeIndent().write('}'); 104 if (!PrinterUtils.isAnonymousClass(this.cls.getName())) { 105 this.printer.writeLine(''); 106 } 107 return this.printer.toString(); 108 } 109 110 private dumpObject(): string { 111 this.printer.write('{'); 112 113 this.cls.getFields().forEach((field, index, array) => { 114 let name = PrinterUtils.escape(field.getName()); 115 if (PrinterUtils.isIdentifierText(field.getName())) { 116 this.printer.write(name); 117 } else { 118 this.printer.write(`'${name}'`); 119 } 120 121 let instanceInitializer = this.parseFieldInitMethod(INSTANCE_INIT_METHOD_NAME); 122 if (instanceInitializer.has(field.getName())) { 123 this.printer.write(`: ${instanceInitializer.get(field.getName())}`); 124 } 125 126 if (index !== array.length - 1) { 127 this.printer.write(`, `); 128 } 129 }); 130 this.printer.write('}'); 131 return this.printer.toString(); 132 } 133 134 private dumpTypeLiteral(): string { 135 this.printer.write('{'); 136 137 this.cls.getFields().forEach((field, index, array) => { 138 let name = PrinterUtils.escape(field.getName()); 139 if (PrinterUtils.isIdentifierText(field.getName())) { 140 this.printer.write(`${name}: ${this.transformer.typeToString(field.getType())}`); 141 } else { 142 this.printer.write(`'${name}': ${this.transformer.typeToString(field.getType())}`); 143 } 144 145 if (index !== array.length - 1) { 146 this.printer.write(`, `); 147 } 148 }); 149 this.printer.write('}'); 150 return this.printer.toString(); 151 } 152 153 protected printMethods(): Dump[] { 154 let items: Dump[] = []; 155 for (let method of this.cls.getMethods()) { 156 if (method.isGenerated() || (PrinterUtils.isConstructorMethod(method.getName()) && this.cls.hasViewTree())) { 157 continue; 158 } 159 160 if (method.isDefaultArkMethod()) { 161 items.push(...new SourceMethod(method, this.printer.getIndent()).dumpDefaultMethod()); 162 } else if (!PrinterUtils.isAnonymousMethod(method.getName())) { 163 items.push(new SourceMethod(method, this.printer.getIndent())); 164 } 165 } 166 return items; 167 } 168 169 private printFields(): Dump[] { 170 let instanceInitializer = this.parseFieldInitMethod(INSTANCE_INIT_METHOD_NAME); 171 let staticInitializer = this.parseFieldInitMethod(STATIC_INIT_METHOD_NAME); 172 let items: Dump[] = []; 173 for (let field of this.cls.getFields()) { 174 if (field.getCategory() === FieldCategory.GET_ACCESSOR) { 175 continue; 176 } 177 if (field.isStatic()) { 178 items.push(new SourceField(field, this.printer.getIndent(), staticInitializer)); 179 } else { 180 items.push(new SourceField(field, this.printer.getIndent(), instanceInitializer)); 181 } 182 } 183 return items; 184 } 185 186 private parseFieldInitMethod(name: string): Map<string, string> { 187 let method = this.cls.getMethodWithName(name); 188 if (!method || method?.getBody() === undefined) { 189 return new Map<string, string>(); 190 } 191 192 let srcBody = new SourceBody(this.printer.getIndent(), method, false); 193 srcBody.dump(); 194 return srcBody.getTempCodeMap(); 195 } 196} 197 198export class SourceDefaultClass extends SourceClass { 199 public constructor(cls: ArkClass, indent: string = '') { 200 super(cls, indent); 201 } 202 203 public getLine(): number { 204 return this.cls.getLine(); 205 } 206 207 public dump(): string { 208 this.printMethods(); 209 return this.printer.toString(); 210 } 211} 212