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


The following commit(s) were added to refs/heads/GROOVY-8258 by this push:
     new eaf593b  GROOVY-8258: Implement inner join
eaf593b is described below

commit eaf593bdb1cf3ad4ff18e3384fa80a23010c9c25
Author: Daniel Sun <[email protected]>
AuthorDate: Tue Oct 6 02:21:19 2020 +0800

    GROOVY-8258: Implement inner join
---
 .../org/apache/groovy/linq/dsl/GinqAstBuilder.java |  46 +++++--
 .../org/apache/groovy/linq/dsl/GinqBuilder.groovy  | 135 +++++++++++++++++++--
 .../org/apache/groovy/linq/dsl/GinqVisitor.java    |   6 +-
 ...{WhereExpression.java => FilterExpression.java} |  18 ++-
 .../linq/dsl/expression/FilterableExpression.java  |  10 +-
 .../groovy/linq/dsl/expression/FromExpression.java |   2 +-
 ...ereExpression.java => InnerJoinExpression.java} |  15 +--
 .../{FromExpression.java => JoinExpression.java}   |  30 ++---
 .../{WhereExpression.java => OnExpression.java}    |  16 +--
 .../linq/dsl/expression/SimpleGinqExpression.java  |   9 ++
 .../linq/dsl/expression/WhereExpression.java       |  10 +-
 .../groovy/org/apache/groovy/linq/GinqTest.groovy  |  14 +++
 12 files changed, 227 insertions(+), 84 deletions(-)

diff --git 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/GinqAstBuilder.java
 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/GinqAstBuilder.java
index c80e347..4905871 100644
--- 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/GinqAstBuilder.java
+++ 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/GinqAstBuilder.java
@@ -18,9 +18,12 @@
  */
 package org.apache.groovy.linq.dsl;
 
+import org.apache.groovy.linq.dsl.expression.FilterExpression;
 import org.apache.groovy.linq.dsl.expression.FilterableExpression;
 import org.apache.groovy.linq.dsl.expression.FromExpression;
 import org.apache.groovy.linq.dsl.expression.GinqExpression;
+import org.apache.groovy.linq.dsl.expression.InnerJoinExpression;
+import org.apache.groovy.linq.dsl.expression.OnExpression;
 import org.apache.groovy.linq.dsl.expression.SelectExpression;
 import org.apache.groovy.linq.dsl.expression.SimpleGinqExpression;
 import org.apache.groovy.linq.dsl.expression.WhereExpression;
