All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twitter.algebird.javaapi.MethodRegistry Maven / Gradle / Ivy

/*
Copyright 2014 Twitter, Inc.

Licensed 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 com.twitter.algebird.javaapi;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 *
 * takes a provider object and looks for parameterless methods providing objects of type provided
 *
 * provided must take one type parameter.
 *
 * resolveAndInvoke(T.class) will return the corresponding provided<T> using the corresponding provider method
 *
 * @author Julien Le Dem
 *
 */
final class MethodRegistry {

  private final Map, Method> classToMethod = new HashMap, Method>();
  private final Object provider;
  private final Class provided;

  /**
   * building a registry of provider functions for the provided class
   * @param provider object to look for methods
   * @param provided parameterized type taking exactly one parameter
   */
  MethodRegistry(Object provider, Class provided) {
    this.provider = provider;
    this.provided = provided;
    TypeVariable[] providedTypeParameters = provided.getTypeParameters();
    if (providedTypeParameters == null || providedTypeParameters.length != 1) {
      throw new IllegalArgumentException("provided must have exactly one type parameter. Found: " + Arrays.toString(providedTypeParameters));
    }
    Class providerClass = provider.getClass();
    Set> conflictingDefinitions = new HashSet>();
    for (Method method : providerClass.getMethods()) {
      if (method.getParameterTypes().length == 0 && method.getReturnType().isAssignableFrom(provided)) {
        // method of signature {name}(): Semigroup[T]
        Type returnType = method.getGenericReturnType();
        if (returnType instanceof ParameterizedType) {
          Class operandType;
          // get the Type argument from the return type Semigroup[T]
          Type type = ((ParameterizedType)returnType).getActualTypeArguments()[0];
          if (type instanceof ParameterizedType) {
            // if itself a parameterized type then just get the erased equivalent
            operandType = (Class)((ParameterizedType)type).getRawType();
          } else {
            operandType = (Class)type;
          }
          // because of type erasure scala primitive types just become Object
          if (operandType != Object.class
              && !conflictingDefinitions.contains(operandType)) {
            if (classToMethod.put(operandType, method) != null) {
              classToMethod.remove(operandType);
              // let's not create ambiguity
              conflictingDefinitions.add(operandType);
            }
          }
        }
      }
    }
  }

  private Method getMethod(Class operandType) {
    Method method = classToMethod.get(operandType);
    if (method == null && operandType.getSuperclass() != null) {
      return getMethod(operandType.getSuperclass());
    } else {
      return method;
    }
  }

  private String typeName(Class typeParameter) {
    return provided.getSimpleName() + "<" + typeParameter + ">";
  }

  /**
   * @param typeParameter
   * @return if found in provider, will return a provided<typeParameter>
   */
  Object resolveAndInvoke(Class typeParameter) {
    Method method = getMethod(typeParameter);
    if (method != null) {
      try {
        return method.invoke(provider);
      } catch (InvocationTargetException e) {
        throw new IllegalArgumentException("Could not resolve " + typeName(typeParameter) + " because " + method.getName() + "() threw an exception",  e.getTargetException());
      } catch (IllegalAccessException e) {
        throw new IllegalArgumentException("Should not happen. Could not resolve " + typeName(typeParameter) + " because " + method.getName() + "() is not accessible",  e);
      } catch (IllegalArgumentException e) {
        throw new IllegalArgumentException("Should not happen. Could not call " + method.getName() + " on provider object",  e);
      }
    } else {
      throw new IllegalArgumentException(typeName(typeParameter) + " not found");
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy