blob: 615bfd9ac63bc96430a891bd9b9cfe94c497816e [file] [log] [blame]
/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
package net.hydromatic.linq4j.test;
import net.hydromatic.linq4j.expressions.*;
import net.hydromatic.linq4j.function.Function1;
import junit.framework.TestCase;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* Unit test for {@link net.hydromatic.linq4j.expressions.Expression}
* and subclasses.
*/
public class ExpressionTest extends TestCase {
public void testLambdaCallsBinaryOp() {
// A parameter for the lambda expression.
ParameterExpression paramExpr =
Expressions.parameter(Integer.TYPE, "arg");
// This expression represents a lambda expression
// that adds 1 to the parameter value.
FunctionExpression lambdaExpr = Expressions.lambda(
Expressions.add(
paramExpr,
Expressions.constant(2)),
Arrays.asList(paramExpr));
// Print out the expression.
String s = Expressions.toString(lambdaExpr);
assertEquals(
"new net.hydromatic.linq4j.function.Function1() {\n"
+ " public Integer apply(Integer arg) {\n"
+ " return arg + 2;\n"
+ " }\n"
+ " public Object apply(Object arg) {\n"
+ " return apply(\n"
+ " (Integer) arg);\n"
+ " }\n"
+ "}\n",
s);
// Compile and run the lambda expression.
// The value of the parameter is 1.
int n = (Integer) lambdaExpr.compile().dynamicInvoke(1);
// This code example produces the following output:
//
// arg => (arg +2)
// 3
assertEquals(3, n);
}
public void testLambdaCallsTwoArgMethod() throws NoSuchMethodException {
// A parameter for the lambda expression.
ParameterExpression paramS =
Expressions.parameter(String.class, "s");
ParameterExpression paramBegin =
Expressions.parameter(Integer.TYPE, "begin");
ParameterExpression paramEnd =
Expressions.parameter(Integer.TYPE, "end");
// This expression represents a lambda expression
// that adds 1 to the parameter value.
FunctionExpression lambdaExpr =
Expressions.lambda(
Expressions.call(
paramS,
String.class.getMethod(
"substring", Integer.TYPE, Integer.TYPE),
paramBegin,
paramEnd),
paramS, paramBegin, paramEnd);
// Compile and run the lambda expression.
String s =
(String) lambdaExpr.compile().dynamicInvoke("hello world", 3, 7);
assertEquals("lo w", s);
}
public void testWrite() {
assertEquals(
"1 + 2 + 3",
Expressions.toString(
Expressions.add(
Expressions.add(
Expressions.constant(1),
Expressions.constant(2)),
Expressions.constant(3))));
// Parentheses needed, to override the left-associativity of +.
assertEquals(
"1 + (2 + 3)",
Expressions.toString(
Expressions.add(
Expressions.constant(1),
Expressions.add(
Expressions.constant(2),
Expressions.constant(3)))));
// No parentheses needed; higher precedence of * achieves the desired
// effect.
assertEquals(
"1 + 2 * 3",
Expressions.toString(
Expressions.add(
Expressions.constant(1),
Expressions.multiply(
Expressions.constant(2),
Expressions.constant(3)))));
assertEquals(
"1 * (2 + 3)",
Expressions.toString(
Expressions.multiply(
Expressions.constant(1),
Expressions.add(
Expressions.constant(2),
Expressions.constant(3)))));
// Parentheses needed, to overcome right-associativity of =.
assertEquals(
"(1 = 2) = 3",
Expressions.toString(
Expressions.assign(
Expressions.assign(
Expressions.constant(1), Expressions.constant(2)),
Expressions.constant(3))));
assertEquals(
"0 + (2 + 3).compareTo(1)",
Expressions.toString(
Expressions.add(
Expressions.constant(0),
Expressions.call(
Expressions.add(
Expressions.constant(2),
Expressions.constant(3)),
"compareTo",
Arrays.<Expression>asList(
Expressions.constant(1))))));
assertEquals(
"a.empno",
Expressions.toString(
Expressions.field(
Expressions.parameter(Linq4jTest.Employee.class, "a"),
"empno")));
final ParameterExpression paramX =
Expressions.parameter(String.class, "x");
assertEquals(
"new net.hydromatic.linq4j.function.Function1() {\n"
+ " public Integer apply(String x) {\n"
+ " return x.length();\n"
+ " }\n"
+ " public Object apply(Object x) {\n"
+ " return apply(\n"
+ " (String) x);\n"
+ " }\n"
+ "}\n",
Expressions.toString(
Expressions.lambda(
Function1.class,
Expressions.return_(
null,
Expressions.call(
paramX,
"length",
Collections.<Expression>emptyList())),
Arrays.asList(paramX))));
assertEquals(
"new String[] {\n"
+ " \"foo\",\n"
+ " null,\n"
+ " \"bar\\\"baz\"}",
Expressions.toString(
Expressions.newArrayInit(
String.class,
Arrays.<Expression>asList(
Expressions.constant("foo"),
Expressions.constant(null),
Expressions.constant("bar\"baz")))));
assertEquals(
"(int) ((String) (Object) \"foo\").length()",
Expressions.toString(
Expressions.convert_(
Expressions.call(
Expressions.convert_(
Expressions.convert_(
Expressions.constant("foo"),
Object.class),
String.class),
"length",
Collections.<Expression>emptyList()),
Integer.TYPE)));
// resolving a static method
assertEquals(
"Integer.valueOf(\"0123\")",
Expressions.toString(
Expressions.call(
Integer.class,
"valueOf",
Collections.<Expression>singletonList(
Expressions.constant("0123")))));
}
public void testWriteConstant() {
// primitives
assertEquals(
"new int[] {\n"
+ " 1,\n"
+ " 2,\n"
+ " -1}",
Expressions.toString(
Expressions.constant(new int[]{1, 2, -1})));
// objects and nulls
assertEquals(
"new String[] {\n"
+ " \"foo\",\n"
+ " null}",
Expressions.toString(
Expressions.constant(new String[] {"foo", null})));
// automatically call constructor if it matches fields
assertEquals(
"new net.hydromatic.linq4j.test.Linq4jTest$Employee[] {\n"
+ " new net.hydromatic.linq4j.test.Linq4jTest$Employee(\n"
+ " 100,\n"
+ " \"Fred\",\n"
+ " 10),\n"
+ " new net.hydromatic.linq4j.test.Linq4jTest$Employee(\n"
+ " 110,\n"
+ " \"Bill\",\n"
+ " 30),\n"
+ " new net.hydromatic.linq4j.test.Linq4jTest$Employee(\n"
+ " 120,\n"
+ " \"Eric\",\n"
+ " 10),\n"
+ " new net.hydromatic.linq4j.test.Linq4jTest$Employee(\n"
+ " 130,\n"
+ " \"Jane\",\n"
+ " 10)}",
Expressions.toString(
Expressions.constant(Linq4jTest.emps)));
}
public void testWriteAnonymousClass() {
// final List<String> baz = Arrays.asList("foo", "bar");
// new AbstractList<String>() {
// public int size() {
// return baz.size();
// }
// public String get(int index) {
// return ((String) baz.get(index)).toUpperCase();
// }
// }
final ParameterExpression bazParameter =
Expressions.parameter(
Types.of(List.class, String.class),
"baz");
final ParameterExpression indexParameter =
Expressions.parameter(
Integer.TYPE,
"index");
Expression e =
Expressions.block(
Arrays.<Expression>asList(
Expressions.declare(
Modifier.FINAL, bazParameter,
Expressions.call(
Arrays.class,
"asList",
Arrays.<Expression>asList(
Expressions.constant("foo"),
Expressions.constant("bar")))),
Expressions.new_(
Types.of(AbstractList.class, String.class),
Collections.<Expression>emptyList(),
Collections.<Member>emptyList(),
Arrays.<MemberDeclaration>asList(
Expressions.methodDecl(
Modifier.PUBLIC,
Integer.TYPE,
"size",
Collections.<ParameterExpression>emptyList(),
Expressions.call(
bazParameter,
"size",
Collections.<Expression>emptyList())),
Expressions.methodDecl(
Modifier.PUBLIC,
String.class,
"get",
Arrays.asList(
indexParameter),
Expressions.call(
Expressions.convert_(
Expressions.call(
bazParameter,
"get",
Arrays.<Expression>asList(
indexParameter)),
String.class),
"toUpperCase",
Collections.<Expression>emptyList()))))));
if (false)
assertEquals(
"xxx",
Expressions.toString(e));
}
public void testCompile() throws NoSuchMethodException {
// Creating a parameter for the expression tree.
ParameterExpression param = Expressions.parameter(String.class);
// Creating an expression for the method call and specifying its
// parameter.
MethodCallExpression methodCall =
Expressions.call(
Integer.class,
"valueOf",
Collections.<Expression>singletonList(param));
// The following statement first creates an expression tree,
// then compiles it, and then runs it.
int x =
Expressions.<Function1<String, Integer>>lambda(
methodCall,
new ParameterExpression[] { param })
.getFunction()
.apply("1234");
assertEquals(1234, x);
}
}
// End ExpressionTest.java