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

org.ldaptive.props.AbstractPropertyInvoker Maven / Gradle / Ivy

There is a newer version: 2.3.2
Show newest version
/*
  $Id: AbstractPropertyInvoker.java 2885 2014-02-05 21:28:49Z dfisher $

  Copyright (C) 2003-2014 Virginia Tech.
  All rights reserved.

  SEE LICENSE FOR MORE INFORMATION

  Author:  Middleware Services
  Email:   [email protected]
  Version: $Revision: 2885 $
  Updated: $Date: 2014-02-05 16:28:49 -0500 (Wed, 05 Feb 2014) $
*/
package org.ldaptive.props;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Provides methods common to property invokers.
 *
 * @author  Middleware Services
 * @version  $Revision: 2885 $ $Date: 2014-02-05 16:28:49 -0500 (Wed, 05 Feb 2014) $
 */
public abstract class AbstractPropertyInvoker implements PropertyInvoker
{

  /** Cache of properties. */
  private static final Map> PROPERTIES_CACHE =
    new HashMap>();

  /** Class to invoke methods on. */
  private Class clazz;

  /** Map of all properties to their getter and setter methods. */
  private Map properties;


  /**
   * Initializes the properties cache with the supplied class. The cache
   * contains a map of properties to an array of the setter and getter methods.
   * If a method named 'initialize' is found, it is also cached.
   *
   * @param  c  to read methods from
   */
  protected synchronized void initialize(final Class c)
  {
    final String cacheKey = c.getName();
    if (PROPERTIES_CACHE.containsKey(cacheKey)) {
      properties = PROPERTIES_CACHE.get(cacheKey);
    } else {
      properties = new HashMap();
      for (Method method : c.getMethods()) {
        if (!method.isBridge()) {
          if (
            method.getName().startsWith("set") &&
              method.getParameterTypes().length == 1) {
            final String mName = method.getName().substring(3);
            final String pName = mName.substring(0, 1).toLowerCase() +
              mName.substring(1, mName.length());
            if (properties.containsKey(pName)) {
              final Method[] m = properties.get(pName);
              m[1] = method;
              properties.put(pName, m);
            } else {
              properties.put(pName, new Method[] {null, method});
            }
          } else if (
            method.getName().startsWith("get") &&
              method.getParameterTypes().length == 0) {
            final String mName = method.getName().substring(3);
            final String pName = mName.substring(0, 1).toLowerCase() +
              mName.substring(1, mName.length());
            if (properties.containsKey(pName)) {
              final Method[] m = properties.get(pName);
              m[0] = method;
              properties.put(pName, m);
            } else {
              properties.put(pName, new Method[] {method, null});
            }
          } else if (
            method.getName().startsWith("is") &&
              method.getParameterTypes().length == 0) {
            final String mName = method.getName().substring(2);
            final String pName = mName.substring(0, 1).toLowerCase() +
              mName.substring(1, mName.length());
            if (properties.containsKey(pName)) {
              final Method[] m = properties.get(pName);
              // prefer any get method that may exist
              if (m[0] == null) {
                m[0] = method;
                properties.put(pName, m);
              }
            } else {
              properties.put(pName, new Method[] {method, null});
            }
          } else if (
            "initialize".equals(method.getName()) &&
              method.getParameterTypes().length == 0) {
            final String pName = method.getName();
            properties.put(pName, new Method[] {method, method});
          }
        }
      }

      // remove any properties that don't have both getters and setters
      final Iterator i = properties.values().iterator();
      while (i.hasNext()) {
        final Method[] m = i.next();
        if (m[0] == null || m[1] == null) {
          i.remove();
        }
      }

      PROPERTIES_CACHE.put(cacheKey, Collections.unmodifiableMap(properties));
    }
    clazz = c;
  }


