net.hydromatic.optiq.rules.java.EnumerableRelImplementor.java Source code

Java tutorial

Introduction

Here is the source code for net.hydromatic.optiq.rules.java.EnumerableRelImplementor.java

Source

/*
// 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.optiq.rules.java;

import net.hydromatic.linq4j.Enumerable;
import net.hydromatic.linq4j.Queryable;
import net.hydromatic.linq4j.expressions.*;

import net.hydromatic.optiq.BuiltinMethod;
import net.hydromatic.optiq.DataContext;
import net.hydromatic.optiq.jdbc.JavaTypeFactoryImpl;
import net.hydromatic.optiq.runtime.*;

import org.eigenbase.rex.RexBuilder;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;

import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.*;

/**
 * Subclass of {@link org.eigenbase.relopt.RelImplementor} for relational
 * operators of {@link EnumerableConvention} calling convention.
 */
public class EnumerableRelImplementor extends JavaRelImplementor {
    public final Map<String, Queryable> map = new LinkedHashMap<String, Queryable>();

    public EnumerableRelImplementor(RexBuilder rexBuilder) {
        super(rexBuilder);
    }

    public EnumerableRel.Result visitChild(EnumerableRel parent, int ordinal, EnumerableRel child,
            EnumerableRel.Prefer prefer) {
        if (parent != null) {
            assert child == parent.getInputs().get(ordinal);
        }
        createFrame(parent, ordinal, child);
        return child.implement(this, prefer);
    }

    public ClassDeclaration implementRoot(EnumerableRel rootRel, EnumerableRel.Prefer prefer) {
        final EnumerableRel.Result result = rootRel.implement(this, prefer);
        List<MemberDeclaration> memberDeclarations = new ArrayList<MemberDeclaration>();
        declareSyntheticClasses(result.block, memberDeclarations);

        // The following is a workaround to
        // http://jira.codehaus.org/browse/JANINO-169. Otherwise we'd remove the
        // member variable, rename the "root0" parameter as "root", and reference it
        // directly from inner classes.
        final ParameterExpression root0_ = Expressions.parameter(Modifier.FINAL, DataContext.class, "root0");
        final BlockStatement block = Expressions.block(Iterables.concat(
                ImmutableList.of(Expressions.statement(Expressions.assign(DataContext.ROOT, root0_))),
                result.block.statements));
        memberDeclarations.add(Expressions.fieldDecl(0, DataContext.ROOT, null));

        memberDeclarations.add(Expressions.methodDecl(Modifier.PUBLIC, Enumerable.class,
                BuiltinMethod.BINDABLE_BIND.method.getName(), Expressions.list(root0_), block));
        memberDeclarations.add(Expressions.methodDecl(Modifier.PUBLIC, Type.class,
                BuiltinMethod.TYPED_GET_ELEMENT_TYPE.method.getName(), Collections.<ParameterExpression>emptyList(),
                Blocks.toFunctionBlock(
                        Expressions.return_(null, Expressions.constant(result.physType.getJavaRowType())))));
        return Expressions.classDecl(Modifier.PUBLIC, "Baz", null, Collections.<Type>singletonList(Bindable.class),
                memberDeclarations);
    }

    private void declareSyntheticClasses(BlockStatement block, List<MemberDeclaration> memberDeclarations) {
        final LinkedHashSet<Type> types = new LinkedHashSet<Type>();
        block.accept(new TypeFinder(types));
        for (Type type : types) {
            if (type instanceof JavaTypeFactoryImpl.SyntheticRecordType) {
                memberDeclarations.add(classDecl((JavaTypeFactoryImpl.SyntheticRecordType) type));
            }
        }
    }

