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 2020/10/06 13:36:31 UTC

[groovy] 06/22: GROOVY-8258: Implement the very basic LINQ

This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch GROOVY-8258
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 1ab90b54e7ed1b68f7eb31cb1398acb3a884ea97
Author: Daniel Sun <su...@apache.org>
AuthorDate: Mon Oct 5 04:08:42 2020 +0800

    GROOVY-8258: Implement the very basic LINQ
---
 .../apache/groovy/linq/LinqGroovyMethods.groovy    | 104 ++++++++++++++++++++-
 .../groovy/linq/provider/QueryableCollection.java  |   5 +
 .../groovy/org/apache/groovy/linq/LinqTest.groovy  |   9 +-
 3 files changed, 114 insertions(+), 4 deletions(-)

diff --git a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/LinqGroovyMethods.groovy b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/LinqGroovyMethods.groovy
index 224c958..cd6b502 100644
--- a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/LinqGroovyMethods.groovy
+++ b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/LinqGroovyMethods.groovy
@@ -18,16 +18,116 @@
  */
 package org.apache.groovy.linq
 
+import groovy.transform.ToString
+import org.codehaus.groovy.ast.ClassHelper
+import org.codehaus.groovy.ast.expr.ArgumentListExpression
 import org.codehaus.groovy.ast.expr.ClosureExpression
-import org.codehaus.groovy.ast.expr.ConstantExpression
 import org.codehaus.groovy.ast.expr.Expression
+import org.codehaus.groovy.ast.expr.MethodCallExpression
+import org.codehaus.groovy.ast.expr.VariableExpression
+import org.codehaus.groovy.ast.stmt.BlockStatement
+import org.codehaus.groovy.ast.stmt.ExpressionStatement
+import org.codehaus.groovy.ast.stmt.Statement
 import org.codehaus.groovy.macro.runtime.Macro
 import org.codehaus.groovy.macro.runtime.MacroContext
 
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callX
+import static org.codehaus.groovy.ast.tools.GeneralUtils.closureX
+import static org.codehaus.groovy.ast.tools.GeneralUtils.param
+import static org.codehaus.groovy.ast.tools.GeneralUtils.params
+import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt
+
 class LinqGroovyMethods {
     @Macro
     static Expression LINQ(MacroContext ctx, final ClosureExpression closureExpression) {
-        return macro { 'TODO LINQ' }
+        BlockStatement code = (BlockStatement) closureExpression.getCode()
+        List<Statement> statementList = code.getStatements()
+
+        LinqContext linqContext = new LinqContext()
+        for (Statement statement : statementList) {
+            ExpressionStatement expressionStatement = (ExpressionStatement) statement
+            MethodCallExpression methodCallExpression = (MethodCallExpression) expressionStatement.getExpression()
+
+            String methodName = methodCallExpression.getMethodAsString()
+            switch (methodName) {
+                case 'from': {
+                    break
+                }
+                case 'of': {
+                    MethodCallExpression fromMethodCallExpression = methodCallExpression.getObjectExpression()
+                    VariableExpression aliasVariable = (VariableExpression) ((ArgumentListExpression) fromMethodCallExpression.getArguments()).getExpression(0)
+                    Expression dataSourceExpression = ((ArgumentListExpression) methodCallExpression.getArguments()).getExpression(0)
+                    linqContext.addFrom(aliasVariable, dataSourceExpression)
+                    break
+                }
+                case 'where': {
+                    Expression conditionExpression = ((ArgumentListExpression) methodCallExpression.getArguments()).getExpression(0)
+                    linqContext.addWhere(conditionExpression)
+                    break
+                }
+                case 'select': {
+                    Expression selectExpression = ((ArgumentListExpression) methodCallExpression.getArguments()).getExpression(0)
+                    linqContext.addSelect(selectExpression)
+                    break
+                }
+                default: {
+                    break
+                }
+            }
+        }
+
+        constructLinqMethodCalls(linqContext)
+    }
+
+    private static constructLinqMethodCalls(LinqContext linqContext) {
+        Map.Entry<VariableExpression, Expression> fromEntry = linqContext.fromMap.entrySet().toList().get(0)
+        VariableExpression aliasVariable = fromEntry.key
+
+        MethodCallExpression from = macro {
+            org.apache.groovy.linq.provider.QueryableCollection
+                    .from($v { fromEntry.value })
+        }
+
+        MethodCallExpression where =
+                callX(
+                        from,
+                        "where",
+                        closureX(
+                                params(param(ClassHelper.DYNAMIC_TYPE, aliasVariable.name)),
+                                stmt(linqContext.whereList[0])
+                        )
+                )
+
+        MethodCallExpression select =
+                callX(
+                        where,
+                        "select",
+                        closureX(
+                                params(param(ClassHelper.DYNAMIC_TYPE, aliasVariable.name)),
+                                stmt(linqContext.selectList[0])
+                        )
+                )
+
+        return select
+    }
+
+    @ToString(includeNames=true)
+    static class LinqContext {
+        Map<VariableExpression, Expression> fromMap = new LinkedHashMap<>()
+        List<Expression> whereList = new ArrayList<>()
+        List<Expression> selectList = new ArrayList<>()
+
+        void addFrom(VariableExpression aliasVariable, Expression dataSourceExpression) {
+            fromMap.put(aliasVariable, dataSourceExpression)
+        }
+
+        void addWhere(Expression... conditionExpressions) {
+            whereList.addAll(conditionExpressions)
+        }
+
+        void addSelect(Expression... selectExpressions) {
+            selectList.addAll(selectExpressions)
+        }
     }
 }
 
diff --git a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/provider/QueryableCollection.java b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/provider/QueryableCollection.java
index 0eb3239..9d5d19d 100644
--- a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/provider/QueryableCollection.java
+++ b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/provider/QueryableCollection.java
@@ -243,4 +243,9 @@ public class QueryableCollection<T> implements Queryable<T>, Iterable<T> {
     private static <T> Iterable<T> toIterable(Stream<T> sourceStream) {
         return sourceStream::iterator;
     }
+
+    @Override
+    public String toString() {
+        return toList().toString();
+    }
 }
diff --git a/subprojects/groovy-linq/src/test/groovy/org/apache/groovy/linq/LinqTest.groovy b/subprojects/groovy-linq/src/test/groovy/org/apache/groovy/linq/LinqTest.groovy
index 701f1c6..a19d39e 100644
--- a/subprojects/groovy-linq/src/test/groovy/org/apache/groovy/linq/LinqTest.groovy
+++ b/subprojects/groovy-linq/src/test/groovy/org/apache/groovy/linq/LinqTest.groovy
@@ -28,9 +28,14 @@ import static groovy.test.GroovyAssert.assertScript
 @CompileStatic
 class LinqTest {
     @Test
-    void testLinqMacroMethod() {
+    void "testLinqMacroMethod - from where select"() {
         assertScript '''
-            assert 'TODO LINQ' == LINQ {}
+            def numbers = [0, 1, 2, 3, 4, 5]
+            assert [2, 4, 6] == LINQ {
+                from n of numbers
+                where n > 0 && n <= 3
+                select n * 2
+            }.toList()
         '''
     }
 }