This is an automated email from the ASF dual-hosted git repository.
mariofusco pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-drools.git
The following commit(s) were added to refs/heads/main by this push:
new 785ef86a9a [DROOLS-7197] fix generics introspection on superclasses
during exec model generation (#5944)
785ef86a9a is described below
commit 785ef86a9a774db20f8610a1b73f4d5cdb635eb4
Author: Mario Fusco <[email protected]>
AuthorDate: Wed May 15 08:57:58 2024 +0200
[DROOLS-7197] fix generics introspection on superclasses during exec model
generation (#5944)
---
.../generator/drlxparse/SpecialComparisonCase.java | 13 +-
.../generator/expressiontyper/ExpressionTyper.java | 27 +---
.../model/codegen/execmodel/GenericsTest.java | 171 ++++++++++++++++++++-
.../java/org/drools/mvelcompiler/RHSPhase.java | 12 +-
.../mvelcompiler/ast/FieldToAccessorTExpr.java | 5 +
.../drools/mvelcompiler/ast/MethodCallExprT.java | 5 +
.../drools/mvelcompiler/ast/TypedExpression.java | 4 +
.../drools/mvel/integrationtests/GenericsTest.java | 101 ++++++++++++
.../KnownExecModelDifferenceTest.java | 55 -------
.../src/main/java/org/drools/util/ClassUtils.java | 74 ++++++---
10 files changed, 355 insertions(+), 112 deletions(-)
diff --git
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/drlxparse/SpecialComparisonCase.java
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/drlxparse/SpecialComparisonCase.java
index 7e935b37b1..2378816ac2 100644
---
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/drlxparse/SpecialComparisonCase.java
+++
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/drlxparse/SpecialComparisonCase.java
@@ -53,9 +53,7 @@ abstract class SpecialComparisonCase {
static SpecialComparisonCase specialComparisonFactory(TypedExpression
left, TypedExpression right) {
if (isNumber(left) && !isObject(right.getRawClass()) ||
isNumber(right) && !isObject(left.getRawClass())) { // Don't coerce Object yet.
EvaluationUtil will handle it dynamically later
- Optional<Class<?>> leftCast = typeNeedsCast(left.getType());
- Optional<Class<?>> rightCast = typeNeedsCast(right.getType());
- if (leftCast.isPresent() || rightCast.isPresent()) {
+ if (typeNeedsCast(left.getType()) ||
typeNeedsCast(right.getType())) {
return new ComparisonWithCast(true, left, right,
of(Number.class), of(Number.class));
} else {
return new NumberComparisonWithoutCast(left, right);
@@ -67,13 +65,8 @@ abstract class SpecialComparisonCase {
return new PlainEvaluation(left, right);
}
- private static Optional<Class<?>> typeNeedsCast(Type t) {
- boolean needCast = isObject((Class<?>)t) || isMap((Class<?>) t) ||
isList((Class<?>) t);
- if (needCast) {
- return of((Class<?>) t);
- } else {
- return Optional.empty();
- }
+ private static boolean typeNeedsCast(Type t) {
+ return t instanceof Class && ( isObject((Class<?>)t) ||
isMap((Class<?>) t) || isList((Class<?>) t) );
}
private static boolean isList(Class<?> t) {
diff --git
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/expressiontyper/ExpressionTyper.java
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/expressiontyper/ExpressionTyper.java
index 928ca3f62a..6cdc43d6d6 100644
---
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/expressiontyper/ExpressionTyper.java
+++
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/expressiontyper/ExpressionTyper.java
@@ -22,7 +22,6 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.TypeVariable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
@@ -94,6 +93,7 @@ import org.drools.mvel.parser.printer.PrintUtil;
import org.drools.mvelcompiler.CompiledExpressionResult;
import org.drools.mvelcompiler.ConstraintCompiler;
import org.drools.mvelcompiler.util.BigDecimalArgumentCoercion;
+import org.drools.util.ClassUtils;
import org.drools.util.MethodUtils;
import org.drools.util.Pair;
import org.drools.util.TypeResolver;
@@ -125,6 +125,7 @@ import static
org.drools.model.codegen.execmodel.generator.expressiontyper.Flatt
import static org.drools.mvel.parser.MvelParser.parseType;
import static org.drools.mvel.parser.printer.PrintUtil.printNode;
import static org.drools.util.ClassUtils.extractGenericType;
+import static org.drools.util.ClassUtils.actualTypeFromGenerics;
import static org.drools.util.ClassUtils.getTypeArgument;
import static org.drools.util.ClassUtils.getter2property;
import static org.drools.util.ClassUtils.toRawClass;
@@ -922,16 +923,7 @@ public class ExpressionTyper {
return new TypedExpressionCursor(methodCallExpr,
((ParameterizedType) originalTypeCursor).getActualTypeArguments()[0]);
}
- java.lang.reflect.Type genericReturnType = m.getGenericReturnType();
- if (genericReturnType instanceof TypeVariable) {
- if (originalTypeCursor instanceof ParameterizedType) {
- return new TypedExpressionCursor( methodCallExpr,
getActualType( rawClassCursor, ( ParameterizedType ) originalTypeCursor, (
TypeVariable ) genericReturnType ) );
- } else {
- return new TypedExpressionCursor(methodCallExpr, Object.class);
- }
- } else {
- return new TypedExpressionCursor(methodCallExpr,
genericReturnType);
- }
+ return new TypedExpressionCursor(methodCallExpr,
actualTypeFromGenerics(originalTypeCursor, m.getGenericReturnType(),
rawClassCursor));
}
private void promoteBigDecimalParameters(MethodCallExpr methodCallExpr,
Class[] argsType, Class<?>[] actualArgumentTypes) {
@@ -967,17 +959,6 @@ public class ExpressionTyper {
}
}
- private java.lang.reflect.Type getActualType(Class<?> rawClassCursor,
ParameterizedType originalTypeCursor, TypeVariable genericReturnType) {
- int genericPos = 0;
- for (TypeVariable typeVar : rawClassCursor.getTypeParameters()) {
- if (typeVar.equals( genericReturnType )) {
- return originalTypeCursor.getActualTypeArguments()[genericPos];
- }
- genericPos++;
- }
- throw new RuntimeException( "Unknonw generic type " +
genericReturnType + " for type " + originalTypeCursor );
- }
-
private TypedExpressionCursor objectCreationExpr(ObjectCreationExpr
objectCreationExpr) {
parseNodeArguments( objectCreationExpr );
return new TypedExpressionCursor(objectCreationExpr,
getClassFromType(ruleContext.getTypeResolver(), objectCreationExpr.getType()));
@@ -1163,7 +1144,7 @@ public class ExpressionTyper {
return of(new
TypedExpressionCursor(addCastToExpression(typeWithoutDollar, fieldAccessor,
false), typeOfFirstAccessor ) );
}
- return of(new TypedExpressionCursor(fieldAccessor,
firstAccessor.getGenericReturnType() ) );
+ return of( new TypedExpressionCursor(fieldAccessor,
ClassUtils.actualTypeFromGenerics(originalTypeCursor,
firstAccessor.getGenericReturnType()) ) );
}
Field field = DrlxParseUtil.getField( classCursor, firstName );
diff --git
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/GenericsTest.java
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/GenericsTest.java
index d6358be006..40ca7d24f6 100644
---
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/GenericsTest.java
+++
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/GenericsTest.java
@@ -20,10 +20,10 @@ package org.drools.model.codegen.execmodel;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import org.drools.model.codegen.execmodel.domain.Address;
import org.drools.model.codegen.execmodel.domain.Person;
-
import org.junit.Test;
import org.kie.api.runtime.KieSession;
@@ -117,4 +117,173 @@ public class GenericsTest extends BaseModelTest {
ksession.insert(classWithGenericField);
assertThat(ksession.fireAllRules()).isEqualTo(1);
}
+
+ @Test
+ public void testGenericsOnSuperclass() {
+ // KIE-DROOLS-5925
+ String str =
+ "import " + DieselCar.class.getCanonicalName() + ";\n " +
+ "dialect \"mvel\"\n" +
+ "\n" +
+ "rule \"Diesel vehicles with more than 95 kW use high-octane
fuel (diesel has no octane, this is a test)\"\n" +
+ " when\n" +
+ " $v: DieselCar(motor.kw > 95, score<=0,
!motor.highOctane)\n" +
+ " then\n" +
+ " System.out.println(\"Diesel vehicle with more than 95
kW: \" + $v+\", score=\"+$v.score);\n" +
+ " $v.engine.highOctane = true;\n" +
+ " update($v);\n" +
+ "end\n" +
+ "\n" +
+ "rule \"High-octane fuel engines newer serial numbers have
slightly higher score\"\n" +
+ " when\n" +
+ " $v: DieselCar(engine.highOctane, score<=1,
motor.serialNumber > 50000)\n" +
+ " then\n" +
+ " System.out.println(\"High octane engine vehicle with
newer serial number: \" + $v.motor.serialNumber);\n" +
+ " $v.score = $v.score + 1;\n" +
+ " update($v);\n" +
+ "end";
+
+ KieSession ksession = getKieSession(str);
+
+ DieselCar vehicle1 = new DieselCar("Volkswagen", "Passat", 100);
+ vehicle1.setFrameMaxTorque(500);
+ vehicle1.getEngine().setMaxTorque(350);
+ vehicle1.getEngine().setSerialNumber(75_000);
+ vehicle1.setScore(0);
+
+ DieselCar vehicle2 = new DieselCar("Peugeot", "208", 50);
+ vehicle2.setFrameMaxTorque(100);
+ vehicle2.getEngine().setMaxTorque(200);
+ vehicle2.setScore(0);
+
+ ksession.insert(vehicle1);
+ ksession.insert(vehicle2);
+ assertThat(ksession.fireAllRules()).isEqualTo(3);
+ }
+
+ public static abstract class Vehicle<TEngine extends Engine> {
+
+ private final String maker;
+ private final String model;
+
+ private int score;
+
+ public Vehicle(String maker, String model) {
+ this.maker = Objects.requireNonNull(maker);
+ this.model = Objects.requireNonNull(model);
+ }
+
+ public String getMaker() {
+ return maker;
+ }
+
+ public String getModel() {
+ return model;
+ }
+
+ public abstract TEngine getEngine();
+
+ public TEngine getMotor() {
+ return getEngine();
+ }
+
+ public int getScore() {
+ return score;
+ }
+
+ public void setScore(int score) {
+ this.score = score;
+ }
+
+ @Override
+ public String toString() {
+ return "Vehicle{" + "maker='" + maker + '\'' + ", model='" + model
+ '\'' + '}';
+ }
+ }
+
+ public static abstract class Engine {
+
+ private final int kw;
+
+ public Engine(int kw) {
+ this.kw = kw;
+ }
+
+ public int getKw() {
+ return kw;
+ }
+
+ public abstract boolean isZeroEmissions();
+
+ }
+
+ public static class DieselEngine extends Engine {
+
+ // diesel has no octanes... but let's pretend it does
+ private boolean highOctane;
+
+ private int maxTorque;
+
+ private long serialNumber;
+
+ public DieselEngine(int kw) {
+ super(kw);
+ }
+
+ @Override
+ public boolean isZeroEmissions() {
+ return false;
+ }
+
+ public boolean isHighOctane() {
+ return highOctane;
+ }
+
+ public void setHighOctane(boolean highOctane) {
+ this.highOctane = highOctane;
+ }
+
+ public int getMaxTorque() {
+ return maxTorque;
+ }
+
+ public void setMaxTorque(int maxTorque) {
+ this.maxTorque = maxTorque;
+ }
+
+ public void setSerialNumber(long serialNumber) {
+ this.serialNumber = serialNumber;
+ }
+
+ public long getSerialNumber() {
+ return serialNumber;
+ }
+
+ }
+
+ public static class DieselCar extends Vehicle<DieselEngine> {
+ private final DieselEngine engine;
+
+ private long frameMaxTorque;
+
+
+
+ public DieselCar(String maker, String model, int kw) {
+ super(maker, model);
+ this.engine = new DieselEngine(kw);
+ }
+
+ @Override
+ public DieselEngine getEngine() {
+ return engine;
+ }
+
+ public long getFrameMaxTorque() {
+ return frameMaxTorque;
+ }
+
+ public void setFrameMaxTorque(long frameMaxTorque) {
+ this.frameMaxTorque = frameMaxTorque;
+ }
+ }
}
\ No newline at end of file
diff --git
a/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/RHSPhase.java
b/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/RHSPhase.java
index 6751147ef9..ef02635054 100644
---
a/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/RHSPhase.java
+++
b/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/RHSPhase.java
@@ -216,12 +216,14 @@ public class RHSPhase implements
DrlGenericVisitor<TypedExpression, VisitorConte
private Optional<TypedExpression> asPropertyAccessor(SimpleName n,
VisitorContext arg) {
Optional<TypedExpression> lastTypedExpression = arg.getScope();
- Optional<Type> scopeType =
lastTypedExpression.filter(ListAccessExprT.class::isInstance)
-
.map(ListAccessExprT.class::cast)
- .map(expr ->
expr.getElementType())
-
.orElse(arg.getScopeType());
+ Optional<Type> propertyType =
lastTypedExpression.filter(ListAccessExprT.class::isInstance)
+
.map(ListAccessExprT.class::cast)
+ .map(expr ->
expr.getElementType())
+
.orElse(arg.getScopeType());
- Optional<Method> optAccessor = scopeType.flatMap(t ->
ofNullable(getAccessor(classFromType(t), n.asString())));
+ Optional<Type> scopeType =
lastTypedExpression.flatMap(TypedExpression::getScopeType);
+
+ Optional<Method> optAccessor = propertyType.flatMap(t ->
ofNullable(getAccessor(classFromType(t, scopeType.orElse(null)),
n.asString())));
return map2(lastTypedExpression, optAccessor,
FieldToAccessorTExpr::new);
}
diff --git
a/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/ast/FieldToAccessorTExpr.java
b/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/ast/FieldToAccessorTExpr.java
index fb4047e780..e96e314389 100644
---
a/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/ast/FieldToAccessorTExpr.java
+++
b/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/ast/FieldToAccessorTExpr.java
@@ -68,6 +68,11 @@ public class FieldToAccessorTExpr implements TypedExpression
{
return Optional.of(type);
}
+ @Override
+ public Optional<Type> getScopeType() {
+ return scope.getType();
+ }
+
@Override
public Node toJavaExpression() {
List<Expression> expressionArguments = this.arguments.stream()
diff --git
a/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/ast/MethodCallExprT.java
b/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/ast/MethodCallExprT.java
index 5d3337edc5..d2d93edce4 100644
---
a/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/ast/MethodCallExprT.java
+++
b/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/ast/MethodCallExprT.java
@@ -57,6 +57,11 @@ public class MethodCallExprT implements TypedExpression {
return type;
}
+ @Override
+ public Optional<Type> getScopeType() {
+ return scope.flatMap(TypedExpression::getScopeType);
+ }
+
@Override
public Node toJavaExpression() {
Node scopeE =
scope.map(TypedExpression::toJavaExpression).orElse(null);
diff --git
a/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/ast/TypedExpression.java
b/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/ast/TypedExpression.java
index d83d9a2a07..9f4ee5dd76 100644
---
a/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/ast/TypedExpression.java
+++
b/drools-model/drools-mvel-compiler/src/main/java/org/drools/mvelcompiler/ast/TypedExpression.java
@@ -28,5 +28,9 @@ public interface TypedExpression {
Optional<Type> getType();
Node toJavaExpression();
+
+ default Optional<Type> getScopeType() {
+ return Optional.empty();
+ }
}
diff --git
a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/GenericsTest.java
b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/GenericsTest.java
new file mode 100644
index 0000000000..cb5a94aa2c
--- /dev/null
+++
b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/GenericsTest.java
@@ -0,0 +1,101 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.drools.mvel.integrationtests;
+
+import java.util.Collection;
+
+import org.drools.mvel.integrationtests.facts.vehicles.DieselCar;
+import org.drools.mvel.integrationtests.facts.vehicles.ElectricCar;
+import org.drools.testcoverage.common.util.KieBaseTestConfiguration;
+import org.drools.testcoverage.common.util.KieBaseUtil;
+import org.drools.testcoverage.common.util.TestParametersUtil;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.kie.api.KieBase;
+import org.kie.api.runtime.KieSession;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * This is a place where known behavior differences between exec-model and
non-exec-model.
+ * They are not treated as a bug and should be documented in "Migration from
non-executable model to executable model" section
+ */
+@RunWith(Parameterized.class)
+public class GenericsTest {
+
+ private final KieBaseTestConfiguration kieBaseTestConfiguration;
+
+ public GenericsTest(final KieBaseTestConfiguration
kieBaseTestConfiguration) {
+ this.kieBaseTestConfiguration = kieBaseTestConfiguration;
+ }
+
+ @Parameterized.Parameters(name = "KieBase type={0}")
+ public static Collection<Object[]> getParameters() {
+ return TestParametersUtil.getKieBaseCloudConfigurations(true);
+ }
+
+ @Test
+ public void property_subClassMethod_genericsReturnType() {
+ // DROOLS-7197
+ String str = "package com.example.reproducer\n" +
+ "import " + DieselCar.class.getCanonicalName() + ";\n" +
+ "rule R\n" +
+ "dialect \"mvel\"\n" +
+ "when\n" +
+ " $v : DieselCar(motor.adBlueRequired == true)\n" +
+ "then\n" +
+ " $v.score = 5;\n" +
+ "end";
+
+ KieBase kbase = KieBaseUtil.getKieBaseFromKieModuleFromDrl("test",
kieBaseTestConfiguration, str);
+ KieSession ksession = kbase.newKieSession();
+
+ DieselCar dieselCar = new DieselCar("ABC", "Model 1.6", 85, true);
+
+ ksession.insert(dieselCar);
+ ksession.fireAllRules();
+
+ assertThat(dieselCar.getScore()).isEqualTo(5);
+ }
+
+ @Test
+ public void property_subClassMethod_explicitReturnType() {
+ // DROOLS-7197
+ String str = "package com.example.reproducer\n" +
+ "import " + ElectricCar.class.getCanonicalName() + ";\n" +
+ "rule R\n" +
+ "dialect \"mvel\"\n" +
+ "when\n" +
+ " $v : ElectricCar(engine.batterySize > 70)\n" +
+ "then\n" +
+ " $v.score = 5;\n" +
+ "end";
+
+ KieBase kbase = KieBaseUtil.getKieBaseFromKieModuleFromDrl("test",
kieBaseTestConfiguration, str);
+ KieSession ksession = kbase.newKieSession();
+
+ ElectricCar electricCar = new ElectricCar("XYZ", "Model 3", 200, 90);
+
+ ksession.insert(electricCar);
+ ksession.fireAllRules();
+
+ assertThat(electricCar.getScore()).isEqualTo(5); // works for both
cases
+ }
+}
diff --git
a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/KnownExecModelDifferenceTest.java
b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/KnownExecModelDifferenceTest.java
index 5282f09451..f42c3ab4cc 100644
---
a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/KnownExecModelDifferenceTest.java
+++
b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/KnownExecModelDifferenceTest.java
@@ -25,8 +25,6 @@ import java.util.List;
import org.drools.mvel.compiler.Person;
import org.drools.mvel.integrationtests.facts.VarargsFact;
-import org.drools.mvel.integrationtests.facts.vehicles.DieselCar;
-import org.drools.mvel.integrationtests.facts.vehicles.ElectricCar;
import org.drools.testcoverage.common.util.KieBaseTestConfiguration;
import org.drools.testcoverage.common.util.KieBaseUtil;
import org.drools.testcoverage.common.util.KieUtil;
@@ -164,59 +162,6 @@ public class KnownExecModelDifferenceTest {
assertThat(fact.getValueList()).containsExactly(10L, 20L); // Coerced
with both cases
}
- @Test
- public void property_subClassMethod_genericsReturnType() {
- // DROOLS-7197
- String str = "package com.example.reproducer\n" +
- "import " + DieselCar.class.getCanonicalName() + ";\n" +
- "rule R\n" +
- "dialect \"mvel\"\n" +
- "when\n" +
- " $v : DieselCar(motor.adBlueRequired == true)\n" +
- "then\n" +
- " $v.score = 5;\n" +
- "end";
-
- if (kieBaseTestConfiguration.isExecutableModel()) {
- KieBuilder kieBuilder =
KieUtil.getKieBuilderFromDrls(kieBaseTestConfiguration, false, str);
-
assertThat(kieBuilder.getResults().hasMessages(Level.ERROR)).isTrue(); // Fail
with exec-model
- } else {
- KieBase kbase = KieBaseUtil.getKieBaseFromKieModuleFromDrl("test",
kieBaseTestConfiguration, str);
- KieSession ksession = kbase.newKieSession();
-
- DieselCar dieselCar = new DieselCar("ABC", "Model 1.6", 85, true);
-
- ksession.insert(dieselCar);
- ksession.fireAllRules();
-
- assertThat(dieselCar.getScore()).isEqualTo(5);
- }
- }
-
- @Test
- public void property_subClassMethod_explicitReturnType() {
- // DROOLS-7197
- String str = "package com.example.reproducer\n" +
- "import " + ElectricCar.class.getCanonicalName() + ";\n" +
- "rule R\n" +
- "dialect \"mvel\"\n" +
- "when\n" +
- " $v : ElectricCar(engine.batterySize > 70)\n" +
- "then\n" +
- " $v.score = 5;\n" +
- "end";
-
- KieBase kbase = KieBaseUtil.getKieBaseFromKieModuleFromDrl("test",
kieBaseTestConfiguration, str);
- KieSession ksession = kbase.newKieSession();
-
- ElectricCar electricCar = new ElectricCar("XYZ", "Model 3", 200, 90);
-
- ksession.insert(electricCar);
- ksession.fireAllRules();
-
- assertThat(electricCar.getScore()).isEqualTo(5); // works for both
cases
- }
-
@Test
public void invalid_cast_intToString() {
// DROOLS-7198
diff --git a/drools-util/src/main/java/org/drools/util/ClassUtils.java
b/drools-util/src/main/java/org/drools/util/ClassUtils.java
index 90ac325228..b9db8dd786 100644
--- a/drools-util/src/main/java/org/drools/util/ClassUtils.java
+++ b/drools-util/src/main/java/org/drools/util/ClassUtils.java
@@ -104,8 +104,8 @@ public final class ClassUtils {
if (c == boolean.class) return Boolean.class;
return c;
}
-
-
+
+
/**
* Please do not use - internal
* org/my/Class.xxx -> org.my.Class
@@ -315,11 +315,11 @@ public final class ClassUtils {
// see
http://download.oracle.com/javase/6/docs/api/java/lang/Class.html#getName%28%29
String qualifiedNamespace = className;
String name = className;
- if( className.indexOf('.') > 0 ) {
+ if( className.indexOf('.') > 0 ) {
qualifiedNamespace = className.substring( 0,
className.lastIndexOf( '.' ) ).trim();
name = className.substring( className.lastIndexOf( '.' ) + 1
).trim();
}
- else if( className.indexOf('[') == 0 ) {
+ else if( className.indexOf('[') == 0 ) {
qualifiedNamespace = className.substring(0,
className.lastIndexOf('[') );
}
Object object = patterns.get( qualifiedNamespace );
@@ -351,7 +351,7 @@ public final class ClassUtils {
} else {
dotPos = cls.getName().lastIndexOf( '.' );
}
-
+
if ( dotPos > 0 ) {
return cls.getName().substring( 0,
dotPos );
@@ -556,17 +556,17 @@ public final class ClassUtils {
}
throw new RuntimeException("No generic type");
}
-
+
public static Type getTypeArgument(Type genericType, int index) {
return genericType instanceof ParameterizedType ? (( ParameterizedType
) genericType).getActualTypeArguments()[index] : Object.class;
}
-
+
public static boolean isAssignableFrom(Type from, Type to) {
Class<?> fromClass = toRawClass( from );
Class<?> toClass = toRawClass( to );
return fromClass.isAssignableFrom(toClass) ||
MethodUtils.areBoxingCompatible(fromClass, toClass);
- }
+ }
public static boolean isCollection(Type t) {
@@ -577,15 +577,20 @@ public final class ClassUtils {
}
public static Class<?> classFromType(Type t) {
- Class<?> clazz;
- if(t instanceof Class<?>) {
- clazz = (Class<?>) t;
- } else if(t instanceof ParameterizedType) {
- clazz = (Class<?>) ((ParameterizedType)t).getRawType();
- } else {
- throw new UnsupportedOperationException("Unable to parse type");
+ return classFromType(t, null);
+ }
+
+ public static Class<?> classFromType(Type t, Type scope) {
+ if (t instanceof Class<?>) {
+ return (Class<?>) t;
}
- return clazz;
+ if (t instanceof ParameterizedType) {
+ return (Class<?>) ((ParameterizedType)t).getRawType();
+ }
+ if (t instanceof TypeVariable && scope != null) {
+ return classFromType( actualTypeFromGenerics(scope, t) );
+ }
+ throw new UnsupportedOperationException("Unable to parse type");
}
public static Class<?> toRawClass(Type type) {
@@ -603,7 +608,7 @@ public final class ClassUtils {
}
throw new UnsupportedOperationException( "Unknown type " + type );
}
-
+
public static Class<?> rawType(Type type) {
if (type == null) {
return null;
@@ -670,7 +675,7 @@ public final class ClassUtils {
public static boolean isFinal(Class<?> clazz) {
return Modifier.isFinal( clazz.getModifiers() );
}
-
+
public static boolean isInterface(Class<?> clazz) {
return Modifier.isInterface( clazz.getModifiers() );
}
@@ -914,4 +919,37 @@ public final class ClassUtils {
public static boolean isNumericClass(Class<?> clazz) {
return numericClasses.contains(clazz);
}
+
+ public static Type actualTypeFromGenerics(Type scope, Type genericType) {
+ return actualTypeFromGenerics(scope, genericType, toRawClass(scope));
+ }
+
+ public static Type actualTypeFromGenerics(Type scope, Type genericType,
Class<?> rawClassCursor) {
+ if (genericType instanceof Class || genericType instanceof
ParameterizedType) {
+ return genericType;
+ }
+ if (genericType instanceof TypeVariable typeVar) {
+ if (scope instanceof ParameterizedType paramType) {
+ return actualTypeFromGenerics(rawClassCursor, paramType,
typeVar);
+ }
+ if (scope instanceof Class classCursor &&
classCursor.getSuperclass() != null && classCursor.getSuperclass() !=
Object.class) {
+ return
actualTypeFromGenerics(classCursor.getGenericSuperclass(), genericType,
classCursor.getSuperclass());
+ }
+ if (typeVar.getBounds().length == 1 && typeVar.getBounds()[0]
instanceof Class) {
+ return typeVar.getBounds()[0];
+ }
+ }
+ return Object.class;
+ }
+
+ private static Type actualTypeFromGenerics(Class<?> rawClassCursor,
ParameterizedType originalTypeCursor, TypeVariable genericType) {
+ int genericPos = 0;
+ for (TypeVariable typeVar : rawClassCursor.getTypeParameters()) {
+ if (typeVar.equals( genericType )) {
+ return originalTypeCursor.getActualTypeArguments()[genericPos];
+ }
+ genericPos++;
+ }
+ throw new RuntimeException( "Unknonw generic type " + genericType + "
for type " + originalTypeCursor );
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]