  /**
   * This invokes the setter method for the supplied property name with the
   * supplied value.
   *
   * @param  object  to invoke method on
   * @param  name  of the property
   * @param  value  of the property
   *
   * @throws  IllegalArgumentException  if an invocation exception occurs
   */
  @Override
  public void setProperty(
    final Object object,
    final String name,
    final String value)
  {
    if (!clazz.isInstance(object)) {
      throw new IllegalArgumentException(
        "Illegal attempt to set property for class " + clazz.getName() +
        " on object of type " + object.getClass().getName());
    }

    final Method getter = properties.get(name) != null ? properties.get(name)[0]
                                                       : null;
    if (getter == null) {
      throw new IllegalArgumentException(
        "No getter method found for " + name + " on object " + clazz.getName());
    }

    final Method setter = properties.get(name) != null ? properties.get(name)[1]
                                                       : null;
    if (setter == null) {
      throw new IllegalArgumentException(
        "No setter method found for " + name + " on object " + clazz.getName());
    }

    invokeMethod(setter, object, convertValue(getter.getReturnType(), value));
  }


  /**
   * Converts the supplied string value into an Object of the supplied type. If
   * value cannot be converted it is returned as is.
   *
   * @param  type  of object to convert value into
   * @param  value  to parse
   *
   * @return  object of the supplied type
   */
  protected abstract Object convertValue(
    final Class type,
    final String value);


  /**
   * Returns whether the supplied property exists for this invoker.
   *
   * @param  name  to check
   *
   * @return  whether the supplied property exists
   */
  @Override
  public boolean hasProperty(final String name)
  {
    return properties.containsKey(name);
  }


  /**
   * Returns the property keys for this invoker.
   *
   * @return  set of property names
   */
  @Override
  public Set getProperties()
  {
    return Collections.unmodifiableSet(properties.keySet());
  }


  /**
   * Creates an instance of the supplied type.
   *
   * @param    type of class returned
   * @param  type  of class to create
   * @param  className  to create
   *
   * @return  class of type T
   *
   * @throws  IllegalArgumentException  if the supplied class name cannot create
   * a new instance of T
   */
  public static  T instantiateType(final T type, final String className)
  {
    try {
      try {
        final Class clazz = createClass(className);
        final Constructor con = clazz.getDeclaredConstructor((Class[]) null);
        @SuppressWarnings("unchecked") final T t = (T) con.newInstance();
        return t;
      } catch (NoSuchMethodException e) {
        throw new IllegalArgumentException(e);
      } catch (InvocationTargetException e) {
        throw new IllegalArgumentException(e);
      } catch (InstantiationException e) {
        throw new IllegalArgumentException(e);
      } catch (IllegalAccessException e) {
        throw new IllegalArgumentException(e);
      }
    } catch (RuntimeException e) {
      final Logger l = LoggerFactory.getLogger(AbstractPropertyInvoker.class);
      l.error("Error instantiating type {}, with {}", type, className);
      throw e;
    }
  }


  /**
   * Creates the class with the supplied name.
   *
   * @param  className  to create
   *
   * @return  class
   *
   * @throws  IllegalArgumentException  if the supplied class name cannot be
   * created
   */
  public static Class createClass(final String className)
  {
    try {
      return Class.forName(className);
    } catch (ClassNotFoundException e) {
      throw new IllegalArgumentException(
        String.format("Could not find class '%s'", className),
        e);
    }
  }


  /**
   * Converts simple types that are common to all property invokers. If value
   * cannot be converted it is returned as is.
   *
   * @param  type  of object to convert value into
   * @param  value  to parse
   *
   * @return  object of the supplied type
   */
  protected Object convertSimpleType(final Class type, final String value)
  {
    Object newValue = value;
    if (Class.class.isAssignableFrom(type)) {
      newValue = createTypeFromPropertyValue(Class.class, value);
    } else if (Class[].class.isAssignableFrom(type)) {
      newValue = createArrayTypeFromPropertyValue(Class.class, value);
    } else if (type.isEnum()) {
      newValue = getEnum(type, value);
    } else if (String[].class == type) {
      newValue = value.split(",");
    } else if (Object[].class == type) {
      newValue = value.split(",");
    } else if (float.class == type) {
      newValue = Float.parseFloat(value);
    } else if (int.class == type) {
      newValue = Integer.parseInt(value);
    } else if (long.class == type) {
      newValue = Long.parseLong(value);
    } else if (short.class == type) {
      newValue = Short.parseShort(value);
    } else if (double.class == type) {
      newValue = Double.parseDouble(value);
    } else if (boolean.class == type) {
      newValue = Boolean.valueOf(value);
    }
    return newValue;
  }


