This is an automated email from the ASF dual-hosted git repository.

tkobayas 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 12467ce780 [incubator-kie-drools-6037] exec-model doesn't populate 
metadata of declared type (#6414)
12467ce780 is described below

commit 12467ce780b854dd0f93defe0b240b2891805a08
Author: Toshiya Kobayashi <[email protected]>
AuthorDate: Tue Aug 12 16:09:38 2025 +0900

    [incubator-kie-drools-6037] exec-model doesn't populate metadata of 
declared type (#6414)
    
    * [incubator-kie-drools-6037] exec-model doesn't populate metadata of 
declared type
    - WIP : test case only
    
    * WIP: class level annotation
    
    * field level annotation
    
    * Update 
drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrTypeDefinition.java
    
    Co-authored-by: Copilot <[email protected]>
    
    * little refactor. Do not add metadata when UnknownKeysInAnnotation
    
    ---------
    
    Co-authored-by: Copilot <[email protected]>
---
 .../main/java/org/drools/model/TypeMetaData.java   |  10 ++
 .../org/drools/model/impl/TypeMetaDataImpl.java    |  53 +++++++++
 .../declaredtype/DescrAnnotationDefinition.java    |   2 +
 .../declaredtype/DescrFieldDefinition.java         |   9 ++
 .../declaredtype/DescrTypeDefinition.java          |  74 ++++++++----
 .../generator/declaredtype/POJOGenerator.java      |  42 ++++++-
 .../model/codegen/execmodel/DeclaredTypesTest.java | 127 ++++++++++++++++++++-
 .../modelcompiler/util/TypeDeclarationUtil.java    |  46 +++++++-
 8 files changed, 336 insertions(+), 27 deletions(-)

diff --git 
a/drools-model/drools-canonical-model/src/main/java/org/drools/model/TypeMetaData.java
 
b/drools-model/drools-canonical-model/src/main/java/org/drools/model/TypeMetaData.java
index 2f176e5d86..2247adc6c2 100644
--- 
a/drools-model/drools-canonical-model/src/main/java/org/drools/model/TypeMetaData.java
+++ 
b/drools-model/drools-canonical-model/src/main/java/org/drools/model/TypeMetaData.java
@@ -18,9 +18,19 @@
  */
 package org.drools.model;
 
+import java.util.List;
 import java.util.Map;
 
 public interface TypeMetaData extends NamedModelItem {
     Class<?> getType();
     Map<String, AnnotationValue[]> getAnnotations();
+
+    List<FieldMetaData> getFields();
+
+    interface FieldMetaData {
+
+        String getFieldName();
+
+        Map<String, AnnotationValue[]> getAnnotations();
+    }
 }
diff --git 
a/drools-model/drools-canonical-model/src/main/java/org/drools/model/impl/TypeMetaDataImpl.java
 
b/drools-model/drools-canonical-model/src/main/java/org/drools/model/impl/TypeMetaDataImpl.java
index 17ffa9796a..a230b9af92 100644
--- 
a/drools-model/drools-canonical-model/src/main/java/org/drools/model/impl/TypeMetaDataImpl.java
+++ 
b/drools-model/drools-canonical-model/src/main/java/org/drools/model/impl/TypeMetaDataImpl.java
@@ -19,8 +19,10 @@
 package org.drools.model.impl;
 
 import java.lang.reflect.Field;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.drools.model.AnnotationValue;
@@ -34,6 +36,7 @@ public class TypeMetaDataImpl implements TypeMetaData, 
ModelComponent {
     private final String pkg;
     private final String name;
     private final Map<String, AnnotationValue[]> annotations = new HashMap<>();
+    private final Map<String, FieldMetaData> fields = new HashMap<>();
 
     public TypeMetaDataImpl( Class<?> type ) {
         this.type = type;
@@ -61,11 +64,37 @@ public class TypeMetaDataImpl implements TypeMetaData, 
ModelComponent {
         return annotations;
     }
 
+    @Override
+    public List<FieldMetaData> getFields() {
+        return new ArrayList<>(fields.values());
+    }
+
     public TypeMetaDataImpl addAnnotation( String name, AnnotationValue... 
values) {
         annotations.put(name, values);
         return this;
     }
 
+    public TypeMetaDataImpl addField(String fieldName) {
+        fields.computeIfAbsent(fieldName, FieldMetaDataImpl::new);
+        return this;
+    }
+
+    public TypeMetaDataImpl addFieldAnnotation(String fieldName, String 
annotationName, AnnotationValue... values) {
+        FieldMetaDataImpl fieldMetaData = (FieldMetaDataImpl) 
fields.computeIfAbsent(fieldName, FieldMetaDataImpl::new);
+        fieldMetaData.addAnnotation(annotationName, values);
+        return this;
+    }
+
+    // DSL-style instance methods for fluent API
+    public TypeMetaDataImpl withField(String fieldName) {
+        return addField(fieldName);
+    }
+
+    public TypeMetaDataImpl withFieldAnnotation(String fieldName, String 
annotationName, AnnotationValue... values) {
+        return addFieldAnnotation(fieldName, annotationName, values);
+    }
+
+
     @Override
     public boolean isEqualTo( ModelComponent o ) {
         if ( this == o ) return true;
@@ -92,4 +121,28 @@ public class TypeMetaDataImpl implements TypeMetaData, 
ModelComponent {
 
         return true;
     }
+
+    public static class FieldMetaDataImpl implements FieldMetaData {
+        private final String fieldName;
+        private final Map<String, AnnotationValue[]> annotations = new 
HashMap<>();
+
+        public FieldMetaDataImpl(String fieldName) {
+            this.fieldName = fieldName;
+        }
+
+        @Override
+        public String getFieldName() {
+            return fieldName;
+        }
+
+        @Override
+        public Map<String, AnnotationValue[]> getAnnotations() {
+            return annotations;
+        }
+
+        public FieldMetaDataImpl addAnnotation(String name, AnnotationValue... 
values) {
+            annotations.put(name, values);
+            return this;
+        }
+    }
 }
diff --git 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrAnnotationDefinition.java
 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrAnnotationDefinition.java
index 040db40405..7be445720f 100644
--- 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrAnnotationDefinition.java
+++ 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrAnnotationDefinition.java
@@ -42,6 +42,8 @@ import static 
org.drools.model.codegen.execmodel.generator.declaredtype.POJOGene
 
 public class DescrAnnotationDefinition implements AnnotationDefinition {
 
+    public static final String BUILTIN_ANNOTATION_PACKAGE = 
"org.kie.api.definition.type.";
+
     static final String VALUE = "value";
 
     private static final Map<String, Class<?>> annotationMapping = new 
HashMap<>();
diff --git 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrFieldDefinition.java
 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrFieldDefinition.java
index 174e1dfa72..7d8454291f 100644
--- 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrFieldDefinition.java
+++ 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrFieldDefinition.java
@@ -40,6 +40,7 @@ public class DescrFieldDefinition implements FieldDefinition {
     private boolean isOverride = false;
 
     private final Map<String, AnnotationDefinition> annotations = new 
HashMap<>();
+    private final Map<String, Object> fieldMetaData = new HashMap<>();
 
     public DescrFieldDefinition(String fieldName, String objectType, String 
initExpr) {
         this.fieldName = fieldName;
@@ -137,6 +138,14 @@ public class DescrFieldDefinition implements 
FieldDefinition {
         return this;
     }
 
+    public Map<String, Object> getFieldMetaData() {
+        return fieldMetaData;
+    }
+
+    public void addFieldMetaData(String key, Object value) {
+        fieldMetaData.put(key, value);
+    }
+
     @Override
     public String toString() {
         return "DescrFieldDefinition{" +
diff --git 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrTypeDefinition.java
 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrTypeDefinition.java
index 274908097d..31b820cdf2 100644
--- 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrTypeDefinition.java
+++ 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/DescrTypeDefinition.java
@@ -22,7 +22,9 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -46,6 +48,7 @@ import static java.util.Optional.of;
 import static java.util.Optional.ofNullable;
 import static java.util.stream.Collectors.toList;
 
+import static 
org.drools.model.codegen.execmodel.generator.declaredtype.DescrAnnotationDefinition.BUILTIN_ANNOTATION_PACKAGE;
 import static 
org.drools.model.codegen.execmodel.generator.declaredtype.POJOGenerator.quote;
 import static org.drools.util.StreamUtils.optionalToStream;
 
@@ -62,6 +65,7 @@ public class DescrTypeDefinition implements TypeDefinition {
     private final TypeResolver typeResolver;
 
     private List<DroolsError> errors = new ArrayList<>();
+    private Map<String, Object> classMetaData = new HashMap<>();
 
     private Optional<String> superTypeName = Optional.empty();
     private Optional<Class<?>> abstractClass = Optional.empty();
@@ -109,11 +113,22 @@ public class DescrTypeDefinition implements 
TypeDefinition {
             }
             try {
                 
annotations.add(DescrAnnotationDefinition.fromDescr(typeResolver, ann));
-            } catch (UnkownAnnotationClassException | UnknownKeysInAnnotation 
e) {
-                // Do not do anything
+            } catch (UnkownAnnotationClassException e) {
+                // Store non-defined custom annotations as metadata
+                // Class level built-in annotations are not added here; they 
are added in TypeDeclarationUtil at a later phase.
+                classMetaData.put(ann.getName(), ann.getSingleValue());
+            } catch (UnknownKeysInAnnotation e) {
+                // Add build errors for unknown annotation properties
+                e.getValues().stream()
+                        .map(p -> new AnnotationDeclarationError(ann, "Unknown 
annotation property " + p))
+                        .forEach(errors::add);
             }
         }
     }
+    
+    private boolean isBuiltInAnnotation(DescrAnnotationDefinition 
descrAnnotationDefinition) {
+        return 
descrAnnotationDefinition.getName().startsWith(BUILTIN_ANNOTATION_PACKAGE);
+    }
 
     @Override
     public String getTypeName() {
@@ -243,15 +258,38 @@ public class DescrTypeDefinition implements 
TypeDefinition {
     private ProcessedTypeField processTypeField(int position, TypeFieldDescr 
typeFieldDescr) {
         DescrFieldDefinition typeField = new 
DescrFieldDefinition(typeFieldDescr);
 
-        List<DescrAnnotationDefinition> parsedAnnotations = 
typeFieldDescr.getAnnotations().stream()
-                .map(this::createAnnotationDefinition)
-                .filter(Optional::isPresent)
-                .map(Optional::get)
-                .collect(toList());
+        // Create a map of successfully parsed annotations, keyed by original 
annotation name
+        Map<String, DescrAnnotationDefinition> parsedAnnotations = new 
HashMap<>();
+        Map<String, Boolean> unknownClassAnnotations = new HashMap<>();  // 
Track which annotations had unknown class
+
+        for (AnnotationDescr ann : typeFieldDescr.getAnnotations()) {
+            try {
+                DescrAnnotationDefinition parsed = 
DescrAnnotationDefinition.fromDescr(typeResolver, ann);
+                parsedAnnotations.put(ann.getName(), parsed);
+            } catch (UnkownAnnotationClassException e) {
+                unknownClassAnnotations.put(ann.getName(), true);
+            } catch (UnknownKeysInAnnotation e) {
+                e.getValues().stream()
+                        .map(p -> new AnnotationDeclarationError(ann, "Unknown 
annotation property " + p))
+                        .forEach(errors::add);
+            }
+        }
 
-        parsedAnnotations.stream().filter(a -> !a.isPosition()).forEach(a -> 
processDefinitions(typeField, a));
+        parsedAnnotations.values().stream().filter(a -> 
!a.isPosition()).forEach(a -> processDefinitions(typeField, a));
+
+        // Add built-in annotations and non-defined custom annotations as 
field metadata
+        for (AnnotationDescr ann : typeFieldDescr.getAnnotations()) {
+            DescrAnnotationDefinition parsed = 
parsedAnnotations.get(ann.getName());
+            if (parsed != null && isBuiltInAnnotation(parsed)) {
+                // Built-in annotation - add to metadata
+                typeField.addFieldMetaData(ann.getName(), 
ann.getSingleValue());
+            } else if (unknownClassAnnotations.containsKey(ann.getName())) {
+                // Non-defined custom annotation - add to metadata
+                typeField.addFieldMetaData(ann.getName(), 
ann.getSingleValue());
+            }
+        }
 
-        int currentFieldPosition = setFieldPosition(position, typeField, 
parsedAnnotations);
+        int currentFieldPosition = setFieldPosition(position, typeField, 
parsedAnnotations.values());
 
         return new ProcessedTypeField(typeField, currentFieldPosition);
     }
@@ -268,7 +306,7 @@ public class DescrTypeDefinition implements TypeDefinition {
         }
     }
 
-    private int setFieldPosition(int initialPosition, DescrFieldDefinition 
typeField, List<DescrAnnotationDefinition> allAnnotations) {
+    private int setFieldPosition(int initialPosition, DescrFieldDefinition 
typeField, Collection<DescrAnnotationDefinition> allAnnotations) {
         int currentFieldPosition = initialPosition;
         Optional<DescrAnnotationDefinition> positionAnnotation = allAnnotations
                 .stream()
@@ -284,19 +322,6 @@ public class DescrTypeDefinition implements TypeDefinition 
{
         return currentFieldPosition;
     }
 
-    private Optional<DescrAnnotationDefinition> 
createAnnotationDefinition(AnnotationDescr ann) {
-        try {
-            return of(DescrAnnotationDefinition.fromDescr(typeResolver, ann));
-        } catch (UnknownKeysInAnnotation e) {
-            e.getValues().stream()
-                    .map(p -> new AnnotationDeclarationError(ann, "Unknown 
annotation property " + p))
-                    .forEach(errors::add);
-            return empty();
-        } catch (UnkownAnnotationClassException e) {
-            // Do not add annotation and silently fail
-            return empty();
-        }
-    }
 
     public List<DroolsError> getErrors() {
         return errors;
@@ -332,5 +357,8 @@ public class DescrTypeDefinition implements TypeDefinition {
         return methods;
     }
 
+    public Map<String, Object> getClassMetaData() {
+        return classMetaData;
+    }
 
 }
diff --git 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/POJOGenerator.java
 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/POJOGenerator.java
index 54cd51cdd0..157a386b22 100644
--- 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/POJOGenerator.java
+++ 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/declaredtype/POJOGenerator.java
@@ -153,7 +153,38 @@ public class POJOGenerator implements CompilationPhase {
                                                                                
    MARKER_INTERFACES)
                 .toClassDeclaration();
         packageModel.addGeneratedPOJO(generatedClass);
-        addTypeMetadata(typeDescr.getTypeName());
+        
+        // Add type metadata including custom annotations
+        String fullTypeName = pkg.getName() + "." + typeDescr.getTypeName();
+        MethodCallExpr typeMetaDataCall = registerTypeMetaData(fullTypeName);
+        
+        // Add custom metadata from non-defined annotations
+        Map<String, Object> classMetaData = 
descrDeclaredTypeDefinition.getClassMetaData();
+        for (Map.Entry<String, Object> entry : classMetaData.entrySet()) {
+            typeMetaDataCall = new MethodCallExpr(typeMetaDataCall, 
ADD_ANNOTATION_CALL);
+            typeMetaDataCall.addArgument(toStringLiteral(entry.getKey()));
+            addAnnotationValueIfNotNull(typeMetaDataCall, entry.getValue());
+        }
+
+        // Add field metadata
+        for (DescrFieldDefinition field : 
descrDeclaredTypeDefinition.getFields()) {
+            Map<String, Object> fieldMetaData = field.getFieldMetaData();
+            if (!fieldMetaData.isEmpty()) {
+                // First, add the field to the TypeMetaData
+                typeMetaDataCall = new MethodCallExpr(typeMetaDataCall, 
"withField");
+                
typeMetaDataCall.addArgument(toStringLiteral(field.getFieldName()));
+
+                // Then add each annotation as field metadata
+                for (Map.Entry<String, Object> entry : 
fieldMetaData.entrySet()) {
+                    typeMetaDataCall = new MethodCallExpr(typeMetaDataCall, 
"withFieldAnnotation");
+                    
typeMetaDataCall.addArgument(toStringLiteral(field.getFieldName()));
+                    
typeMetaDataCall.addArgument(toStringLiteral(entry.getKey()));
+                    addAnnotationValueIfNotNull(typeMetaDataCall, 
entry.getValue());
+                }
+            }
+        }
+        
+        packageModel.addTypeMetaDataExpressions(typeMetaDataCall);
     }
 
     private void addTypeMetadata(String typeName) {
@@ -193,6 +224,15 @@ public class POJOGenerator implements CompilationPhase {
         return typeMetaDataCall;
     }
 
+    private static void addAnnotationValueIfNotNull(MethodCallExpr 
typeMetaDataCall, Object value) {
+        if (value != null) {
+            MethodCallExpr annotationValueCall = 
createDslTopLevelMethod(ANNOTATION_VALUE_CALL);
+            annotationValueCall.addArgument(toStringLiteral("value"));
+            annotationValueCall.addArgument(quote(value.toString()));
+            typeMetaDataCall.addArgument(annotationValueCall);
+        }
+    }
+
     public static String quote(String str) {
         return "\"" + str.replace("\"", "\\\"") + "\"";
     }
diff --git 
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/DeclaredTypesTest.java
 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/DeclaredTypesTest.java
index 9ad0a275c1..3a1587b4a9 100644
--- 
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/DeclaredTypesTest.java
+++ 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/DeclaredTypesTest.java
@@ -18,26 +18,36 @@
  */
 package org.drools.model.codegen.execmodel;
 
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.text.SimpleDateFormat;
 import java.util.Collection;
+import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 
+import org.drools.base.rule.IndexableConstraint;
+import org.drools.base.rule.constraint.AlphaNodeFieldConstraint;
 import org.drools.core.impl.InternalRuleBase;
 import org.drools.core.reteoo.AlphaNode;
 import org.drools.core.reteoo.EntryPointNode;
 import org.drools.core.reteoo.ObjectTypeNode;
-import org.drools.base.rule.IndexableConstraint;
-import org.drools.base.rule.constraint.AlphaNodeFieldConstraint;
+import org.drools.model.codegen.execmodel.domain.Address;
 import org.drools.model.codegen.execmodel.domain.Person;
 import org.drools.model.codegen.execmodel.domain.Result;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
 import org.kie.api.KieBase;
+import org.kie.api.builder.Message;
+import org.kie.api.builder.Results;
+import org.kie.api.definition.type.FactField;
 import org.kie.api.definition.type.FactType;
 import org.kie.api.runtime.KieSession;
 
@@ -588,4 +598,117 @@ public class DeclaredTypesTest extends BaseModelTest {
         ksession.insert(f1);
         assertThat(ksession.fireAllRules()).isEqualTo(1);
     }
+
+    @ParameterizedTest
+    @MethodSource("parameters")
+    void testNonDefinedCustomAnnotation(RUN_TYPE runType) {
+        String str = "package org.example.custom; \n" +
+                "import " + Date.class.getCanonicalName() + ";\n" +
+                "import " + Address.class.getCanonicalName() + ";\n" +
+                "\n" +
+                "declare Person\n" +
+                "    @xxxAuthor( Bob )\n" +
+                "    @role(event)\n" + // @role is a built-in annotation
+                "\n" +
+                "    name : String @key @xxxMaxLength( 30 )\n" + // @key is a 
built-in annotation
+                "    dateOfBirth : Date\n" +
+                "    address : Address\n" +
+                "end";
+
+        KieSession ksession = getKieSession(runType, str);
+        KieBase kBase = ksession.getKieBase();
+
+        FactType person = kBase.getFactType( "org.example.custom", "Person" );
+
+        Map<String, Object> classMetaData = person.getMetaData();
+        assertThat(classMetaData).isNotNull();
+        assertThat(classMetaData.get("xxxAuthor")).isEqualTo("Bob");
+        assertThat(classMetaData.containsKey("role")).isTrue();
+        assertThat(classMetaData.get("event")).isNull();
+
+        FactField field = person.getField("name" );
+        Map<String, Object> fieldMetaData = field.getMetaData();
+        assertThat(fieldMetaData).isNotNull();
+        assertThat(fieldMetaData.get("xxxMaxLength")).isEqualTo("30");
+        assertThat(fieldMetaData.containsKey("key")).isTrue();
+        assertThat(fieldMetaData.get("key")).isNull();
+    }
+
+    @ParameterizedTest
+    @MethodSource("parameters")
+    void testDefinedCustomAnnotation(RUN_TYPE runType) throws Exception {
+        String str = "package org.example.custom; \n" +
+                "import " + Date.class.getCanonicalName() + ";\n" +
+                "import " + Address.class.getCanonicalName() + ";\n" +
+                "import " + MyAuthor.class.getCanonicalName() + ";\n" +
+                "import " + MyMaxLength.class.getCanonicalName() + ";\n" +
+                "\n" +
+                "declare Person\n" +
+                "    @MyAuthor( \"Bob\" )\n" +
+                "\n" +
+                "    name : String @MyMaxLength( 30 )\n" +
+                "    dateOfBirth : Date\n" +
+                "    address : Address\n" +
+                "end";
+
+        KieSession ksession = getKieSession(runType, str);
+        KieBase kBase = ksession.getKieBase();
+
+        FactType person = kBase.getFactType( "org.example.custom", "Person" );
+
+        Class<?> factClass = person.getFactClass();
+        MyAuthor classAnnotation = factClass.getAnnotation(MyAuthor.class);
+        assertThat(classAnnotation).isNotNull();
+        assertThat(classAnnotation.value()).isEqualTo("Bob");
+
+        Field factField = factClass.getDeclaredField("name");
+        MyMaxLength fieldAnnotation = 
factField.getAnnotation(MyMaxLength.class);
+        assertThat(fieldAnnotation).isNotNull();
+        assertThat(fieldAnnotation.value()).isEqualTo(30);
+
+        Map<String, Object> classMetaData = person.getMetaData();
+        assertThat(classMetaData).as("Defined custom annotation should not be 
stored in metaData").isNull();
+
+        FactField field = person.getField("name" );
+        Map<String, Object> fieldMetaData = field.getMetaData();
+        assertThat(fieldMetaData).as("Defined custom annotation should not be 
stored in metaData").isNull();
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.TYPE})
+    public @interface MyAuthor {
+        String value();
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.FIELD})
+    public @interface MyMaxLength {
+        int value();
+    }
+
+    @ParameterizedTest
+    @MethodSource("parameters")
+    void testDefinedCustomAnnotationWithUnknownProperty(RUN_TYPE runType) {
+        String str = "package org.example.custom; \n" +
+                "import " + Date.class.getCanonicalName() + ";\n" +
+                "import " + Address.class.getCanonicalName() + ";\n" +
+                "import " + MyAuthor.class.getCanonicalName() + ";\n" +
+                "import " + MyMaxLength.class.getCanonicalName() + ";\n" +
+                "\n" +
+                "declare Person\n" +
+                "    @MyAuthor( yyy = \"Bob\" )\n" +
+                "\n" +
+                "    name : String @MyMaxLength( zzz = 30 )\n" +
+                "    dateOfBirth : Date\n" +
+                "    address : Address\n" +
+                "end";
+
+        Results results = createKieBuilder(runType, str).getResults();
+
+        assertThat(results.getMessages(Message.Level.ERROR))
+                .hasSize(2)
+                .extracting(Message::getText)
+                .containsExactlyInAnyOrder("Unknown annotation property yyy",
+                                           "Unknown annotation property zzz");
+    }
 }
\ No newline at end of file
diff --git 
a/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/util/TypeDeclarationUtil.java
 
b/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/util/TypeDeclarationUtil.java
index ac811cb0d5..16071370fa 100644
--- 
a/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/util/TypeDeclarationUtil.java
+++ 
b/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/util/TypeDeclarationUtil.java
@@ -23,6 +23,7 @@ import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.drools.base.factmodel.AccessibleFact;
 import org.drools.base.factmodel.AnnotationDefinition;
@@ -49,6 +50,10 @@ import static 
org.drools.base.rule.TypeDeclaration.createTypeDeclarationForBean;
 
 public class TypeDeclarationUtil {
 
+    private static final Set<String> KNOWN_ANNOTATIONS = Set.of(
+        "role", "duration", "timestamp", "expires", "propertyReactive", 
"classReactive"
+    );
+
     public static TypeDeclaration createTypeDeclaration(TypeMetaData metaType, 
PropertySpecificOption propertySpecificOption, TypeResolver typeResolver) {
         Class<?> typeClass = metaType.getType();
 
@@ -65,8 +70,12 @@ public class TypeDeclarationUtil {
     }
 
     private static void wireMetaTypeAnnotations( TypeMetaData metaType, 
TypeDeclaration typeDeclaration ) {
+        ClassDefinition classDef = typeDeclaration.getTypeClassDef();
         for (Map.Entry<String, AnnotationValue[]> ann : 
metaType.getAnnotations().entrySet()) {
-            switch (ann.getKey()) {
+            String annotationName = ann.getKey();
+            boolean isKnownAnnotation = 
KNOWN_ANNOTATIONS.contains(annotationName);
+            
+            switch (annotationName) {
                 case "role":
                     for (AnnotationValue annVal : ann.getValue()) {
                         if (annVal.getKey().equals( "value" ) && 
annVal.getValue().equals( "event" )) {
@@ -106,6 +115,41 @@ public class TypeDeclarationUtil {
                     typeDeclaration.setPropertyReactive( false );
                     break;
             }
+            
+            // For non-defined custom annotations, add them as metadata to the 
ClassDefinition
+            if (!isKnownAnnotation && classDef != null) {
+                // Get the value from the annotation values
+                Object value = null;
+                for (AnnotationValue annVal : ann.getValue()) {
+                    if (annVal.getKey().equals("value")) {
+                        value = annVal.getValue();
+                        break;
+                    }
+                }
+                classDef.addMetaData(annotationName, value);
+            }
+        }
+
+        // Process field metadata from TypeMetaData
+        if (classDef != null) {
+            for (TypeMetaData.FieldMetaData fieldMetaData : 
metaType.getFields()) {
+                String fieldName = fieldMetaData.getFieldName();
+                FieldDefinition fieldDef = classDef.getField(fieldName);
+
+                if (fieldDef != null) {
+                    // Add field metadata to the FieldDefinition
+                    for (Map.Entry<String, AnnotationValue[]> fieldAnn : 
fieldMetaData.getAnnotations().entrySet()) {
+                        Object value = null;
+                        for (AnnotationValue annVal : fieldAnn.getValue()) {
+                            if (annVal.getKey().equals("value")) {
+                                value = annVal.getValue();
+                                break;
+                            }
+                        }
+                        fieldDef.addMetaData(fieldAnn.getKey(), value);
+                    }
+                }
+            }
         }
     }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to