You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2021/07/25 09:49:33 UTC
[groovy] branch master updated: Support GINQ methods
This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 53db604 Support GINQ methods
53db604 is described below
commit 53db604641b8f4b56343ece679a374c797828b41
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sun Jul 25 14:32:09 2021 +0800
Support GINQ methods
---
.../src/main/groovy/groovy/ginq/transform/GQ.java | 26 ++++
.../apache/groovy/ginq/GinqGroovyMethods.groovy | 2 +-
.../ginq/provider/collection/GinqAstWalker.groovy | 2 +-
.../ginq/transform/GinqASTTransformation.java | 94 +++++++++++++++
.../test/org/apache/groovy/ginq/GinqTest.groovy | 134 +++++++++++++++++++++
5 files changed, 256 insertions(+), 2 deletions(-)
diff --git a/subprojects/groovy-ginq/src/main/groovy/groovy/ginq/transform/GQ.java b/subprojects/groovy-ginq/src/main/groovy/groovy/ginq/transform/GQ.java
new file mode 100644
index 0000000..80373cd
--- /dev/null
+++ b/subprojects/groovy-ginq/src/main/groovy/groovy/ginq/transform/GQ.java
@@ -0,0 +1,26 @@
+package groovy.ginq.transform;
+
+import org.apache.groovy.lang.annotation.Incubating;
+import org.codehaus.groovy.transform.GroovyASTTransformationClass;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Method annotation to make a method call returning GINQ result
+ *
+ * @since 4.0.0
+ */
+@Incubating
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+@GroovyASTTransformationClass("org.apache.groovy.ginq.transform.GinqASTTransformation")
+public @interface GQ {
+ boolean optimize() default true;
+ boolean parallel() default false;
+ String astWalker() default "org.apache.groovy.ginq.provider.collection.GinqAstWalker";
+}
diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/GinqGroovyMethods.groovy b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/GinqGroovyMethods.groovy
index 5517bbb..0e41754 100644
--- a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/GinqGroovyMethods.groovy
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/GinqGroovyMethods.groovy
@@ -146,10 +146,10 @@ class GinqGroovyMethods {
private GinqGroovyMethods() {}
+ public static final List<String> CONF_LIST = [CONF_PARALLEL, CONF_AST_WALKER, CONF_OPTIMIZE]
public static final String CONF_PARALLEL = 'parallel'
private static final String CONF_AST_WALKER = 'astWalker'
private static final String CONF_OPTIMIZE = 'optimize'
- private static final List<String> CONF_LIST = [CONF_PARALLEL, CONF_AST_WALKER, CONF_OPTIMIZE]
private static final String DEFAULT_AST_WALKER_CLASS_NAME = GinqAstWalker.class.name
private static final String TRUE_STR = 'true'
}
diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
index 6f33278..235af22 100644
--- a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
@@ -217,7 +217,7 @@ class GinqAstWalker implements GinqAstVisitor<Expression>, SyntaxErrorReportable
}
statementList << returnS(varX(resultName))
- def resultLambda = lambdaX(block(statementList as Statement[]))
+ def resultLambda = lambdaX(null, block(statementList as Statement[]))
def result = parallelEnabled
? callX(callX(QUERYABLE_HELPER_TYPE, 'submit', args(resultLambda)), "get")
: callX(resultLambda, "call")
diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/transform/GinqASTTransformation.java b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/transform/GinqASTTransformation.java
new file mode 100644
index 0000000..845fc48
--- /dev/null
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/transform/GinqASTTransformation.java
@@ -0,0 +1,94 @@
+package org.apache.groovy.ginq.transform;
+
+import groovy.ginq.transform.GQ;
+import org.apache.groovy.ginq.GinqGroovyMethods;
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MapEntryExpression;
+import org.codehaus.groovy.ast.expr.MapExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.classgen.VariableScopeVisitor;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.AbstractASTTransformation;
+import org.codehaus.groovy.transform.GroovyASTTransformation;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.apache.groovy.ginq.GinqGroovyMethods.transformGinqCode;
+import static org.codehaus.groovy.ast.ClassHelper.make;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.mapX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+
+/**
+ * Handles generation of code for the {@code @GQ} annotation.
+ * @since 4.0.0
+ */
+@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
+public class GinqASTTransformation extends AbstractASTTransformation {
+ private static final ClassNode GQ_CLASS_NODE = make(GQ.class);
+
+ @Override
+ public void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
+ ModuleNode moduleNode = sourceUnit.getAST();
+ List<ClassNode> classNodeList = moduleNode.getClasses();
+ if (classNodeList == null) return;
+
+ classNodeList.stream().flatMap(c -> c.getMethods().stream())
+ .filter(m -> !m.getAnnotations(GQ_CLASS_NODE).isEmpty())
+ .map(m -> {
+ if (m.isAbstract()) {
+ addError("Error during " + GQ_CLASS_NODE.getName() + " processing: annotation not allowed on abstract method '" + m.getName() + "'", m);
+ return m.getDeclaringClass();
+ }
+ BlockStatement origCode = (BlockStatement) m.getCode();
+ MapExpression ginqConfigurationMapExpression = makeGinqConfigurationMapExpression(m);
+ BlockStatement newCode = block(
+ returnS(transformGinqCode(sourceUnit, ginqConfigurationMapExpression, origCode))
+ );
+ newCode.setSourcePosition(origCode);
+ m.setCode(newCode);
+ return m.getDeclaringClass();
+ }).distinct()
+ .forEach(c -> {
+ VariableScopeVisitor variableScopeVisitor = new VariableScopeVisitor(sourceUnit);
+ variableScopeVisitor.visitClass(c);
+ });
+ }
+
+ private MapExpression makeGinqConfigurationMapExpression(MethodNode m) {
+ Map<String, Expression> resultMembers = new HashMap<>();
+ Map<String, Expression> defaultMembers = GinqGroovyMethods.CONF_LIST.stream().collect(Collectors.toMap(
+ c -> c,
+ c -> {
+ try {
+ return constX(GQ_CLASS_NODE.getTypeClass().getMethod(c).getDefaultValue());
+ } catch (NoSuchMethodException e) {
+ throw new GroovyBugError("Unknown GINQ option: " + c, e);
+ }
+ }
+ ));
+ resultMembers.putAll(defaultMembers);
+
+ AnnotationNode gqAnnotationNode = m.getAnnotations(GQ_CLASS_NODE).get(0);
+ Map<String, Expression> members = gqAnnotationNode.getMembers();
+ resultMembers.putAll(members);
+
+ MapExpression ginqConfigurationMapExpression =
+ mapX(resultMembers.entrySet().stream()
+ .map(e -> new MapEntryExpression(constX(e.getKey()), constX(e.getValue().getText())))
+ .collect(Collectors.toList()));
+
+ return ginqConfigurationMapExpression;
+ }
+}
diff --git a/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy b/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
index 1577535..1b0a3d0 100644
--- a/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
+++ b/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
@@ -6112,6 +6112,140 @@ class GinqTest {
'''
}
+ @Test
+ void "testGinqMethod - GQ - 1"() {
+ assertScript '''
+ import groovy.ginq.transform.GQ
+
+ @GQ
+ def ginq(x) {
+ from n in [1, 2, 3]
+ where n < x
+ select n
+ }
+
+ assert [1] == ginq(2).toList()
+ '''
+ }
+
+ @Test
+ void "testGinqMethod - GQ - 2"() {
+ assertScript '''
+ import groovy.ginq.transform.GQ
+
+ class GinqClass {
+ @GQ
+ def ginq(x) {
+ from n in [1, 2, 3]
+ where n < x
+ select n
+ }
+ }
+
+ assert [1, 2] == new GinqClass().ginq(3).toList()
+ '''
+ }
+
+ @Test
+ void "testGinqMethod - GQ - 3"() {
+ assertScript '''
+ import groovy.ginq.transform.GQ
+
+ class GinqClass {
+ static class Holder {
+ @GQ
+ def ginq(x) {
+ from n in [1, 2, 3]
+ where n < x
+ select n
+ }
+ }
+ }
+
+ assert [1, 2] == new GinqClass.Holder().ginq(3).toList()
+ '''
+ }
+
+ @Test
+ void "testGinqMethod - GQ - 4"() {
+ assertScript '''
+ import groovy.ginq.transform.GQ
+
+ @GQ(optimize=false)
+ def ginq(x) {
+ from n in [1, 2, 3]
+ where n < x
+ select n
+ }
+
+ assert [1] == ginq(2).toList()
+ '''
+ }
+
+ @Test
+ void "testGinqMethod - GQ - 5"() {
+ assertScript '''
+ import groovy.ginq.transform.GQ
+
+ @GQ(parallel=true)
+ def ginq(x) {
+ from n in [1, 2, 3]
+ where n < x
+ select n
+ }
+
+ assert [1] == ginq(2).toList()
+ '''
+ }
+
+ @Test
+ void "testGinqMethod - GQ - 6"() {
+ assertScript '''
+ import groovy.ginq.transform.GQ
+
+ @GQ(astWalker='org.apache.groovy.ginq.provider.collection.GinqAstWalker')
+ def ginq(x) {
+ from n in [1, 2, 3]
+ where n < x
+ select n
+ }
+
+ assert [1] == ginq(2).toList()
+ '''
+ }
+
+ @Test
+ void "testGinqMethod - GQ - 7"() {
+ assertScript '''
+ import groovy.ginq.transform.GQ
+
+ @GQ(optimize=false, parallel=true)
+ def ginq(x) {
+ from n in [1, 2, 3]
+ where n < x
+ select n
+ }
+
+ assert [1] == ginq(2).toList()
+ '''
+ }
+
+ @Test
+ void "testGinqMethod - GQ - 8"() {
+ assertScript '''
+ import groovy.ginq.transform.GQ
+
+ @GQ(optimize=false, parallel=true, astWalker='org.apache.groovy.ginq.provider.collection.GinqAstWalker')
+ def ginq(x) {
+ from n in [1, 2, 3]
+ where n < x
+ select n
+ }
+
+ assert [1] == ginq(2).toList()
+ '''
+ }
+
@AfterClass
static void "testGinq - shutdown - 0"() {
assertScript '''