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 <[email protected]> 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() ''' } }
