• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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