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)
}
}
}