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

org.apache.gobblin.util.reflection.GobblinConstructorUtils Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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.apache.gobblin.util.reflection;

import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.reflect.ConstructorUtils;


/**
 * Helper methods to instantiate classes
 */
@Slf4j
public class GobblinConstructorUtils {

  /**
   * Convenience method on top of {@link ConstructorUtils#invokeConstructor(Class, Object[])} that returns a new
   * instance of the cls based on a constructor priority order. Each {@link List} in the
   * constructorArgs array contains the arguments for a constructor of cls. The first
   * constructor whose signature matches the argument types will be invoked.
   *
   * @param cls the class to be instantiated
   * @param constructorArgs An array of constructor argument list. Order defines the priority of a constructor.
   * @return
   *
   * @throws NoSuchMethodException if no constructor matched was found
   */
  @SafeVarargs
  public static  T invokeFirstConstructor(Class cls, List... constructorArgs)
      throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

    for (List args : constructorArgs) {

      Class[] parameterTypes = new Class[args.size()];
      for (int i = 0; i < args.size(); i++) {
        parameterTypes[i] = args.get(i).getClass();
      }

      if (ConstructorUtils.getMatchingAccessibleConstructor(cls, parameterTypes) != null) {
        return ConstructorUtils.invokeConstructor(cls, args.toArray(new Object[args.size()]));
      }
    }
    throw new NoSuchMethodException("No accessible constructor found");
  }

  /**
   * Returns a new instance of the cls based on a set of arguments. The method will search for a
   * constructor accepting the first k arguments in args for every k from args.length to 0, and will
   * invoke the first constructor found.
   *
   * For example, {@link #invokeLongestConstructor}(cls, myString, myInt) will first attempt to create an object with
   * of class cls with constructor (String, int), if it fails it will attempt (String), and
   * finally ().
   *
   * @param cls the class to instantiate.
   * @param args the arguments to use for instantiation.
   * @throws ReflectiveOperationException
   */
  public static  T invokeLongestConstructor(Class cls, Object... args) throws ReflectiveOperationException {

    Class[] parameterTypes = new Class[args.length];
    for (int i = 0; i < args.length; i++) {
      parameterTypes[i] = args[i].getClass();
    }

    for (int i = args.length; i >= 0; i--) {
      if (ConstructorUtils.getMatchingAccessibleConstructor(cls, Arrays.copyOfRange(parameterTypes, 0, i)) != null) {
        log.debug(
            String.format("Found accessible constructor for class %s with parameter types %s.", cls,
                Arrays.toString(Arrays.copyOfRange(parameterTypes, 0, i))));
        return ConstructorUtils.invokeConstructor(cls, Arrays.copyOfRange(args, 0, i));
      }
    }
    throw new NoSuchMethodException(String.format("No accessible constructor for class %s with parameters a subset of %s.",
        cls, Arrays.toString(parameterTypes)));
  }

  /**
   * Utility method to create an instance of clsName using the constructor matching the arguments, args
   *
   * @param superType of clsName. The new instance is cast to superType
   * @param clsName complete cannonical name of the class to be instantiated
   * @param args constructor args to be used
   *
   * @throws IllegalArgumentException if there was an issue creating the instance due to
   * {@link NoSuchMethodException}, {@link InvocationTargetException},{@link InstantiationException},
   *  {@link ClassNotFoundException}
   *
   * @return A new instance of clsName
   */
  @SuppressWarnings("unchecked")
  public static  T invokeConstructor(final Class superType, final String clsName, Object... args) {

    try {
      return (T) ConstructorUtils.invokeConstructor(Class.forName(clsName), args);
    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException
        | ClassNotFoundException e) {
      throw new IllegalArgumentException(e);
    }
  }
}