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]

Reply via email to