Skip to content

Commit

Permalink
GRAILS-6915 Make Methods aware of command objects
Browse files Browse the repository at this point in the history
- def action = { Command cmd -> ... }
- def action(Command cmd) { ... }
- become
@action(commandObjects=[Command])
def action(){ def cmd = new Command(); bindData(cmd,params); ... }

- upgrade tests
- Adding tests for closures actions conversion
- Upgrade ArtefactTypeAst -> now scan all injectors
- adding BuildSettings CONVERT CLOSURE key to System properties
-Testing command objects
- Adding Command injection
  • Loading branch information
Stephane Maldini committed Mar 29, 2011
1 parent 3097f5b commit e337955
Show file tree
Hide file tree
Showing 10 changed files with 278 additions and 123 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ class BuildSettings extends AbstractBuildSettings {
* A property name to enable/disable AST conversion of closures actions&tags to methods
*/

private static final String CONVERT_CLOSURES_KEY = "grails.compile.artefacts.closures.convert"
public static final String CONVERT_CLOSURES_KEY = "grails.compile.artefacts.closures.convert"


/**
Expand Down Expand Up @@ -271,8 +271,6 @@ class BuildSettings extends AbstractBuildSettings {

List buildListeners = []

boolean convertClosuresArtefacts = false


/**
* Setting for whether or not to enable verbose compilation, can be overridden via -verboseCompile(=[true|false])?
Expand Down Expand Up @@ -644,10 +642,10 @@ class BuildSettings extends AbstractBuildSettings {
projectWarExplodedDirSet = true
}

boolean getConvertClosuresArtefacts(){ convertClosuresArtefacts }
boolean getConvertClosuresArtefacts(){ System.properties[CONVERT_CLOSURES_KEY]?.toBoolean() }

void setConvertClosuresArtefacts(boolean convert){
convertClosuresArtefacts = convert
System.properties[CONVERT_CLOSURES_KEY] = convert
convertClosuresArtefactsSet = true
}

Expand Down
3 changes: 2 additions & 1 deletion grails-core/src/main/groovy/grails/web/Action.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2004-2005 the original author or authors.
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -31,4 +31,5 @@
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Action {
Class[] commandObjects() default {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,8 @@ public void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
ConstantExpression ce = (ConstantExpression) value;
String artefactType = ce.getText();
try {
ClassInjector[] classInjectors = GrailsAwareInjectionOperation.getClassInjectors();
GrailsArtefactClassInjector injector = findInjector(artefactType, classInjectors);
if(injector != null) {
injector.performInjection(sourceUnit,cNode);
return;
}
if(injectIfArtefact(artefactType, cNode, sourceUnit))return;

} catch (RuntimeException e) {
e.printStackTrace();
System.out.println("Error occurred calling AST injector: " + e.getMessage());
Expand All @@ -80,16 +76,19 @@ public void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
throw new RuntimeException("Class ["+cName+"] contains an invalid @Artefact annotation. No artefact found for value specified.");
}

private GrailsArtefactClassInjector findInjector(String artefactType, ClassInjector[] classInjectors) {
private boolean injectIfArtefact(String artefactType, ClassNode cNode, SourceUnit sourceUnit) {
ClassInjector[] classInjectors = GrailsAwareInjectionOperation.getClassInjectors();
boolean isArtefactFound=false;
for (ClassInjector classInjector : classInjectors) {
if(classInjector instanceof GrailsArtefactClassInjector) {
GrailsArtefactClassInjector gace = (GrailsArtefactClassInjector) classInjector;

if(artefactType.equals(gace.getArtefactType())) {
return gace;
gace.performInjection(sourceUnit,cNode);
isArtefactFound=true;
}
}
}
return null;
return isArtefactFound;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public void afterPropertiesSet() throws Exception {

Class<?>[] loadedPlugins = classes.toArray(new Class[classes.size()]);

pluginManager = new DefaultGrailsPluginManager(loadedPlugins, application);
pluginManager = new DefaultGrailsPluginManager(loadedPlugins, application);
pluginManager.setApplicationContext(applicationContext);
PluginManagerHolder.setPluginManager(pluginManager);
pluginManager.loadPlugins();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.classgen.GeneratorContext;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.grails.commons.ControllerArtefactHandler;
import org.codehaus.groovy.grails.compiler.injection.AstTransformer;
import org.codehaus.groovy.grails.compiler.injection.ClassInjector;
import org.codehaus.groovy.grails.compiler.injection.GrailsArtefactClassInjector;
import org.codehaus.groovy.syntax.Token;
import org.codehaus.groovy.syntax.Types;

Expand Down Expand Up @@ -82,7 +83,7 @@ def someAction(){
}
/ becomes behind the scene :
@Action def lol4(){
@Action(commandObjects={PeterCommand}) def lol4(){
PeterCommand cmd = new PeterCommand(); bindData(cmd, params)
render 'testxx'
}
Expand All @@ -93,11 +94,12 @@ def someAction(){
*/

@AstTransformer
public class MethodActionTransformer implements ClassInjector {
public class MethodActionTransformer implements GrailsArtefactClassInjector {

private static final AnnotationNode ACTION_ANNOTATION_NODE = new AnnotationNode(new ClassNode(Action.class));
private static final AnnotationNode ARTEFACT_ANNOTATION_NODE = new AnnotationNode(new ClassNode(Artefact.class));
private static final Parameter[] EMPTY_PARAMS = new Parameter[0];
private static final String ACTION_MEMBER_TARGET = "commandObjects";
private static final ClassNode[] EMPTY_EXCEPTIONS = new ClassNode[0];
private static final VariableExpression THIS_EXPRESSION = new VariableExpression("this");
private static final VariableExpression PARAMS_EXPRESSION = new VariableExpression("params");
Expand All @@ -106,8 +108,11 @@ public class MethodActionTransformer implements ClassInjector {
private Boolean converterEnabled;

public MethodActionTransformer() {
final BuildSettings settings = BuildSettingsHolder.getSettings();
converterEnabled = settings != null && settings.getConvertClosuresArtefacts();
converterEnabled = Boolean.parseBoolean(System.getProperties().getProperty(BuildSettings.CONVERT_CLOSURES_KEY));
}

public String getArtefactType() {
return ControllerArtefactHandler.TYPE;
}

public void performInjection(SourceUnit source, GeneratorContext context, ClassNode classNode) {
Expand All @@ -120,45 +125,81 @@ public void performInjection(SourceUnit source, GeneratorContext context, ClassN
private void annotateCandidateActionMethods(ClassNode classNode) {
for (MethodNode method : classNode.getMethods()) {
if (!method.isStatic() &&
method.getParameters().length == 0 &&
method.getAnnotations(ACTION_ANNOTATION_NODE.getClassNode()).isEmpty() &&
method.getLineNumber() >= 0
) {

method.addAnnotation(ACTION_ANNOTATION_NODE);
method.setCode(bodyCode(method.getParameters(), method.getCode()));
convertToMethodAction(method);
}
}
}

private void convertToMethodAction(MethodNode method) {
if (isCommandObjectAction(method.getParameters())) {

ListExpression initArray = new ListExpression();


for (Parameter parameter : method.getParameters()) {
initArray.addExpression(new ClassExpression(parameter.getType()));
}

AnnotationNode paramActionAnn = new AnnotationNode(new ClassNode(Action.class));
paramActionAnn.setMember(ACTION_MEMBER_TARGET, initArray);
method.addAnnotation(paramActionAnn);

} else {
method.addAnnotation(ACTION_ANNOTATION_NODE);
}
method.setParameters(EMPTY_PARAMS);
}

//See WebMetaUtils#isCommandObjectAction
private boolean isCommandObjectAction(Parameter[] params) {
return params != null && params.length > 0
&& params[0].getType() != new ClassNode(Object[].class)
&& params[0].getType() != new ClassNode(Object.class);
}


private void transformClosuresToMethods(ClassNode classNode) {
List<PropertyNode> fields = new ArrayList<PropertyNode>(classNode.getProperties());
List<PropertyNode> propertyNodes = new ArrayList<PropertyNode>(classNode.getProperties());

Expression initialExpression;
ClosureExpression closureAction;
MethodNode actionMethod;

for (PropertyNode field : fields) {
initialExpression = field.getInitialExpression();
if (!field.isStatic() &&
for (PropertyNode property : propertyNodes) {
initialExpression = property.getInitialExpression();
if (!property.isStatic() &&
initialExpression != null && initialExpression.getClass().equals(ClosureExpression.class)) {
closureAction = (ClosureExpression) initialExpression;
actionMethod = new MethodNode(field.getName(), Modifier.PUBLIC, field.getType(), EMPTY_PARAMS, EMPTY_EXCEPTIONS, closureCode(closureAction));
actionMethod.addAnnotation(ACTION_ANNOTATION_NODE);

classNode.getProperties().remove(field);
actionMethod = new MethodNode(
property.getName(),
Modifier.PUBLIC, property.getType(),
closureAction.getParameters(),
EMPTY_EXCEPTIONS,
bodyCode(closureAction.getParameters(), closureAction.getCode())
);

convertToMethodAction(actionMethod);

classNode.getProperties().remove(property);
classNode.getFields().remove(property.getField());
classNode.addMethod(actionMethod);
}
}
}

private Statement closureCode(ClosureExpression closureAction) {
private Statement bodyCode(Parameter[] actionParameters, Statement actionCode) {
Statement newCommandCode;
ConstructorCallExpression constructorCallExpression;
ArgumentListExpression arguments;

BlockStatement wrapper = new BlockStatement();

for (Parameter param : closureAction.getParameters()) {
for (Parameter param : actionParameters) {
constructorCallExpression = new ConstructorCallExpression(param.getType(), EMPTY_TUPLE);
newCommandCode = new ExpressionStatement(
new DeclarationExpression(new VariableExpression(param.getName(), param.getType()),
Expand All @@ -174,7 +215,7 @@ private Statement closureCode(ClosureExpression closureAction) {
wrapper.addStatement(new ExpressionStatement(new MethodCallExpression(THIS_EXPRESSION, "bindData", arguments)));
}

wrapper.addStatement(closureAction.getCode());
wrapper.addStatement(actionCode);

return wrapper;
}
Expand Down
Loading

0 comments on commit e337955

Please sign in to comment.