org.eigenbase.rel.metadata.ReflectiveRelMetadataProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.eigenbase.rel.metadata.ReflectiveRelMetadataProvider.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 org.eigenbase.rel.metadata;

import java.lang.reflect.*;
import java.util.*;

import org.eigenbase.rel.*;
import org.eigenbase.util.*;

import net.hydromatic.optiq.BuiltinMethod;

import com.google.common.base.Function;
import com.google.common.collect.*;

/**
 * Implementation of the {@link RelMetadataProvider} interface that dispatches
 * metadata methods to methods on a given object via reflection.
 *
 * <p>The methods on the target object must be public and non-static, and have
 * the same signature as the implemented metadata method except for an
 * additional first parameter of type {@link RelNode} or a sub-class. That
 * parameter gives this provider an indication of that relational expressions it
 * can handle.</p>
 *
 * <p>For an example, see {@link RelMdColumnOrigins#SOURCE}.
 */
public class ReflectiveRelMetadataProvider implements RelMetadataProvider, ReflectiveVisitor {

    /** Comparator that sorts derived classes before their base classes. */
    private static final Comparator<Class<RelNode>> SUPERCLASS_COMPARATOR = new Comparator<Class<RelNode>>() {
        public int compare(Class<RelNode> c1, Class<RelNode> c2) {
            return c1 == c2 ? 0 : c2.isAssignableFrom(c1) ? -1 : 1;
        }
    };

    //~ Instance fields --------------------------------------------------------
    private final ImmutableMap<Class<RelNode>, Function<RelNode, Metadata>> map;
    private final Class<?> metadataClass0;

    //~ Constructors -----------------------------------------------------------

    /**
     * Creates a ReflectiveRelMetadataProvider.
     *
     * @param map Map
     * @param metadataClass0 Metadata class
     */
    protected ReflectiveRelMetadataProvider(ImmutableMap<Class<RelNode>, Function<RelNode, Metadata>> map,
            Class<?> metadataClass0) {
        assert !map.isEmpty() : "are your methods named wrong?";
        this.map = map;
        this.metadataClass0 = metadataClass0;
    }

    /** Returns an implementation of {@link RelMetadataProvider} that scans for
     * methods with a preceding argument.
     *
     * <p>For example, {@link BuiltInMetadata.Selectivity} has a method
     * {@link BuiltInMetadata.Selectivity#getSelectivity(org.eigenbase.rex.RexNode)}.
     * A class</p>
     *
     * <blockquote><pre><code>
     * class RelMdSelectivity {
     *   public Double getSelectivity(UnionRel rel, RexNode predicate) { ... }
     *   public Double getSelectivity(FilterRel rel, RexNode predicate) { ... }
     * </code></pre></blockquote>
     *
     * <p>provides implementations of selectivity for relational expressions
     * that extend {@link UnionRel} or {@link FilterRel}.</p>
     */
    public static RelMetadataProvider reflectiveSource(Method method, final Object target) {
        final Class<?> metadataClass0 = method.getDeclaringClass();
        assert Metadata.class.isAssignableFrom(metadataClass0);
        final Map<Class<RelNode>, Function<RelNode, Metadata>> treeMap = Maps
                .<Class<RelNode>, Class<RelNode>, Function<RelNode, Metadata>>newTreeMap(SUPERCLASS_COMPARATOR);
        for (final Method method1 : target.getClass().getMethods()) {
            if (method1.getName().equals(method.getName()) && (method1.getModifiers() & Modifier.STATIC) == 0
                    && (method1.getModifiers() & Modifier.PUBLIC) != 0) {
                final Class<?>[] parameterTypes1 = method1.getParameterTypes();
                final Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes1.length == parameterTypes.length + 1
                        && RelNode.class.isAssignableFrom(parameterTypes1[0])
                        && Util.skip(Arrays.asList(parameterTypes1)).equals(Arrays.asList(parameterTypes))) {
                    //noinspection unchecked
                    final Class<RelNode> key = (Class) parameterTypes1[0];
                    final Function<RelNode, Metadata> function = new Function<RelNode, Metadata>() {
                        public Metadata apply(final RelNode rel) {
                            return (Metadata) Proxy.newProxyInstance(metadataClass0.getClassLoader(),
                                    new Class[] { metadataClass0 }, new InvocationHandler() {
                                        public Object invoke(Object proxy, Method method, Object[] args)
                                                throws Throwable {
                                            // Suppose we are an implementation of Selectivity
                                            // that wraps "filter", a FilterRel, Then we implement
                                            //   Selectivity.selectivity(rex)
                                            // by calling method
                                            //   new SelectivityImpl().selectivity(filter, rex)
                                            if (method.equals(BuiltinMethod.METADATA_REL.method)) {
                                                return rel;
                                            }
                                            final Object[] args1;
                                            if (args == null) {
                                                args1 = new Object[] { rel };
                                            } else {
                                                args1 = new Object[args.length + 1];
                                                args1[0] = rel;
                                                System.arraycopy(args, 0, args1, 1, args.length);
                                            }
                                            return method1.invoke(target, args1);
                                        }
                                    });
                        }
                    };
                    treeMap.put(key, function);
                }
            }
        }
        // Due to the comparator, the TreeMap is sorted such that any derived class
        // will occur before its base class. The immutable map is not a sorted map,
        // but it retains the traversal order, and that is sufficient.
        final ImmutableMap<Class<RelNode>, Function<RelNode, Metadata>> map = ImmutableMap.copyOf(treeMap);
        return new ReflectiveRelMetadataProvider(map, metadataClass0);
    }

    //~ Methods ----------------------------------------------------------------

    public Function<RelNode, Metadata> apply(Class<? extends RelNode> relClass,
            Class<? extends Metadata> metadataClass) {
        if (metadataClass == metadataClass0) {
            //noinspection SuspiciousMethodCalls
            final Function<RelNode, Metadata> function = map.get(relClass);
            if (function != null) {
                return function;
            }
            for (Map.Entry<Class<RelNode>, Function<RelNode, Metadata>> entry : map.entrySet()) {
                if (entry.getKey().isAssignableFrom(relClass)) {
                    // REVIEW: We are assuming that the first we find is the "best".
                    return entry.getValue();
                }
            }
        }
        return null;
    }
}

// End ReflectiveRelMetadataProvider.java