UNPKG

@specs-feup/kadabra

Version:

A Java source-to-source compiler written in Typescript

171 lines (155 loc) 5.36 kB
import Query from "@specs-feup/lara/api/weaver/Query.js"; import { Class, FileJp, Method, InterfaceType, App } from "../Joinpoints.js"; /** * Retrieves an existing class or creates a new one. * * @param qualifiedName - The qualified name of the class. * @param extend - The class to extend (optional). * @param implement - The interfaces to implement (optional). * @param target - The target join point (optional). * @returns The class join point. */ export function getOrNewClass( qualifiedName: string, extend: string = "", implement: string[] = [], target?: App | FileJp ): Class { const existingClass = Query.search( Class, (cls) => RegExp(qualifiedName).exec(cls.qualifiedName) !== null ).getFirst(); return existingClass ?? newClass(qualifiedName, extend, implement, target); } /** * Creates a new class. * * @param qualifiedName - The qualified name of the class. * @param extend - The class to extend (optional). * @param implement - The interfaces to implement (optional). * @param target - The target join point (optional). * @returns The newly created class join point. */ export function newClass( qualifiedName: string, extend: string = "", implement: string[] = [], target: App | FileJp = Query.root() as App ): Class { if (target === undefined) { throw new Error( "The target join point for a new class must be of type App or FileJp." ); } return target.newClass(qualifiedName, extend, implement); } /** * Generates a provider function. * * @param code - The code to be executed by the provider. * @param args - The arguments for the provider (optional). * @returns The provider function as a string. */ export function providerOf(code: string, args?: string[]): string { let providerCode = "("; if (args !== undefined && args.length > 0) { providerCode += args[0]; for (let i = 1; i < args.length; i++) { providerCode += "," + args[i]; } } return providerCode + ") -> " + code; } /** * Generates a functional interface based on a method of a class. * * @param targetMethod - The name of the target method. * @param targetClass - The name of the target class (optional). * @param targetFile - The name of the target file (optional). * @param associate - Whether to associate the interface with the class (optional). * @param newFile - Whether to create the interface in a new file (optional). * @returns The generated functional interface and related information. */ export function generateFunctionalInterface( targetMethod: string, targetClass: string = ".*", targetFile: string = ".*", associate: boolean = false, newFile: boolean = true ): { $interface: InterfaceType; $defaultMethod: Method; $function: Method; targetMethodName: string; } { let $interface: InterfaceType | undefined = undefined; let tempClass: Class | undefined = undefined; const search = Query.search( FileJp, (file) => RegExp(targetFile).exec(file.name) !== null ) .search( Class, (cls) => RegExp(targetClass).exec(cls.qualifiedName) !== null ) .search(Method, targetMethod) .chain(); let method: Method | undefined = undefined; for (const result of search) { const cls = result.class as Class; method = result.method as Method; if (tempClass !== undefined) { throw new Error( `More than one method to be extracted, please redefine this aspect call. Target method: ${targetMethod}. ` + `1st Location${tempClass.qualifiedName}. 2nd Location ${cls.qualifiedName}` ); } console.log( `[LOG] Extracting functional interface from ${cls.name}#${method.name}` ); const interfacePackage = cls.packageName; const interfaceName = "I" + method.name.charAt(0).toUpperCase() + method.name.slice(1); const newInterface = cls.extractInterface( interfaceName, interfacePackage, method, associate, newFile ); if (!newFile) { newInterface.modifiers = ["static"]; cls.addInterface(newInterface); } $interface = newInterface; tempClass = cls; } if (method === undefined || $interface === undefined) { throw new Error( "Could not find the method to extract a functional interface, specified by the conditions: " + `file{"${targetFile}"}.class{"${targetClass}"}.method{"${targetMethod}"}` ); } const $function = Query.searchFrom( $interface, Method, method.name ).getFirst()!; return { $interface: $interface, $defaultMethod: method, $function: $function, targetMethodName: targetMethod, }; } /** * Utility class for defining common modifiers. */ export class Mod { static readonly PRIVATE = "private"; static readonly PUBLIC = "public"; static readonly PROTECTED = "protected"; static readonly STATIC = "static"; static readonly PUBLIC_STATIC = ["public", "static"]; static readonly PRIVATE_STATIC = ["private", "static"]; }