diff --git a/Lara-JS/src-api/LaraJoinPoint.ts b/Lara-JS/src-api/LaraJoinPoint.ts index 9542ef7bc..580f95d70 100644 --- a/Lara-JS/src-api/LaraJoinPoint.ts +++ b/Lara-JS/src-api/LaraJoinPoint.ts @@ -165,13 +165,16 @@ export function wrapJoinPoint(obj: any): any { ); } - const jpType: string = obj.getJoinPointType(); + // Get join point class from name of the Java class, since getJoinPointType() might + // not always correspond to the actual join point class (e.g., anyweaver) + const jpClass: string = obj.get_class(); + for (const mapper of JoinpointMappers) { - if (mapper[jpType]) { - return new mapper[jpType](obj); + if (mapper[jpClass]) { + return new mapper[jpClass](obj); } } - throw new Error("No mapper found for join point type: " + jpType); + throw new Error("No mapper found for join point type: " + jpClass); } export function unwrapJoinPoint(obj: any): any { diff --git a/Lara-JS/src-api/weaver/JoinPoints.ts b/Lara-JS/src-api/weaver/JoinPoints.ts index 82889d72a..29ee5da65 100644 --- a/Lara-JS/src-api/weaver/JoinPoints.ts +++ b/Lara-JS/src-api/weaver/JoinPoints.ts @@ -1,5 +1,6 @@ import { LaraJoinPoint, wrapJoinPoint } from "../LaraJoinPoint.js"; import Weaver from "./Weaver.js"; +import JpPredicate from "./predicate/JpPredicate.js"; /** * Object which provides low-level join point-related methods. @@ -70,9 +71,9 @@ export default class JoinPoints { * * @returns the nodes inside the scope of the given node. */ - static scope( + static scope( $jp: LaraJoinPoint, - jpType?: T + jpType?: JpPredicate ): LaraJoinPoint[] { return JoinPoints._getNodes(jpType, JoinPoints._all_scope_nodes($jp)); } @@ -81,9 +82,9 @@ export default class JoinPoints { * * @returns the children of the given node, according to the AST */ - static children( + static children( $jp: LaraJoinPoint, - jpType?: T + jpType?: JpPredicate ): LaraJoinPoint[] { return JoinPoints._getNodes(jpType, JoinPoints._all_children($jp)); } @@ -92,9 +93,9 @@ export default class JoinPoints { * * @returns the descendants of the given node, according to the AST, preorder traversal */ - static descendants( + static descendants( $jp: LaraJoinPoint, - jpType?: T + jpType?: JpPredicate ): LaraJoinPoint[] { return JoinPoints._getNodes(jpType, JoinPoints._all_descendants($jp)); } @@ -103,9 +104,9 @@ export default class JoinPoints { * * @returns the descendants of the given node, according to the AST, postorder traversal */ - static descendantsPostorder( + static descendantsPostorder( $jp: LaraJoinPoint, - jpType?: T + jpType?: JpPredicate ): LaraJoinPoint[] { return JoinPoints._getNodes( jpType, @@ -117,8 +118,8 @@ export default class JoinPoints { * * @returns the nodes related with the given node, according to the search function */ - private static _getNodes( - jpType?: T, + private static _getNodes( + jpType?: JpPredicate, $allJps: LaraJoinPoint[] = [] ): LaraJoinPoint[] { // TODO: This function can be optimized by using streaming @@ -126,7 +127,7 @@ export default class JoinPoints { return $allJps; } - return $allJps.filter((jp) => jp instanceof jpType); + return $allJps.filter((jp) => jpType.isInstance(jp)); } /** diff --git a/Lara-JS/src-api/weaver/Selector.ts b/Lara-JS/src-api/weaver/Selector.ts index 6a27c0700..956807d35 100644 --- a/Lara-JS/src-api/weaver/Selector.ts +++ b/Lara-JS/src-api/weaver/Selector.ts @@ -9,6 +9,9 @@ import { type JpFilterRules } from "../lara/util/JpFilter.js"; import JoinPoints from "./JoinPoints.js"; import TraversalType from "./TraversalType.js"; import Weaver from "./Weaver.js"; +import JpPredicate from "./predicate/JpPredicate.js"; +import StringPredicate from "./predicate/StringPredicate.js"; +import TypePredicate from "./predicate/TypePredicate.js"; /** * @internal Lara Common Language dirty hack. IMPROPER USAGE WILL BREAK THE WHOLE WEAVER! @@ -275,7 +278,7 @@ export default class Selector< * @returns The results of the search. */ search( - name?: string, + type?: string, filter?: Filter_StringVariant, traversal?: TraversalType ): Selector; @@ -285,33 +288,31 @@ export default class Selector< traversal: TraversalType = TraversalType.PREORDER ): Selector { let jpFilter: JpFilterFunction; + let jpPredicate : JpPredicate; if (typeof type === "string") { jpFilter = Selector.convertStringFilterToWrapperFilter( type, filter as Filter_StringVariant ); - const jpType = Weaver.findJoinpointType(type); - if (!jpType) { - throw new Error(`Join point type '${type}' not found.`); - } - return this.search(jpType, jpFilter, traversal); + jpPredicate = new StringPredicate(type); } else { jpFilter = Selector.parseWrapperFilter( type, (filter as Filter_WrapperVariant) ?? (() => true) ); + jpPredicate = new TypePredicate(type); } let fn; switch (traversal) { case TraversalType.PREORDER: - fn = function ($jp: LaraJoinPoint, name?: T) { + fn = function ($jp: LaraJoinPoint, name?: JpPredicate) { return selectorJoinPointsClass.descendants($jp, name); }; break; case TraversalType.POSTORDER: - fn = function ($jp: LaraJoinPoint, name?: T) { + fn = function ($jp: LaraJoinPoint, name?: JpPredicate) { return selectorJoinPointsClass.descendantsPostorder($jp, name); }; break; @@ -322,7 +323,7 @@ export default class Selector< ); } - return this.searchPrivate(type, fn, jpFilter); + return this.searchPrivate(jpPredicate, fn, jpFilter); } /** @@ -353,28 +354,26 @@ export default class Selector< filter: Filter_WrapperVariant | Filter_StringVariant ): Selector { let jpFilter: JpFilterFunction; + let jpPredicate : JpPredicate; if (typeof type === "string") { jpFilter = Selector.convertStringFilterToWrapperFilter( type, filter as Filter_StringVariant ); - const jpType = Weaver.findJoinpointType(type); - if (!jpType) { - throw new Error(`Join point type '${type}' not found.`); - } - return this.children(jpType, jpFilter); + jpPredicate = new StringPredicate(type); } else { jpFilter = Selector.parseWrapperFilter( type, (filter as Filter_WrapperVariant) ?? (() => true) ); + jpPredicate = new TypePredicate(type); } return this.searchPrivate( - type, - function ($jp: LaraJoinPoint, name?: T) { - return selectorJoinPointsClass.children($jp, name); + jpPredicate, + function ($jp: LaraJoinPoint, type?: JpPredicate) { + return selectorJoinPointsClass.children($jp, type); }, jpFilter ); @@ -408,27 +407,24 @@ export default class Selector< filter: Filter_WrapperVariant | Filter_StringVariant ): Selector { let jpFilter: JpFilterFunction; - + let jpPredicate: JpPredicate; if (typeof type === "string") { jpFilter = Selector.convertStringFilterToWrapperFilter( type, filter as Filter_StringVariant ); - const jpType = Weaver.findJoinpointType(type); - if (!jpType) { - throw new Error(`Join point type '${type}' not found.`); - } - return this.scope(jpType, jpFilter); + jpPredicate = new StringPredicate(type); } else { jpFilter = Selector.parseWrapperFilter( type, (filter as Filter_WrapperVariant) ?? (() => true) ); + jpPredicate = new TypePredicate(type); } return this.searchPrivate( - type, - function ($jp: LaraJoinPoint, name?: T) { + jpPredicate, + function ($jp: LaraJoinPoint, name?: JpPredicate) { return selectorJoinPointsClass.scope($jp, name); }, jpFilter @@ -436,17 +432,17 @@ export default class Selector< } private searchPrivate( - type: T, - selectFunction: (jp: LaraJoinPoint, name?: T) => LaraJoinPoint[], + type: JpPredicate, + selectFunction: (jp: LaraJoinPoint, name?: JpPredicate) => LaraJoinPoint[], jpFilter: JpFilterFunction = () => true ): Selector { - const name = Weaver.findJoinpointTypeName(type) ?? "joinpoint"; + const name = type.jpName(); const $newJps: SelectorChain[] = []; /** * Lara Common Language dirty hack. REMOVE ME PLEASE! */ - if (selectorJoinPointsClass !== JoinPoints && type === LaraJoinPoint) { + if (selectorJoinPointsClass !== JoinPoints && type.isLaraJoinPoint()) { // We are in LCL mode. throw new Error("In LCL mode you are required to specify a type in a search."); } @@ -463,7 +459,7 @@ export default class Selector< // Filter does not test if the join point is of the right type const $root = this.$currentJps[0].jpAttributes[this.lastName]; - if ($root instanceof type) { + if (type.isInstance($root)) { this.addJps(name, $newJps, [$root], jpFilter, this.$currentJps[0]); } } diff --git a/Lara-JS/src-api/weaver/predicate/JpPredicate.ts b/Lara-JS/src-api/weaver/predicate/JpPredicate.ts new file mode 100644 index 000000000..c30f6e3d5 --- /dev/null +++ b/Lara-JS/src-api/weaver/predicate/JpPredicate.ts @@ -0,0 +1,21 @@ +import { LaraJoinPoint } from "../../LaraJoinPoint.js"; + +export default abstract class JpPredicate { + + /** + * @returns the name of the join point + */ + abstract jpName() : string; + + /** + * @returns true if the underlying type is THE class LaraJoinPoint + */ + abstract isLaraJoinPoint() : boolean; + + /** + * + * @param jp the join point we want to test + * @returns true if the join point is accepted by this predicate + */ + abstract isInstance(jp: T):boolean +} \ No newline at end of file diff --git a/Lara-JS/src-api/weaver/predicate/StringPredicate.ts b/Lara-JS/src-api/weaver/predicate/StringPredicate.ts new file mode 100644 index 000000000..e3d98fb16 --- /dev/null +++ b/Lara-JS/src-api/weaver/predicate/StringPredicate.ts @@ -0,0 +1,22 @@ +import { LaraJoinPoint } from "../../LaraJoinPoint.js"; +import JpPredicate from "./JpPredicate.js"; + +export default class StringPredicate extends JpPredicate { + + constructor(private name: string) { + super(); + } + + jpName(): string { + return this.name; + } + isLaraJoinPoint(): boolean + { + return false; + } + + isInstance(jp: T): boolean { + return jp.instanceOf(this.name); + } + +} \ No newline at end of file diff --git a/Lara-JS/src-api/weaver/predicate/TypePredicate.ts b/Lara-JS/src-api/weaver/predicate/TypePredicate.ts new file mode 100644 index 000000000..226ce0295 --- /dev/null +++ b/Lara-JS/src-api/weaver/predicate/TypePredicate.ts @@ -0,0 +1,22 @@ +import { LaraJoinPoint } from "../../LaraJoinPoint.js"; +import JpPredicate from "./JpPredicate.js"; +import Weaver from "../Weaver.js"; + +export default class TypePredicate extends JpPredicate { + + constructor(private type: T) { + super(); + } + + jpName(): string { + return Weaver.findJoinpointTypeName(this.type) ?? "joinpoint" + } + + isLaraJoinPoint(): boolean { + return this.type === LaraJoinPoint; + } + isInstance(jp: T): boolean { + return jp instanceof this.type; + } + +} diff --git a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java index d2f874858..a382ca4e5 100644 --- a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java +++ b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java @@ -114,6 +114,9 @@ public enum LaraApiJsResource implements LaraResourceProvider { WEAVER_JS("weaver/Weaver.js"), WEAVERLAUNCHERBASE_JS("weaver/WeaverLauncherBase.js"), WEAVEROPTIONS_JS("weaver/WeaverOptions.js"), + JPPREDICATE_JS("weaver/predicate/JpPredicate.js"), + STRINGPREDICATE_JS("weaver/predicate/StringPredicate.js"), + TYPEPREDICATE_JS("weaver/predicate/TypePredicate.js"), ACTIONAWARECACHE_JS("weaver/util/ActionAwareCache.js"), WEAVERDATASTORE_JS("weaver/util/WeaverDataStore.js"); diff --git a/LaraApi/src-lara/LaraJoinPoint.js b/LaraApi/src-lara/LaraJoinPoint.js index f19c7e858..5af351770 100644 --- a/LaraApi/src-lara/LaraJoinPoint.js +++ b/LaraApi/src-lara/LaraJoinPoint.js @@ -95,13 +95,15 @@ export function wrapJoinPoint(obj) { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `Given Java join point is a Java class but is not a JoinPoint: ${obj.getClass()}`); } - const jpType = obj.getJoinPointType(); + // Get join point class from name of the Java class, since getJoinPointType() might + // not always correspond to the actual join point class (e.g., anyweaver) + const jpClass = obj.get_class(); for (const mapper of JoinpointMappers) { - if (mapper[jpType]) { - return new mapper[jpType](obj); + if (mapper[jpClass]) { + return new mapper[jpClass](obj); } } - throw new Error("No mapper found for join point type: " + jpType); + throw new Error("No mapper found for join point type: " + jpClass); } export function unwrapJoinPoint(obj) { if (obj instanceof LaraJoinPoint) { diff --git a/LaraApi/src-lara/weaver/JoinPoints.js b/LaraApi/src-lara/weaver/JoinPoints.js index 755f42cc4..065ff18be 100644 --- a/LaraApi/src-lara/weaver/JoinPoints.js +++ b/LaraApi/src-lara/weaver/JoinPoints.js @@ -90,7 +90,7 @@ export default class JoinPoints { if (jpType === undefined) { return $allJps; } - return $allJps.filter((jp) => jp instanceof jpType); + return $allJps.filter((jp) => jpType.isInstance(jp)); } /** * Iterates of attributeNames, returns the first value that is not null or undefined. diff --git a/LaraApi/src-lara/weaver/Selector.js b/LaraApi/src-lara/weaver/Selector.js index d019c8cc6..9cd4c8fbd 100644 --- a/LaraApi/src-lara/weaver/Selector.js +++ b/LaraApi/src-lara/weaver/Selector.js @@ -4,6 +4,8 @@ import Accumulator from "../lara/util/Accumulator.js"; import JoinPoints from "./JoinPoints.js"; import TraversalType from "./TraversalType.js"; import Weaver from "./Weaver.js"; +import StringPredicate from "./predicate/StringPredicate.js"; +import TypePredicate from "./predicate/TypePredicate.js"; /** * @internal Lara Common Language dirty hack. IMPROPER USAGE WILL BREAK THE WHOLE WEAVER! */ @@ -139,16 +141,14 @@ export default class Selector { } search(type = LaraJoinPoint, filter, traversal = TraversalType.PREORDER) { let jpFilter; + let jpPredicate; if (typeof type === "string") { jpFilter = Selector.convertStringFilterToWrapperFilter(type, filter); - const jpType = Weaver.findJoinpointType(type); - if (!jpType) { - throw new Error(`Join point type '${type}' not found.`); - } - return this.search(jpType, jpFilter, traversal); + jpPredicate = new StringPredicate(type); } else { jpFilter = Selector.parseWrapperFilter(type, filter ?? (() => true)); + jpPredicate = new TypePredicate(type); } let fn; switch (traversal) { @@ -167,49 +167,45 @@ export default class Selector { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `Traversal type not implemented: ${traversal}`); } - return this.searchPrivate(type, fn, jpFilter); + return this.searchPrivate(jpPredicate, fn, jpFilter); } children(type = LaraJoinPoint, filter) { let jpFilter; + let jpPredicate; if (typeof type === "string") { jpFilter = Selector.convertStringFilterToWrapperFilter(type, filter); - const jpType = Weaver.findJoinpointType(type); - if (!jpType) { - throw new Error(`Join point type '${type}' not found.`); - } - return this.children(jpType, jpFilter); + jpPredicate = new StringPredicate(type); } else { jpFilter = Selector.parseWrapperFilter(type, filter ?? (() => true)); + jpPredicate = new TypePredicate(type); } - return this.searchPrivate(type, function ($jp, name) { - return selectorJoinPointsClass.children($jp, name); + return this.searchPrivate(jpPredicate, function ($jp, type) { + return selectorJoinPointsClass.children($jp, type); }, jpFilter); } scope(type = LaraJoinPoint, filter) { let jpFilter; + let jpPredicate; if (typeof type === "string") { jpFilter = Selector.convertStringFilterToWrapperFilter(type, filter); - const jpType = Weaver.findJoinpointType(type); - if (!jpType) { - throw new Error(`Join point type '${type}' not found.`); - } - return this.scope(jpType, jpFilter); + jpPredicate = new StringPredicate(type); } else { jpFilter = Selector.parseWrapperFilter(type, filter ?? (() => true)); + jpPredicate = new TypePredicate(type); } - return this.searchPrivate(type, function ($jp, name) { + return this.searchPrivate(jpPredicate, function ($jp, name) { return selectorJoinPointsClass.scope($jp, name); }, jpFilter); } searchPrivate(type, selectFunction, jpFilter = () => true) { - const name = Weaver.findJoinpointTypeName(type) ?? "joinpoint"; + const name = type.jpName(); const $newJps = []; /** * Lara Common Language dirty hack. REMOVE ME PLEASE! */ - if (selectorJoinPointsClass !== JoinPoints && type === LaraJoinPoint) { + if (selectorJoinPointsClass !== JoinPoints && type.isLaraJoinPoint()) { // We are in LCL mode. throw new Error("In LCL mode you are required to specify a type in a search."); } @@ -221,7 +217,7 @@ export default class Selector { this.addBaseJp = false; // Filter does not test if the join point is of the right type const $root = this.$currentJps[0].jpAttributes[this.lastName]; - if ($root instanceof type) { + if (type.isInstance($root)) { this.addJps(name, $newJps, [$root], jpFilter, this.$currentJps[0]); } } diff --git a/LaraApi/src-lara/weaver/predicate/JpPredicate.js b/LaraApi/src-lara/weaver/predicate/JpPredicate.js new file mode 100644 index 000000000..003c660a8 --- /dev/null +++ b/LaraApi/src-lara/weaver/predicate/JpPredicate.js @@ -0,0 +1,3 @@ +export default class JpPredicate { +} +//# sourceMappingURL=JpPredicate.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/weaver/predicate/StringPredicate.js b/LaraApi/src-lara/weaver/predicate/StringPredicate.js new file mode 100644 index 000000000..7efd58097 --- /dev/null +++ b/LaraApi/src-lara/weaver/predicate/StringPredicate.js @@ -0,0 +1,18 @@ +import JpPredicate from "./JpPredicate.js"; +export default class StringPredicate extends JpPredicate { + name; + constructor(name) { + super(); + this.name = name; + } + jpName() { + return this.name; + } + isLaraJoinPoint() { + return false; + } + isInstance(jp) { + return jp.instanceOf(this.name); + } +} +//# sourceMappingURL=StringPredicate.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/weaver/predicate/TypePredicate.js b/LaraApi/src-lara/weaver/predicate/TypePredicate.js new file mode 100644 index 000000000..b156ce61e --- /dev/null +++ b/LaraApi/src-lara/weaver/predicate/TypePredicate.js @@ -0,0 +1,20 @@ +import { LaraJoinPoint } from "../../LaraJoinPoint.js"; +import JpPredicate from "./JpPredicate.js"; +import Weaver from "../Weaver.js"; +export default class TypePredicate extends JpPredicate { + type; + constructor(type) { + super(); + this.type = type; + } + jpName() { + return Weaver.findJoinpointTypeName(this.type) ?? "joinpoint"; + } + isLaraJoinPoint() { + return this.type === LaraJoinPoint; + } + isInstance(jp) { + return jp instanceof this.type; + } +} +//# sourceMappingURL=TypePredicate.js.map \ No newline at end of file