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]