• 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 { ModuleScene, Scene } from '../../Scene';
17import { ArkExport, ExportInfo } from './ArkExport';
18import { ImportInfo } from './ArkImport';
19import { ArkClass } from './ArkClass';
20import { ArkNamespace } from './ArkNamespace';
21import { AliasClassSignature, ClassSignature, FileSignature, NamespaceSignature } from './ArkSignature';
22import { ALL } from '../common/TSConst';
23import { NAME_DELIMITER } from '../common/Const';
24
25export const notStmtOrExprKind = [
26    'ModuleDeclaration',
27    'ClassDeclaration',
28    'InterfaceDeclaration',
29    'EnumDeclaration',
30    'ExportDeclaration',
31    'ExportAssignment',
32    'MethodDeclaration',
33    'Constructor',
34    'FunctionDeclaration',
35    'GetAccessor',
36    'SetAccessor',
37    'ArrowFunction',
38    'FunctionExpression',
39    'MethodSignature',
40    'ConstructSignature',
41    'CallSignature',
42];
43
44export enum Language {
45    TYPESCRIPT = 0,
46    ARKTS1_1 = 1,
47    ARKTS1_2 = 2,
48    JAVASCRIPT = 3,
49    UNKNOWN = -1,
50}
51
52/**
53 * @category core/model
54 */
55export class ArkFile {
56    private language: Language;
57    private absoluteFilePath: string = '';
58    private projectDir: string = '';
59    private code: string = '';
60
61    private defaultClass!: ArkClass;
62
63    // name to model
64    private namespaces: Map<string, ArkNamespace> = new Map<string, ArkNamespace>(); // don't contain nested namespaces
65    private classes: Map<string, ArkClass> = new Map<string, ArkClass>(); // don't contain class in namespace
66
67    private importInfoMap: Map<string, ImportInfo> = new Map<string, ImportInfo>();
68    private exportInfoMap: Map<string, ExportInfo> = new Map<string, ExportInfo>();
69
70    private scene!: Scene;
71    private moduleScene?: ModuleScene;
72
73    private fileSignature: FileSignature = FileSignature.DEFAULT;
74
75    private ohPackageJson5Path: string[] = [];
76
77    private anonymousClassNumber: number = 0;
78
79    constructor(language: Language) {
80        this.language = language;
81    }
82
83    /**
84     * Returns the program language of the file.
85     */
86    public getLanguage(): Language {
87        return this.language;
88    }
89
90    public setLanguage(language: Language): void {
91        this.language = language;
92    }
93
94    /**
95     * Returns the **string** name of the file, which also acts as the file's relative path.
96     * @returns The file's name (also means its relative path).
97     */
98    public getName(): string {
99        return this.fileSignature.getFileName();
100    }
101
102    public setScene(scene: Scene): void {
103        this.scene = scene;
104    }
105
106    /**
107     * Returns the scene (i.e., {@link Scene}) built for the project. The {@link Scene} is the core class of ArkAnalyzer,
108     * through which users can access all the information of the analyzed code (project),
109     * including file list, class list, method list, property list, etc.
110     * @returns The scene of the file.
111     */
112    public getScene(): Scene {
113        return this.scene;
114    }
115
116    public getModuleScene(): ModuleScene | undefined {
117        return this.moduleScene;
118    }
119
120    public setModuleScene(moduleScene: ModuleScene): void {
121        this.moduleScene = moduleScene;
122    }
123
124    public setProjectDir(projectDir: string): void {
125        this.projectDir = projectDir;
126    }
127
128    public getProjectDir(): string {
129        return this.projectDir;
130    }
131
132    /**
133     * Get a file path.
134     * @returns The absolute file path.
135     * @example
136     * 1. Read source code based on file path.
137
138    ```typescript
139    let str = fs.readFileSync(arkFile.getFilePath(), 'utf8');
140    ```
141     */
142    public getFilePath(): string {
143        return this.absoluteFilePath;
144    }
145
146    public setFilePath(absoluteFilePath: string): void {
147        this.absoluteFilePath = absoluteFilePath;
148    }
149
150    public setCode(code: string): void {
151        this.code = code;
152    }
153
154    /**
155     * Returns the codes of file as a **string.**
156     * @returns the codes of file.
157     */
158    public getCode(): string {
159        return this.code;
160    }
161
162    public addArkClass(arkClass: ArkClass, originName?: string): void {
163        const name = originName ?? arkClass.getName();
164        this.classes.set(name, arkClass);
165        if (!originName && !arkClass.isAnonymousClass()) {
166            const index = name.indexOf(NAME_DELIMITER);
167            if (index > 0) {
168                const originName = name.substring(0, index);
169                this.addArkClass(arkClass, originName);
170            }
171        }
172    }
173
174    public getDefaultClass(): ArkClass {
175        return this.defaultClass;
176    }
177
178    public setDefaultClass(defaultClass: ArkClass): void {
179        this.defaultClass = defaultClass;
180    }
181
182    public getNamespace(namespaceSignature: NamespaceSignature): ArkNamespace | null {
183        const namespaceName = namespaceSignature.getNamespaceName();
184        return this.getNamespaceWithName(namespaceName);
185    }
186
187    public getNamespaceWithName(namespaceName: string): ArkNamespace | null {
188        return this.namespaces.get(namespaceName) || null;
189    }
190
191    public getNamespaces(): ArkNamespace[] {
192        return Array.from(this.namespaces.values());
193    }
194
195    /**
196     * Returns the class based on its class signature. If the class could not be found, **null** will be returned.
197     * @param classSignature - the class signature.
198     * @returns A class. If there is no class, the return will be a **null**.
199     */
200    public getClass(classSignature: ClassSignature): ArkClass | null {
201        const className = classSignature instanceof AliasClassSignature ? classSignature.getOriginName() : classSignature.getClassName();
202        return this.getClassWithName(className);
203    }
204
205    public getClassWithName(Class: string): ArkClass | null {
206        return this.classes.get(Class) || null;
207    }
208
209    public getClasses(): ArkClass[] {
210        return Array.from(new Set(this.classes.values()));
211    }
212
213    public addNamespace(namespace: ArkNamespace): void {
214        this.namespaces.set(namespace.getName(), namespace);
215    }
216
217    /**
218     * Returns an **array** of import information.
219     * The import information includes: clause's name, type, modifiers, location where it is imported from, etc.
220     * @returns An **array** of import information.
221     */
222    public getImportInfos(): ImportInfo[] {
223        return Array.from(this.importInfoMap.values());
224    }
225
226    public getImportInfoBy(name: string): ImportInfo | undefined {
227        return this.importInfoMap.get(name);
228    }
229
230    public addImportInfo(importInfo: ImportInfo): void {
231        this.importInfoMap.set(importInfo.getImportClauseName(), importInfo);
232    }
233
234    public removeImportInfo(importInfo: ImportInfo): boolean {
235        return this.importInfoMap.delete(importInfo.getImportClauseName());
236    }
237
238    public removeNamespace(namespace: ArkNamespace): boolean {
239        let rtn = this.namespaces.delete(namespace.getName());
240        rtn &&= this.getScene().removeNamespace(namespace);
241        return rtn;
242    }
243
244    public removeArkClass(arkClass: ArkClass): boolean {
245        let rtn = this.classes.delete(arkClass.getName());
246        rtn &&= this.getScene().removeClass(arkClass);
247        return rtn;
248    }
249
250    public getExportInfos(): ExportInfo[] {
251        const exportInfos: ExportInfo[] = [];
252        this.exportInfoMap.forEach((value, key) => {
253            if (key !== ALL || value.getFrom()) {
254                exportInfos.push(value);
255            }
256        });
257        return exportInfos;
258    }
259
260    /**
261     * Find out the {@link ExportInfo} of this {@link ArkFile} by the given export name.
262     * It returns an {@link ExportInfo} or 'undefined' if it failed to find.
263     * @param name
264     * @returns
265     * @example
266     ```typescript
267     // abc.ts ArkFile
268     export class A {
269     ...
270     }
271
272     export namespace B {
273     export namespace C {
274     export class D {}
275     }
276     }
277
278     // xyz.ts call getExportInfoBy
279     let arkFile = scene.getFile(fileSignature);
280
281     // a is the export class A defined in abc.ts
282     let a = arkFile.getExportInfoBy('A');
283
284     // b is the export class D within namespace C defined in abc.ts
285     let b = arkFile.getExportInfoBy('B.C.D');
286     ```
287     */
288    public getExportInfoBy(name: string): ExportInfo | undefined {
289        const separator = '.';
290        const names = name.split(separator);
291        if (names.length === 1) {
292            return this.exportInfoMap.get(names[0]);
293        }
294
295        let index = 0;
296        let currExportInfo = this.exportInfoMap.get(names[index]);
297        if (currExportInfo === undefined) {
298            return undefined;
299        }
300
301        for (let i = 1; i < names.length; i++) {
302            const arkExport: ArkExport | null | undefined = currExportInfo.getArkExport();
303            if (arkExport && arkExport instanceof ArkNamespace) {
304                currExportInfo = arkExport.getExportInfoBy(names[i]);
305                if (currExportInfo === undefined) {
306                    return undefined;
307                }
308            }
309        }
310        return currExportInfo;
311    }
312
313    public addExportInfo(exportInfo: ExportInfo, key?: string): void {
314        this.exportInfoMap.set(key ?? exportInfo.getExportClauseName(), exportInfo);
315    }
316
317    public removeExportInfo(exportInfo: ExportInfo, key?: string): void {
318        if (key) {
319            this.exportInfoMap.delete(key);
320            return;
321        }
322        this.exportInfoMap.delete(exportInfo.getExportClauseName());
323    }
324
325    public getProjectName(): string {
326        return this.fileSignature.getProjectName();
327    }
328
329    public getModuleName(): string | undefined {
330        return this.moduleScene?.getModuleName();
331    }
332
333    public setOhPackageJson5Path(ohPackageJson5Path: string[]): void {
334        this.ohPackageJson5Path = ohPackageJson5Path;
335    }
336
337    public getOhPackageJson5Path(): string[] {
338        return this.ohPackageJson5Path;
339    }
340
341    /**
342     * Returns the file signature of this file. A file signature consists of project's name and file's name.
343     * @returns The file signature of this file.
344     */
345    public getFileSignature(): FileSignature {
346        return this.fileSignature;
347    }
348
349    public setFileSignature(fileSignature: FileSignature): void {
350        this.fileSignature = fileSignature;
351    }
352
353    public getAllNamespacesUnderThisFile(): ArkNamespace[] {
354        let namespaces: ArkNamespace[] = [];
355        namespaces.push(...this.namespaces.values());
356        this.namespaces.forEach(ns => {
357            namespaces.push(...ns.getAllNamespacesUnderThisNamespace());
358        });
359        return namespaces;
360    }
361
362    public getAnonymousClassNumber(): number {
363        return this.anonymousClassNumber++;
364    }
365}
366