Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@ScaffoldContoller and @ScaffoldService support #114

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ build/
*.ipr
# Ignore Gradle GUI config
gradle-app.setting
.DS_Store
2 changes: 0 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ dependencies {
api "org.grails:grails-web-boot"
api 'javax.servlet:javax.servlet-api'


api "io.github.gpc:fields:$fieldsVersion"
testImplementation "org.grails:grails-plugin-testing"

console "org.grails:grails-console"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,23 @@ package grails.plugin.scaffolding

import grails.codegen.model.ModelBuilder
import grails.io.IOUtils
import grails.plugin.scaffolding.annotation.ScaffoldController
import grails.util.BuildSettings
import grails.util.Environment
import groovy.text.GStringTemplateEngine
import groovy.text.Template
import groovy.transform.CompileStatic
import org.grails.buffer.FastStringWriter
import org.grails.compiler.scaffolding.ScaffoldingControllerInjector
import org.grails.web.servlet.mvc.GrailsWebRequest
import org.grails.web.servlet.view.GroovyPageView
import org.grails.web.servlet.view.GroovyPageViewResolver
import org.springframework.context.ResourceLoaderAware
import org.springframework.core.io.*
import org.springframework.web.servlet.View

import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.concurrent.ConcurrentHashMap

/**
Expand Down Expand Up @@ -82,6 +86,14 @@ class ScaffoldingViewResolver extends GroovyPageViewResolver implements Resource
def controllerClass = webR.controllerClass

def scaffoldValue = controllerClass?.getPropertyValue("scaffold")
if (!scaffoldValue) {
ScaffoldController scaffoldAnnotation = controllerClass?.clazz?.getAnnotation(ScaffoldController)
scaffoldValue = scaffoldAnnotation?.domain()
if (scaffoldValue == Void) {
scaffoldValue = null
}
}

if (scaffoldValue instanceof Class) {
def shortViewName = viewName.substring(viewName.lastIndexOf('/') + 1)
Resource res = null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package grails.plugin.scaffolding.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ScaffoldController {
Class<?> value() default Void.class;
Class<?> domain() default Void.class;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package grails.plugin.scaffolding.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ScaffoldService {
Class<?> value() default Void.class;
Class<?> domain() default Void.class;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.grails.compiler.scaffolding

import grails.compiler.ast.AstTransformer
import grails.compiler.ast.GrailsArtefactClassInjector
import grails.plugin.scaffolding.annotation.ScaffoldController
import grails.rest.RestfulController
import groovy.transform.CompileStatic
import org.codehaus.groovy.ast.ClassHelper
Expand All @@ -13,8 +14,9 @@ import org.grails.compiler.injection.GrailsASTUtils
import org.grails.compiler.web.ControllerActionTransformer
import org.grails.core.artefact.ControllerArtefactHandler
import org.grails.plugins.web.rest.transform.ResourceTransform

/**
* Transformation that turns a controller into a scaffolding controller at compile time of 'static scaffold = Foo'
* Transformation that turns a controller into a scaffolding controller at compile time if 'static scaffold = Foo'
* is specified
*
* @author Graeme Rocher
Expand All @@ -41,26 +43,46 @@ class ScaffoldingControllerInjector implements GrailsArtefactClassInjector {
@Override
void performInjectionOnAnnotatedClass(SourceUnit source, ClassNode classNode) {
def propertyNode = classNode.getProperty(PROPERTY_SCAFFOLD)
def annotationNode = classNode.getAnnotations(ClassHelper.make(ScaffoldController)).find()

def expression = propertyNode?.getInitialExpression()
if(expression instanceof ClassExpression) {

ClassNode superClassNode = ClassHelper.make(RestfulController).getPlainNodeReference()
def currentSuperClass = classNode.getSuperClass()
if(currentSuperClass.equals( GrailsASTUtils.OBJECT_CLASS_NODE )) {
def domainClass = ((ClassExpression) expression).getType()
if (expression instanceof ClassExpression || annotationNode) {
ClassNode controllerClassNode = annotationNode?.getMember("value")?.type
ClassNode superClassNode = ClassHelper.make(controllerClassNode?.getTypeClass()?:RestfulController).getPlainNodeReference()
ClassNode currentSuperClass = classNode.getSuperClass()
if (currentSuperClass.equals(GrailsASTUtils.OBJECT_CLASS_NODE)) {
def domainClass = expression? ((ClassExpression) expression).getType() : null
if (!domainClass) {
domainClass = annotationNode.getMember("domain")?.type
if (!domainClass) {
domainClass = extractGenericDomainClass(controllerClassNode)
if (domainClass) {
// set the domain value on the annotation so that ScaffoldingViewResolver can identify the domain object.
annotationNode.addMember("domain", new ClassExpression(domainClass))
}
}
if (!domainClass) {
GrailsASTUtils.error(source, classNode, "Scaffolded controller (${classNode.name}) with @ScaffoldController does not have domain class set.", true)
}
}
classNode.setSuperClass(GrailsASTUtils.nonGeneric(superClassNode, domainClass))
new ResourceTransform().addConstructor(classNode, domainClass, false)
}
else if( ! currentSuperClass.isDerivedFrom(superClassNode)) {
} else if (!currentSuperClass.isDerivedFrom(superClassNode)) {
GrailsASTUtils.error(source, classNode, "Scaffolded controllers (${classNode.name}) cannot extend other classes: ${currentSuperClass.getName()}", true)
}
}
else if(propertyNode != null) {
} else if (propertyNode != null) {
GrailsASTUtils.error(source, propertyNode, "The 'scaffold' property must refer to a domain class.", true)
}
}

protected static ClassNode extractGenericDomainClass(ClassNode controllerClassNode) {
def genericsTypes = controllerClassNode?.genericsTypes
if (genericsTypes && genericsTypes.length > 0) {
return genericsTypes[0].type
}
return null
}

@Override
boolean shouldInject(URL url) {
return url != null && ControllerActionTransformer.CONTROLLER_PATTERN.matcher(url.getFile()).find()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.grails.compiler.scaffolding

import grails.compiler.ast.AstTransformer
import grails.compiler.ast.GrailsArtefactClassInjector
import grails.plugin.scaffolding.annotation.ScaffoldService
import groovy.transform.CompileStatic
import org.codehaus.groovy.ast.ClassHelper
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.classgen.GeneratorContext
import org.codehaus.groovy.control.SourceUnit
import org.grails.compiler.injection.GrailsASTUtils
import org.grails.core.artefact.ServiceArtefactHandler
import org.grails.io.support.GrailsResourceUtils
import org.grails.plugins.web.rest.transform.ResourceTransform

import java.util.regex.Pattern

/**
* Transformation that turns a service into a scaffolding service at compile time if '@ScaffoldService'
* is specified
*
* @author Scott Murphy Heiberg
* @since 5.1
*/
@AstTransformer
@CompileStatic
class ScaffoldingServiceInjector implements GrailsArtefactClassInjector {

final String[] artefactTypes = [ServiceArtefactHandler.TYPE] as String[]
public static Pattern SERVICE_PATTERN = Pattern.compile(".+/" +
GrailsResourceUtils.GRAILS_APP_DIR + "/services/(.+)Service\\.groovy");

@Override
void performInjection(SourceUnit source, GeneratorContext context, ClassNode classNode) {
performInjectionOnAnnotatedClass(source, classNode)
}

@Override
void performInjection(SourceUnit source, ClassNode classNode) {
performInjectionOnAnnotatedClass(source, classNode)
}

@Override
void performInjectionOnAnnotatedClass(SourceUnit source, ClassNode classNode) {
def annotationNode = classNode.getAnnotations(ClassHelper.make(ScaffoldService)).find()
if (annotationNode) {
ClassNode serviceClassNode = annotationNode?.getMember("value")?.type
Class serviceClass = serviceClassNode?.getTypeClass()
ClassNode superClassNode = ClassHelper.make(serviceClass).getPlainNodeReference()
ClassNode currentSuperClass = classNode.getSuperClass()
if (currentSuperClass.equals(GrailsASTUtils.OBJECT_CLASS_NODE)) {
def domainClass = annotationNode.getMember("domain")?.type
if (!domainClass) {
domainClass = ScaffoldingControllerInjector.extractGenericDomainClass(serviceClassNode)
}
if (!domainClass) {
GrailsASTUtils.error(source, classNode, "Scaffolded service (${classNode.name}) with @ScaffoldService does not have domain class set.", true)
}
classNode.setSuperClass(GrailsASTUtils.nonGeneric(superClassNode, domainClass))
new ResourceTransform().addConstructor(classNode, domainClass, false)
} else if (!currentSuperClass.isDerivedFrom(superClassNode)) {
GrailsASTUtils.error(source, classNode, "Scaffolded services (${classNode.name}) cannot extend other classes: ${currentSuperClass.getName()}", true)
}
}
}

@Override
boolean shouldInject(URL url) {
return url != null && SERVICE_PATTERN.matcher(url.getFile()).find()
}
}
Loading