Expression support for anonymous classes; work in progress.
diff --git a/.idea/dictionaries/jhyde.xml b/.idea/dictionaries/jhyde.xml
index 4445648..3fc3d71 100644
--- a/.idea/dictionaries/jhyde.xml
+++ b/.idea/dictionaries/jhyde.xml
@@ -5,7 +5,10 @@
<w>deptno</w>
<w>depts</w>
<w>emps</w>
+ <w>lprec</w>
+ <w>multi</w>
<w>nullable</w>
+ <w>rprec</w>
</words>
</dictionary>
-</component>
\ No newline at end of file
+</component>
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/DeclarationExpression.java b/src/main/java/net/hydromatic/linq4j/expressions/DeclarationExpression.java
new file mode 100644
index 0000000..6689538
--- /dev/null
+++ b/src/main/java/net/hydromatic/linq4j/expressions/DeclarationExpression.java
@@ -0,0 +1,42 @@
+/*
+// 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.expressions;
+
+import java.lang.reflect.Type;
+
+/**
+ * Expression that declares and optionally initializes a variable.
+ *
+ * @author jhyde
+ */
+public class DeclarationExpression extends Expression {
+ public final int modifiers;
+ public final ParameterExpression parameter;
+ public final Expression initializer;
+
+ public DeclarationExpression(
+ int modifiers, ParameterExpression parameter, Expression initializer)
+ {
+ super(ExpressionType.Declaration, Void.TYPE);
+ this.modifiers = modifiers;
+ this.parameter = parameter;
+ this.initializer = initializer;
+ }
+}
+
+// End DeclarationExpression.java
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/ExpressionType.java b/src/main/java/net/hydromatic/linq4j/expressions/ExpressionType.java
index 5b37433..1c64f3c 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/ExpressionType.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/ExpressionType.java
@@ -405,7 +405,10 @@
IsTrue,
/** A false condition value. */
- IsFalse;
+ IsFalse,
+
+ /** Declaration of a variable. */
+ Declaration;
final String op;
final boolean postfix;
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/Expressions.java b/src/main/java/net/hydromatic/linq4j/expressions/Expressions.java
index 9bf078b..2b238eb 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/Expressions.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/Expressions.java
@@ -1742,6 +1742,18 @@
return memberInit(newExpression, Arrays.asList(bindings));
}
+ /** Declares a method. */
+ public static MethodDeclaration methodDecl(
+ int modifier,
+ Type resultType,
+ String name,
+ Iterable<ParameterExpression> parameters,
+ Expression body)
+ {
+ return new MethodDeclaration(
+ modifier, name, resultType, toList(parameters), body);
+ }
+
/** Creates a BinaryExpression that represents an arithmetic
* remainder operation. */
public static BinaryExpression modulo(
@@ -1977,15 +1989,15 @@
/** Creates a NewExpression that represents calling the
* parameterless constructor of the specified type. */
- public static NewExpression new_(Class type) {
- throw Extensions.todo();
+ public static NewExpression new_(Type type) {
+ return new_(type, Collections.<Expression>emptyList());
}
/** Creates a NewExpression that represents calling the constructor of the
* specified type whose parameters are assignable from the specified
* arguments. */
public static NewExpression new_(
- Class type, Iterable<Expression> arguments)
+ Type type, Iterable<Expression> arguments)
{
final Constructor constructor =
Types.lookupConstructor(
@@ -1993,18 +2005,35 @@
return new_(constructor, arguments);
}
- /** Creates a NewExpression that represents calling the specified
- * constructor with the specified arguments. */
+ /** Creates a NewExpression that represents calling the constructor of the
+ * specified type whose parameters are assignable from the specified
+ * arguments. */
public static NewExpression new_(
- Constructor constructor, Iterable<Expression> expressions)
+ Type type,
+ Iterable<Expression> arguments,
+ Iterable<Member> members,
+ Iterable<MemberDeclaration> memberDeclarations)
{
- return new NewExpression(constructor, toList(expressions));
+ final Constructor constructor =
+ Types.lookupConstructor(
+ type, Types.toClassArray(arguments));
+ return new_(constructor, arguments, members, memberDeclarations);
}
/** Creates a NewExpression that represents calling the specified
* constructor with the specified arguments. */
public static NewExpression new_(
- Constructor constructor, Expression[] expressions)
+ Constructor constructor, Iterable<Expression> expressions)
+ {
+ return new_(
+ constructor, expressions, Collections.<Member>emptyList(),
+ Collections.<MemberDeclaration>emptyList());
+ }
+
+ /** Creates a NewExpression that represents calling the specified
+ * constructor with the specified arguments. */
+ public static NewExpression new_(
+ Constructor constructor, Expression... expressions)
{
return new_(constructor, Arrays.asList(expressions));
}
@@ -2015,9 +2044,12 @@
public static NewExpression new_(
Constructor constructor,
Iterable<Expression> expressions,
- Iterable<Member> members)
+ Iterable<Member> members,
+ Iterable<MemberDeclaration> memberDeclarations)
{
- throw Extensions.todo();
+ return new NewExpression(
+ constructor, toList(expressions), toList(members),
+ toList(memberDeclarations));
}
/** Creates a NewExpression that represents calling the specified
@@ -2029,7 +2061,9 @@
Iterable<Expression> expressions,
Member[] members)
{
- return new_(constructor, expressions, Arrays.asList(members));
+ return new_(
+ constructor, expressions, Arrays.asList(members),
+ Collections.<MemberDeclaration>emptyList());
}
/** Creates a NewArrayExpression that represents creating an array
@@ -2196,13 +2230,13 @@
/** Creates a ParameterExpression node that can be used to
* identify a parameter or a variable in an expression tree. */
- public static ParameterExpression parameter(Class type) {
+ public static ParameterExpression parameter(Type type) {
return new ParameterExpression(type);
}
/** Creates a ParameterExpression node that can be used to
* identify a parameter or a variable in an expression tree. */
- public static ParameterExpression parameter(Class type, String name) {
+ public static ParameterExpression parameter(Type type, String name) {
return new ParameterExpression(type, name);
}
@@ -2934,6 +2968,13 @@
throw Extensions.todo();
}
+ /** Creates an expression that declares a variable. */
+ public static DeclarationExpression declare(
+ int modifiers, ParameterExpression parameter, Expression initializer)
+ {
+ return new DeclarationExpression(modifiers, parameter, initializer);
+ }
+
// Some interfaces we'd rather not implement yet. They don't seem relevant
// in the Java world.
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/MemberDeclaration.java b/src/main/java/net/hydromatic/linq4j/expressions/MemberDeclaration.java
new file mode 100644
index 0000000..972b32f
--- /dev/null
+++ b/src/main/java/net/hydromatic/linq4j/expressions/MemberDeclaration.java
@@ -0,0 +1,26 @@
+/*
+// 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.expressions;
+
+/**
+ * Declaration of a member of a class.
+ */
+public abstract class MemberDeclaration {
+}
+
+// End MemberDeclaration.java
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/MethodDeclaration.java b/src/main/java/net/hydromatic/linq4j/expressions/MethodDeclaration.java
new file mode 100644
index 0000000..8d85888
--- /dev/null
+++ b/src/main/java/net/hydromatic/linq4j/expressions/MethodDeclaration.java
@@ -0,0 +1,48 @@
+/*
+// 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.expressions;
+
+import java.lang.reflect.Type;
+import java.util.List;
+
+/**
+ * Declaration of a method.
+ */
+public class MethodDeclaration extends MemberDeclaration {
+ public final int modifier;
+ public final String name;
+ public final Type resultType;
+ public final List<ParameterExpression> parameters;
+ public final Expression body;
+
+ public MethodDeclaration(
+ int modifier,
+ String name,
+ Type resultType,
+ List<ParameterExpression> parameters,
+ Expression body)
+ {
+ this.modifier = modifier;
+ this.name = name;
+ this.resultType = resultType;
+ this.parameters = parameters;
+ this.body = body;
+ }
+}
+
+// End MethodDeclaration.java
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/NewExpression.java b/src/main/java/net/hydromatic/linq4j/expressions/NewExpression.java
index de13910..f76e1ae 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/NewExpression.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/NewExpression.java
@@ -18,22 +18,30 @@
package net.hydromatic.linq4j.expressions;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Member;
import java.util.List;
/**
* Represents a constructor call.
+ *
+ * <p>If {@link #memberDeclarations} is not null (even if empty) represents
+ * an anonymous class.</p>
*/
public class NewExpression extends Expression {
public final Constructor constructor;
public final List<Expression> arguments;
+ public final List<MemberDeclaration> memberDeclarations;
public NewExpression(
Constructor constructor,
- List<Expression> arguments)
+ List<Expression> arguments,
+ List<Member> members, // not used
+ List<MemberDeclaration> memberDeclarations)
{
super(ExpressionType.New, constructor.getDeclaringClass());
this.constructor = constructor;
this.arguments = arguments;
+ this.memberDeclarations = memberDeclarations;
}
}
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/ParameterExpression.java b/src/main/java/net/hydromatic/linq4j/expressions/ParameterExpression.java
index 6ec72da..18f0eaf 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/ParameterExpression.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/ParameterExpression.java
@@ -17,6 +17,8 @@
*/
package net.hydromatic.linq4j.expressions;
+import java.lang.reflect.Type;
+
/**
* Represents a named parameter expression.
*/
@@ -25,11 +27,11 @@
final String name;
- public ParameterExpression(Class type) {
+ public ParameterExpression(Type type) {
this(type, "p" + seq++);
}
- public ParameterExpression(Class type, String name) {
+ public ParameterExpression(Type type, String name) {
super(ExpressionType.Parameter, type);
this.name = name;
}
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/Types.java b/src/main/java/net/hydromatic/linq4j/expressions/Types.java
index d49a95d..976ab1a 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/Types.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/Types.java
@@ -173,13 +173,23 @@
}
static boolean allAssignable(
- Class[] parameterTypes, Class[] argumentTypes)
+ boolean varArgs, Class[] parameterTypes, Class[] argumentTypes)
{
- if (parameterTypes.length != argumentTypes.length) {
- return false;
+ if (varArgs) {
+ if (argumentTypes.length < parameterTypes.length - 1) {
+ return false;
+ }
+ } else {
+ if (parameterTypes.length != argumentTypes.length) {
+ return false;
+ }
}
- for (int i = 0; i < parameterTypes.length; i++) {
- if (!parameterTypes[i].isAssignableFrom(argumentTypes[i])) {
+ for (int i = 0; i < argumentTypes.length; i++) {
+ Class parameterType =
+ !varArgs || i < parameterTypes.length - 1
+ ? parameterTypes[i]
+ : Object.class;
+ if (!parameterType.isAssignableFrom(argumentTypes[i])) {
return false;
}
}
@@ -207,6 +217,7 @@
for (Method method : clazz.getMethods()) {
if (method.getName().equals(methodName)
&& allAssignable(
+ method.isVarArgs(),
method.getParameterTypes(),
argumentTypes))
{
@@ -224,25 +235,28 @@
* Finds a constructor of a given class that accepts a given set of
* arguments. Includes in its search methods with wider argument types.
*
- *
- * @param clazz Class against which method is invoked
+ * @param type Class against which method is invoked
* @param argumentTypes Types of arguments
* @return A method with the given name that matches the arguments given
*
* @throws RuntimeException if method not found
*/
public static Constructor lookupConstructor(
- Class clazz, Class... argumentTypes)
+ Type type, Class... argumentTypes)
{
+ final Class clazz = toClass(type);
for (Constructor constructor : clazz.getConstructors()) {
if (allAssignable(
- constructor.getParameterTypes(), argumentTypes))
+ constructor.isVarArgs(),
+ constructor.getParameterTypes(),
+ argumentTypes))
{
return constructor;
}
}
throw new RuntimeException(
- "while resolving static constructor in class " + clazz);
+ "while resolving constructor in class " + type + " with types "
+ + Arrays.toString(argumentTypes));
}
static class ParameterizedTypeImpl implements ParameterizedType {
@@ -259,6 +273,22 @@
this.ownerType = ownerType;
}
+ @Override
+ public String toString() {
+ final StringBuilder buf = new StringBuilder();
+ buf.append(className(rawType));
+ buf.append("<");
+ int i = 0;
+ for (Type typeArgument : typeArguments) {
+ if (i++ > 0) {
+ buf.append(", ");
+ }
+ buf.append(className(typeArgument));
+ }
+ buf.append(">");
+ return buf.toString();
+ }
+
public Type[] getActualTypeArguments() {
return typeArguments.toArray(new Type[typeArguments.size()]);
}
diff --git a/src/test/java/net/hydromatic/linq4j/test/ExpressionTest.java b/src/test/java/net/hydromatic/linq4j/test/ExpressionTest.java
index c1b1c97..e7a1cc0 100644
--- a/src/test/java/net/hydromatic/linq4j/test/ExpressionTest.java
+++ b/src/test/java/net/hydromatic/linq4j/test/ExpressionTest.java
@@ -22,6 +22,8 @@
import junit.framework.TestCase;
+import java.lang.reflect.Member;
+import java.lang.reflect.Modifier;
import java.util.*;
/**
@@ -266,6 +268,70 @@
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()))))));
+ assertEquals(
+ "xxx",
+ Expressions.toString(e));
+ }
+
public void testCompile() throws NoSuchMethodException {
// Creating a parameter for the expression tree.
ParameterExpression param = Expressions.parameter(String.class);