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
