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

borinquenkid pushed a commit to branch test/document-datamapping-core-services
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 8814967002ba717ca6d9afeba50edaf99f9d363d
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Sat Aug 15 20:06:52 2026 -0500

    Deduplicate interface-projection property compatibility check
    
    isInterfaceProjection()'s per-property compatibility loop was copy-pasted
    almost verbatim between InterfaceProjectionBuilder and
    IterableInterfaceProjectionBuilder (only the candidate type variable name
    differed). Extracted into a shared hasCompatibleProperties(domainClass,
    candidateType) method on the base trait; the iterable variant now just
    resolves its generic type and delegates. Drops the now-unused
    AstPropertyResolveUtils import from IterableInterfaceProjectionBuilder.
    
    Also fixes a cast IntelliJ's stricter Groovy static checker rejects in
    buildInterfaceImpl: casting a List<ConstantExpression> (from .collect{})
    directly to List<Expression> is generics-invariant-illegal even though
    groovyc accepts it. Uses the double-cast-through-raw-List trick already
    established elsewhere in this codebase for the same class of warning.
    
    Both isInterfaceProjection() variants and buildInterfaceImpl() are
    already exercised by ServiceTransformSpec's existing interface/iterable
    projection tests; full module suite + codeStyle pass unchanged.
    
    Co-Authored-By: Claude Sonnet 5 <[email protected]>
---
 .../implementers/InterfaceProjectionBuilder.groovy | 39 ++++++++++++++--------
 .../IterableInterfaceProjectionBuilder.groovy      | 16 +--------
 2 files changed, 27 insertions(+), 28 deletions(-)

diff --git 
a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/services/implementers/InterfaceProjectionBuilder.groovy
 
b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/services/implementers/InterfaceProjectionBuilder.groovy
index 147be84735..d39c8a875e 100644
--- 
a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/services/implementers/InterfaceProjectionBuilder.groovy
+++ 
b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/services/implementers/InterfaceProjectionBuilder.groovy
@@ -60,26 +60,39 @@ trait InterfaceProjectionBuilder {
     @Generated
     boolean isInterfaceProjection(ClassNode domainClass, MethodNode 
methodNode, ClassNode returnType) {
         if (returnType.isInterface() && 
!returnType.packageName?.startsWith('java.')) {
-            List<String> interfacePropertyNames = 
AstPropertyResolveUtils.getPropertyNames(returnType)
+            return hasCompatibleProperties(domainClass, returnType)
+        }
+        return false
+    }
 
-            for (prop in interfacePropertyNames) {
-                ClassNode existingType = 
AstPropertyResolveUtils.getPropertyType(domainClass, prop)
-                ClassNode propertyType = 
AstPropertyResolveUtils.getPropertyType(returnType, prop)
-                if (existingType == null) {
-                    return false
-                }
-                else if 
(!AstUtils.isSubclassOfOrImplementsInterface(existingType, propertyType)) {
-                    return false
-                }
+    /**
+     * Whether every property declared by the candidate interface has a 
compatible
+     * property of the same name on the domain class
+     *
+     * @param domainClass The domain class
+     * @param candidateType The interface being considered as a projection
+     * @return True if every property is compatible
+     */
+    @Generated
+    boolean hasCompatibleProperties(ClassNode domainClass, ClassNode 
candidateType) {
+        List<String> interfacePropertyNames = 
AstPropertyResolveUtils.getPropertyNames(candidateType)
+
+        for (prop in interfacePropertyNames) {
+            ClassNode existingType = 
AstPropertyResolveUtils.getPropertyType(domainClass, prop)
+            ClassNode propertyType = 
AstPropertyResolveUtils.getPropertyType(candidateType, prop)
+            if (existingType == null) {
+                return false
+            }
+            else if (!AstUtils.isSubclassOfOrImplementsInterface(existingType, 
propertyType)) {
+                return false
             }
-            return true
         }
-        return false
+        return true
     }
 
     @Generated
     MethodNode buildInterfaceImpl(ClassNode interfaceNode, ClassNode 
declaringClass, ClassNode targetDomainClass, MethodNode abstractMethodNode) {
-        List<Expression> getterNames = (List<Expression>) 
AstPropertyResolveUtils.getPropertyNames(interfaceNode)
+        List<Expression> getterNames = (List<Expression>) (List) 
AstPropertyResolveUtils.getPropertyNames(interfaceNode)
                 .collect() {
                     new ConstantExpression(NameUtils.getGetterName(it))
                 }
diff --git 
a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/services/implementers/IterableInterfaceProjectionBuilder.groovy
 
b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/services/implementers/IterableInterfaceProjectionBuilder.groovy
index 78bda7209a..b7dd045357 100644
--- 
a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/services/implementers/IterableInterfaceProjectionBuilder.groovy
+++ 
b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/services/implementers/IterableInterfaceProjectionBuilder.groovy
@@ -31,7 +31,6 @@ import org.codehaus.groovy.ast.expr.VariableExpression
 import org.codehaus.groovy.ast.stmt.Statement
 
 import org.grails.datastore.gorm.services.ServiceImplementer
-import org.grails.datastore.gorm.transform.AstPropertyResolveUtils
 import org.grails.datastore.mapping.reflect.AstGenericsUtils
 import org.grails.datastore.mapping.reflect.AstUtils
 
@@ -71,20 +70,7 @@ trait IterableInterfaceProjectionBuilder extends 
InterfaceProjectionBuilder {
         if (AstUtils.isSubclassOfOrImplementsInterface(returnType, 
Iterable.name) || returnType.isArray()) {
             ClassNode genericType = 
AstGenericsUtils.resolveSingleGenericType(returnType)
             if (genericType != null && genericType.isInterface() && 
!genericType.packageName?.startsWith('java.')) {
-
-                List<String> interfacePropertyNames = 
AstPropertyResolveUtils.getPropertyNames(genericType)
-
-                for (prop in interfacePropertyNames) {
-                    ClassNode existingType = 
AstPropertyResolveUtils.getPropertyType(domainClass, prop)
-                    ClassNode propertyType = 
AstPropertyResolveUtils.getPropertyType(genericType, prop)
-                    if (existingType == null) {
-                        return false
-                    }
-                    else if 
(!AstUtils.isSubclassOfOrImplementsInterface(existingType, propertyType)) {
-                        return false
-                    }
-                }
-                return true
+                return hasCompatibleProperties(domainClass, genericType)
             }
         }
         return false

Reply via email to