  /**
   * Returns the enum for the supplied type and value.
   *
   * @param  clazz  of the enum
   * @param  value  of the enum
   *
   * @return  enum that matches the supplied value
   */
  protected static Enum getEnum(final Class clazz, final String value)
  {
    for (Object o : clazz.getEnumConstants()) {
      final Enum e = (Enum) o;
      if (e.name().equals(value)) {
        return e;
      }
    }
    throw new IllegalArgumentException(
      String.format("Unknown enum value %s for %s", value, clazz));
  }


  /**
   * Returns the object which represents the supplied class given the supplied
   * string representation.
   *
   * @param  c  type to instantiate
   * @param  s  property value to parse
   *
   * @return  the supplied type or null
   */
  protected Object createTypeFromPropertyValue(final Class c, final String s)
  {
    Object newObject;
    if ("null".equals(s)) {
      newObject = null;
    } else {
      if (PropertyValueParser.isConfig(s)) {
        final PropertyValueParser configParser = new PropertyValueParser(s);
        newObject = configParser.initializeType();
      } else {
        if (Class.class == c) {
          newObject = createClass(s);
        } else {
          newObject = instantiateType(c, s);
        }
      }
    }
    return newObject;
  }


  /**
   * Returns the object which represents an array of the supplied class given
   * the supplied string representation.
   *
   * @param  c  type to instantiate
   * @param  s  property value to parse
   *
   * @return  an array or null
   */
  protected Object createArrayTypeFromPropertyValue(
    final Class c,
    final String s)
  {
    Object newObject;
    if ("null".equals(s)) {
      newObject = null;
    } else {
      if (s.contains("},")) {
        final String[] classes = s.split("\\},");
        newObject = Array.newInstance(c, classes.length);
        for (int i = 0; i < classes.length; i++) {
          classes[i] = classes[i] + "}";
          if (PropertyValueParser.isConfig(classes[i])) {
            final PropertyValueParser configParser = new PropertyValueParser(
              classes[i]);
            Array.set(newObject, i, configParser.initializeType());
          } else {
            throw new IllegalArgumentException(
              String.format("Could not parse property string: %s", classes[i]));
          }
        }
      } else {
        final String[] classes = s.split(",");
        newObject = Array.newInstance(c, classes.length);
        for (int i = 0; i < classes.length; i++) {
          if (PropertyValueParser.isConfig(classes[i])) {
            final PropertyValueParser configParser = new PropertyValueParser(
              classes[i]);
            Array.set(newObject, i, configParser.initializeType());
          } else {
            if (Class.class == c) {
              Array.set(newObject, i, createClass(classes[i]));
            } else {
              Array.set(newObject, i, instantiateType(c, classes[i]));
            }
          }
        }
      }
    }
    return newObject;
  }


  /**
   * Returns the enum array which represents the supplied class given the
   * supplied string representation.
   *
   * @param  c  type to instantiate
   * @param  s  property value to parse
   *
   * @return  Enum[] of the supplied type or null
   */
  protected Object createArrayEnumFromPropertyValue(
    final Class c,
    final String s)
  {
    Object newObject;
    if ("null".equals(s)) {
      newObject = null;
    } else {
      final String[] values = s.split(",");
      newObject = Array.newInstance(c, values.length);
      for (int i = 0; i < values.length; i++) {
        Array.set(newObject, i, getEnum(c, values[i]));
      }
    }
    return newObject;
  }


  /**
   * Invokes the supplied method on the supplied object with the supplied
   * argument.
   *
   * @param  method  to invoke
   * @param  object  to invoke method on
   * @param  arg  to invoke method with
   *
   * @return  object produced by the invocation
   *
   * @throws  IllegalArgumentException  if an error occurs invoking the method
   */
  public static Object invokeMethod(
    final Method method,
    final Object object,
    final Object arg)
  {
    try {
      try {
        Object[] params = new Object[] {arg};
        if (arg == null && method.getParameterTypes().length == 0) {
          params = (Object[]) null;
        }
        return method.invoke(object, params);
      } catch (InvocationTargetException e) {
        throw new IllegalArgumentException(e);
      } catch (IllegalAccessException e) {
        throw new IllegalArgumentException(e);
      }
    } catch (RuntimeException e) {
      final Logger l = LoggerFactory.getLogger(AbstractPropertyInvoker.class);
      l.error(
        "Error invoking {}, on {}, with params {}",
        new Object[] {method, object, arg});
      throw e;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy