From d2d6ae71cfb98516979b14879e6a6a05f6218317 Mon Sep 17 00:00:00 2001 From: nilslambertz <62618726+nilslambertz@users.noreply.github.com> Date: Sat, 11 Apr 2026 22:09:43 +0200 Subject: [PATCH 1/4] Reference operators and expression parser --- lang/expression-parser/model/pom.xml | 31 +++ .../expression_parser/model/FeatureCall.java | 49 ++++ .../model/FeatureInformation.java | 10 + .../expression_parser/model/FindAny.java | 27 +++ .../expression_parser/model/FlatMap.java | 37 +++ .../neojoin/expression_parser/model/Map.java | 37 +++ .../model/MemberFeatureCall.java | 38 +++ .../model/ReferenceFilter.java | 49 ++++ .../model/ReferenceOperator.java | 25 ++ .../ComparisonOperator.java | 17 ++ .../predicate_expression/ConstantValue.java | 29 +++ lang/expression-parser/parser/pom.xml | 42 ++++ ...supportedReferenceExpressionException.java | 18 ++ .../strategy/PatternMatchingStrategy.java | 12 + .../ManualPatternMatchingStrategy.java | 43 ++++ .../parsers/CollectReferencesParser.java | 38 +++ .../parsers/FeatureCallParser.java | 31 +++ .../parsers/FilterParser.java | 64 +++++ .../parsers/FindAnyParser.java | 55 +++++ .../parsers/FlatMapParser.java | 116 +++++++++ .../parsers/MapParser.java | 108 +++++++++ .../parsers/MemberFeatureCallParser.java | 99 ++++++++ .../parsers/ReferenceOperatorParser.java | 51 ++++ .../utils/BlockExpressionUtils.java | 35 +++ .../utils/CastingUtils.java | 69 ++++++ .../utils/JvmMemberCallUtils.java | 28 +++ .../utils/JvmTypeReferenceUtils.java | 29 +++ .../utils/PredicateExpressionUtils.java | 103 ++++++++ .../fixtures/JvmFieldFixtures.java | 10 + .../fixtures/JvmFormalParameterFixtures.java | 17 ++ .../fixtures/JvmOperationFixtures.java | 10 + .../fixtures/JvmTypeFixtures.java | 14 ++ .../fixtures/JvmTypeReferenceFixtures.java | 19 ++ .../fixtures/XBinaryOperationFixtures.java | 21 ++ .../fixtures/XBlockExpressionFixtures.java | 10 + .../fixtures/XClosureFixtures.java | 10 + .../fixtures/XFeatureCallFixtures.java | 32 +++ .../fixtures/XMemberFeatureCallFixtures.java | 119 +++++++++ .../fixtures/XNumberLiteralFixtures.java | 16 ++ .../parsers/CollectReferencesParserTest.java | 51 ++++ .../parsers/ExpressionParserTest.java | 90 +++++++ .../parsers/FeatureCallParserTest.java | 74 ++++++ .../parsers/FilterParserTest.java | 63 +++++ .../parsers/FindAnyParserTest.java | 87 +++++++ .../parsers/FlatMapParserTest.java | 145 +++++++++++ .../parsers/MapParserTest.java | 226 ++++++++++++++++++ .../parsers/MemberFeatureCallParserTest.java | 136 +++++++++++ lang/expression-parser/pom.xml | 22 ++ 48 files changed, 2462 insertions(+) create mode 100644 lang/expression-parser/model/pom.xml create mode 100644 lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FeatureCall.java create mode 100644 lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FeatureInformation.java create mode 100644 lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FindAny.java create mode 100644 lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FlatMap.java create mode 100644 lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/Map.java create mode 100644 lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/MemberFeatureCall.java create mode 100644 lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/ReferenceFilter.java create mode 100644 lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/ReferenceOperator.java create mode 100644 lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ComparisonOperator.java create mode 100644 lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ConstantValue.java create mode 100644 lang/expression-parser/parser/pom.xml create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/exception/UnsupportedReferenceExpressionException.java create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/PatternMatchingStrategy.java create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/ManualPatternMatchingStrategy.java create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/CollectReferencesParser.java create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FeatureCallParser.java create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FilterParser.java create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FindAnyParser.java create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FlatMapParser.java create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParser.java create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MemberFeatureCallParser.java create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/ReferenceOperatorParser.java create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/BlockExpressionUtils.java create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/CastingUtils.java create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/JvmMemberCallUtils.java create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/JvmTypeReferenceUtils.java create mode 100644 lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/PredicateExpressionUtils.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmFieldFixtures.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmFormalParameterFixtures.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmOperationFixtures.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmTypeFixtures.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmTypeReferenceFixtures.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XBinaryOperationFixtures.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XBlockExpressionFixtures.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XClosureFixtures.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XFeatureCallFixtures.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XMemberFeatureCallFixtures.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XNumberLiteralFixtures.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/CollectReferencesParserTest.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/ExpressionParserTest.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FeatureCallParserTest.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FilterParserTest.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FindAnyParserTest.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FlatMapParserTest.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParserTest.java create mode 100644 lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MemberFeatureCallParserTest.java create mode 100644 lang/expression-parser/pom.xml diff --git a/lang/expression-parser/model/pom.xml b/lang/expression-parser/model/pom.xml new file mode 100644 index 00000000..d17840fe --- /dev/null +++ b/lang/expression-parser/model/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + tools.vitruv + tools.vitruv.neojoin.expression_parser + 0.1.0-SNAPSHOT + + + tools.vitruv.neojoin.expression_parser.model + + Expression Parser Model + + + + + org.eclipse.xtext + org.eclipse.xtext.xbase + + + org.projectlombok + lombok + + + org.jspecify + jspecify + + + diff --git a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FeatureCall.java b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FeatureCall.java new file mode 100644 index 00000000..d636b254 --- /dev/null +++ b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FeatureCall.java @@ -0,0 +1,49 @@ +package tools.vitruv.neojoin.expression_parser.model; + +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import org.jspecify.annotations.Nullable; + +/** + * A FeatureCall represents a feature (e.g. a variable), stores information about the type and is + * the first operation in a reference chain. + * + *

An example expression may look like + * + *

+ *     {@code someResult = car.axis.flatMap(a -> a.wheels).toList()}
+ * 
+ * + * Here, {@code car} is a FeatureCall + * + *

A FeatureCall is also the first operation in a nested expression: + * + *

+ *     {@code someResult = car.axis.flatMap(oneAxis -> oneAxis.wheels).toList()}
+ * 
+ * + * Here, {@code oneAxis} is also a FeatureCall + */ +@Data +@RequiredArgsConstructor +public class FeatureCall implements ReferenceOperator { + @Nullable final String identifier; + @Nullable final String simpleName; + + @Nullable ReferenceOperator followingOperator; + + public static FeatureCall empty() { + return new FeatureCall(null, null); + } + + @Override + public String toString() { + final String stringRepresentation = "FeatureCall(" + simpleName + ")"; + if (followingOperator == null) { + return stringRepresentation; + } + + return stringRepresentation + "->" + followingOperator; + } +} diff --git a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FeatureInformation.java b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FeatureInformation.java new file mode 100644 index 00000000..ca0f1554 --- /dev/null +++ b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FeatureInformation.java @@ -0,0 +1,10 @@ +package tools.vitruv.neojoin.expression_parser.model; + +import lombok.Value; + +@Value +public class FeatureInformation { + String featureName; + String featureClassSimpleName; + String featureClassIdentifier; +} diff --git a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FindAny.java b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FindAny.java new file mode 100644 index 00000000..c0c8a92f --- /dev/null +++ b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FindAny.java @@ -0,0 +1,27 @@ +package tools.vitruv.neojoin.expression_parser.model; + +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import org.jspecify.annotations.Nullable; + +/** + * FindAny selects any element from a collection of elements. There are no guarantees which element + * will be selected + * + *

Example expressions may look like + * + *

+ *     {@code
+ *          someResult = car.axis.findFirst()
+ *          someResult = car.axis.findLast()
+ *     }
+ * 
+ * + * Here, {@code X.findFirst()} and {@code X.findLast()} are FindAny operations + */ +@Data +@RequiredArgsConstructor +public class FindAny implements ReferenceOperator { + @Nullable ReferenceOperator followingOperator; +} diff --git a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FlatMap.java b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FlatMap.java new file mode 100644 index 00000000..2eab98f0 --- /dev/null +++ b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FlatMap.java @@ -0,0 +1,37 @@ +package tools.vitruv.neojoin.expression_parser.model; + +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +/** + * A FlatMap represents mapping a parent object to some children along a one-to-many + * reference. It contains information about the reference and the child type + * + *

An example expression may look like + * + *

+ *     {@code someResult = car.axis.flatMap(a -> a.wheels).toList()}
+ * 
+ * + * Here, {@code X.flatMap(a -> a.wheels)} is a FlatMap + */ +@Data +@RequiredArgsConstructor +public class FlatMap implements ReferenceOperator { + @NonNull final FeatureInformation featureInformation; + + @Nullable ReferenceOperator followingOperator; + + @Override + public String toString() { + final String stringRepresentation = "FlatMap(" + featureInformation.getFeatureName() + ")"; + if (followingOperator == null) { + return stringRepresentation; + } + + return stringRepresentation + "->" + followingOperator; + } +} diff --git a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/Map.java b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/Map.java new file mode 100644 index 00000000..69e8362c --- /dev/null +++ b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/Map.java @@ -0,0 +1,37 @@ +package tools.vitruv.neojoin.expression_parser.model; + +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +/** + * A Map represents mapping a parent object to some child along a one-to-one reference. It + * contains information about the reference and the child type + * + *

An example expression may look like + * + *

+ *     {@code someResult = car.axis.map(a -> a.axisInformation).toList()}
+ * 
+ * + * Here, {@code X.map(a -> a.axisInformation)} is a Map + */ +@Data +@RequiredArgsConstructor +public class Map implements ReferenceOperator { + @NonNull final FeatureInformation featureInformation; + + @Nullable ReferenceOperator followingOperator; + + @Override + public String toString() { + final String stringRepresentation = "Map(" + featureInformation.getFeatureName() + ")"; + if (followingOperator == null) { + return stringRepresentation; + } + + return stringRepresentation + "->" + followingOperator; + } +} diff --git a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/MemberFeatureCall.java b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/MemberFeatureCall.java new file mode 100644 index 00000000..512ddc4d --- /dev/null +++ b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/MemberFeatureCall.java @@ -0,0 +1,38 @@ +package tools.vitruv.neojoin.expression_parser.model; + +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +/** + * A MemberFeatureCall represents a field/reference access of a parent class. It contains + * information about the type and reference name. The reference can have an upper and/or lower bound + * + *

An example expression may look like + * + *

+ *     {@code someResult = car.axis.flatMap(a -> a.wheels).toList()}
+ * 
+ * + * Here, {@code X.axis} is a MemberFeatureCall + */ +@Data +@RequiredArgsConstructor +public class MemberFeatureCall implements ReferenceOperator { + @NonNull final FeatureInformation featureInformation; + final boolean isCollection; + + @Nullable ReferenceOperator followingOperator; + + @Override + public String toString() { + final String stringRepresentation = "MemberFeatureCall(" + featureInformation.getFeatureName() + ")"; + if (followingOperator == null) { + return stringRepresentation; + } + + return stringRepresentation + "->" + followingOperator; + } +} diff --git a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/ReferenceFilter.java b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/ReferenceFilter.java new file mode 100644 index 00000000..8f6e16ae --- /dev/null +++ b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/ReferenceFilter.java @@ -0,0 +1,49 @@ +package tools.vitruv.neojoin.expression_parser.model; + +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import tools.vitruv.neojoin.expression_parser.model.predicate_expression.ComparisonOperator; +import tools.vitruv.neojoin.expression_parser.model.predicate_expression.ConstantValue; + +/** + * A ReferenceFilter represents a predicate for the previous ReferenceOperator. Only predicates that + * compare a feature to some constant value are supported + * + *

An example expression may look like + * + *

+ *     {@code someResult = car.axis.filter(a -> a.position == "front").toList()}
+ * 
+ * + * Here, {@code X.filter(a -> a.position == "front")} is a ReferenceFilter + */ +@Data +@RequiredArgsConstructor +public class ReferenceFilter implements ReferenceOperator { + @NonNull final String feature; + @NonNull final ComparisonOperator operator; + @NonNull final ConstantValue constantValue; + + @Nullable ReferenceOperator followingOperator; + + @Override + public String toString() { + final String stringRepresentation = + "ReferenceFilter(" + + feature + + " " + + operator.getRepresentation() + + " " + + constantValue + + ")"; + if (followingOperator == null) { + return stringRepresentation; + } + + return stringRepresentation + "->" + followingOperator; + } +} diff --git a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/ReferenceOperator.java b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/ReferenceOperator.java new file mode 100644 index 00000000..80a4d22c --- /dev/null +++ b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/ReferenceOperator.java @@ -0,0 +1,25 @@ +package tools.vitruv.neojoin.expression_parser.model; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +/** + * A ReferenceOperator is a (partial) parsed expression that can be used for model transformations. + * It contains the required properties (e.g. type, field names) for the following steps and possibly + * the following ReferenceOperator-chain + */ +public interface ReferenceOperator { + @Nullable ReferenceOperator getFollowingOperator(); + + void setFollowingOperator(ReferenceOperator followingOperator); + + /** Returns the last ReferenceOperator in this chain */ + @NonNull + default ReferenceOperator getLastOperatorInChain() { + ReferenceOperator lastOperator = this; + while (lastOperator.getFollowingOperator() != null) { + lastOperator = lastOperator.getFollowingOperator(); + } + return lastOperator; + } +} diff --git a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ComparisonOperator.java b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ComparisonOperator.java new file mode 100644 index 00000000..4941cf47 --- /dev/null +++ b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ComparisonOperator.java @@ -0,0 +1,17 @@ +package tools.vitruv.neojoin.expression_parser.model.predicate_expression; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ComparisonOperator { + Equals("=="), + NotEquals("!="), + LessThan("<"), + LessEquals("<="), + GreaterThan(">"), + GreaterEquals(">="); + + final String representation; +} diff --git a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ConstantValue.java b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ConstantValue.java new file mode 100644 index 00000000..e316ba4e --- /dev/null +++ b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ConstantValue.java @@ -0,0 +1,29 @@ +package tools.vitruv.neojoin.expression_parser.model.predicate_expression; + +import lombok.Value; + +@Value +public class ConstantValue { + String value; + boolean isString; + + public static ConstantValue String(String value) { + return new ConstantValue(value, true); + } + + public static ConstantValue Boolean(boolean isTrue) { + return ConstantValue.of(isTrue ? "true" : "false"); + } + + public static ConstantValue of(String value) { + return new ConstantValue(value, false); + } + + @Override + public String toString() { + if (isString) { + return "\"" + value + "\""; + } + return value; + } +} diff --git a/lang/expression-parser/parser/pom.xml b/lang/expression-parser/parser/pom.xml new file mode 100644 index 00000000..58f62472 --- /dev/null +++ b/lang/expression-parser/parser/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + tools.vitruv + tools.vitruv.neojoin.expression_parser + 0.1.0-SNAPSHOT + + + tools.vitruv.neojoin.expression_parser.parser + + Expression Parser Logic + + + + + org.eclipse.xtext + org.eclipse.xtext.xbase + + + org.projectlombok + lombok + + + org.jspecify + jspecify + + + ${project.groupId} + tools.vitruv.neojoin.expression_parser.model + ${project.version} + + + + org.junit.jupiter + junit-jupiter-api + test + + + diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/exception/UnsupportedReferenceExpressionException.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/exception/UnsupportedReferenceExpressionException.java new file mode 100644 index 00000000..1f70178f --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/exception/UnsupportedReferenceExpressionException.java @@ -0,0 +1,18 @@ +package tools.vitruv.neojoin.expression_parser.parser.exception; + +import org.eclipse.xtext.xbase.XExpression; + +public class UnsupportedReferenceExpressionException extends Exception { + public static UnsupportedReferenceExpressionException fromExpression(XExpression expression) { + return new UnsupportedReferenceExpressionException( + String.format("The expression %s is not supported", expression)); + } + + public UnsupportedReferenceExpressionException(String message) { + super(message); + } + + public UnsupportedReferenceExpressionException(String message, XExpression expression) { + super(String.format(message + ". The expression was %s", expression)); + } +} diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/PatternMatchingStrategy.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/PatternMatchingStrategy.java new file mode 100644 index 00000000..1beec37a --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/PatternMatchingStrategy.java @@ -0,0 +1,12 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy; + +import org.eclipse.xtext.xbase.XExpression; +import org.jspecify.annotations.NonNull; + +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; + +public interface PatternMatchingStrategy { + @NonNull ReferenceOperator parseReferenceOperator(@NonNull XExpression expression) + throws UnsupportedReferenceExpressionException; +} diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/ManualPatternMatchingStrategy.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/ManualPatternMatchingStrategy.java new file mode 100644 index 00000000..63bebced --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/ManualPatternMatchingStrategy.java @@ -0,0 +1,43 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching; + +import org.eclipse.xtext.xbase.XExpression; +import org.jspecify.annotations.NonNull; + +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; +import tools.vitruv.neojoin.expression_parser.parser.strategy.PatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers.CollectReferencesParser; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers.FeatureCallParser; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers.FilterParser; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers.FindAnyParser; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers.FlatMapParser; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers.MapParser; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers.MemberFeatureCallParser; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers.ReferenceOperatorParser; + +import java.util.List; + +public class ManualPatternMatchingStrategy implements PatternMatchingStrategy { + private static final List PARSERS = + List.of( + new FeatureCallParser(), + new MemberFeatureCallParser(), + new FilterParser(), + new CollectReferencesParser(), + new FlatMapParser(), + new MapParser(), + new FindAnyParser()); + + @Override + public @NonNull ReferenceOperator parseReferenceOperator(@NonNull XExpression expression) + throws UnsupportedReferenceExpressionException { + for (var parser : PARSERS) { + final var result = parser.parse(this, expression); + if (result.isPresent()) { + return result.get(); + } + } + + throw UnsupportedReferenceExpressionException.fromExpression(expression); + } +} diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/CollectReferencesParser.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/CollectReferencesParser.java new file mode 100644 index 00000000..d61aebaf --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/CollectReferencesParser.java @@ -0,0 +1,38 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import org.eclipse.xtext.common.types.JvmIdentifiableElement; +import org.eclipse.xtext.xbase.XAbstractFeatureCall; +import org.eclipse.xtext.xbase.XExpression; + +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; +import tools.vitruv.neojoin.expression_parser.parser.strategy.PatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.CastingUtils; + +import java.util.Optional; +import java.util.Set; + +public class CollectReferencesParser implements ReferenceOperatorParser { + private static final String TO_LIST_OPERATION_SIMPLE_NAME = "toList"; + private static final Set COLLECT_OPERATIONS_SIMPLE_NAMES = + Set.of(TO_LIST_OPERATION_SIMPLE_NAME); + + public Optional parse( + PatternMatchingStrategy strategy, XExpression expression) + throws UnsupportedReferenceExpressionException { + final Optional jvmOperationSimpleName = + CastingUtils.asMemberFeatureCall(expression) + .map(XAbstractFeatureCall::getFeature) + .flatMap(CastingUtils::asJvmOperation) + .map(JvmIdentifiableElement::getSimpleName); + if (jvmOperationSimpleName.isEmpty()) { + return Optional.empty(); + } + + if (!COLLECT_OPERATIONS_SIMPLE_NAMES.contains(jvmOperationSimpleName.get())) { + return Optional.empty(); + } + + return parseAndAppendFollowingExpressionOperators(strategy, expression, null); + } +} diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FeatureCallParser.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FeatureCallParser.java new file mode 100644 index 00000000..efb9e877 --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FeatureCallParser.java @@ -0,0 +1,31 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import org.eclipse.xtext.common.types.JvmType; +import org.eclipse.xtext.xbase.XAbstractFeatureCall; +import org.eclipse.xtext.xbase.XExpression; + +import tools.vitruv.neojoin.expression_parser.model.FeatureCall; +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.strategy.PatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.CastingUtils; + +import java.util.Optional; + +public class FeatureCallParser implements ReferenceOperatorParser { + public Optional parse( + PatternMatchingStrategy strategy, XExpression expression) { + return CastingUtils.asFeatureCall(expression) + .map(XAbstractFeatureCall::getFeature) + .flatMap(CastingUtils::asJvmFormalParameter) + .map( + parameter -> { + if (parameter.getParameterType() == null) { + return FeatureCall.empty(); + } + + JvmType parameterType = parameter.getParameterType().getType(); + return new FeatureCall( + parameterType.getIdentifier(), parameterType.getSimpleName()); + }); + } +} diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FilterParser.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FilterParser.java new file mode 100644 index 00000000..507ec74d --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FilterParser.java @@ -0,0 +1,64 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import org.eclipse.xtext.common.types.JvmIdentifiableElement; +import org.eclipse.xtext.xbase.XAbstractFeatureCall; +import org.eclipse.xtext.xbase.XBinaryOperation; +import org.eclipse.xtext.xbase.XClosure; +import org.eclipse.xtext.xbase.XExpression; +import org.eclipse.xtext.xbase.XMemberFeatureCall; + +import tools.vitruv.neojoin.expression_parser.model.ReferenceFilter; +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; +import tools.vitruv.neojoin.expression_parser.parser.strategy.PatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.BlockExpressionUtils; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.CastingUtils; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.JvmMemberCallUtils; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.PredicateExpressionUtils; + +import java.util.Optional; + +public class FilterParser implements ReferenceOperatorParser { + private static final String FILTER_MAP_OPERATION_SIMPLE_NAME = "filter"; + + public Optional parse( + PatternMatchingStrategy strategy, XExpression expression) + throws UnsupportedReferenceExpressionException { + final Optional binaryOperation = + Optional.of(expression) + .flatMap(CastingUtils::asMemberFeatureCall) + .filter(FilterParser::isFilterOperation) + .filter(JvmMemberCallUtils::hasExactlyOneMemberCallArgument) + .flatMap(JvmMemberCallUtils::getFirstArgument) + .flatMap(CastingUtils::asClosure) + .map(XClosure::getExpression) + .flatMap(CastingUtils::asBlockExpression) + .filter(BlockExpressionUtils::hasExactlyOneExpression) + .flatMap(BlockExpressionUtils::getFirstExpression) + .flatMap(CastingUtils::asBinaryOperation); + if (binaryOperation.isEmpty()) { + return Optional.empty(); + } + + // Try to parse filter expression (throws if not possible) + final PredicateExpressionUtils.ConstantPredicate constantFilterPredicate = + PredicateExpressionUtils.extractConstantPredicate(binaryOperation.get()); + + return parseAndAppendFollowingExpressionOperators( + strategy, + expression, + new ReferenceFilter( + constantFilterPredicate.getFeature(), + constantFilterPredicate.getOperator(), + constantFilterPredicate.getConstantValue())); + } + + private static boolean isFilterOperation(XMemberFeatureCall featureCall) { + return Optional.ofNullable(featureCall) + .map(XAbstractFeatureCall::getFeature) + .flatMap(CastingUtils::asJvmOperation) + .map(JvmIdentifiableElement::getSimpleName) + .map(FILTER_MAP_OPERATION_SIMPLE_NAME::equals) + .orElse(false); + } +} diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FindAnyParser.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FindAnyParser.java new file mode 100644 index 00000000..6098de11 --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FindAnyParser.java @@ -0,0 +1,55 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import org.eclipse.xtext.common.types.JvmOperation; +import org.eclipse.xtext.xbase.XAbstractFeatureCall; +import org.eclipse.xtext.xbase.XClosure; +import org.eclipse.xtext.xbase.XExpression; +import org.eclipse.xtext.xbase.XMemberFeatureCall; + +import tools.vitruv.neojoin.expression_parser.model.FindAny; +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; +import tools.vitruv.neojoin.expression_parser.parser.strategy.PatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.BlockExpressionUtils; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.CastingUtils; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.JvmMemberCallUtils; + +import java.util.Optional; +import java.util.Set; + +public class FindAnyParser implements ReferenceOperatorParser { + private static final String FIND_FIRST_OPERATION_SIMPLE_NAME = "findFirst"; + private static final String FIND_LAST_OPERATION_SIMPLE_NAME = "findLast"; + private static final Set FIND_ANY_OPERATION_SIMPLE_NAMES = + Set.of(FIND_FIRST_OPERATION_SIMPLE_NAME, FIND_LAST_OPERATION_SIMPLE_NAME); + + public Optional parse( + PatternMatchingStrategy strategy, XExpression expression) + throws UnsupportedReferenceExpressionException { + boolean isFindAnyWithoutArguments = + Optional.ofNullable(expression) + .flatMap(CastingUtils::asMemberFeatureCall) + .filter(FindAnyParser::isFindAnyOperation) + .filter(JvmMemberCallUtils::hasExactlyOneMemberCallArgument) + .flatMap(JvmMemberCallUtils::getFirstArgument) + .flatMap(CastingUtils::asClosure) + .map(XClosure::getExpression) + .flatMap(CastingUtils::asBlockExpression) + .filter(BlockExpressionUtils::hasNoExpressions) + .isPresent(); + if (!isFindAnyWithoutArguments) { + return Optional.empty(); + } + + return parseAndAppendFollowingExpressionOperators(strategy, expression, new FindAny()); + } + + private static boolean isFindAnyOperation(XMemberFeatureCall featureCall) { + return Optional.ofNullable(featureCall) + .map(XAbstractFeatureCall::getFeature) + .flatMap(CastingUtils::asJvmOperation) + .map(JvmOperation::getSimpleName) + .map(FIND_ANY_OPERATION_SIMPLE_NAMES::contains) + .orElse(false); + } +} diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FlatMapParser.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FlatMapParser.java new file mode 100644 index 00000000..386285ea --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FlatMapParser.java @@ -0,0 +1,116 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import org.eclipse.xtext.common.types.JvmIdentifiableElement; +import org.eclipse.xtext.xbase.XAbstractFeatureCall; +import org.eclipse.xtext.xbase.XClosure; +import org.eclipse.xtext.xbase.XExpression; +import org.eclipse.xtext.xbase.XMemberFeatureCall; + +import tools.vitruv.neojoin.expression_parser.model.FeatureCall; +import tools.vitruv.neojoin.expression_parser.model.FlatMap; +import tools.vitruv.neojoin.expression_parser.model.Map; +import tools.vitruv.neojoin.expression_parser.model.MemberFeatureCall; +import tools.vitruv.neojoin.expression_parser.model.ReferenceFilter; +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; +import tools.vitruv.neojoin.expression_parser.parser.strategy.PatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.BlockExpressionUtils; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.CastingUtils; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.JvmMemberCallUtils; + +import java.util.Optional; + +public class FlatMapParser implements ReferenceOperatorParser { + private static final String FLAT_MAP_OPERATION_SIMPLE_NAME = "flatMap"; + + public Optional parse( + PatternMatchingStrategy strategy, XExpression expression) + throws UnsupportedReferenceExpressionException { + // Check that expression is flatMap and get single argument + final Optional flatMapArgument = + CastingUtils.asMemberFeatureCall(expression) + .filter(FlatMapParser::isFlatMapOperation) + .filter(JvmMemberCallUtils::hasExactlyOneMemberCallArgument) + .flatMap(JvmMemberCallUtils::getFirstArgument); + if (flatMapArgument.isEmpty()) { + return Optional.empty(); + } + + // Check that expression can be parsed + final Optional flatMapArgumentExpression = + flatMapArgument + .flatMap(CastingUtils::asClosure) + .map(XClosure::getExpression) + .flatMap(CastingUtils::asBlockExpression) + .filter(BlockExpressionUtils::hasExactlyOneExpression) + .flatMap(BlockExpressionUtils::getFirstExpression); + if (flatMapArgumentExpression.isEmpty()) { + return Optional.empty(); + } + + final ReferenceOperator flatMapArgumentOperator = + strategy.parseReferenceOperator(flatMapArgumentExpression.get()); + if (!(flatMapArgumentOperator instanceof FeatureCall)) { + throw new UnsupportedReferenceExpressionException( + "The first element of a flatMap expression must be a feature call", + flatMapArgumentExpression.get()); + } + + ReferenceOperator currentFlatMapArgumentOperator = + flatMapArgumentOperator.getFollowingOperator(); + if (currentFlatMapArgumentOperator == null) { + throw new UnsupportedReferenceExpressionException( + "The flatMap expression must contain more than a feature call", + flatMapArgumentExpression.get()); + } + + final ReferenceOperator operatorHead = + extractFlatMapArgumentOperator( + currentFlatMapArgumentOperator, flatMapArgumentExpression.get()); + currentFlatMapArgumentOperator = currentFlatMapArgumentOperator.getFollowingOperator(); + + ReferenceOperator lastOperator = operatorHead; + while (currentFlatMapArgumentOperator != null) { + final ReferenceOperator nextOperator = + extractFlatMapArgumentOperator( + currentFlatMapArgumentOperator, flatMapArgumentExpression.get()); + + lastOperator.setFollowingOperator(nextOperator); + lastOperator = nextOperator; + currentFlatMapArgumentOperator = currentFlatMapArgumentOperator.getFollowingOperator(); + } + + return parseAndAppendFollowingExpressionOperators(strategy, expression, operatorHead); + } + + private static ReferenceOperator extractFlatMapArgumentOperator( + ReferenceOperator flatMapArgumentOperator, XExpression flatMapArgumentExpression) + throws UnsupportedReferenceExpressionException { + if (flatMapArgumentOperator instanceof MemberFeatureCall memberFeatureCall + && memberFeatureCall.isCollection()) { + return new FlatMap(memberFeatureCall.getFeatureInformation()); + } else if (flatMapArgumentOperator instanceof MemberFeatureCall memberFeatureCall + && !memberFeatureCall.isCollection()) { + return new Map(memberFeatureCall.getFeatureInformation()); + } else if (flatMapArgumentOperator instanceof Map mapCall) { + return new Map(mapCall.getFeatureInformation()); + } else if (flatMapArgumentOperator instanceof FlatMap flatMapCall) { + return new FlatMap(flatMapCall.getFeatureInformation()); + } else if (flatMapArgumentOperator instanceof ReferenceFilter filter) { + return new ReferenceFilter( + filter.getFeature(), filter.getOperator(), filter.getConstantValue()); + } + + throw new UnsupportedReferenceExpressionException( + "The flatMap expression is not supported", flatMapArgumentExpression); + } + + private static boolean isFlatMapOperation(XMemberFeatureCall featureCall) { + return Optional.ofNullable(featureCall) + .map(XAbstractFeatureCall::getFeature) + .flatMap(CastingUtils::asJvmOperation) + .map(JvmIdentifiableElement::getSimpleName) + .map(FLAT_MAP_OPERATION_SIMPLE_NAME::equals) + .orElse(false); + } +} diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParser.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParser.java new file mode 100644 index 00000000..67ba43b4 --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParser.java @@ -0,0 +1,108 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import org.eclipse.xtext.common.types.JvmIdentifiableElement; +import org.eclipse.xtext.xbase.XAbstractFeatureCall; +import org.eclipse.xtext.xbase.XClosure; +import org.eclipse.xtext.xbase.XExpression; +import org.eclipse.xtext.xbase.XMemberFeatureCall; + +import tools.vitruv.neojoin.expression_parser.model.FeatureCall; +import tools.vitruv.neojoin.expression_parser.model.Map; +import tools.vitruv.neojoin.expression_parser.model.MemberFeatureCall; +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; +import tools.vitruv.neojoin.expression_parser.parser.strategy.PatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.BlockExpressionUtils; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.CastingUtils; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.JvmMemberCallUtils; + +import java.util.Optional; + +public class MapParser implements ReferenceOperatorParser { + private static final String MAP_OPERATION_SIMPLE_NAME = "map"; + + public Optional parse( + PatternMatchingStrategy strategy, XExpression expression) + throws UnsupportedReferenceExpressionException { + // Check that expression is map and get single argument + final Optional mapArgument = + CastingUtils.asMemberFeatureCall(expression) + .filter(MapParser::isMapOperation) + .filter(JvmMemberCallUtils::hasExactlyOneMemberCallArgument) + .flatMap(JvmMemberCallUtils::getFirstArgument); + if (mapArgument.isEmpty()) { + return Optional.empty(); + } + + // Check that expression can be parsed + final Optional mapArgumentExpression = + mapArgument + .flatMap(CastingUtils::asClosure) + .map(XClosure::getExpression) + .flatMap(CastingUtils::asBlockExpression) + .filter(BlockExpressionUtils::hasExactlyOneExpression) + .flatMap(BlockExpressionUtils::getFirstExpression); + if (mapArgumentExpression.isEmpty()) { + return Optional.empty(); + } + + final ReferenceOperator mapArgumentOperator = + strategy.parseReferenceOperator(mapArgumentExpression.get()); + if (!(mapArgumentOperator instanceof FeatureCall)) { + throw new UnsupportedReferenceExpressionException( + "The first element of a map expression must be a feature call", + mapArgumentExpression.get()); + } + + ReferenceOperator currentMapArgumentOperator = mapArgumentOperator.getFollowingOperator(); + if (currentMapArgumentOperator == null) { + throw new UnsupportedReferenceExpressionException( + "The map expression must contain more than a feature call", + mapArgumentExpression.get()); + } + + // Loop through each parsed ReferenceOperators inside the map arguments and map/extract them + // to the top level ReferenceOperator chain + final ReferenceOperator extractedOperatorHead = + extractMapArgumentOperator(currentMapArgumentOperator, mapArgumentExpression.get()); + currentMapArgumentOperator = currentMapArgumentOperator.getFollowingOperator(); + + ReferenceOperator currentExtractedOperator = extractedOperatorHead; + while (currentMapArgumentOperator != null) { + final ReferenceOperator nextOperator = + extractMapArgumentOperator( + currentMapArgumentOperator, mapArgumentExpression.get()); + + currentExtractedOperator.setFollowingOperator(nextOperator); + currentExtractedOperator = nextOperator; + currentMapArgumentOperator = currentMapArgumentOperator.getFollowingOperator(); + } + + return parseAndAppendFollowingExpressionOperators( + strategy, expression, extractedOperatorHead); + } + + private static ReferenceOperator extractMapArgumentOperator( + ReferenceOperator mapArgumentOperator, XExpression mapArgumentExpression) + throws UnsupportedReferenceExpressionException { + // Currently, only one-to-one references are supported inside the map expression, since we + // cannot handle the FindAny operator inside nested expression currently. Therefore, all + // operators that return a collection are not possible + if (mapArgumentOperator instanceof MemberFeatureCall memberFeatureCall + && !memberFeatureCall.isCollection()) { + return new Map(memberFeatureCall.getFeatureInformation()); + } + + throw new UnsupportedReferenceExpressionException( + "The map expression is not supported", mapArgumentExpression); + } + + private static boolean isMapOperation(XMemberFeatureCall featureCall) { + return Optional.ofNullable(featureCall) + .map(XAbstractFeatureCall::getFeature) + .flatMap(CastingUtils::asJvmOperation) + .map(JvmIdentifiableElement::getSimpleName) + .map(MAP_OPERATION_SIMPLE_NAME::equals) + .orElse(false); + } +} diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MemberFeatureCallParser.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MemberFeatureCallParser.java new file mode 100644 index 00000000..860fa1f9 --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MemberFeatureCallParser.java @@ -0,0 +1,99 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import org.eclipse.xtext.common.types.JvmField; +import org.eclipse.xtext.common.types.JvmType; +import org.eclipse.xtext.common.types.JvmTypeReference; +import org.eclipse.xtext.xbase.XAbstractFeatureCall; +import org.eclipse.xtext.xbase.XExpression; +import org.eclipse.xtext.xbase.XMemberFeatureCall; + +import tools.vitruv.neojoin.expression_parser.model.FeatureInformation; +import tools.vitruv.neojoin.expression_parser.model.MemberFeatureCall; +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; +import tools.vitruv.neojoin.expression_parser.parser.strategy.PatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.CastingUtils; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.JvmTypeReferenceUtils; + +import java.util.Optional; + +public class MemberFeatureCallParser implements ReferenceOperatorParser { + private static final String LIST_IDENTIFIER = "java.util.List"; + + public Optional parse( + PatternMatchingStrategy strategy, XExpression expression) + throws UnsupportedReferenceExpressionException { + final Optional memberFeatureCall = + CastingUtils.asMemberFeatureCall(expression); + if (memberFeatureCall.isEmpty()) { + return Optional.empty(); + } + + final Optional jvmField = + memberFeatureCall + .map(XAbstractFeatureCall::getFeature) + .flatMap(CastingUtils::asJvmField); + if (jvmField.isEmpty()) { + return Optional.empty(); + } + + final Optional foundOperatorOptional; + if (MemberFeatureCallParser.isListType(jvmField.get().getType())) { + foundOperatorOptional = + jvmField.flatMap(MemberFeatureCallParser::getListFeatureInformation) + .map( + featureInformation -> + new MemberFeatureCall(featureInformation, true)); + } else { + foundOperatorOptional = + jvmField.flatMap(MemberFeatureCallParser::getFeatureInformation) + .map( + featureInformation -> + new MemberFeatureCall(featureInformation, false)); + } + final ReferenceOperator foundOperator = + foundOperatorOptional.orElseThrow( + () -> + new UnsupportedReferenceExpressionException( + "The MemberFeatureCall couldn't be parsed")); + + return parseAndAppendFollowingExpressionOperators(strategy, expression, foundOperator); + } + + private static Optional getFeatureInformation(JvmField jvmField) { + return Optional.ofNullable(jvmField) + .map(JvmField::getType) + .flatMap( + jvmTypeReference -> { + final JvmType jvmType = jvmTypeReference.getType(); + return Optional.of( + new FeatureInformation( + jvmField.getSimpleName(), + jvmType.getSimpleName(), + jvmType.getIdentifier())); + }); + } + + private static boolean isListType(JvmTypeReference typeReference) { + return Optional.ofNullable(typeReference) + .map(JvmTypeReference::getType) + .map(JvmType::getIdentifier) + .map(LIST_IDENTIFIER::equals) + .orElse(false); + } + + private static Optional getListFeatureInformation(JvmField jvmField) { + return Optional.ofNullable(jvmField) + .map(JvmField::getType) + .flatMap(CastingUtils::asParameterizedTypeReference) + .filter(JvmTypeReferenceUtils::hasExactlyOneArgument) + .flatMap(JvmTypeReferenceUtils::getFirstArgument) + .flatMap(CastingUtils::asParameterizedTypeReference) + .map( + field -> + new FeatureInformation( + jvmField.getSimpleName(), + field.getType().getSimpleName(), + field.getType().getIdentifier())); + } +} diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/ReferenceOperatorParser.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/ReferenceOperatorParser.java new file mode 100644 index 00000000..5ddc386a --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/ReferenceOperatorParser.java @@ -0,0 +1,51 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import org.eclipse.xtext.xbase.XAbstractFeatureCall; +import org.eclipse.xtext.xbase.XExpression; +import org.eclipse.xtext.xbase.XMemberFeatureCall; +import org.jspecify.annotations.Nullable; + +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; +import tools.vitruv.neojoin.expression_parser.parser.strategy.PatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils.CastingUtils; + +import java.util.Optional; + +public interface ReferenceOperatorParser { + Optional parse(PatternMatchingStrategy strategy, XExpression expression) + throws UnsupportedReferenceExpressionException; + + /** Returns the next call target if the expression is a {@code XMemberFeatureCall} */ + default Optional findNextCallTarget(XExpression expression) { + return CastingUtils.asMemberFeatureCall(expression) + .map(XMemberFeatureCall::getMemberCallTarget) + .flatMap(CastingUtils::asAbstractFeatureCall); + } + + /** + * Runs the Reference operator extraction on the following {@code memberCallTarget} and appends + * it to the existing ReferenceOperator chain + */ + default Optional parseAndAppendFollowingExpressionOperators( + PatternMatchingStrategy strategy, + XExpression currentExpression, + @Nullable ReferenceOperator currentOperator) + throws UnsupportedReferenceExpressionException { + final Optional nextMemberCallTarget = + findNextCallTarget(currentExpression); + if (nextMemberCallTarget.isPresent()) { + // Parse the following expression + final ReferenceOperator followingOperator = + strategy.parseReferenceOperator(nextMemberCallTarget.get()); + + // As the Expression "AST" contains the expressions in reverse order, the current + // expression should come after the expression we parsed afterward + followingOperator.getLastOperatorInChain().setFollowingOperator(currentOperator); + return Optional.of(followingOperator); + } + + // If there is no following operator, we are at the end of the expression chain + return Optional.ofNullable(currentOperator); + } +} diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/BlockExpressionUtils.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/BlockExpressionUtils.java new file mode 100644 index 00000000..4ef2d689 --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/BlockExpressionUtils.java @@ -0,0 +1,35 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.xtext.xbase.XBlockExpression; +import org.eclipse.xtext.xbase.XExpression; + +import java.util.Optional; +import java.util.stream.Stream; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class BlockExpressionUtils { + public static boolean hasNoExpressions(XBlockExpression blockExpression) { + return Optional.ofNullable(blockExpression) + .map(XBlockExpression::getExpressions) + .map(EList::isEmpty) + .orElse(false); + } + + public static boolean hasExactlyOneExpression(XBlockExpression blockExpression) { + return Optional.ofNullable(blockExpression) + .map(XBlockExpression::getExpressions) + .map(expressions -> expressions.size() == 1) + .orElse(false); + } + + public static Optional getFirstExpression(XBlockExpression blockExpression) { + return Optional.ofNullable(blockExpression) + .map(XBlockExpression::getExpressions) + .map(EList::stream) + .flatMap(Stream::findFirst); + } +} diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/CastingUtils.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/CastingUtils.java new file mode 100644 index 00000000..60d504a3 --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/CastingUtils.java @@ -0,0 +1,69 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.eclipse.xtext.common.types.JvmField; +import org.eclipse.xtext.common.types.JvmFormalParameter; +import org.eclipse.xtext.common.types.JvmIdentifiableElement; +import org.eclipse.xtext.common.types.JvmOperation; +import org.eclipse.xtext.common.types.JvmParameterizedTypeReference; +import org.eclipse.xtext.common.types.JvmTypeReference; +import org.eclipse.xtext.xbase.XAbstractFeatureCall; +import org.eclipse.xtext.xbase.XBinaryOperation; +import org.eclipse.xtext.xbase.XBlockExpression; +import org.eclipse.xtext.xbase.XClosure; +import org.eclipse.xtext.xbase.XExpression; +import org.eclipse.xtext.xbase.XFeatureCall; +import org.eclipse.xtext.xbase.XMemberFeatureCall; + +import java.util.Optional; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class CastingUtils { + public static Optional cast(Object object, Class clazz) { + return Optional.ofNullable(object).filter(clazz::isInstance).map(clazz::cast); + } + + public static Optional asBlockExpression(XExpression expression) { + return cast(expression, XBlockExpression.class); + } + + public static Optional asClosure(XExpression expression) { + return cast(expression, XClosure.class); + } + + public static Optional asMemberFeatureCall(XExpression expression) { + return cast(expression, XMemberFeatureCall.class); + } + + public static Optional asFeatureCall(XExpression expression) { + return cast(expression, XFeatureCall.class); + } + + public static Optional asAbstractFeatureCall(XExpression expression) { + return cast(expression, XAbstractFeatureCall.class); + } + + public static Optional asJvmField(JvmIdentifiableElement jvmIdentifiableElement) { + return cast(jvmIdentifiableElement, JvmField.class); + } + + public static Optional asJvmOperation( + JvmIdentifiableElement jvmIdentifiableElement) { + return cast(jvmIdentifiableElement, JvmOperation.class); + } + + public static Optional asJvmFormalParameter( + JvmIdentifiableElement jvmIdentifiableElement) { + return cast(jvmIdentifiableElement, JvmFormalParameter.class); + } + + public static Optional asParameterizedTypeReference( + JvmTypeReference typeReference) { + return cast(typeReference, JvmParameterizedTypeReference.class); + } + + public static Optional asBinaryOperation(XExpression expression) { + return cast(expression, XBinaryOperation.class); + } +} diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/JvmMemberCallUtils.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/JvmMemberCallUtils.java new file mode 100644 index 00000000..ccbad70d --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/JvmMemberCallUtils.java @@ -0,0 +1,28 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.xtext.xbase.XExpression; +import org.eclipse.xtext.xbase.XMemberFeatureCall; + +import java.util.Optional; +import java.util.stream.Stream; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class JvmMemberCallUtils { + public static boolean hasExactlyOneMemberCallArgument(XMemberFeatureCall featureCall) { + return Optional.ofNullable(featureCall) + .map(XMemberFeatureCall::getMemberCallArguments) + .map(args -> args.size() == 1) + .orElse(false); + } + + public static Optional getFirstArgument(XMemberFeatureCall featureCall) { + return Optional.ofNullable(featureCall) + .map(XMemberFeatureCall::getMemberCallArguments) + .map(EList::stream) + .flatMap(Stream::findFirst); + } +} diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/JvmTypeReferenceUtils.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/JvmTypeReferenceUtils.java new file mode 100644 index 00000000..ef037414 --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/JvmTypeReferenceUtils.java @@ -0,0 +1,29 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.xtext.common.types.JvmParameterizedTypeReference; +import org.eclipse.xtext.common.types.JvmTypeReference; + +import java.util.Optional; +import java.util.stream.Stream; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class JvmTypeReferenceUtils { + public static boolean hasExactlyOneArgument(JvmParameterizedTypeReference typeReference) { + return Optional.ofNullable(typeReference) + .map(JvmParameterizedTypeReference::getArguments) + .map(args -> args.size() == 1) + .orElse(false); + } + + public static Optional getFirstArgument( + JvmParameterizedTypeReference typeReference) { + return Optional.ofNullable(typeReference) + .map(JvmParameterizedTypeReference::getArguments) + .map(EList::stream) + .flatMap(Stream::findFirst); + } +} diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/PredicateExpressionUtils.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/PredicateExpressionUtils.java new file mode 100644 index 00000000..9738eeaf --- /dev/null +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/PredicateExpressionUtils.java @@ -0,0 +1,103 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.Value; + +import org.eclipse.xtext.common.types.JvmField; +import org.eclipse.xtext.common.types.JvmOperation; +import org.eclipse.xtext.xbase.XAbstractFeatureCall; +import org.eclipse.xtext.xbase.XBinaryOperation; +import org.eclipse.xtext.xbase.XBooleanLiteral; +import org.eclipse.xtext.xbase.XExpression; +import org.eclipse.xtext.xbase.XNumberLiteral; +import org.eclipse.xtext.xbase.XStringLiteral; + +import tools.vitruv.neojoin.expression_parser.model.predicate_expression.ComparisonOperator; +import tools.vitruv.neojoin.expression_parser.model.predicate_expression.ConstantValue; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; + +import java.util.Map; +import java.util.Optional; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class PredicateExpressionUtils { + private static final Map OPERATOR_MAP = + Map.ofEntries( + Map.entry("operator_equals", ComparisonOperator.Equals), + Map.entry("operator_notEquals", ComparisonOperator.NotEquals), + Map.entry("operator_lessThan", ComparisonOperator.LessThan), + Map.entry("operator_lessEqualsThan", ComparisonOperator.LessEquals), + Map.entry("operator_greaterThan", ComparisonOperator.GreaterThan), + Map.entry("operator_greaterEqualsThan", ComparisonOperator.GreaterEquals)); + + /** + * Extracts a ConstantPredicate out of the binary operation. The order of operations is expected + * to be FIELD - COMPARISON_OPERATOR - CONSTANT_VALUE + * + * @throws UnsupportedReferenceExpressionException If the expression doesn't match the expected + * format + */ + public static ConstantPredicate extractConstantPredicate(XBinaryOperation operation) + throws UnsupportedReferenceExpressionException { + // Left side should be a feature/field call + final XExpression leftOperand = operation.getLeftOperand(); + final Optional fieldSimpleName = + Optional.ofNullable(leftOperand) + .flatMap(CastingUtils::asMemberFeatureCall) + .map(XAbstractFeatureCall::getFeature) + .flatMap(CastingUtils::asJvmField) + .map(JvmField::getSimpleName); + if (fieldSimpleName.isEmpty()) { + throw new UnsupportedReferenceExpressionException( + String.format( + "The left side of a filter expression must be a field, but was: %s", + leftOperand)); + } + + final Optional comparisonOperator = getComparisonOperator(operation); + if (comparisonOperator.isEmpty()) { + throw new UnsupportedReferenceExpressionException( + "The comparison operator is not supported"); + } + + // Right side should be a constant + final XExpression rightOperand = operation.getRightOperand(); + final Optional constantValue = getConstantExpressionAsString(rightOperand); + if (constantValue.isEmpty()) { + throw new UnsupportedReferenceExpressionException( + String.format( + "The right side of a filter expression must be a constant, but was: %s", + rightOperand)); + } + + return new ConstantPredicate( + fieldSimpleName.get(), comparisonOperator.get(), constantValue.get()); + } + + private static Optional getComparisonOperator(XBinaryOperation operation) { + return Optional.ofNullable(operation) + .map(XAbstractFeatureCall::getFeature) + .flatMap(CastingUtils::asJvmOperation) + .map(JvmOperation::getSimpleName) + .map(OPERATOR_MAP::get); + } + + private static Optional getConstantExpressionAsString(XExpression expression) { + if (expression instanceof XNumberLiteral numberLiteral) { + return Optional.of(ConstantValue.of(numberLiteral.getValue())); + } else if (expression instanceof XStringLiteral stringLiteral) { + return Optional.of(ConstantValue.String(stringLiteral.getValue())); + } else if (expression instanceof XBooleanLiteral booleanLiteral) { + return Optional.of(ConstantValue.Boolean(booleanLiteral.isIsTrue())); + } + return Optional.empty(); + } + + @Value + public static class ConstantPredicate { + String feature; + ComparisonOperator operator; + ConstantValue constantValue; + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmFieldFixtures.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmFieldFixtures.java new file mode 100644 index 00000000..58d4ecd3 --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmFieldFixtures.java @@ -0,0 +1,10 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures; + +import org.eclipse.xtext.common.types.JvmField; +import org.eclipse.xtext.common.types.TypesFactory; + +public class JvmFieldFixtures { + public static JvmField createJvmField() { + return TypesFactory.eINSTANCE.createJvmField(); + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmFormalParameterFixtures.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmFormalParameterFixtures.java new file mode 100644 index 00000000..f3949974 --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmFormalParameterFixtures.java @@ -0,0 +1,17 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures; + +import org.eclipse.xtext.common.types.JvmFormalParameter; +import org.eclipse.xtext.common.types.JvmTypeReference; +import org.eclipse.xtext.common.types.TypesFactory; + +public class JvmFormalParameterFixtures { + public static JvmFormalParameter createJvmFormalParameter() { + return TypesFactory.eINSTANCE.createJvmFormalParameter(); + } + + public static JvmFormalParameter createJvmFormalParameter(JvmTypeReference typeReference) { + final JvmFormalParameter formalParameter = createJvmFormalParameter(); + formalParameter.setParameterType(typeReference); + return formalParameter; + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmOperationFixtures.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmOperationFixtures.java new file mode 100644 index 00000000..1db5fc4a --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmOperationFixtures.java @@ -0,0 +1,10 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures; + +import org.eclipse.xtext.common.types.JvmOperation; +import org.eclipse.xtext.common.types.TypesFactory; + +public class JvmOperationFixtures { + public static JvmOperation createJvmOperation() { + return TypesFactory.eINSTANCE.createJvmOperation(); + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmTypeFixtures.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmTypeFixtures.java new file mode 100644 index 00000000..fb1dde1a --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmTypeFixtures.java @@ -0,0 +1,14 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures; + +import org.eclipse.xtext.common.types.JvmGenericType; +import org.eclipse.xtext.common.types.JvmType; +import org.eclipse.xtext.common.types.TypesFactory; + +public class JvmTypeFixtures { + public static JvmType createJvmType(String identifier, String simpleName) { + final JvmGenericType jvmType = TypesFactory.eINSTANCE.createJvmGenericType(); + jvmType.setIdentifier(identifier); + jvmType.setSimpleName(simpleName); + return jvmType; + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmTypeReferenceFixtures.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmTypeReferenceFixtures.java new file mode 100644 index 00000000..01166d80 --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/JvmTypeReferenceFixtures.java @@ -0,0 +1,19 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures; + +import org.eclipse.xtext.common.types.JvmParameterizedTypeReference; +import org.eclipse.xtext.common.types.JvmType; +import org.eclipse.xtext.common.types.JvmTypeReference; +import org.eclipse.xtext.common.types.TypesFactory; + +public class JvmTypeReferenceFixtures { + public static JvmTypeReference createJvmTypeReference(JvmType type) { + final JvmParameterizedTypeReference jvmTypeReference = + TypesFactory.eINSTANCE.createJvmParameterizedTypeReference(); + jvmTypeReference.setType(type); + return jvmTypeReference; + } + + public static JvmParameterizedTypeReference createJvmParameterizedTypeReference() { + return TypesFactory.eINSTANCE.createJvmParameterizedTypeReference(); + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XBinaryOperationFixtures.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XBinaryOperationFixtures.java new file mode 100644 index 00000000..f31eafb2 --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XBinaryOperationFixtures.java @@ -0,0 +1,21 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures; + +import org.eclipse.xtext.common.types.JvmIdentifiableElement; +import org.eclipse.xtext.xbase.XBinaryOperation; +import org.eclipse.xtext.xbase.XExpression; +import org.eclipse.xtext.xbase.impl.XBinaryOperationImplCustom; + +public class XBinaryOperationFixtures { + public static XBinaryOperation createXBinaryOperation() { + return new XBinaryOperationImplCustom(); + } + + public static XBinaryOperation binaryOperation( + XExpression leftOperand, JvmIdentifiableElement feature, XExpression rightOperand) { + final XBinaryOperation binaryOperation = createXBinaryOperation(); + binaryOperation.setLeftOperand(leftOperand); + binaryOperation.setFeature(feature); + binaryOperation.setRightOperand(rightOperand); + return binaryOperation; + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XBlockExpressionFixtures.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XBlockExpressionFixtures.java new file mode 100644 index 00000000..af19af48 --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XBlockExpressionFixtures.java @@ -0,0 +1,10 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures; + +import org.eclipse.xtext.xbase.XBlockExpression; +import org.eclipse.xtext.xbase.impl.XBlockExpressionImplCustom; + +public class XBlockExpressionFixtures { + public static XBlockExpression createXBlockExpression() { + return new XBlockExpressionImplCustom(); + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XClosureFixtures.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XClosureFixtures.java new file mode 100644 index 00000000..adb2f4ef --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XClosureFixtures.java @@ -0,0 +1,10 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures; + +import org.eclipse.xtext.xbase.XClosure; +import org.eclipse.xtext.xbase.impl.XClosureImplCustom; + +public class XClosureFixtures { + public static XClosure createXClosure() { + return new XClosureImplCustom(); + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XFeatureCallFixtures.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XFeatureCallFixtures.java new file mode 100644 index 00000000..2685c19a --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XFeatureCallFixtures.java @@ -0,0 +1,32 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures; + +import org.eclipse.xtext.common.types.JvmFormalParameter; +import org.eclipse.xtext.common.types.JvmType; +import org.eclipse.xtext.common.types.JvmTypeReference; +import org.eclipse.xtext.xbase.XFeatureCall; +import org.eclipse.xtext.xbase.impl.XFeatureCallImplCustom; + +public class XFeatureCallFixtures { + public static XFeatureCall createXFeatureCall() { + return new XFeatureCallImplCustom(); + } + + public static XFeatureCall featureCallWithEmptyFormalParameter() { + final JvmFormalParameter emptyFormalParameter = + JvmFormalParameterFixtures.createJvmFormalParameter(); + final XFeatureCall featureCall = XFeatureCallFixtures.createXFeatureCall(); + featureCall.setFeature(emptyFormalParameter); + return featureCall; + } + + public static XFeatureCall featureCall(String identifier, String simpleName) { + final JvmType jvmType = JvmTypeFixtures.createJvmType(identifier, simpleName); + final JvmTypeReference jvmTypeReference = + JvmTypeReferenceFixtures.createJvmTypeReference(jvmType); + final JvmFormalParameter formalParameter = + JvmFormalParameterFixtures.createJvmFormalParameter(jvmTypeReference); + final XFeatureCall featureCall = XFeatureCallFixtures.createXFeatureCall(); + featureCall.setFeature(formalParameter); + return featureCall; + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XMemberFeatureCallFixtures.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XMemberFeatureCallFixtures.java new file mode 100644 index 00000000..94554328 --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XMemberFeatureCallFixtures.java @@ -0,0 +1,119 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures; + +import org.eclipse.xtext.common.types.JvmField; +import org.eclipse.xtext.common.types.JvmOperation; +import org.eclipse.xtext.common.types.JvmParameterizedTypeReference; +import org.eclipse.xtext.common.types.JvmType; +import org.eclipse.xtext.common.types.JvmTypeReference; +import org.eclipse.xtext.xbase.XBlockExpression; +import org.eclipse.xtext.xbase.XClosure; +import org.eclipse.xtext.xbase.XExpression; +import org.eclipse.xtext.xbase.XMemberFeatureCall; +import org.eclipse.xtext.xbase.impl.XMemberFeatureCallImplCustom; + +public class XMemberFeatureCallFixtures { + public static XMemberFeatureCall createXMemberFeatureCall() { + return new XMemberFeatureCallImplCustom(); + } + + public static XMemberFeatureCall simpleFieldXMemberFeatureCall(String simpleName) { + final JvmField jvmField = JvmFieldFixtures.createJvmField(); + jvmField.setSimpleName(simpleName); + + final XMemberFeatureCall memberFeatureCall = createXMemberFeatureCall(); + memberFeatureCall.setFeature(jvmField); + + return memberFeatureCall; + } + + public static XMemberFeatureCall oneToOneFieldXMemberFeatureCall( + String identifier, String simpleName, String referenceName) { + final JvmType jvmType = JvmTypeFixtures.createJvmType(identifier, simpleName); + final JvmTypeReference typeReference = + JvmTypeReferenceFixtures.createJvmTypeReference(jvmType); + + final JvmField jvmField = JvmFieldFixtures.createJvmField(); + jvmField.setSimpleName(referenceName); + jvmField.setType(typeReference); + + final XMemberFeatureCall memberFeatureCall = createXMemberFeatureCall(); + memberFeatureCall.setFeature(jvmField); + + return memberFeatureCall; + } + + public static XMemberFeatureCall oneToManyFieldXMemberFeatureCall( + String identifier, String simpleName, String referenceName) { + final JvmType listJvmType = JvmTypeFixtures.createJvmType("java.util.List", null); + final JvmParameterizedTypeReference typeReference = + JvmTypeReferenceFixtures.createJvmParameterizedTypeReference(); + typeReference.setType(listJvmType); + + final JvmType innerJvmType = JvmTypeFixtures.createJvmType(identifier, simpleName); + final JvmTypeReference innerTypeReference = + JvmTypeReferenceFixtures.createJvmTypeReference(innerJvmType); + typeReference.getArguments().add(innerTypeReference); + + final JvmField jvmField = JvmFieldFixtures.createJvmField(); + jvmField.setSimpleName(referenceName); + jvmField.setType(typeReference); + + final XMemberFeatureCall memberFeatureCall = + XMemberFeatureCallFixtures.createXMemberFeatureCall(); + memberFeatureCall.setFeature(jvmField); + + return memberFeatureCall; + } + + public static XMemberFeatureCall mapOperationMemberFeatureCall(XExpression innerExpression) { + final XClosure closure = XClosureFixtures.createXClosure(); + final XBlockExpression blockExpression = XBlockExpressionFixtures.createXBlockExpression(); + blockExpression.getExpressions().add(innerExpression); + closure.setExpression(blockExpression); + + final XMemberFeatureCall mapMemberFeatureCall = + XMemberFeatureCallFixtures.createXMemberFeatureCall(); + mapMemberFeatureCall.getMemberCallArguments().add(closure); + + final JvmOperation mapOperation = JvmOperationFixtures.createJvmOperation(); + mapOperation.setSimpleName("map"); + mapMemberFeatureCall.setFeature(mapOperation); + + return mapMemberFeatureCall; + } + + public static XMemberFeatureCall flatMapOperationMemberFeatureCall( + XExpression innerExpression) { + final XClosure closure = XClosureFixtures.createXClosure(); + final XBlockExpression blockExpression = XBlockExpressionFixtures.createXBlockExpression(); + blockExpression.getExpressions().add(innerExpression); + closure.setExpression(blockExpression); + + final XMemberFeatureCall flatMapMemberFeatureCall = + XMemberFeatureCallFixtures.createXMemberFeatureCall(); + flatMapMemberFeatureCall.getMemberCallArguments().add(closure); + + final JvmOperation flatMapOperation = JvmOperationFixtures.createJvmOperation(); + flatMapOperation.setSimpleName("flatMap"); + flatMapMemberFeatureCall.setFeature(flatMapOperation); + + return flatMapMemberFeatureCall; + } + + public static XMemberFeatureCall filterOperationMemberFeatureCall(XExpression innerExpression) { + final XClosure closure = XClosureFixtures.createXClosure(); + final XBlockExpression blockExpression = XBlockExpressionFixtures.createXBlockExpression(); + blockExpression.getExpressions().add(innerExpression); + closure.setExpression(blockExpression); + + final XMemberFeatureCall filterMemberFeatureCall = + XMemberFeatureCallFixtures.createXMemberFeatureCall(); + filterMemberFeatureCall.getMemberCallArguments().add(closure); + + final JvmOperation filterOperation = JvmOperationFixtures.createJvmOperation(); + filterOperation.setSimpleName("filter"); + filterMemberFeatureCall.setFeature(filterOperation); + + return filterMemberFeatureCall; + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XNumberLiteralFixtures.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XNumberLiteralFixtures.java new file mode 100644 index 00000000..99fa9727 --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/fixtures/XNumberLiteralFixtures.java @@ -0,0 +1,16 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures; + +import org.eclipse.xtext.xbase.XNumberLiteral; +import org.eclipse.xtext.xbase.XbaseFactory; + +public class XNumberLiteralFixtures { + public static XNumberLiteral createXNumberLiteral() { + return XbaseFactory.eINSTANCE.createXNumberLiteral(); + } + + public static XNumberLiteral XNumberLiteralWithValue(String value) { + final XNumberLiteral numberLiteral = createXNumberLiteral(); + numberLiteral.setValue(value); + return numberLiteral; + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/CollectReferencesParserTest.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/CollectReferencesParserTest.java new file mode 100644 index 00000000..87d3df6b --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/CollectReferencesParserTest.java @@ -0,0 +1,51 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.xtext.common.types.JvmOperation; +import org.eclipse.xtext.xbase.XClosure; +import org.eclipse.xtext.xbase.XMemberFeatureCall; +import org.junit.jupiter.api.Test; + +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.ManualPatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.JvmOperationFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XBlockExpressionFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XClosureFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XMemberFeatureCallFixtures; + +import java.util.Optional; + +class CollectReferencesParserTest implements ExpressionParserTest { + private static final CollectReferencesParser parser = new CollectReferencesParser(); + + @Test + public void parseToList() throws UnsupportedReferenceExpressionException { + // given + final JvmOperation toListOperation = JvmOperationFixtures.createJvmOperation(); + toListOperation.setSimpleName("toList"); + + final XClosure closure = XClosureFixtures.createXClosure(); + closure.setExpression(XBlockExpressionFixtures.createXBlockExpression()); + + final XMemberFeatureCall memberFeatureCall = + XMemberFeatureCallFixtures.createXMemberFeatureCall(); + memberFeatureCall.setFeature(toListOperation); + memberFeatureCall.getMemberCallArguments().add(closure); + memberFeatureCall.setMemberCallTarget(exampleExpressionChain()); + + // when + final ManualPatternMatchingStrategy strategy = new ManualPatternMatchingStrategy(); + final Optional resultOptional = + parser.parse(strategy, memberFeatureCall); + + // then + assertTrue(resultOptional.isPresent()); + + final ReferenceOperator result = + assertExampleExpressionChainResultAndGetFollowingReferenceOperator( + resultOptional.get()); + assertNull(result); + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/ExpressionParserTest.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/ExpressionParserTest.java new file mode 100644 index 00000000..a1ab6662 --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/ExpressionParserTest.java @@ -0,0 +1,90 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.eclipse.xtext.xbase.XFeatureCall; +import org.eclipse.xtext.xbase.XMemberFeatureCall; + +import tools.vitruv.neojoin.expression_parser.model.FeatureCall; +import tools.vitruv.neojoin.expression_parser.model.FeatureInformation; +import tools.vitruv.neojoin.expression_parser.model.Map; +import tools.vitruv.neojoin.expression_parser.model.MemberFeatureCall; +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XFeatureCallFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XMemberFeatureCallFixtures; + +interface ExpressionParserTest { + default XMemberFeatureCall exampleExpressionChain() { + final XFeatureCall featureCall = + XFeatureCallFixtures.featureCall("some.feature.Call", "SomeCall"); + + final XMemberFeatureCall oneToOneMemberFeatureCall = + XMemberFeatureCallFixtures.oneToOneFieldXMemberFeatureCall( + "some.oneToOne.member", "MemberFeatureCall", "oneToOneReference"); + oneToOneMemberFeatureCall.setMemberCallTarget(featureCall); + + final XMemberFeatureCall oneToManyMemberFeatureCall = + XMemberFeatureCallFixtures.oneToManyFieldXMemberFeatureCall( + "some.oneToMany.member", "ManyMembers", "oneToManyRef"); + oneToManyMemberFeatureCall.setMemberCallTarget(oneToOneMemberFeatureCall); + + final XFeatureCall mapExpressionFeatureCall = + XFeatureCallFixtures.featureCallWithEmptyFormalParameter(); + final XMemberFeatureCall mapExpressionMemberFeatureCall = + XMemberFeatureCallFixtures.oneToOneFieldXMemberFeatureCall( + "some.test.package.SomeMapMemberFeatureCall", + "SomeMapMemberSimpleName", + "someMapMemberReference"); + mapExpressionMemberFeatureCall.setMemberCallTarget(mapExpressionFeatureCall); + final XMemberFeatureCall mapOperationMemberFeatureCall = + XMemberFeatureCallFixtures.mapOperationMemberFeatureCall( + mapExpressionMemberFeatureCall); + mapOperationMemberFeatureCall.setMemberCallTarget(oneToManyMemberFeatureCall); + + return mapOperationMemberFeatureCall; + } + + default ReferenceOperator assertExampleExpressionChainResultAndGetFollowingReferenceOperator( + ReferenceOperator referenceOperator) { + assertInstanceOf(FeatureCall.class, referenceOperator); + final FeatureCall firstFeatureCall = (FeatureCall) referenceOperator; + assertEquals("some.feature.Call", firstFeatureCall.getIdentifier()); + assertEquals("SomeCall", firstFeatureCall.getSimpleName()); + + final ReferenceOperator secondReferenceOperator = firstFeatureCall.getFollowingOperator(); + assertInstanceOf(MemberFeatureCall.class, secondReferenceOperator); + final MemberFeatureCall oneToOneMemberFeatureCall = + (MemberFeatureCall) secondReferenceOperator; + assertEquals( + new FeatureInformation( + "oneToOneReference", "MemberFeatureCall", "some.oneToOne.member"), + oneToOneMemberFeatureCall.getFeatureInformation()); + assertFalse(oneToOneMemberFeatureCall.isCollection()); + + final ReferenceOperator thirdReferenceOperator = + oneToOneMemberFeatureCall.getFollowingOperator(); + assertInstanceOf(MemberFeatureCall.class, thirdReferenceOperator); + final MemberFeatureCall oneToManyMemberFeatureCall = + (MemberFeatureCall) thirdReferenceOperator; + assertEquals( + new FeatureInformation("oneToManyRef", "ManyMembers", "some.oneToMany.member"), + oneToManyMemberFeatureCall.getFeatureInformation()); + assertTrue(oneToManyMemberFeatureCall.isCollection()); + + final ReferenceOperator fourthReferenceOperator = + oneToManyMemberFeatureCall.getFollowingOperator(); + assertInstanceOf(Map.class, fourthReferenceOperator); + final Map map = (Map) fourthReferenceOperator; + assertEquals( + new FeatureInformation( + "someMapMemberReference", + "SomeMapMemberSimpleName", + "some.test.package.SomeMapMemberFeatureCall"), + map.getFeatureInformation()); + + return map.getFollowingOperator(); + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FeatureCallParserTest.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FeatureCallParserTest.java new file mode 100644 index 00000000..7cfbe254 --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FeatureCallParserTest.java @@ -0,0 +1,74 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.eclipse.xtext.common.types.JvmFormalParameter; +import org.eclipse.xtext.common.types.JvmType; +import org.eclipse.xtext.common.types.JvmTypeReference; +import org.eclipse.xtext.xbase.XFeatureCall; +import org.junit.jupiter.api.Test; + +import tools.vitruv.neojoin.expression_parser.model.FeatureCall; +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.JvmFormalParameterFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.JvmTypeFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.JvmTypeReferenceFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XFeatureCallFixtures; + +import java.util.Optional; + +class FeatureCallParserTest { + private static final FeatureCallParser parser = new FeatureCallParser(); + + @Test + public void parseEmptyFeatureCall() { + // given + final JvmFormalParameter emptyFormalParameter = + JvmFormalParameterFixtures.createJvmFormalParameter(); + final XFeatureCall featureCall = XFeatureCallFixtures.createXFeatureCall(); + featureCall.setFeature(emptyFormalParameter); + + // when + final Optional resultOptional = parser.parse(null, featureCall); + + // then + assertTrue(resultOptional.isPresent()); + + final ReferenceOperator result = resultOptional.get(); + assertInstanceOf(FeatureCall.class, result); + + final FeatureCall resultFeatureCall = (FeatureCall) result; + assertNull(resultFeatureCall.getIdentifier()); + assertNull(resultFeatureCall.getSimpleName()); + assertNull(resultFeatureCall.getFollowingOperator()); + } + + @Test + public void parseNonEmptyFeatureCall() { + // given + final JvmType jvmType = JvmTypeFixtures.createJvmType("my.test.package.Car", "Car"); + final JvmTypeReference jvmTypeReference = + JvmTypeReferenceFixtures.createJvmTypeReference(jvmType); + final JvmFormalParameter formalParameter = + JvmFormalParameterFixtures.createJvmFormalParameter(jvmTypeReference); + final XFeatureCall featureCall = XFeatureCallFixtures.createXFeatureCall(); + featureCall.setFeature(formalParameter); + + // when + final Optional resultOptional = parser.parse(null, featureCall); + + // then + assertTrue(resultOptional.isPresent()); + + final ReferenceOperator result = resultOptional.get(); + assertInstanceOf(FeatureCall.class, result); + + final FeatureCall resultAsFeatureCall = (FeatureCall) result; + assertEquals("my.test.package.Car", resultAsFeatureCall.getIdentifier()); + assertEquals("Car", resultAsFeatureCall.getSimpleName()); + assertNull(resultAsFeatureCall.getFollowingOperator()); + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FilterParserTest.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FilterParserTest.java new file mode 100644 index 00000000..2e0716f9 --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FilterParserTest.java @@ -0,0 +1,63 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.xtext.common.types.JvmOperation; +import org.eclipse.xtext.xbase.XBinaryOperation; +import org.eclipse.xtext.xbase.XMemberFeatureCall; +import org.eclipse.xtext.xbase.XNumberLiteral; +import org.junit.jupiter.api.Test; + +import tools.vitruv.neojoin.expression_parser.model.ReferenceFilter; +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.model.predicate_expression.ComparisonOperator; +import tools.vitruv.neojoin.expression_parser.model.predicate_expression.ConstantValue; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.ManualPatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.JvmOperationFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XBinaryOperationFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XMemberFeatureCallFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XNumberLiteralFixtures; + +import java.util.Optional; + +class FilterParserTest implements ExpressionParserTest { + private static final FilterParser parser = new FilterParser(); + + @Test + public void parse() throws UnsupportedReferenceExpressionException { + // given + XMemberFeatureCall leftOperand = + XMemberFeatureCallFixtures.simpleFieldXMemberFeatureCall("someField"); + JvmOperation comparisonOperator = JvmOperationFixtures.createJvmOperation(); + comparisonOperator.setSimpleName("operator_lessThan"); + XNumberLiteral rightOperand = XNumberLiteralFixtures.XNumberLiteralWithValue("2.1"); + + XBinaryOperation binaryOperation = + XBinaryOperationFixtures.binaryOperation( + leftOperand, comparisonOperator, rightOperand); + + final XMemberFeatureCall filterMemberFeatureCall = + XMemberFeatureCallFixtures.filterOperationMemberFeatureCall(binaryOperation); + filterMemberFeatureCall.setMemberCallTarget(exampleExpressionChain()); + + // when + final ManualPatternMatchingStrategy strategy = new ManualPatternMatchingStrategy(); + final Optional resultOptional = + parser.parse(strategy, filterMemberFeatureCall); + + // then + assertTrue(resultOptional.isPresent()); + + final ReferenceOperator result = + assertExampleExpressionChainResultAndGetFollowingReferenceOperator( + resultOptional.get()); + assertInstanceOf(ReferenceFilter.class, result); + + final ReferenceFilter resultAsFilter = (ReferenceFilter) result; + assertEquals("someField", resultAsFilter.getFeature()); + assertEquals(ComparisonOperator.LessThan, resultAsFilter.getOperator()); + assertEquals(ConstantValue.of("2.1"), resultAsFilter.getConstantValue()); + assertNull(resultAsFilter.getFollowingOperator()); + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FindAnyParserTest.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FindAnyParserTest.java new file mode 100644 index 00000000..51930c82 --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FindAnyParserTest.java @@ -0,0 +1,87 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.xtext.common.types.JvmOperation; +import org.eclipse.xtext.xbase.XClosure; +import org.eclipse.xtext.xbase.XMemberFeatureCall; +import org.junit.jupiter.api.Test; + +import tools.vitruv.neojoin.expression_parser.model.FindAny; +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.ManualPatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.JvmOperationFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XBlockExpressionFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XClosureFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XMemberFeatureCallFixtures; + +import java.util.Optional; + +class FindAnyParserTest implements ExpressionParserTest { + private static final FindAnyParser parser = new FindAnyParser(); + + @Test + public void parseFindFirst() throws UnsupportedReferenceExpressionException { + // given + final JvmOperation findFirstOperation = JvmOperationFixtures.createJvmOperation(); + findFirstOperation.setSimpleName("findFirst"); + + final XClosure closure = XClosureFixtures.createXClosure(); + closure.setExpression(XBlockExpressionFixtures.createXBlockExpression()); + + final XMemberFeatureCall memberFeatureCall = + XMemberFeatureCallFixtures.createXMemberFeatureCall(); + memberFeatureCall.setFeature(findFirstOperation); + memberFeatureCall.getMemberCallArguments().add(closure); + memberFeatureCall.setMemberCallTarget(exampleExpressionChain()); + + // when + final ManualPatternMatchingStrategy strategy = new ManualPatternMatchingStrategy(); + final Optional resultOptional = + parser.parse(strategy, memberFeatureCall); + + // then + assertTrue(resultOptional.isPresent()); + + final ReferenceOperator result = + assertExampleExpressionChainResultAndGetFollowingReferenceOperator( + resultOptional.get()); + assertInstanceOf(FindAny.class, result); + + final FindAny resultAsFindAny = (FindAny) result; + assertNull(resultAsFindAny.getFollowingOperator()); + } + + @Test + public void parseFindLast() throws UnsupportedReferenceExpressionException { + // given + final JvmOperation findLastOperation = JvmOperationFixtures.createJvmOperation(); + findLastOperation.setSimpleName("findLast"); + + final XClosure closure = XClosureFixtures.createXClosure(); + closure.setExpression(XBlockExpressionFixtures.createXBlockExpression()); + + final XMemberFeatureCall memberFeatureCall = + XMemberFeatureCallFixtures.createXMemberFeatureCall(); + memberFeatureCall.setFeature(findLastOperation); + memberFeatureCall.getMemberCallArguments().add(closure); + memberFeatureCall.setMemberCallTarget(exampleExpressionChain()); + + // when + final ManualPatternMatchingStrategy strategy = new ManualPatternMatchingStrategy(); + final Optional resultOptional = + parser.parse(strategy, memberFeatureCall); + + // then + assertTrue(resultOptional.isPresent()); + + final ReferenceOperator result = + assertExampleExpressionChainResultAndGetFollowingReferenceOperator( + resultOptional.get()); + assertInstanceOf(FindAny.class, result); + + final FindAny resultAsFindAny = (FindAny) result; + assertNull(resultAsFindAny.getFollowingOperator()); + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FlatMapParserTest.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FlatMapParserTest.java new file mode 100644 index 00000000..3ba0ac11 --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FlatMapParserTest.java @@ -0,0 +1,145 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.xtext.xbase.XFeatureCall; +import org.eclipse.xtext.xbase.XMemberFeatureCall; +import org.junit.jupiter.api.Test; + +import tools.vitruv.neojoin.expression_parser.model.FeatureInformation; +import tools.vitruv.neojoin.expression_parser.model.FlatMap; +import tools.vitruv.neojoin.expression_parser.model.Map; +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.ManualPatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XFeatureCallFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XMemberFeatureCallFixtures; + +import java.util.Optional; + +class FlatMapParserTest implements ExpressionParserTest { + private static final FlatMapParser parser = new FlatMapParser(); + + @Test + public void parseWithSimpleLambdaExpression() throws UnsupportedReferenceExpressionException { + // given + final XFeatureCall flatMapExpressionFeatureCall = + XFeatureCallFixtures.featureCallWithEmptyFormalParameter(); + + final XMemberFeatureCall flatMapExpressionMemberFeatureCall = + XMemberFeatureCallFixtures.oneToManyFieldXMemberFeatureCall( + "some.test.package.SomeMember", + "SomeMemberSimpleName", + "someMemberReference"); + flatMapExpressionMemberFeatureCall.setMemberCallTarget(flatMapExpressionFeatureCall); + + final XMemberFeatureCall flatMapMemberFeatureCall = + XMemberFeatureCallFixtures.flatMapOperationMemberFeatureCall( + flatMapExpressionMemberFeatureCall); + flatMapMemberFeatureCall.setMemberCallTarget(exampleExpressionChain()); + + // when + final ManualPatternMatchingStrategy strategy = new ManualPatternMatchingStrategy(); + final Optional resultOptional = + parser.parse(strategy, flatMapMemberFeatureCall); + + // then + assertTrue(resultOptional.isPresent()); + + final ReferenceOperator result = + assertExampleExpressionChainResultAndGetFollowingReferenceOperator( + resultOptional.get()); + assertInstanceOf(FlatMap.class, result); + + final FlatMap resultAsFlatMap = (FlatMap) result; + assertEquals( + new FeatureInformation( + "someMemberReference", + "SomeMemberSimpleName", + "some.test.package.SomeMember"), + resultAsFlatMap.getFeatureInformation()); + assertNull(resultAsFlatMap.getFollowingOperator()); + } + + @Test + public void parseWithOneToOneAndOneToManyMemberFeatureCalls() + throws UnsupportedReferenceExpressionException { + // given + final XFeatureCall flatMapExpressionFeatureCall = + XFeatureCallFixtures.featureCallWithEmptyFormalParameter(); + + final XMemberFeatureCall flatMapExpressionOneToOneMemberFeatureCall = + XMemberFeatureCallFixtures.oneToOneFieldXMemberFeatureCall( + "some.oneToOne.package.SomeMember", + "SomeMemberSimpleName", + "someMemberReference"); + flatMapExpressionOneToOneMemberFeatureCall.setMemberCallTarget( + flatMapExpressionFeatureCall); + + final XMemberFeatureCall flatMapExpressionDeeperMemberFeatureCall = + XMemberFeatureCallFixtures.oneToOneFieldXMemberFeatureCall( + "some.oneToOne.package.SomeDeeperMember", + "SomeDeeperMemberSimpleName", + "someDeeperMemberReference"); + flatMapExpressionDeeperMemberFeatureCall.setMemberCallTarget( + flatMapExpressionOneToOneMemberFeatureCall); + + final XMemberFeatureCall flatMapExpressionDeepestMemberFeatureCall = + XMemberFeatureCallFixtures.oneToManyFieldXMemberFeatureCall( + "some.oneToMany.package.SomeDeepestMember", + "SomeDeepestMemberSimpleName", + "someDeepestMemberReference"); + flatMapExpressionDeepestMemberFeatureCall.setMemberCallTarget( + flatMapExpressionDeeperMemberFeatureCall); + + final XMemberFeatureCall flatMapMemberFeatureCall = + XMemberFeatureCallFixtures.flatMapOperationMemberFeatureCall( + flatMapExpressionDeepestMemberFeatureCall); + flatMapMemberFeatureCall.setMemberCallTarget(exampleExpressionChain()); + + // when + final ManualPatternMatchingStrategy strategy = new ManualPatternMatchingStrategy(); + final Optional resultOptional = + parser.parse(strategy, flatMapMemberFeatureCall); + + // then + assertTrue(resultOptional.isPresent()); + + final ReferenceOperator result = + assertExampleExpressionChainResultAndGetFollowingReferenceOperator( + resultOptional.get()); + assertInstanceOf(Map.class, result); + + final Map firstMapOperator = (Map) result; + assertEquals( + new FeatureInformation( + "someMemberReference", + "SomeMemberSimpleName", + "some.oneToOne.package.SomeMember"), + firstMapOperator.getFeatureInformation()); + + final ReferenceOperator secondOperator = firstMapOperator.getFollowingOperator(); + assertInstanceOf(Map.class, secondOperator); + + final Map secondMapOperator = (Map) secondOperator; + assertEquals( + new FeatureInformation( + "someDeeperMemberReference", + "SomeDeeperMemberSimpleName", + "some.oneToOne.package.SomeDeeperMember"), + secondMapOperator.getFeatureInformation()); + + final ReferenceOperator thirdOperator = secondMapOperator.getFollowingOperator(); + assertInstanceOf(FlatMap.class, thirdOperator); + + final FlatMap thirdFlatMapOperator = (FlatMap) thirdOperator; + assertEquals( + new FeatureInformation( + "someDeepestMemberReference", + "SomeDeepestMemberSimpleName", + "some.oneToMany.package.SomeDeepestMember"), + thirdFlatMapOperator.getFeatureInformation()); + + assertNull(thirdFlatMapOperator.getFollowingOperator()); + } +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParserTest.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParserTest.java new file mode 100644 index 00000000..dadcccde --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParserTest.java @@ -0,0 +1,226 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.xtext.xbase.XFeatureCall; +import org.eclipse.xtext.xbase.XMemberFeatureCall; +import org.junit.jupiter.api.Test; + +import tools.vitruv.neojoin.expression_parser.model.FeatureInformation; +import tools.vitruv.neojoin.expression_parser.model.FlatMap; +import tools.vitruv.neojoin.expression_parser.model.Map; +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.ManualPatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XFeatureCallFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XMemberFeatureCallFixtures; + +import java.util.Optional; + +class MapParserTest implements ExpressionParserTest { + private static final MapParser parser = new MapParser(); + + @Test + public void parseWithSimpleLambdaExpression() throws UnsupportedReferenceExpressionException { + // given + final XFeatureCall mapExpressionFeatureCall = + XFeatureCallFixtures.featureCallWithEmptyFormalParameter(); + + final XMemberFeatureCall mapExpressionMemberFeatureCall = + XMemberFeatureCallFixtures.oneToOneFieldXMemberFeatureCall( + "some.test.package.SomeMember", + "SomeMemberSimpleName", + "someMemberReference"); + mapExpressionMemberFeatureCall.setMemberCallTarget(mapExpressionFeatureCall); + + final XMemberFeatureCall mapMemberFeatureCall = + XMemberFeatureCallFixtures.mapOperationMemberFeatureCall( + mapExpressionMemberFeatureCall); + mapMemberFeatureCall.setMemberCallTarget(exampleExpressionChain()); + + // when + final ManualPatternMatchingStrategy strategy = new ManualPatternMatchingStrategy(); + final Optional resultOptional = + parser.parse(strategy, mapMemberFeatureCall); + + // then + assertTrue(resultOptional.isPresent()); + + final ReferenceOperator result = + assertExampleExpressionChainResultAndGetFollowingReferenceOperator( + resultOptional.get()); + assertInstanceOf(Map.class, result); + + final Map resultAsMap = (Map) result; + assertEquals( + new FeatureInformation( + "someMemberReference", + "SomeMemberSimpleName", + "some.test.package.SomeMember"), + resultAsMap.getFeatureInformation()); + assertNull(resultAsMap.getFollowingOperator()); + } + + @Test + public void parseWithMultipleOneToOneMemberFeatureCalls() + throws UnsupportedReferenceExpressionException { + // given + final XFeatureCall mapExpressionFeatureCall = + XFeatureCallFixtures.featureCallWithEmptyFormalParameter(); + + final XMemberFeatureCall mapExpressionMemberFeatureCall = + XMemberFeatureCallFixtures.oneToOneFieldXMemberFeatureCall( + "some.test.package.SomeMember", + "SomeMemberSimpleName", + "someMemberReference"); + mapExpressionMemberFeatureCall.setMemberCallTarget(mapExpressionFeatureCall); + + final XMemberFeatureCall mapExpressionDeeperMemberFeatureCall = + XMemberFeatureCallFixtures.oneToOneFieldXMemberFeatureCall( + "some.other.package.SomeDeeperMember", + "SomeDeeperMemberSimpleName", + "someDeeperMemberReference"); + mapExpressionDeeperMemberFeatureCall.setMemberCallTarget(mapExpressionMemberFeatureCall); + + final XMemberFeatureCall mapExpressionDeepestMemberFeatureCall = + XMemberFeatureCallFixtures.oneToOneFieldXMemberFeatureCall( + "some.deep.package.SomeDeepestMember", + "SomeDeepestMemberSimpleName", + "someDeepestMemberReference"); + mapExpressionDeepestMemberFeatureCall.setMemberCallTarget( + mapExpressionDeeperMemberFeatureCall); + + final XMemberFeatureCall mapMemberFeatureCall = + XMemberFeatureCallFixtures.mapOperationMemberFeatureCall( + mapExpressionDeepestMemberFeatureCall); + mapMemberFeatureCall.setMemberCallTarget(exampleExpressionChain()); + + // when + final ManualPatternMatchingStrategy strategy = new ManualPatternMatchingStrategy(); + final Optional resultOptional = + parser.parse(strategy, mapMemberFeatureCall); + + // then + assertTrue(resultOptional.isPresent()); + + final ReferenceOperator result = + assertExampleExpressionChainResultAndGetFollowingReferenceOperator( + resultOptional.get()); + assertInstanceOf(Map.class, result); + + final Map firstMapOperator = (Map) result; + assertEquals( + new FeatureInformation( + "someMemberReference", + "SomeMemberSimpleName", + "some.test.package.SomeMember"), + firstMapOperator.getFeatureInformation()); + + final ReferenceOperator secondOperator = firstMapOperator.getFollowingOperator(); + assertInstanceOf(Map.class, secondOperator); + + final Map secondMapOperator = (Map) secondOperator; + assertEquals( + new FeatureInformation( + "someDeeperMemberReference", + "SomeDeeperMemberSimpleName", + "some.other.package.SomeDeeperMember"), + secondMapOperator.getFeatureInformation()); + + final ReferenceOperator thirdOperator = secondMapOperator.getFollowingOperator(); + assertInstanceOf(Map.class, thirdOperator); + + final Map thirdMapOperator = (Map) thirdOperator; + assertEquals( + new FeatureInformation( + "someDeepestMemberReference", + "SomeDeepestMemberSimpleName", + "some.deep.package.SomeDeepestMember"), + thirdMapOperator.getFeatureInformation()); + + assertNull(thirdMapOperator.getFollowingOperator()); + } + + @Test + public void parseWithOneToOneAndOneToManyMemberFeatureCalls() + throws UnsupportedReferenceExpressionException { + // given + final XFeatureCall mapExpressionFeatureCall = + XFeatureCallFixtures.featureCallWithEmptyFormalParameter(); + + final XMemberFeatureCall mapExpressionOneToManyMemberFeatureCall = + XMemberFeatureCallFixtures.oneToManyFieldXMemberFeatureCall( + "some.oneToMany.package.SomeMember", + "SomeMemberSimpleName", + "someMemberReference"); + mapExpressionOneToManyMemberFeatureCall.setMemberCallTarget(mapExpressionFeatureCall); + + final XMemberFeatureCall mapExpressionDeeperMemberFeatureCall = + XMemberFeatureCallFixtures.oneToOneFieldXMemberFeatureCall( + "some.oneToOne.package.SomeDeeperMember", + "SomeDeeperMemberSimpleName", + "someDeeperMemberReference"); + mapExpressionDeeperMemberFeatureCall.setMemberCallTarget( + mapExpressionOneToManyMemberFeatureCall); + + final XMemberFeatureCall mapExpressionDeepestMemberFeatureCall = + XMemberFeatureCallFixtures.oneToManyFieldXMemberFeatureCall( + "some.oneToMany.package.SomeDeepestMember", + "SomeDeepestMemberSimpleName", + "someDeepestMemberReference"); + mapExpressionDeepestMemberFeatureCall.setMemberCallTarget( + mapExpressionDeeperMemberFeatureCall); + + final XMemberFeatureCall mapMemberFeatureCall = + XMemberFeatureCallFixtures.mapOperationMemberFeatureCall( + mapExpressionDeepestMemberFeatureCall); + mapMemberFeatureCall.setMemberCallTarget(exampleExpressionChain()); + + // when + final ManualPatternMatchingStrategy strategy = new ManualPatternMatchingStrategy(); + final Optional resultOptional = + parser.parse(strategy, mapMemberFeatureCall); + + // then + assertTrue(resultOptional.isPresent()); + + final ReferenceOperator result = + assertExampleExpressionChainResultAndGetFollowingReferenceOperator( + resultOptional.get()); + assertInstanceOf(FlatMap.class, result); + + final FlatMap firstFlatMapOperator = (FlatMap) result; + assertEquals( + new FeatureInformation( + "someMemberReference", + "SomeMemberSimpleName", + "some.oneToMany.package.SomeMember"), + firstFlatMapOperator.getFeatureInformation()); + + final ReferenceOperator secondOperator = firstFlatMapOperator.getFollowingOperator(); + assertInstanceOf(Map.class, secondOperator); + + final Map secondMapOperator = (Map) secondOperator; + assertEquals( + new FeatureInformation( + "someDeeperMemberReference", + "SomeDeeperMemberSimpleName", + "some.oneToOne.package.SomeDeeperMember"), + secondMapOperator.getFeatureInformation()); + + final ReferenceOperator thirdOperator = secondMapOperator.getFollowingOperator(); + assertInstanceOf(FlatMap.class, thirdOperator); + + final FlatMap thirdFlatMapOperator = (FlatMap) thirdOperator; + assertEquals( + new FeatureInformation( + "someDeepestMemberReference", + "SomeDeepestMemberSimpleName", + "some.oneToMany.package.SomeDeepestMember"), + thirdFlatMapOperator.getFeatureInformation()); + + assertNull(thirdFlatMapOperator.getFollowingOperator()); + } + + // TODO: Nested expressions (multiple layers) and filter operator +} diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MemberFeatureCallParserTest.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MemberFeatureCallParserTest.java new file mode 100644 index 00000000..d439b47a --- /dev/null +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MemberFeatureCallParserTest.java @@ -0,0 +1,136 @@ +package tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.parsers; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.xtext.common.types.JvmField; +import org.eclipse.xtext.common.types.JvmParameterizedTypeReference; +import org.eclipse.xtext.common.types.JvmType; +import org.eclipse.xtext.common.types.JvmTypeReference; +import org.eclipse.xtext.xbase.XMemberFeatureCall; +import org.junit.jupiter.api.Test; + +import tools.vitruv.neojoin.expression_parser.model.FeatureInformation; +import tools.vitruv.neojoin.expression_parser.model.MemberFeatureCall; +import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.ManualPatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.JvmFieldFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.JvmTypeFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.JvmTypeReferenceFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XMemberFeatureCallFixtures; + +import java.util.Optional; + +class MemberFeatureCallParserTest implements ExpressionParserTest { + private static final MemberFeatureCallParser parser = new MemberFeatureCallParser(); + + @Test + public void parseOneToOneField() throws UnsupportedReferenceExpressionException { + // given + final JvmType jvmType = + JvmTypeFixtures.createJvmType( + "my.test.package.SomeChildField", "SomeChildFieldSimpleName"); + final JvmTypeReference typeReference = + JvmTypeReferenceFixtures.createJvmTypeReference(jvmType); + + final JvmField jvmField = JvmFieldFixtures.createJvmField(); + jvmField.setSimpleName("someChildFieldReference"); + jvmField.setType(typeReference); + + final XMemberFeatureCall memberFeatureCall = + XMemberFeatureCallFixtures.createXMemberFeatureCall(); + memberFeatureCall.setFeature(jvmField); + memberFeatureCall.setMemberCallTarget(exampleExpressionChain()); + + // when + final ManualPatternMatchingStrategy strategy = new ManualPatternMatchingStrategy(); + final Optional resultOptional = + parser.parse(strategy, memberFeatureCall); + + // then + assertTrue(resultOptional.isPresent()); + + final ReferenceOperator result = + assertExampleExpressionChainResultAndGetFollowingReferenceOperator( + resultOptional.get()); + assertInstanceOf(MemberFeatureCall.class, result); + + final MemberFeatureCall resultAsMemberFeatureCall = (MemberFeatureCall) result; + assertEquals( + new FeatureInformation( + "someChildFieldReference", + "SomeChildFieldSimpleName", + "my.test.package.SomeChildField"), + resultAsMemberFeatureCall.getFeatureInformation()); + assertFalse(resultAsMemberFeatureCall.isCollection()); + assertNull(resultAsMemberFeatureCall.getFollowingOperator()); + } + + @Test + public void parseListField() throws UnsupportedReferenceExpressionException { + // given + final JvmType listJvmType = JvmTypeFixtures.createJvmType("java.util.List", null); + final JvmParameterizedTypeReference typeReference = + JvmTypeReferenceFixtures.createJvmParameterizedTypeReference(); + typeReference.setType(listJvmType); + + final JvmType innerJvmType = + JvmTypeFixtures.createJvmType( + "my.test.package.SomeChildField", "SomeChildFieldSimpleName"); + final JvmTypeReference innerTypeReference = + JvmTypeReferenceFixtures.createJvmTypeReference(innerJvmType); + typeReference.getArguments().add(innerTypeReference); + + final JvmField jvmField = JvmFieldFixtures.createJvmField(); + jvmField.setSimpleName("someChildFieldReference"); + jvmField.setType(typeReference); + + final XMemberFeatureCall memberFeatureCall = + XMemberFeatureCallFixtures.createXMemberFeatureCall(); + memberFeatureCall.setFeature(jvmField); + memberFeatureCall.setMemberCallTarget(exampleExpressionChain()); + + // when + final ManualPatternMatchingStrategy strategy = new ManualPatternMatchingStrategy(); + final Optional resultOptional = + parser.parse(strategy, memberFeatureCall); + + // then + assertTrue(resultOptional.isPresent()); + + final ReferenceOperator result = + assertExampleExpressionChainResultAndGetFollowingReferenceOperator( + resultOptional.get()); + assertInstanceOf(MemberFeatureCall.class, result); + + final MemberFeatureCall resultAsMemberFeatureCall = (MemberFeatureCall) result; + assertEquals( + new FeatureInformation( + "someChildFieldReference", + "SomeChildFieldSimpleName", + "my.test.package.SomeChildField"), + resultAsMemberFeatureCall.getFeatureInformation()); + assertTrue(resultAsMemberFeatureCall.isCollection()); + assertNull(resultAsMemberFeatureCall.getFollowingOperator()); + } + + @Test + public void shouldThrowWhenTypeIsNull() { + // given + final JvmField jvmField = JvmFieldFixtures.createJvmField(); + jvmField.setSimpleName("someChildFieldReference"); + jvmField.setType(null); + + final XMemberFeatureCall memberFeatureCall = + XMemberFeatureCallFixtures.createXMemberFeatureCall(); + memberFeatureCall.setFeature(jvmField); + + // when & then + final ManualPatternMatchingStrategy strategy = new ManualPatternMatchingStrategy(); + UnsupportedReferenceExpressionException exception = + assertThrows( + UnsupportedReferenceExpressionException.class, + () -> parser.parse(strategy, memberFeatureCall)); + assertEquals("The MemberFeatureCall couldn't be parsed", exception.getMessage()); + } +} diff --git a/lang/expression-parser/pom.xml b/lang/expression-parser/pom.xml new file mode 100644 index 00000000..316074bd --- /dev/null +++ b/lang/expression-parser/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + tools.vitruv + tools.vitruv.neojoin + 0.1.0-SNAPSHOT + + + tools.vitruv.neojoin.expression_parser + pom + + Expression Parser + + + + model + parser + + From 99a107f0c93e1f5754814907bce8a095a08fa7c0 Mon Sep 17 00:00:00 2001 From: nilslambertz <62618726+nilslambertz@users.noreply.github.com> Date: Sat, 11 Apr 2026 22:22:03 +0200 Subject: [PATCH 2/4] Improvements --- .../vitruv/neojoin/expression_parser/model/FlatMap.java | 2 +- .../model/predicate_expression/ConstantValue.java | 2 +- lang/pom.xml | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FlatMap.java b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FlatMap.java index 2eab98f0..2439c39a 100644 --- a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FlatMap.java +++ b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/FlatMap.java @@ -8,7 +8,7 @@ /** * A FlatMap represents mapping a parent object to some children along a one-to-many - * reference. It contains information about the reference and the child type + * reference and flattening the result. It contains information about the reference and the child type * *

An example expression may look like * diff --git a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ConstantValue.java b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ConstantValue.java index e316ba4e..16d145b0 100644 --- a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ConstantValue.java +++ b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ConstantValue.java @@ -12,7 +12,7 @@ public static ConstantValue String(String value) { } public static ConstantValue Boolean(boolean isTrue) { - return ConstantValue.of(isTrue ? "true" : "false"); + return ConstantValue.of(Boolean.toString(isTrue)); } public static ConstantValue of(String value) { diff --git a/lang/pom.xml b/lang/pom.xml index 3c1956c4..82eb5fc0 100644 --- a/lang/pom.xml +++ b/lang/pom.xml @@ -48,6 +48,7 @@ backend-tgg frontend model + expression-parser p2wrappers @@ -99,6 +100,12 @@ assertj-core 3.27.7 + + org.projectlombok + lombok + 1.18.38 + provided + org.eclipse.emf org.eclipse.emf.common From 666daba6d59a2930732b72a167ed168aa8fe7fda Mon Sep 17 00:00:00 2001 From: nilslambertz <62618726+nilslambertz@users.noreply.github.com> Date: Sat, 11 Apr 2026 22:36:44 +0200 Subject: [PATCH 3/4] Improvements --- .../model/predicate_expression/ConstantValue.java | 4 ++-- .../exception/UnsupportedReferenceExpressionException.java | 2 +- .../manual_pattern_matching/parsers/FilterParser.java | 4 ++-- .../utils/PredicateExpressionUtils.java | 4 ++-- .../manual_pattern_matching/parsers/MapParserTest.java | 2 -- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ConstantValue.java b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ConstantValue.java index 16d145b0..5df71b1f 100644 --- a/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ConstantValue.java +++ b/lang/expression-parser/model/src/main/java/tools/vitruv/neojoin/expression_parser/model/predicate_expression/ConstantValue.java @@ -7,11 +7,11 @@ public class ConstantValue { String value; boolean isString; - public static ConstantValue String(String value) { + public static ConstantValue fromString(String value) { return new ConstantValue(value, true); } - public static ConstantValue Boolean(boolean isTrue) { + public static ConstantValue fromBoolean(boolean isTrue) { return ConstantValue.of(Boolean.toString(isTrue)); } diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/exception/UnsupportedReferenceExpressionException.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/exception/UnsupportedReferenceExpressionException.java index 1f70178f..cc30190f 100644 --- a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/exception/UnsupportedReferenceExpressionException.java +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/exception/UnsupportedReferenceExpressionException.java @@ -13,6 +13,6 @@ public UnsupportedReferenceExpressionException(String message) { } public UnsupportedReferenceExpressionException(String message, XExpression expression) { - super(String.format(message + ". The expression was %s", expression)); + super(String.format("%s. The expression was %s", message, expression)); } } diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FilterParser.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FilterParser.java index 507ec74d..08443884 100644 --- a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FilterParser.java +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FilterParser.java @@ -19,7 +19,7 @@ import java.util.Optional; public class FilterParser implements ReferenceOperatorParser { - private static final String FILTER_MAP_OPERATION_SIMPLE_NAME = "filter"; + private static final String FILTER_OPERATION_SIMPLE_NAME = "filter"; public Optional parse( PatternMatchingStrategy strategy, XExpression expression) @@ -58,7 +58,7 @@ private static boolean isFilterOperation(XMemberFeatureCall featureCall) { .map(XAbstractFeatureCall::getFeature) .flatMap(CastingUtils::asJvmOperation) .map(JvmIdentifiableElement::getSimpleName) - .map(FILTER_MAP_OPERATION_SIMPLE_NAME::equals) + .map(FILTER_OPERATION_SIMPLE_NAME::equals) .orElse(false); } } diff --git a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/PredicateExpressionUtils.java b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/PredicateExpressionUtils.java index 9738eeaf..50e378b3 100644 --- a/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/PredicateExpressionUtils.java +++ b/lang/expression-parser/parser/src/main/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/utils/PredicateExpressionUtils.java @@ -87,9 +87,9 @@ private static Optional getConstantExpressionAsString(XExpression if (expression instanceof XNumberLiteral numberLiteral) { return Optional.of(ConstantValue.of(numberLiteral.getValue())); } else if (expression instanceof XStringLiteral stringLiteral) { - return Optional.of(ConstantValue.String(stringLiteral.getValue())); + return Optional.of(ConstantValue.fromString(stringLiteral.getValue())); } else if (expression instanceof XBooleanLiteral booleanLiteral) { - return Optional.of(ConstantValue.Boolean(booleanLiteral.isIsTrue())); + return Optional.of(ConstantValue.fromBoolean(booleanLiteral.isIsTrue())); } return Optional.empty(); } diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParserTest.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParserTest.java index dadcccde..ea8afa71 100644 --- a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParserTest.java +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParserTest.java @@ -221,6 +221,4 @@ public void parseWithOneToOneAndOneToManyMemberFeatureCalls() assertNull(thirdFlatMapOperator.getFollowingOperator()); } - - // TODO: Nested expressions (multiple layers) and filter operator } From 35fea0c189efa886b8e6a261e9a665283a229290 Mon Sep 17 00:00:00 2001 From: nilslambertz <62618726+nilslambertz@users.noreply.github.com> Date: Tue, 14 Apr 2026 20:58:14 +0200 Subject: [PATCH 4/4] Fix tests --- .../parsers/FlatMapParserTest.java | 117 ++++++++++++++++++ .../parsers/MapParserTest.java | 82 ------------ 2 files changed, 117 insertions(+), 82 deletions(-) diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FlatMapParserTest.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FlatMapParserTest.java index 3ba0ac11..c34b1642 100644 --- a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FlatMapParserTest.java +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/FlatMapParserTest.java @@ -2,18 +2,27 @@ import static org.junit.jupiter.api.Assertions.*; +import org.eclipse.xtext.common.types.JvmOperation; +import org.eclipse.xtext.xbase.XBinaryOperation; import org.eclipse.xtext.xbase.XFeatureCall; import org.eclipse.xtext.xbase.XMemberFeatureCall; +import org.eclipse.xtext.xbase.XNumberLiteral; import org.junit.jupiter.api.Test; import tools.vitruv.neojoin.expression_parser.model.FeatureInformation; import tools.vitruv.neojoin.expression_parser.model.FlatMap; import tools.vitruv.neojoin.expression_parser.model.Map; +import tools.vitruv.neojoin.expression_parser.model.ReferenceFilter; import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; +import tools.vitruv.neojoin.expression_parser.model.predicate_expression.ComparisonOperator; +import tools.vitruv.neojoin.expression_parser.model.predicate_expression.ConstantValue; import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.ManualPatternMatchingStrategy; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.JvmOperationFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XBinaryOperationFixtures; import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XFeatureCallFixtures; import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XMemberFeatureCallFixtures; +import tools.vitruv.neojoin.expression_parser.parser.strategy.manual_pattern_matching.fixtures.XNumberLiteralFixtures; import java.util.Optional; @@ -142,4 +151,112 @@ public void parseWithOneToOneAndOneToManyMemberFeatureCalls() assertNull(thirdFlatMapOperator.getFollowingOperator()); } + + @Test + public void parseWithNestedFlatMapContainingFilterAndMap() + throws UnsupportedReferenceExpressionException { + // given: outer flatMap's closure body is itself another flatMap operation whose inner + // closure contains a filter and a map, i.e. + // `flatMap(x -> x.flatMap(y -> y.someList.filter(someField < 2.1).map(z -> z.nestedRef)))` + + // Innermost closure body for the nested map: `z.nestedRef` + final XFeatureCall nestedMapInnerFeatureCall = + XFeatureCallFixtures.featureCallWithEmptyFormalParameter(); + + final XMemberFeatureCall nestedMapInnerMemberFeatureCall = + XMemberFeatureCallFixtures.oneToOneFieldXMemberFeatureCall( + "some.nested.package.NestedMember", + "NestedMemberSimpleName", + "nestedMemberReference"); + nestedMapInnerMemberFeatureCall.setMemberCallTarget(nestedMapInnerFeatureCall); + + // Filter predicate `someField < 2.1` used inside the inner flatMap's closure + final XMemberFeatureCall filterLeftOperand = + XMemberFeatureCallFixtures.simpleFieldXMemberFeatureCall("someField"); + final JvmOperation comparisonOperator = JvmOperationFixtures.createJvmOperation(); + comparisonOperator.setSimpleName("operator_lessThan"); + final XNumberLiteral filterRightOperand = + XNumberLiteralFixtures.XNumberLiteralWithValue("2.1"); + final XBinaryOperation binaryOperation = + XBinaryOperationFixtures.binaryOperation( + filterLeftOperand, comparisonOperator, filterRightOperand); + + // Inner flatMap closure body: `y.someList.filter(...).map(z -> z.nestedRef)` + final XFeatureCall innerFlatMapLambdaParameter = + XFeatureCallFixtures.featureCallWithEmptyFormalParameter(); + + final XMemberFeatureCall someListMemberFeatureCall = + XMemberFeatureCallFixtures.oneToManyFieldXMemberFeatureCall( + "some.nested.package.SomeList", + "SomeListSimpleName", + "someListReference"); + someListMemberFeatureCall.setMemberCallTarget(innerFlatMapLambdaParameter); + + final XMemberFeatureCall innerFilterMemberFeatureCall = + XMemberFeatureCallFixtures.filterOperationMemberFeatureCall(binaryOperation); + innerFilterMemberFeatureCall.setMemberCallTarget(someListMemberFeatureCall); + + final XMemberFeatureCall innerMapMemberFeatureCall = + XMemberFeatureCallFixtures.mapOperationMemberFeatureCall( + nestedMapInnerMemberFeatureCall); + innerMapMemberFeatureCall.setMemberCallTarget(innerFilterMemberFeatureCall); + + // Inner flatMap call: `x.flatMap(y -> ...)` — its target is the outer flatMap lambda + // parameter `x`, which is itself treated as a collection that flatMap operates on + final XFeatureCall outerFlatMapLambdaParameter = + XFeatureCallFixtures.featureCallWithEmptyFormalParameter(); + + final XMemberFeatureCall innerFlatMapMemberFeatureCall = + XMemberFeatureCallFixtures.flatMapOperationMemberFeatureCall( + innerMapMemberFeatureCall); + innerFlatMapMemberFeatureCall.setMemberCallTarget(outerFlatMapLambdaParameter); + + // Outer flatMap call whose closure body is the inner flatMap call above + final XMemberFeatureCall flatMapMemberFeatureCall = + XMemberFeatureCallFixtures.flatMapOperationMemberFeatureCall( + innerFlatMapMemberFeatureCall); + flatMapMemberFeatureCall.setMemberCallTarget(exampleExpressionChain()); + + // when + final ManualPatternMatchingStrategy strategy = new ManualPatternMatchingStrategy(); + final Optional resultOptional = + parser.parse(strategy, flatMapMemberFeatureCall); + + // then + assertTrue(resultOptional.isPresent()); + + final ReferenceOperator result = + assertExampleExpressionChainResultAndGetFollowingReferenceOperator( + resultOptional.get()); + assertInstanceOf(FlatMap.class, result); + + final FlatMap flatMapOperator = (FlatMap) result; + assertEquals( + new FeatureInformation( + "someListReference", + "SomeListSimpleName", + "some.nested.package.SomeList"), + flatMapOperator.getFeatureInformation()); + + final ReferenceOperator secondOperator = flatMapOperator.getFollowingOperator(); + assertInstanceOf(ReferenceFilter.class, secondOperator); + + final ReferenceFilter filterOperator = (ReferenceFilter) secondOperator; + assertEquals("someField", filterOperator.getFeature()); + assertEquals(ComparisonOperator.LessThan, filterOperator.getOperator()); + assertEquals(ConstantValue.of("2.1"), filterOperator.getConstantValue()); + + final ReferenceOperator thirdOperator = filterOperator.getFollowingOperator(); + assertInstanceOf(Map.class, thirdOperator); + + final Map mapOperator = (Map) thirdOperator; + assertEquals( + new FeatureInformation( + "nestedMemberReference", + "NestedMemberSimpleName", + "some.nested.package.NestedMember"), + mapOperator.getFeatureInformation()); + + assertNull(mapOperator.getFollowingOperator()); + } } diff --git a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParserTest.java b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParserTest.java index ea8afa71..314d1eb3 100644 --- a/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParserTest.java +++ b/lang/expression-parser/parser/src/test/java/tools/vitruv/neojoin/expression_parser/parser/strategy/manual_pattern_matching/parsers/MapParserTest.java @@ -7,7 +7,6 @@ import org.junit.jupiter.api.Test; import tools.vitruv.neojoin.expression_parser.model.FeatureInformation; -import tools.vitruv.neojoin.expression_parser.model.FlatMap; import tools.vitruv.neojoin.expression_parser.model.Map; import tools.vitruv.neojoin.expression_parser.model.ReferenceOperator; import tools.vitruv.neojoin.expression_parser.parser.exception.UnsupportedReferenceExpressionException; @@ -140,85 +139,4 @@ public void parseWithMultipleOneToOneMemberFeatureCalls() assertNull(thirdMapOperator.getFollowingOperator()); } - - @Test - public void parseWithOneToOneAndOneToManyMemberFeatureCalls() - throws UnsupportedReferenceExpressionException { - // given - final XFeatureCall mapExpressionFeatureCall = - XFeatureCallFixtures.featureCallWithEmptyFormalParameter(); - - final XMemberFeatureCall mapExpressionOneToManyMemberFeatureCall = - XMemberFeatureCallFixtures.oneToManyFieldXMemberFeatureCall( - "some.oneToMany.package.SomeMember", - "SomeMemberSimpleName", - "someMemberReference"); - mapExpressionOneToManyMemberFeatureCall.setMemberCallTarget(mapExpressionFeatureCall); - - final XMemberFeatureCall mapExpressionDeeperMemberFeatureCall = - XMemberFeatureCallFixtures.oneToOneFieldXMemberFeatureCall( - "some.oneToOne.package.SomeDeeperMember", - "SomeDeeperMemberSimpleName", - "someDeeperMemberReference"); - mapExpressionDeeperMemberFeatureCall.setMemberCallTarget( - mapExpressionOneToManyMemberFeatureCall); - - final XMemberFeatureCall mapExpressionDeepestMemberFeatureCall = - XMemberFeatureCallFixtures.oneToManyFieldXMemberFeatureCall( - "some.oneToMany.package.SomeDeepestMember", - "SomeDeepestMemberSimpleName", - "someDeepestMemberReference"); - mapExpressionDeepestMemberFeatureCall.setMemberCallTarget( - mapExpressionDeeperMemberFeatureCall); - - final XMemberFeatureCall mapMemberFeatureCall = - XMemberFeatureCallFixtures.mapOperationMemberFeatureCall( - mapExpressionDeepestMemberFeatureCall); - mapMemberFeatureCall.setMemberCallTarget(exampleExpressionChain()); - - // when - final ManualPatternMatchingStrategy strategy = new ManualPatternMatchingStrategy(); - final Optional resultOptional = - parser.parse(strategy, mapMemberFeatureCall); - - // then - assertTrue(resultOptional.isPresent()); - - final ReferenceOperator result = - assertExampleExpressionChainResultAndGetFollowingReferenceOperator( - resultOptional.get()); - assertInstanceOf(FlatMap.class, result); - - final FlatMap firstFlatMapOperator = (FlatMap) result; - assertEquals( - new FeatureInformation( - "someMemberReference", - "SomeMemberSimpleName", - "some.oneToMany.package.SomeMember"), - firstFlatMapOperator.getFeatureInformation()); - - final ReferenceOperator secondOperator = firstFlatMapOperator.getFollowingOperator(); - assertInstanceOf(Map.class, secondOperator); - - final Map secondMapOperator = (Map) secondOperator; - assertEquals( - new FeatureInformation( - "someDeeperMemberReference", - "SomeDeeperMemberSimpleName", - "some.oneToOne.package.SomeDeeperMember"), - secondMapOperator.getFeatureInformation()); - - final ReferenceOperator thirdOperator = secondMapOperator.getFollowingOperator(); - assertInstanceOf(FlatMap.class, thirdOperator); - - final FlatMap thirdFlatMapOperator = (FlatMap) thirdOperator; - assertEquals( - new FeatureInformation( - "someDeepestMemberReference", - "SomeDeepestMemberSimpleName", - "some.oneToMany.package.SomeDeepestMember"), - thirdFlatMapOperator.getFeatureInformation()); - - assertNull(thirdFlatMapOperator.getFollowingOperator()); - } }