@@ -57,14 +60,13 @@ public class GinqAstBuilder extends CodeVisitorSupport {
     public void visitMethodCallExpression(MethodCallExpression call) {
         super.visitMethodCallExpression(call);
         final String methodName = call.getMethodAsString();
-        System.out.println(methodName + " : " + call);
 
-        if ("from".equals(methodName)) {
+        if ("from".equals(methodName)  || "innerJoin".equals(methodName)) {
             ArgumentListExpression arguments = (ArgumentListExpression) 
call.getArguments();
             if (arguments.getExpressions().size() != 1) {
                 this.collectSyntaxError(
                         new GinqSyntaxError(
-                                "Only 1 argument expected for `from`, e.g. 
`from n in nums`",
+                                "Only 1 argument expected for `" + methodName 
+ "`, e.g. `" + methodName + " n in nums`",
                                 call.getLineNumber(), call.getColumnNumber()
                         )
                 );
@@ -74,7 +76,7 @@ public class GinqAstBuilder extends CodeVisitorSupport {
                     && ((BinaryExpression) 
expression).getOperation().getType() == Types.KEYWORD_IN)) {
                 this.collectSyntaxError(
                         new GinqSyntaxError(
-                                "`in` is expected for `from`, e.g. `from n in 
nums`",
+                                "`in` is expected for `" + methodName + "`, 
e.g. `" + methodName + " n in nums`",
                                 call.getLineNumber(), call.getColumnNumber()
                         )
                 );
@@ -83,21 +85,39 @@ public class GinqAstBuilder extends CodeVisitorSupport {
             Expression aliasExpr = binaryExpression.getLeftExpression();
             Expression dataSourceExpr = binaryExpression.getRightExpression();
 
-            FromExpression fromExpression = new FromExpression(aliasExpr, 
dataSourceExpr);
-            fromExpression.setSourcePosition(call);
+            if ("from".equals(methodName)) {
+                FromExpression fromExpression = new FromExpression(aliasExpr, 
dataSourceExpr);
+                fromExpression.setSourcePosition(call);
+                simpleGinqExpression.addFromExpression(fromExpression);
+                ginqExpression = fromExpression;
+            } else if ("innerJoin".equals(methodName)) {
+                InnerJoinExpression innerJoinExpression = new 
InnerJoinExpression(aliasExpr, dataSourceExpr);
+                innerJoinExpression.setSourcePosition(call);
+                simpleGinqExpression.addJoinExpression(innerJoinExpression);
+                ginqExpression = innerJoinExpression;
+            }
 
-            simpleGinqExpression.addFromExpression(fromExpression);
-            ginqExpression = fromExpression;
             return;
         }
 
-        if ("where".equals(methodName)) {
+        if ("where".equals(methodName) || "on".equals(methodName)) {
             Expression filterExpr = ((ArgumentListExpression) 
call.getArguments()).getExpression(0);
-            WhereExpression whereExpression = new WhereExpression(filterExpr);
-            whereExpression.setSourcePosition(call);
 
-            if (ginqExpression instanceof FilterableExpression) {
-                ((FilterableExpression) 
ginqExpression).setWhereExpression(whereExpression);
+            FilterExpression filterExpression = null;
+            if ("where".equals(methodName)) {
+                filterExpression = new WhereExpression(filterExpr);
+            } else if ("on".equals(methodName)) {
+                filterExpression = new OnExpression(filterExpr);
+            }
+
+            if (null == filterExpression) {
+                throw new GroovyBugError("Unknown method: " + methodName);
+            }
+
+            filterExpression.setSourcePosition(call);
+
+            if (ginqExpression instanceof FilterableExpression) { // TODO more 
strict check
+                ((FilterableExpression) 
ginqExpression).setFilterExpression(filterExpression);
             } else {
                 throw new GroovyBugError("The preceding expression is not a 
FilterableExpression: " + ginqExpression);
             }
diff --git 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/GinqBuilder.groovy
 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/GinqBuilder.groovy
index 098bd41..bbfe3cb 100644
--- 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/GinqBuilder.groovy
+++ 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/GinqBuilder.groovy
@@ -22,13 +22,19 @@ import groovy.transform.CompileDynamic
 import groovy.transform.CompileStatic
 import org.apache.groovy.linq.dsl.expression.FromExpression
 import org.apache.groovy.linq.dsl.expression.GinqExpression
-import org.apache.groovy.linq.dsl.expression.SimpleGinqExpression
+import org.apache.groovy.linq.dsl.expression.InnerJoinExpression
+import org.apache.groovy.linq.dsl.expression.JoinExpression
+import org.apache.groovy.linq.dsl.expression.OnExpression
 import org.apache.groovy.linq.dsl.expression.SelectExpression
+import org.apache.groovy.linq.dsl.expression.SimpleGinqExpression
 import org.apache.groovy.linq.dsl.expression.WhereExpression
 import org.codehaus.groovy.ast.ClassHelper
 import org.codehaus.groovy.ast.expr.ArgumentListExpression
 import org.codehaus.groovy.ast.expr.Expression
+import org.codehaus.groovy.ast.expr.ExpressionTransformer
+import org.codehaus.groovy.ast.expr.ListExpression
 import org.codehaus.groovy.ast.expr.MethodCallExpression
+import org.codehaus.groovy.ast.expr.VariableExpression
 
 import static org.codehaus.groovy.ast.tools.GeneralUtils.callX
 import static org.codehaus.groovy.ast.tools.GeneralUtils.lambdaX
@@ -43,20 +49,50 @@ import static 
org.codehaus.groovy.ast.tools.GeneralUtils.stmt
  */
 @CompileStatic
 class GinqBuilder implements GinqVisitor<Object> {
+
+    public static final String __RECEIVER_ALIAS_EXPR = "__receiverAliasExpr"
+    public static final String __INNER_JOIN_METHOD_RECEIVER = 
"__inner_join_method_receiver"
+    public static final String __T = "__t"
+    public static final String __FIRST_ALIAS_EXPR = "__first_alias_expr"
+    public static final String __SECOND_ALIAS_EXPR = "__second_alias_expr"
+
     @Override
     MethodCallExpression visitSimpleGinqExpression(SimpleGinqExpression 
simpleGinqExpression) {
         List<MethodCallExpression> fromMethodCallExpressionList = new 
LinkedList<>()
         List<FromExpression> fromExpressionList = 
simpleGinqExpression.getFromExpressionList()
-        for (FromExpression fromExpression : (fromExpressionList)) {
+        for (FromExpression fromExpression : fromExpressionList) {
             MethodCallExpression methodCallExpression = 
this.visitFromExpression(fromExpression)
             fromMethodCallExpressionList.add(methodCallExpression)
         }
 
         MethodCallExpression selectMethodReceiver = 
fromMethodCallExpressionList.getLast()
 
+        List<MethodCallExpression> innerJoinMethodCallExpressionList = new 
LinkedList<>()
+        List<JoinExpression> joinExpressionList = 
simpleGinqExpression.getJoinExpressionList()
+        for (JoinExpression joinExpression : joinExpressionList) {
+            joinExpression.putNodeMetaData(__INNER_JOIN_METHOD_RECEIVER, 
fromMethodCallExpressionList.getLast())
+            joinExpression.putNodeMetaData(__RECEIVER_ALIAS_EXPR, 
fromExpressionList.get(fromExpressionList.size() - 1).aliasExpr)
+            MethodCallExpression methodCallExpression = 
this.visitInnerJoinExpression((InnerJoinExpression) joinExpression)
+            innerJoinMethodCallExpressionList.add(methodCallExpression);
+        }
+
+        if (innerJoinMethodCallExpressionList) {
+            selectMethodReceiver = innerJoinMethodCallExpressionList.getLast()
+        }
+
         SelectExpression selectExpression = 
simpleGinqExpression.getSelectExpression()
         selectExpression.putNodeMetaData(__SELECT_METHOD_RECEIVER, 
selectMethodReceiver)
-        selectExpression.putNodeMetaData(__ALIAS_EXPR, 
fromExpressionList.get(fromExpressionList.size() - 1).aliasExpr)
+
+        if (joinExpressionList) {
+            JoinExpression lastJoinExpression = 
joinExpressionList.get(joinExpressionList.size() - 1)
+            selectExpression.putNodeMetaData(__FIRST_ALIAS_EXPR, 
lastJoinExpression.getNodeMetaData(__RECEIVER_ALIAS_EXPR))
+            selectExpression.putNodeMetaData(__SECOND_ALIAS_EXPR, 
lastJoinExpression.aliasExpr)
+
+            selectExpression.putNodeMetaData(__ALIAS_EXPR, new 
VariableExpression(__T))
+        } else {
+            selectExpression.putNodeMetaData(__ALIAS_EXPR, 
fromExpressionList.get(fromExpressionList.size() - 1).aliasExpr)
+        }
+
         MethodCallExpression selectMethodCallExpression = 
this.visitSelectExpression(selectExpression)
 
 
@@ -67,7 +103,7 @@ class GinqBuilder implements GinqVisitor<Object> {
     MethodCallExpression visitFromExpression(FromExpression fromExpression) {
         MethodCallExpression fromMethodCallExpression = 
constructFromMethodCallExpression(fromExpression)
 
-        WhereExpression whereExpression = fromExpression.getWhereExpression()
+        WhereExpression whereExpression = (WhereExpression) 
fromExpression.getFilterExpression()
         if (whereExpression) {
             whereExpression.putNodeMetaData(__FROM_EXPRESSION, fromExpression)
             whereExpression.putNodeMetaData(__FROM_METHOD_CALL_EXPRESSION, 
fromMethodCallExpression)
@@ -78,14 +114,47 @@ class GinqBuilder implements GinqVisitor<Object> {
         return fromMethodCallExpression
     }
 
+    @Override
+    MethodCallExpression visitInnerJoinExpression(InnerJoinExpression 
innerJoinExpression) {
+        Expression receiver = 
innerJoinExpression.getNodeMetaData(__INNER_JOIN_METHOD_RECEIVER)
+        Expression receiverAliasExpr = 
innerJoinExpression.getNodeMetaData(__RECEIVER_ALIAS_EXPR)
+        OnExpression onExpression = (OnExpression) 
innerJoinExpression.getFilterExpression()
+        MethodCallExpression innerJoinMethodCallExpression = 
constructInnerJoinMethodCallExpression(receiver, receiverAliasExpr, 
innerJoinExpression, onExpression)
+
+        return innerJoinMethodCallExpression
+    }
+
+    @Override
+    MethodCallExpression visitOnExpression(OnExpression onExpression) {
+        return null // do nothing
+    }
+
     @CompileDynamic
     private MethodCallExpression 
constructFromMethodCallExpression(FromExpression fromExpression) {
         macro {
-            org.apache.groovy.linq.provider.QueryableCollection
-                    .from($v { fromExpression.dataSourceExpr })
+            org.apache.groovy.linq.provider.QueryableCollection.from($v { 
fromExpression.dataSourceExpr })
         }
     }
 
+    @CompileDynamic
+    private MethodCallExpression 
constructInnerJoinMethodCallExpression(Expression receiver, Expression 
receiverAliasExpr, InnerJoinExpression innerJoinExpression, OnExpression 
onExpression) {
+        MethodCallExpression innerJoinMethodCallExpression = macro {
+            
$v{receiver}.innerJoin(org.apache.groovy.linq.provider.QueryableCollection.from($v
 { innerJoinExpression.dataSourceExpr }))
+        }
+
+        ((ArgumentListExpression) 
innerJoinMethodCallExpression.getArguments()).getExpressions().add(
+                lambdaX(
+                        params(
+                                param(ClassHelper.DYNAMIC_TYPE, 
receiverAliasExpr.text),
+                                param(ClassHelper.DYNAMIC_TYPE, 
innerJoinExpression.aliasExpr.text)
+                        ),
+                        stmt(onExpression.getFilterExpr())
+                )
+        )
+
+        return innerJoinMethodCallExpression
+    }
+
     @Override
     MethodCallExpression visitWhereExpression(WhereExpression whereExpression) 
{
         FromExpression fromExpression = 
whereExpression.getNodeMetaData(__FROM_EXPRESSION)
@@ -97,7 +166,59 @@ class GinqBuilder implements GinqVisitor<Object> {
     MethodCallExpression visitSelectExpression(SelectExpression 
selectExpression) {
         Expression selectMethodReceiver = 
selectExpression.getNodeMetaData(__SELECT_METHOD_RECEIVER)
         Expression aliasExpr = selectExpression.getNodeMetaData(__ALIAS_EXPR)
-        return callXWithLambda(selectMethodReceiver, "select", aliasExpr.text, 
((ArgumentListExpression) 
selectExpression.getProjectionExpr()).getExpression(0))
+        Expression projectionExpr = selectExpression.getProjectionExpr()
+
+        if (__T.equals(aliasExpr.text)) {
+            projectionExpr = 
correctVariablesOfProjectExpression(selectExpression, projectionExpr)
+        }
+
+        List<Expression> expressionList = ((ArgumentListExpression) 
projectionExpr).getExpressions()
+        Expression lambdaCode
+        if (expressionList.size() > 1) {
+            lambdaCode = new ListExpression(expressionList)
+        } else {
+            lambdaCode = expressionList.get(0)
+        }
+
+        return callXWithLambda(selectMethodReceiver, "select", aliasExpr.text, 
lambdaCode)
+    }
+
+    private Expression correctVariablesOfProjectExpression(SelectExpression 
selectExpression, Expression projectionExpr) {
+        final Expression firstAliasExpr = 
selectExpression.getNodeMetaData(__FIRST_ALIAS_EXPR)
+        final Expression secondAliasExpr = 
selectExpression.getNodeMetaData(__SECOND_ALIAS_EXPR)
+
+        projectionExpr = projectionExpr.transformExpression(new 
ExpressionTransformer() {
+            @Override
+            Expression transform(Expression expression) {
+                if (expression instanceof VariableExpression) {
+                    Expression transformedExpression = null
+                    if (firstAliasExpr.text == expression.text) {
+                        // replace `n1` with `__t.v1`
+                        transformedExpression = 
constructFirstAliasVariableAccess()
+                    } else if (secondAliasExpr.text == expression.text) {
+                        // replace `n2` with `__t.v2`
+                        transformedExpression = 
constructSecondAliasVariableAccess()
+                    }
+
+                    if (null != transformedExpression) {
+                        return transformedExpression
+                    }
+                }
+
+                return expression.transformExpression(this)
+            }
+        })
+        return projectionExpr
+    }
+
+    @CompileDynamic
+    private Expression constructFirstAliasVariableAccess() {
+        macro { __t.v1 }
+    }
+
+    @CompileDynamic
+    private Expression constructSecondAliasVariableAccess() {
+        macro { __t.v2 }
     }
 
     @Override
diff --git 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/GinqVisitor.java
 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/GinqVisitor.java
index 3ee3f4f..3e5f27e 100644
--- 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/GinqVisitor.java
+++ 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/GinqVisitor.java
@@ -20,8 +20,10 @@ package org.apache.groovy.linq.dsl;
 
 import org.apache.groovy.linq.dsl.expression.FromExpression;
 import org.apache.groovy.linq.dsl.expression.GinqExpression;
-import org.apache.groovy.linq.dsl.expression.SimpleGinqExpression;
+import org.apache.groovy.linq.dsl.expression.InnerJoinExpression;
+import org.apache.groovy.linq.dsl.expression.OnExpression;
 import org.apache.groovy.linq.dsl.expression.SelectExpression;
+import org.apache.groovy.linq.dsl.expression.SimpleGinqExpression;
 import org.apache.groovy.linq.dsl.expression.WhereExpression;
 
 /**
@@ -33,6 +35,8 @@ import org.apache.groovy.linq.dsl.expression.WhereExpression;
 public interface GinqVisitor<R> {
     R visitSimpleGinqExpression(SimpleGinqExpression simpleGinqExpression);
     R visitFromExpression(FromExpression fromExpression);
+    R visitInnerJoinExpression(InnerJoinExpression innerJoinExpression);
+    R visitOnExpression(OnExpression onExpression);
     R visitWhereExpression(WhereExpression whereExpression);
     R visitSelectExpression(SelectExpression selectExpression);
     R visit(GinqExpression expression);
diff --git 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/WhereExpression.java
 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/FilterExpression.java
similarity index 75%
copy from 
subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/WhereExpression.java
copy to 
subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/FilterExpression.java
index 052347d..0fb4dde 100644
--- 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/WhereExpression.java
+++ 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/FilterExpression.java
@@ -18,27 +18,25 @@
  */
 package org.apache.groovy.linq.dsl.expression;
 
-import org.apache.groovy.linq.dsl.GinqVisitor;
 import org.codehaus.groovy.ast.expr.Expression;
 
 /**
- * Represent the where expression
+ * Represents filter expression
  *
  * @since 4.0.0
  */
-public class WhereExpression extends AbstractGinqExpression {
-    private final Expression filterExpr;
+public abstract class FilterExpression extends AbstractGinqExpression {
+    protected Expression filterExpr;
 
-    public WhereExpression(Expression filterExpr) {
+    public FilterExpression(Expression filterExpr) {
         this.filterExpr = filterExpr;
     }
 
-    @Override
-    public <R> R accept(GinqVisitor<R> visitor) {
-        return visitor.visitWhereExpression(this);
-    }
-
     public Expression getFilterExpr() {
         return filterExpr;
     }
+
+    public void setFilterExpr(Expression filterExpr) {
+        this.filterExpr = filterExpr;
+    }
 }
diff --git 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/FilterableExpression.java
 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/FilterableExpression.java
index c0c1e8f..3ce3523 100644
--- 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/FilterableExpression.java
+++ 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/FilterableExpression.java
@@ -24,13 +24,13 @@ package org.apache.groovy.linq.dsl.expression;
  * @since 4.0.0
  */
 public abstract class FilterableExpression extends AbstractGinqExpression {
-    protected WhereExpression whereExpression;
+    protected FilterExpression filterExpression;
 
-    public WhereExpression getWhereExpression() {
-        return whereExpression;
+    public FilterExpression getFilterExpression() {
+        return filterExpression;
     }
 
-    public void setWhereExpression(WhereExpression whereExpression) {
-        this.whereExpression = whereExpression;
+    public void setFilterExpression(FilterExpression filterExpression) {
+        this.filterExpression = filterExpression;
     }
 }
diff --git 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/FromExpression.java
 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/FromExpression.java
index 248e9c0..b880cf8 100644
--- 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/FromExpression.java
+++ 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/FromExpression.java
@@ -53,7 +53,7 @@ public class FromExpression extends FilterableExpression {
         return "FromExpression{" +
                 "aliasExpr=" + aliasExpr +
                 ", dataSourceExpr=" + dataSourceExpr +
-                ", whereExpression=" + whereExpression +
+                ", whereExpression=" + filterExpression +
                 '}';
     }
 }
diff --git 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/WhereExpression.java
 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/InnerJoinExpression.java
similarity index 75%
copy from 
subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/WhereExpression.java
copy to 
subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/InnerJoinExpression.java
index 052347d..8df0377 100644
--- 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/WhereExpression.java
+++ 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/InnerJoinExpression.java
@@ -22,23 +22,18 @@ import org.apache.groovy.linq.dsl.GinqVisitor;
 import org.codehaus.groovy.ast.expr.Expression;
 
 /**
- * Represent the where expression
+ * Represents inner join expression
  *
  * @since 4.0.0
  */
-public class WhereExpression extends AbstractGinqExpression {
-    private final Expression filterExpr;
+public class InnerJoinExpression extends JoinExpression {
 
-    public WhereExpression(Expression filterExpr) {
-        this.filterExpr = filterExpr;
+    public InnerJoinExpression(Expression aliasExpr, Expression 
dataSourceExpr) {
+        super(aliasExpr, dataSourceExpr);
     }
 
     @Override
     public <R> R accept(GinqVisitor<R> visitor) {
-        return visitor.visitWhereExpression(this);
-    }
-
-    public Expression getFilterExpr() {
-        return filterExpr;
+        return visitor.visitInnerJoinExpression(this);
     }
 }
diff --git 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/FromExpression.java
 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/JoinExpression.java
similarity index 64%
copy from 
subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/FromExpression.java
copy to 
subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/JoinExpression.java
index 248e9c0..85debc4 100644
--- 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/FromExpression.java
+++ 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/JoinExpression.java
@@ -18,26 +18,29 @@
  */
 package org.apache.groovy.linq.dsl.expression;
 
-import org.apache.groovy.linq.dsl.GinqVisitor;
 import org.codehaus.groovy.ast.expr.Expression;
 
 /**
- * Represents the from expression
+ * Represents join expression
  *
  * @since 4.0.0
  */
-public class FromExpression extends FilterableExpression {
-    private final Expression aliasExpr;
-    private final Expression dataSourceExpr;
+public abstract class JoinExpression extends FilterableExpression {
+    protected OnExpression onExpression;
+    protected Expression aliasExpr;
+    protected Expression dataSourceExpr;
 
-    public FromExpression(Expression aliasExpr, Expression dataSourceExpr) {
+    public JoinExpression(Expression aliasExpr, Expression dataSourceExpr) {
         this.aliasExpr = aliasExpr;
         this.dataSourceExpr = dataSourceExpr;
     }
 
-    @Override
-    public <R> R accept(GinqVisitor<R> visitor) {
-        return visitor.visitFromExpression(this);
+    public OnExpression getOnExpression() {
+        return onExpression;
+    }
+
+    public void setOnExpression(OnExpression onExpression) {
+        this.onExpression = onExpression;
     }
 
     public Expression getAliasExpr() {
@@ -47,13 +50,4 @@ public class FromExpression extends FilterableExpression {
     public Expression getDataSourceExpr() {
         return dataSourceExpr;
     }
-
-    @Override
-    public String toString() {
-        return "FromExpression{" +
-                "aliasExpr=" + aliasExpr +
-                ", dataSourceExpr=" + dataSourceExpr +
-                ", whereExpression=" + whereExpression +
-                '}';
-    }
 }
diff --git 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/WhereExpression.java
 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/OnExpression.java
similarity index 75%
copy from 
subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/WhereExpression.java
copy to 
subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/OnExpression.java
index 052347d..98394d7 100644
--- 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/WhereExpression.java
+++ 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/OnExpression.java
@@ -22,23 +22,17 @@ import org.apache.groovy.linq.dsl.GinqVisitor;
 import org.codehaus.groovy.ast.expr.Expression;
 
 /**
- * Represent the where expression
+ * Represents on expression
  *
  * @since 4.0.0
  */
-public class WhereExpression extends AbstractGinqExpression {
-    private final Expression filterExpr;
-
-    public WhereExpression(Expression filterExpr) {
-        this.filterExpr = filterExpr;
+public class OnExpression extends FilterExpression {
+    public OnExpression(Expression filterExpr) {
+        super(filterExpr);
     }
 
     @Override
     public <R> R accept(GinqVisitor<R> visitor) {
-        return visitor.visitWhereExpression(this);
-    }
-
-    public Expression getFilterExpr() {
-        return filterExpr;
+        return visitor.visitOnExpression(this);
     }
 }
diff --git 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/SimpleGinqExpression.java
 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/SimpleGinqExpression.java
index 21c00f4..23d59ed 100644
--- 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/SimpleGinqExpression.java
+++ 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/SimpleGinqExpression.java
@@ -30,6 +30,7 @@ import java.util.List;
  */
 public class SimpleGinqExpression extends AbstractGinqExpression {
     private final List<FromExpression> fromExpressionList = new ArrayList<>();
+    private final List<JoinExpression> joinExpressionList = new ArrayList<>();
     private SelectExpression selectExpression;
 
     @Override
@@ -45,6 +46,14 @@ public class SimpleGinqExpression extends 
AbstractGinqExpression {
         this.fromExpressionList.add(fromExpression);
     }
 
+    public List<JoinExpression> getJoinExpressionList() {
+        return joinExpressionList;
+    }
+
+    public void addJoinExpression(JoinExpression joinExpression) {
+        joinExpressionList.add(joinExpression);
+    }
+
     public SelectExpression getSelectExpression() {
         return selectExpression;
     }
diff --git 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/WhereExpression.java
 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/WhereExpression.java
index 052347d..66bde4f 100644
--- 
a/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/WhereExpression.java
+++ 
b/subprojects/groovy-linq/src/main/groovy/org/apache/groovy/linq/dsl/expression/WhereExpression.java
@@ -26,19 +26,13 @@ import org.codehaus.groovy.ast.expr.Expression;
  *
  * @since 4.0.0
  */
-public class WhereExpression extends AbstractGinqExpression {
-    private final Expression filterExpr;
-
+public class WhereExpression extends FilterExpression {
     public WhereExpression(Expression filterExpr) {
-        this.filterExpr = filterExpr;
+        super(filterExpr);
     }
 
     @Override
     public <R> R accept(GinqVisitor<R> visitor) {
         return visitor.visitWhereExpression(this);
     }
-
-    public Expression getFilterExpr() {
-        return filterExpr;
-    }
 }
diff --git 
a/subprojects/groovy-linq/src/test/groovy/org/apache/groovy/linq/GinqTest.groovy
 
b/subprojects/groovy-linq/src/test/groovy/org/apache/groovy/linq/GinqTest.groovy
index 1cdb0ef..96d04cb 100644
--- 
a/subprojects/groovy-linq/src/test/groovy/org/apache/groovy/linq/GinqTest.groovy
+++ 
b/subprojects/groovy-linq/src/test/groovy/org/apache/groovy/linq/GinqTest.groovy
@@ -60,4 +60,18 @@ class GinqTest {
             }.toList()
         '''
     }
+
+    @Test
+    void "testGinq - from innerJoin select"() {
+        assertScript '''
+            def nums1 = [1, 2, 3]
+            def nums2 = [1, 2, 3]
+            assert [[1, 1], [2, 2], [3, 3]] == GINQ {
+                from n1 in nums1
+                innerJoin n2 in nums2
+                on n1 == n2
+                select n1, n2
+            }.toList()
+        '''
+    }
 }

Reply via email to