void visitAfterTraitApplied()

in grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/services/transform/ServiceTransformation.groovy [158:329]


    void visitAfterTraitApplied(SourceUnit sourceUnit, AnnotationNode annotationNode, ClassNode classNode) {
        // if the class node is an interface we are going to try and generate an implementation
        // and add the implementation as an inner class. If any method of the interface cannot be implemented
        // a compilation error occurs
        boolean isInterface = classNode.isInterface()
        boolean isAbstractClass = !isInterface && Modifier.isAbstract(classNode.modifiers)

        List<FieldNode> propertiesFields = []
        if (isAbstractClass) {
            List<PropertyNode> properties = classNode.getProperties()
            for (PropertyNode pn in properties) {
                ClassNode propertyType = pn.type
                if (hasAnnotation(propertyType, Service) && propertyType != classNode && Modifier.isPublic(pn.modifiers) && pn.getterBlock == null && pn.setterBlock == null) {
                    FieldNode field = pn.field
                    VariableExpression fieldVar = varX(field)
                    propertiesFields.add(field)
                    pn.setGetterBlock(
                        block(
                            ifS( equalsNullX(fieldVar),
                                assignX(fieldVar, callX( varX("datastore"), "getService", classX(propertyType.plainNodeReference)))
                            ),
                            returnS(fieldVar)
                        )
                    )
                }
            }

            List<ConstructorNode> constructors = classNode.getDeclaredConstructors()
            if(!constructors.isEmpty()) {
                error(sourceUnit, classNode, "Abstract data Services should not define constructors")
            }

        }

        if (isInterface || isAbstractClass) {
            // create a new class to represent the implementation
            String packageName = classNode.packageName ? "${classNode.packageName}." : ""
            ClassNode[] interfaces = isInterface ? ([classNode.plainNodeReference] as ClassNode[]) : new ClassNode[0]
            ClassNode superClass = isInterface ? ClassHelper.OBJECT_TYPE : classNode.plainNodeReference
            String serviceClassName = classNode.nameWithoutPackage
            ClassNode impl = new ClassNode("${packageName}\$${serviceClassName}Implementation", // class name
                    Opcodes.ACC_PUBLIC, // public
                    superClass,
                    interfaces)

            if(!propertiesFields.isEmpty()) {

                ClassNode datastoreType = ClassHelper.make(Datastore)
                FieldNode datastoreField = impl.addField("datastore", Modifier.PRIVATE, datastoreType, null)
                VariableExpression datastoreFieldVar = varX(datastoreField)


                BlockStatement body = block()
                Parameter datastoreParam = param(datastoreType, "d")
                impl.addMethod("setDatastore", Modifier.PUBLIC, ClassHelper.VOID_TYPE, params(
                        datastoreParam
                ), null, body )
                body.addStatement(
                        assignS(datastoreFieldVar, varX(datastoreParam))
                )
                impl.addMethod("getDatastore", Modifier.PUBLIC, datastoreType.plainNodeReference, ZERO_PARAMETERS, null,
                        returnS( datastoreFieldVar )
                )
                for(FieldNode fn in propertiesFields) {
                    body.addStatement(
                            assignS(varX(fn), callX(datastoreFieldVar, "getService", classX(fn.type.plainNodeReference)))
                    )
                }
            }

            copyAnnotations(classNode, impl)
            AnnotationNode serviceAnnotation = findAnnotation(impl, Service)
            if(serviceAnnotation.getMember("name") == null) {
                serviceAnnotation
                        .setMember("name", new ConstantExpression(Introspector.decapitalize(serviceClassName)))
            }
            // add compile static by default
            impl.addAnnotation(new AnnotationNode(COMPILE_STATIC_TYPE))
            // weave the trait class
            ClassExpression ce = (ClassExpression) annotationNode.getMember("value")
            ClassNode targetDomainClass = ce != null ? ce.type : ClassHelper.OBJECT_TYPE
            // weave with generic argument
            weaveTraitWithGenerics(impl, getTraitClass(), targetDomainClass)

            List<MethodNode> abstractMethods = findAllUnimplementedAbstractMethods(classNode)
            Iterable<ServiceImplementer> implementers = findServiceImplementors(annotationNode)


            // first go through the existing implemented methods and just enhance them
            if (!isInterface) {
                for (MethodNode existing in classNode.methods) {
                    int modifiers = existing.modifiers
                    if (!Modifier.isAbstract(modifiers) && Modifier.isPublic(modifiers) && !existing.isStatic()) {
                        for (ServiceImplementer implementer in implementers) {
                            if (implementer instanceof ServiceEnhancer) {
                                ServiceEnhancer enhancer = (ServiceEnhancer) implementer
                                if (enhancer.doesEnhance(targetDomainClass, existing)) {
                                    enhancer.enhance(targetDomainClass, existing, existing, impl)
                                }
                            }
                        }
                    }
                }
            }

            // go through the abstract methods and implement them
            for (MethodNode method in abstractMethods) {

                // find an implementer that implements the method
                MethodNode methodImpl = null
                for (ServiceImplementer implementer in implementers) {
                    if (implementer.doesImplement(targetDomainClass, method)) {
                        methodImpl = new MethodNode(
                                method.name,
                                Modifier.PUBLIC,
                                GenericsUtils.makeClassSafeWithGenerics(method.returnType, method.returnType.genericsTypes),
                                copyParameters(method.parameters),
                                method.exceptions,
                                new BlockStatement())
                        methodImpl.setDeclaringClass(impl)
                        if (Modifier.isProtected(method.modifiers)) {
                            if (!TransactionalTransform.hasTransactionalAnnotation(methodImpl)) {
                                addAnnotationIfNecessary(methodImpl, NotTransactional)
                            }
                        }
                        implementer.implement(targetDomainClass, method, methodImpl, impl)
                        def implementedAnn = new AnnotationNode(ClassHelper.make(Implemented))
                        Class implementedClass = implementer.getClass()
                        if(implementer instanceof AdaptedImplementer) {
                            implementedClass = ((AdaptedImplementer)implementer).getAdapted().getClass()
                        }
                        implementedAnn.setMember("by", classX(implementedClass))
                        methodImpl.addAnnotation(implementedAnn)
                        impl.addMethod(methodImpl)
                        break
                    }
                }

                // the method couldn't be implemented so error
                if (methodImpl == null) {
                    error(sourceUnit, classNode.isPrimaryClassNode() ? method : classNode, "No implementations possible for method '${method.typeDescriptor}'. Please use an abstract class instead and provide an implementation.")
                    break
                } else {
                    for (ServiceImplementer implementer in implementers) {
                        if (implementer instanceof ServiceEnhancer) {
                            ServiceEnhancer enhancer = ((ServiceEnhancer) implementer)
                            if (enhancer.doesEnhance(targetDomainClass, method)) {
                                enhancer.enhance(targetDomainClass, method, methodImpl, impl)
                            }
                        }
                    }
                }
            }

            if (compilationUnit != null) {
                TraitComposer.doExtendTraits(impl, sourceUnit, compilationUnit)
            }


            Expression exposeExpr = annotationNode.getMember("expose")
            if (exposeExpr == null || (exposeExpr instanceof ConstantExpression && exposeExpr == ConstantExpression.TRUE)) {
                generateServiceDescriptor(sourceUnit, impl)
            }

            sourceUnit.getAST().addClass(impl)
        } else {
            Expression exposeExpr = annotationNode.getMember("expose")
            if (exposeExpr == null || (exposeExpr instanceof ConstantExpression && exposeExpr == ConstantExpression.TRUE)) {
                generateServiceDescriptor(sourceUnit, classNode)
            }
        }
    }