content: /*
   * 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.bookkeeper.common.util;
  
  import java.lang.reflect.Constructor;
  import java.util.Map;
  import java.util.concurrent.ConcurrentHashMap;
  import org.apache.commons.configuration2.Configuration;
  import org.apache.commons.configuration2.ex.ConfigurationException;
  
  /**
   * General Class Reflection Utils.
   */
  public class ReflectionUtils {
  
      private static final Map<Class<?>, Constructor<?>> constructorCache =
              new ConcurrentHashMap<Class<?>, Constructor<?>>();
  
      /**
       * Returns the {@code Class} object object associated with the class or interface
       * with the given string name, which is a subclass of {@code xface}.
       *
       * @param className class name
       * @param xface class interface
       * @return the class object associated with the class or interface with the given string name.
       */
      public static <T> Class<? extends T> forName(String className,
                                                   Class<T> xface) {
  
          // Construct the class
          Class<?> theCls;
          try {
              theCls = Class.forName(className);
          } catch (ClassNotFoundException cnfe) {
              throw new RuntimeException(cnfe);
          }
          if (!xface.isAssignableFrom(theCls)) {
              throw new RuntimeException(className + " not " + xface.getName());
          }
          return theCls.asSubclass(xface);
      }
  
      /**
       * Get the value of the <code>name</code> property as a <code>Class</code>.
       * If no such property is specified, then <code>defaultCls</code> is returned.
       *
       * @param conf
       *          Configuration Object.
       * @param name
       *          Class Property Name.
       * @param defaultCls
       *          Default Class to be returned.
       * @param classLoader
       *          Class Loader to load class.
       * @return property value as a <code>Class</code>, or <code>defaultCls</code>.
       * @throws ConfigurationException
       */
      public static Class<?> getClass(Configuration conf, String name,
                                      Class<?> defaultCls, ClassLoader classLoader)
              throws ConfigurationException {
          String valueStr = conf.getString(name);
          if (null == valueStr) {
              return defaultCls;
          }
          try {
              return Class.forName(valueStr, true, classLoader);
          } catch (ClassNotFoundException cnfe) {
              throw new ConfigurationException(cnfe);
          }
      }
  
      /**
       * Get the value of the <code>name</code> property as a <code>Class</code> implementing
       * the interface specified by <code>xface</code>.
       *
       * <p>If no such property is specified, then <code>defaultValue</code> is returned.
       *
       * <p>An exception is thrown if the returned class does not implement the named interface.
       *
       * @param conf
       *          Configuration Object.
       * @param name
       *          Class Property Name.
       * @param defaultValue
       *          Default Class to be returned.
       * @param xface
       *          The interface implemented by the named class.
       * @param classLoader
       *          Class Loader to load class.
       * @return property value as a <code>Class</code>, or <code>defaultValue</code>.
       * @throws ConfigurationException
       */
      public static <T> Class<? extends T> getClass(Configuration conf,
                                                    String name, Class<? extends T> defaultValue,
                                                    Class<T> xface, ClassLoader classLoader)
          throws ConfigurationException {
          try {
              Class<?> theCls = getClass(conf, name, defaultValue, classLoader);
              if (null != theCls && !xface.isAssignableFrom(theCls)) {
                  throw new ConfigurationException(theCls + " not " + xface.getName());
              } else if (null != theCls) {
                  return theCls.asSubclass(xface);
              } else {
                  return null;
              }
          } catch (Exception e) {
              throw new ConfigurationException(e);
          }
      }
  
      /**
       * Create an object for the given class.
       *
       * @param theCls
       *          class of which an object is created.
       * @return a new object
       */
      @SuppressWarnings("unchecked")
      public static <T> T newInstance(Class<T> theCls) {
          T result;
          try {
              Constructor<T> meth = (Constructor<T>) constructorCache.get(theCls);
              if (null == meth) {
                  meth = theCls.getDeclaredConstructor();
                  meth.setAccessible(true);
                  constructorCache.put(theCls, meth);
              }
              result = meth.newInstance();
          } catch (Exception e) {
              throw new RuntimeException(e);
          }
          return result;
      }
  
      /**
       * Create an object using the given class name.
       *
       * @param clsName
       *          class name of which an object is created.
       * @param xface
       *          The interface implemented by the named class.
       * @return a new object
       */
      @SuppressWarnings("unchecked")
      public static <T> T newInstance(String clsName, Class<T> xface) {
          Class<?> theCls;
          try {
              theCls = Class.forName(clsName);
          } catch (ClassNotFoundException cnfe) {
              throw new RuntimeException(cnfe);
          }
          if (!xface.isAssignableFrom(theCls)) {
              throw new RuntimeException(clsName + " not " + xface.getName());
          }
          return newInstance(theCls.asSubclass(xface));
      }
  }
  