    private ClassDeclaration classDecl(JavaTypeFactoryImpl.SyntheticRecordType type) {
        ClassDeclaration classDeclaration = Expressions.classDecl(Modifier.PUBLIC | Modifier.STATIC, type.getName(),
                null, ImmutableList.<Type>of(Serializable.class), new ArrayList<MemberDeclaration>());

        // For each field:
        //   public T0 f0;
        //   ...
        for (Types.RecordField field : type.getRecordFields()) {
            classDeclaration.memberDeclarations.add(Expressions.fieldDecl(field.getModifiers(),
                    Expressions.parameter(field.getType(), field.getName()), null));
        }

        // Constructor:
        //   Foo(T0 f0, ...) { this.f0 = f0; ... }
        final BlockBuilder blockBuilder = new BlockBuilder();
        final List<ParameterExpression> parameters = new ArrayList<ParameterExpression>();
        final ParameterExpression thisParameter = Expressions.parameter(type, "this");
        for (Types.RecordField field : type.getRecordFields()) {
            final ParameterExpression parameter = Expressions.parameter(field.getType(), field.getName());
            parameters.add(parameter);
            blockBuilder.add(
                    Expressions.statement(Expressions.assign(Expressions.field(thisParameter, field), parameter)));
        }
        classDeclaration.memberDeclarations
                .add(Expressions.constructorDecl(Modifier.PUBLIC, type, parameters, blockBuilder.toBlock()));

        // equals method():
        //   public boolean equals(Object o) {
        //       if (this == o) return true;
        //       if (!(o instanceof MyClass)) return false;
        //       final MyClass that = (MyClass) o;
        //       return this.f0 == that.f0
        //         && equal(this.f1, that.f1)
        //         ...
        //   }
        final BlockBuilder blockBuilder2 = new BlockBuilder();
        final ParameterExpression thatParameter = Expressions.parameter(type, "that");
        final ParameterExpression oParameter = Expressions.parameter(Object.class, "o");
        blockBuilder2.add(Expressions.ifThen(Expressions.equal(thisParameter, oParameter),
                Expressions.return_(null, Expressions.constant(true))));
        blockBuilder2.add(Expressions.ifThen(Expressions.not(Expressions.typeIs(oParameter, type)),
                Expressions.return_(null, Expressions.constant(false))));
        blockBuilder2
                .add(Expressions.declare(Modifier.FINAL, thatParameter, Expressions.convert_(oParameter, type)));
        List<Expression> conditions = new ArrayList<Expression>();
        for (Types.RecordField field : type.getRecordFields()) {
            conditions.add(Primitive.is(field.getType())
                    ? Expressions.equal(Expressions.field(thisParameter, field.getName()),
                            Expressions.field(thatParameter, field.getName()))
                    : Expressions.call(Utilities.class, "equal", Expressions.field(thisParameter, field.getName()),
                            Expressions.field(thatParameter, field.getName())));
        }
        blockBuilder2.add(Expressions.return_(null, Expressions.foldAnd(conditions)));
        classDeclaration.memberDeclarations.add(Expressions.methodDecl(Modifier.PUBLIC, boolean.class, "equals",
                Collections.singletonList(oParameter), blockBuilder2.toBlock()));

        // hashCode method:
        //   public int hashCode() {
        //     int h = 0;
        //     h = hash(h, f0);
        //     ...
        //     return h;
        //   }
        final BlockBuilder blockBuilder3 = new BlockBuilder();
        final ParameterExpression hParameter = Expressions.parameter(int.class, "h");
        final ConstantExpression constantZero = Expressions.constant(0);
        blockBuilder3.add(Expressions.declare(0, hParameter, constantZero));
        for (Types.RecordField field : type.getRecordFields()) {
            blockBuilder3.add(Expressions.statement(Expressions.assign(hParameter, Expressions.call(Utilities.class,
                    "hash", Arrays.<Expression>asList(hParameter, Expressions.field(thisParameter, field))))));
        }
        blockBuilder3.add(Expressions.return_(null, hParameter));
        classDeclaration.memberDeclarations.add(Expressions.methodDecl(Modifier.PUBLIC, int.class, "hashCode",
                Collections.<ParameterExpression>emptyList(), blockBuilder3.toBlock()));

        // compareTo method:
        //   public int compareTo(MyClass that) {
        //     int c;
        //     c = compare(this.f0, that.f0);
        //     if (c != 0) return c;
        //     ...
        //     return 0;
        //   }
        final BlockBuilder blockBuilder4 = new BlockBuilder();
        final ParameterExpression cParameter = Expressions.parameter(int.class, "c");
        blockBuilder4.add(Expressions.declare(0, cParameter, null));
        final ConditionalStatement conditionalStatement = Expressions
                .ifThen(Expressions.notEqual(cParameter, constantZero), Expressions.return_(null, cParameter));
        for (Types.RecordField field : type.getRecordFields()) {
            blockBuilder4.add(Expressions.statement(Expressions.assign(cParameter,
                    Expressions.call(Utilities.class, field.nullable() ? "compareNullsLast" : "compare",
                            Expressions.field(thisParameter, field), Expressions.field(thatParameter, field)))));
            blockBuilder4.add(conditionalStatement);
        }
        blockBuilder4.add(Expressions.return_(null, constantZero));
        classDeclaration.memberDeclarations.add(Expressions.methodDecl(Modifier.PUBLIC, int.class, "compareTo",
                Collections.singletonList(thatParameter), blockBuilder4.toBlock()));

        // toString method:
        //   public String toString() {
        //     return "{f0=" + f0
        //       + ", f1=" + f1
        //       ...
        //       + "}";
        //   }
        final BlockBuilder blockBuilder5 = new BlockBuilder();
        Expression expression5 = null;
        for (Types.RecordField field : type.getRecordFields()) {
            if (expression5 == null) {
                expression5 = Expressions.constant("{" + field.getName() + "=");
            } else {
                expression5 = Expressions.add(expression5, Expressions.constant(", " + field.getName() + "="));
            }
            expression5 = Expressions.add(expression5, Expressions.field(thisParameter, field.getName()));
        }
        expression5 = expression5 == null ? Expressions.constant("{}")
                : Expressions.add(expression5, Expressions.constant("}"));
        blockBuilder5.add(Expressions.return_(null, expression5));
        classDeclaration.memberDeclarations.add(Expressions.methodDecl(Modifier.PUBLIC, String.class, "toString",
                Collections.<ParameterExpression>emptyList(), blockBuilder5.toBlock()));

        return classDeclaration;
    }

    public Expression register(Queryable queryable) {
        String name = "v" + map.size();
        map.put(name, queryable);
        return Expressions.variable(queryable.getClass(), name);
    }

    public EnumerableRel.Result result(PhysType physType, BlockStatement block) {
        return new EnumerableRel.Result(block, physType, ((PhysTypeImpl) physType).format);
    }

    /** Visitor that finds types in an {@link Expression} tree. */
    private static class TypeFinder extends Visitor {
        private final LinkedHashSet<Type> types;

        TypeFinder(LinkedHashSet<Type> types) {
            this.types = types;
        }

        @Override
        public Expression visit(NewExpression newExpression, List<Expression> arguments,
                List<MemberDeclaration> memberDeclarations) {
            types.add(newExpression.type);
            return super.visit(newExpression, arguments, memberDeclarations);
        }

        @Override
        public Expression visit(NewArrayExpression newArrayExpression, int dimension, Expression bound,
                List<Expression> expressions) {
            Type type = newArrayExpression.type;
            for (;;) {
                final Type componentType = Types.getComponentType(type);
                if (componentType == null) {
                    break;
                }
                type = componentType;
            }
            types.add(type);
            return super.visit(newArrayExpression, dimension, bound, expressions);
        }
    }
}

// End EnumerableRelImplementor.java